From 1868a7d3705e4dc32b438ab24303052ed2cc9f09 Mon Sep 17 00:00:00 2001 From: ADS Merger Date: Wed, 8 Apr 2020 06:33:38 +0000 Subject: [PATCH 1/3] Merge from vscode 10492ba146318412cbee8b76a8c630f226914734 --- .eslintrc.json | 240 +++-- .github/workflows/ci.yml | 17 +- build/builtInExtensions.json | 7 - build/builtin/browser-main.js | 10 +- build/gulpfile.hygiene.js | 2 +- build/lib/builtInExtensions.js | 2 +- build/lib/extensions.js | 5 +- build/lib/extensions.ts | 6 +- build/package.json | 2 +- build/yarn.lock | 8 +- extensions/git/package.json | 31 +- extensions/git/src/timelineProvider.ts | 2 +- extensions/merge-conflict/package.nls.json | 4 +- extensions/python/cgmanifest.json | 2 +- .../syntaxes/MagicPython.tmLanguage.json | 2 +- extensions/sql/cgmanifest.json | 4 +- extensions/sql/syntaxes/sql.tmLanguage.json | 8 +- package.json | 2 +- product.json | 30 +- remote/package.json | 2 +- remote/yarn.lock | 8 +- src/main.js | 6 + .../ui/scrollableSplitview/heightMap.ts | 6 +- .../scrollableSplitview.ts | 10 +- src/sql/base/common/navigator.ts | 23 + .../api/common/extHostRequireInterceptor.ts | 2 +- .../electron-browser/commandLine.ts | 6 +- .../test/electron-browser/commandLine.test.ts | 2 +- .../dashboardWidgetWrapper.component.ts | 1 - .../browser/core/dashboardPage.component.ts | 1 - .../explorer/explorerWidget.component.ts | 1 - .../browser/dataExplorer.contribution.ts | 1 - .../test/browser}/jobActions.test.ts | 0 .../find/notebookFindDecorations.ts | 0 .../{ => browser}/find/notebookFindModel.ts | 2 +- .../{ => browser}/find/notebookFindWidget.ts | 0 .../browser/models/notebookFindModel.ts | 2 +- .../notebook/browser/models/notebookInput.ts | 2 +- .../notebook/browser/notebookActions.ts | 2 +- .../notebook/browser/notebookEditor.ts | 4 +- .../outputs/markdownOutput.component.ts | 4 - .../notebookFindModel.test.ts | 2 +- .../workbench/contrib/notebook/test/stubs.ts | 4 +- .../objectExplorer/test/browser/treeMock.ts | 2 +- .../contrib/query/browser/actions.ts | 4 +- .../query/browser/query.contribution.ts | 1 - .../contrib/webview/browser/webViewDialog.ts | 1 - .../welcome/page/browser/welcomePage.ts | 4 +- .../electron-browser/insightsUtils.test.ts | 6 +- .../browser/objectExplorerService.test.ts | 2 +- .../test/browser/restoreViewModel.test.ts | 2 +- src/vs/base/browser/canIUse.ts | 6 +- src/vs/base/browser/touch.ts | 4 +- src/vs/base/browser/ui/list/listView.ts | 3 +- src/vs/base/browser/ui/tree/asyncDataTree.ts | 24 +- .../ui/tree/compressedObjectTreeModel.ts | 39 +- src/vs/base/browser/ui/tree/dataTree.ts | 8 +- src/vs/base/browser/ui/tree/indexTree.ts | 4 +- src/vs/base/browser/ui/tree/indexTreeModel.ts | 18 +- src/vs/base/browser/ui/tree/objectTree.ts | 6 +- .../base/browser/ui/tree/objectTreeModel.ts | 24 +- src/vs/base/browser/ui/tree/tree.ts | 7 +- src/vs/base/common/date.ts | 4 + src/vs/base/common/filters.ts | 10 + src/vs/base/common/fuzzyScorer.ts | 40 +- src/vs/base/common/history.ts | 6 +- src/vs/base/common/iterator.ts | 325 +----- src/vs/base/common/labels.ts | 6 +- src/vs/base/common/map.ts | 161 ++- src/vs/base/common/navigator.ts | 50 + src/vs/base/common/resourceTree.ts | 11 +- src/vs/base/common/resources.ts | 25 +- src/vs/base/common/strings.ts | 22 +- src/vs/base/common/uri.ts | 8 +- src/vs/base/node/id.ts | 2 +- .../quickinput/browser/media/quickInput.css | 5 +- .../parts/quickinput/browser/quickInput.ts | 28 +- .../quickinput/browser/quickInputList.ts | 4 + src/vs/base/parts/tree/browser/tree.ts | 2 +- src/vs/base/parts/tree/browser/treeImpl.ts | 2 +- src/vs/base/parts/tree/browser/treeModel.ts | 2 +- src/vs/base/parts/tree/browser/treeView.ts | 15 +- .../base/parts/tree/browser/treeViewModel.ts | 10 +- .../tree/test/browser/treeViewModel.test.ts | 8 +- .../ui/tree/compressedObjectTreeModel.test.ts | 62 +- .../browser/ui/tree/indexTreeModel.test.ts | 121 ++- .../test/browser/ui/tree/objectTree.test.ts | 81 +- .../browser/ui/tree/objectTreeModel.test.ts | 35 +- src/vs/base/test/common/filters.test.ts | 30 + src/vs/base/test/common/iterator.test.ts | 14 +- src/vs/base/test/common/map.test.ts | 165 ++- src/vs/base/test/common/strings.test.ts | 21 + src/vs/base/test/common/uri.test.ts | 6 +- src/vs/code/browser/workbench/workbench.ts | 6 +- .../issue/issueReporterMain.ts | 7 +- .../issue/issueReporterModel.ts | 7 + .../issue/test/testReporterModel.test.ts | 53 +- .../contrib/languagePackCachedDataCleaner.ts | 6 +- .../contrib/nodeCachedDataCleaner.ts | 3 +- .../contrib/storageDataCleaner.ts | 3 +- .../sharedProcess/sharedProcessMain.ts | 16 +- src/vs/code/electron-main/app.ts | 10 +- src/vs/code/electron-main/main.ts | 36 +- src/vs/code/electron-main/sharedProcess.ts | 8 +- src/vs/code/electron-main/window.ts | 38 +- src/vs/code/node/cli.ts | 3 +- src/vs/code/node/cliProcessMain.ts | 18 +- src/vs/code/node/paths.ts | 2 +- src/vs/code/node/shellEnv.ts | 10 +- .../services/markerDecorationsServiceImpl.ts | 7 +- .../editor/common/view/editorColorRegistry.ts | 2 +- .../contrib/codelens/codelensController.ts | 4 +- .../contrib/documentSymbols/outlineTree.ts | 66 +- .../editor/contrib/hover/modesContentHover.ts | 47 +- .../backup/electron-main/backupMainService.ts | 3 +- .../test/common/testConfigurationService.ts | 2 +- .../diagnostics/common/diagnostics.ts | 8 + .../diagnostics/node/diagnosticsService.ts | 12 +- .../platform/driver/electron-main/driver.ts | 4 +- .../electron-main/electronMainService.ts | 7 +- src/vs/platform/electron/node/electron.ts | 4 +- .../environment/common/environment.ts | 162 +-- src/vs/platform/environment/node/argv.ts | 94 +- .../platform/environment/node/argvHelper.ts | 3 +- .../environment/node/environmentService.ts | 152 +-- .../common/extensionGalleryService.ts | 4 +- .../common/extensionManagement.ts | 15 + .../common/extensionManagementIpc.ts | 22 +- .../common/extensionManagementUtil.ts | 23 +- .../node/extensionLifecycle.ts | 4 +- .../node/extensionManagementService.ts | 19 +- .../node/extensionTipsService.ts | 122 +++ .../node/extensionsManifestCache.ts | 4 +- src/vs/platform/files/common/fileService.ts | 8 +- .../issue/common}/issueReporterUtil.ts | 0 .../issue/electron-main/issueMainService.ts | 3 +- .../launch/electron-main/launchMainService.ts | 6 +- .../lifecycle/common/lifecycleService.ts | 10 +- .../electron-main/lifecycleMainService.ts | 48 +- .../localizations/node/localizations.ts | 5 +- src/vs/platform/log/common/log.ts | 4 +- .../platform/menubar/electron-main/menubar.ts | 6 +- src/vs/platform/product/common/product.ts | 11 +- .../platform/product/common/productService.ts | 9 + .../common/serviceMachineId.ts | 32 +- src/vs/platform/state/node/stateService.ts | 3 +- .../storage/node/storageMainService.ts | 3 +- .../platform/storage/node/storageService.ts | 3 +- .../electron-main/abstractUpdateService.ts | 3 +- .../electron-main/updateService.darwin.ts | 3 +- .../electron-main/updateService.linux.ts | 3 +- .../electron-main/updateService.snap.ts | 5 +- .../electron-main/updateService.win32.ts | 3 +- .../url/electron-main/electronUrlListener.ts | 4 +- .../userDataSync/common/snippetsSync.ts | 3 +- .../common/userDataSyncEnablementService.ts | 2 +- .../common/userDataSyncStoreService.ts | 4 +- .../test/common/snippetsSync.test.ts | 62 ++ .../test/common/userDataSyncClient.ts | 3 +- src/vs/platform/windows/common/windows.ts | 87 +- .../platform/windows/electron-main/windows.ts | 6 +- .../electron-main/windowsMainService.ts | 14 +- src/vs/platform/windows/node/window.ts | 66 +- .../platform/windows/test/node/window.test.ts | 3 +- src/vs/platform/workspace/common/workspace.ts | 2 +- .../workspacesHistoryMainService.ts | 3 +- .../electron-main/workspacesMainService.ts | 3 +- src/vs/vscode.d.ts | 4 +- src/vs/vscode.proposed.d.ts | 24 +- .../api/browser/mainThreadAuthentication.ts | 31 +- .../workbench/api/common/extHost.api.impl.ts | 2 +- .../workbench/api/common/extHost.protocol.ts | 5 +- .../api/common/extHostAuthentication.ts | 28 +- .../api/common/extHostExtensionService.ts | 6 +- .../workbench/api/common/extHostNotebook.ts | 8 +- .../api/common/extHostRequireInterceptor.ts | 4 +- .../api/common/extHostTerminalService.ts | 23 +- .../workbench/api/common/extHostTimeline.ts | 2 +- .../api/common/shared/semanticTokensDto.ts | 4 +- .../browser/actions/media/actions.css | 2 +- src/vs/workbench/browser/media/part.css | 1 - src/vs/workbench/browser/panecomposite.ts | 12 +- .../parts/activitybar/activitybarActions.ts | 4 +- .../parts/activitybar/activitybarPart.ts | 9 +- .../workbench/browser/parts/compositeBar.ts | 10 +- .../browser/parts/compositeBarActions.ts | 2 +- .../workbench/browser/parts/compositePart.ts | 31 +- .../workbench/browser/parts/editor/editor.ts | 5 +- .../browser/parts/editor/editorPart.ts | 3 +- .../parts/editor/noTabsTitleControl.ts | 1 - .../browser/parts/editor/textEditor.ts | 4 +- .../parts/notifications/notificationsList.ts | 10 + .../browser/parts/panel/panelPart.ts | 16 +- .../browser/parts/sidebar/sidebarPart.ts | 17 +- .../browser/parts/statusbar/statusbarPart.ts | 15 +- .../browser/parts/titlebar/menubarControl.ts | 3 +- .../browser/parts/titlebar/titlebarPart.ts | 8 +- .../browser/parts/views/media/views.css | 7 +- .../browser/parts/views/viewPaneContainer.ts | 4 +- src/vs/workbench/browser/web.main.ts | 8 +- .../browser/workbench.contribution.ts | 7 +- src/vs/workbench/common/component.ts | 1 + src/vs/workbench/common/editor.ts | 3 +- src/vs/workbench/common/memento.ts | 1 + .../comments/browser/commentThreadWidget.ts | 6 +- .../comments/browser/commentsTreeViewer.ts | 22 + .../configurationExportHelper.contribution.ts | 5 +- .../configurationExportHelper.ts | 5 +- .../browser/webviewEditor.contribution.ts | 16 +- .../contrib/debug/browser/breakpointsView.ts | 43 +- .../debug/browser/debug.contribution.ts | 4 +- .../contrib/debug/browser/debugHover.ts | 3 +- .../contrib/debug/browser/linkDetector.ts | 9 +- .../debug/browser/loadedScriptsView.ts | 17 +- .../contrib/debug/browser/media/repl.css | 20 +- .../workbench/contrib/debug/browser/repl.ts | 25 +- .../debug/test/browser/breakpoints.test.ts | 6 +- .../extensions/browser/extensionEditor.ts | 22 +- ....ts => extensionRecommendationsService.ts} | 976 ++++++++---------- .../browser/extensions.contribution.ts | 8 +- .../extensions/browser/extensionsActions.ts | 6 +- .../extensions/browser/extensionsViews.ts | 6 +- .../extensions/browser/extensionsWidgets.ts | 8 +- .../browser/media/extensionEditor.css | 5 + .../extensions/common/extensionsUtils.ts | 6 +- .../extensions.contribution.ts | 3 +- .../electron-browser/extensionsActions.ts | 5 +- .../runtimeExtensionsEditor.ts | 10 +- ...> extensionRecommendationsService.test.ts} | 98 +- .../extensionsActions.test.ts | 6 +- .../electron-browser/extensionsViews.test.ts | 14 +- .../extensionsWorkbenchService.test.ts | 6 +- .../node/externalTerminalService.test.ts | 22 + .../node/externalTerminalService.ts | 4 + .../files/browser/views/openEditorsView.ts | 14 +- .../electron-browser/files.contribution.ts | 8 +- .../contrib/issue/browser/issueService.ts | 2 +- .../contrib/markers/browser/markersView.ts | 15 +- .../browser/contrib/notebookActions.ts | 106 +- .../notebook/browser/{ => media}/notebook.css | 5 +- .../notebook/browser/notebookEditor.ts | 2 +- .../notebook/browser/notebookEditorInput.ts | 4 +- .../notebook/browser/notebookService.ts | 30 +- .../notebook/browser/view/notebookCellList.ts | 4 +- .../browser/view/renderers/cellRenderer.ts | 8 +- .../browser/view/renderers/codeCell.ts | 28 +- .../viewModel/markdownCellViewModel.ts | 4 +- .../outline/browser/outline.contribution.ts | 3 +- .../outline/browser/outlineNavigation.ts | 202 ---- .../contrib/output/browser/media/output.css | 20 +- .../contrib/output/browser/outputView.ts | 2 +- .../electron-browser/startupProfiler.ts | 5 +- .../electron-browser/startupTimings.ts | 5 +- .../preferences/browser/keybindingsEditor.ts | 31 +- .../browser/preferences.contribution.ts | 2 +- .../preferences/browser/preferencesEditor.ts | 2 +- .../preferences/browser/settingsEditor2.ts | 8 +- .../contrib/preferences/browser/tocTree.ts | 7 +- .../contrib/scm/browser/repositoryPane.ts | 12 +- .../search/browser/anythingQuickAccess.ts | 27 +- .../contrib/search/browser/searchView.ts | 25 +- .../contrib/search/common/queryBuilder.ts | 9 +- .../contrib/search/common/searchModel.ts | 2 +- .../electron-browser/queryBuilder.test.ts | 4 +- .../electron-browser/workspaceTagsService.ts | 11 +- .../tasks/browser/abstractTaskService.ts | 36 +- .../contrib/tasks/common/taskService.ts | 2 +- .../terminal/browser/media/terminal.css | 19 +- .../terminal/browser/terminal.contribution.ts | 81 +- .../contrib/terminal/browser/terminal.ts | 10 +- .../terminal/browser/terminalActions.ts | 138 +-- .../terminal/browser/terminalConfigHelper.ts | 24 +- .../terminal/browser/terminalInstance.ts | 18 +- .../browser/terminalProcessManager.ts | 22 +- .../terminal/browser/terminalService.ts | 83 +- .../contrib/terminal/browser/terminalTab.ts | 5 +- .../contrib/terminal/browser/terminalView.ts | 25 +- .../terminal/browser/xterm-private.d.ts | 4 + .../common/environmentVariableCollection.ts | 14 +- .../contrib/terminal/common/terminal.ts | 8 + .../contrib/terminal/node/terminal.ts | 4 +- .../environmentVariableCollection.test.ts | 36 +- .../timeline/browser/media/timelinePane.css | 6 +- .../timeline/browser/timeline.contribution.ts | 1 + .../contrib/timeline/browser/timelinePane.ts | 115 ++- .../contrib/timeline/common/timeline.ts | 5 +- .../update/browser/releaseNotesEditor.ts | 4 +- .../contrib/update/browser/update.ts | 2 +- .../welcome/page/browser/welcomePage.ts | 4 +- .../actions/media/actions.css | 2 +- .../electron-browser/actions/windowActions.ts | 12 +- .../electron-browser/desktop.contribution.ts | 1 + .../electron-browser/desktop.main.ts | 6 +- src/vs/workbench/electron-browser/window.ts | 7 +- .../backup/electron-browser/backup.ts | 4 +- .../browser/configurationService.ts | 42 +- .../configuration/node/configurationCache.ts | 7 +- .../configurationEditingService.test.ts | 3 +- .../configurationService.test.ts | 12 +- .../browser/configurationResolverService.ts | 6 +- .../configurationResolverService.ts | 9 +- .../configurationResolverService.test.ts | 16 +- .../decorations/browser/decorationsService.ts | 32 +- .../dialogs/browser/simpleFileDialog.ts | 2 +- .../services/editor/browser/editorService.ts | 4 - .../test/browser/editorsObserver.test.ts | 6 +- .../environment/browser/environmentService.ts | 102 +- .../environment/common/environmentService.ts | 6 +- .../electron-browser/environmentService.ts | 14 +- .../common/extensionManagement.ts | 4 +- .../common/extensionTipsService.ts | 29 + .../electron-browser/extensionTipsService.ts | 37 + .../browser/webWorkerExtensionHostStarter.ts | 2 - .../cachedExtensionScanner.ts | 26 +- .../electron-browser/extensionHost.ts | 2 +- .../electron-browser/extensionHostProfiler.ts | 6 +- .../browserKeyboardMapper.test.ts | 4 +- .../keybindingEditing.test.ts | 1 - .../services/label/common/labelService.ts | 7 +- .../services/label/test/browser/label.test.ts | 4 +- .../services/path/common/remotePathService.ts | 27 +- .../progress/browser/progressService.ts | 24 +- .../quickinput/browser/quickInputService.ts | 4 +- .../remoteAgentServiceImpl.ts | 11 +- .../services/search/node/searchService.ts | 8 +- .../electron-browser/telemetryService.ts | 2 +- .../electron-browser/nativeTextFileService.ts | 20 +- .../themes/common/fileIconThemeSchema.ts | 2 +- .../themes/common/productIconThemeSchema.ts | 2 +- .../themes/common/themeConfiguration.ts | 6 +- .../abstractWorkspaceEditingService.ts | 12 +- .../test/browser/workbenchTestServices.ts | 11 +- .../test/common/api/semanticTokensDto.test.ts | 26 + .../electron-browser/workbenchTestServices.ts | 4 +- src/vs/workbench/workbench.desktop.main.ts | 3 +- src/vs/workbench/workbench.web.main.ts | 1 + test/automation/src/code.ts | 2 - test/unit/browser/index.js | 2 +- yarn.lock | 8 +- 339 files changed, 3795 insertions(+), 3146 deletions(-) delete mode 100644 build/builtInExtensions.json create mode 100644 src/sql/base/common/navigator.ts rename src/sql/workbench/{services/jobManagement/test/electron-browser => contrib/jobManagement/test/browser}/jobActions.test.ts (100%) rename src/sql/workbench/contrib/notebook/{ => browser}/find/notebookFindDecorations.ts (100%) rename src/sql/workbench/contrib/notebook/{ => browser}/find/notebookFindModel.ts (99%) rename src/sql/workbench/contrib/notebook/{ => browser}/find/notebookFindWidget.ts (100%) create mode 100644 src/vs/base/common/navigator.ts create mode 100644 src/vs/platform/extensionManagement/node/extensionTipsService.ts rename src/vs/{code/common/issue => platform/issue/common}/issueReporterUtil.ts (100%) rename src/vs/workbench/contrib/configExporter/{node => electron-browser}/configurationExportHelper.contribution.ts (83%) rename src/vs/workbench/contrib/configExporter/{node => electron-browser}/configurationExportHelper.ts (91%) rename src/vs/workbench/contrib/extensions/browser/{extensionTipsService.ts => extensionRecommendationsService.ts} (55%) rename src/vs/workbench/contrib/extensions/test/electron-browser/{extensionsTipsService.test.ts => extensionRecommendationsService.test.ts} (82%) rename src/vs/workbench/contrib/notebook/browser/{ => media}/notebook.css (98%) delete mode 100644 src/vs/workbench/contrib/outline/browser/outlineNavigation.ts create mode 100644 src/vs/workbench/services/extensionManagement/common/extensionTipsService.ts create mode 100644 src/vs/workbench/services/extensionManagement/electron-browser/extensionTipsService.ts diff --git a/.eslintrc.json b/.eslintrc.json index 30d84ee44b..dac79100b8 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -110,8 +110,8 @@ "target": "**/{vs,sql}/base/node/**", "restrictions": [ "vs/nls", - "**/{vs,sql}/base/{common,node}/**", - "!path" // node modules (except path where we have our own impl) + "**/{vs,sql}/base/{common,node}/**", + "*" // node modules ] }, { @@ -152,7 +152,7 @@ "vs/nls", "**/{vs,sql}/base/{common,node}/**", "**/{vs,sql}/base/parts/*/{common,node}/**", - "!path" // node modules (except path where we have our own impl) + "*" // node modules ] }, { @@ -162,9 +162,7 @@ "vs/css!./**/*", "**/{vs,sql}/base/{common,browser,node,electron-browser}/**", "**/{vs,sql}/base/parts/*/{common,browser,node,electron-browser}/**", - "!path", // node modules (except path where we have our own impl) - "@angular/*", - "rxjs/*" + "*" // node modules ] }, { @@ -173,9 +171,7 @@ "vs/nls", "**/{vs,sql}/base/{common,node,electron-main}/**", "**/{vs,sql}/base/parts/*/{common,node,electron-main}/**", - "!path", // node modules (except path where we have our own impl) - "@angular/*", - "rxjs/*" + "*" // node modules ] }, { @@ -217,12 +213,12 @@ { "target": "**/{vs,sql}/platform/*/node/**", "restrictions": [ - "vs/nls", - "azdata", + "vs/nls", + "azdata", "**/{vs,sql}/base/{common,node}/**", "**/{vs,sql}/base/parts/*/{common,node}/**", "**/{vs,sql}/platform/*/{common,node}/**", - "!path" // node modules (except path where we have our own impl) + "*" // node modules ] }, { @@ -234,18 +230,19 @@ "**/{vs,sql}/base/{common,browser,node}/**", "**/{vs,sql}/base/parts/*/{common,browser,node,electron-browser}/**", "**/{vs,sql}/platform/*/{common,browser,node,electron-browser}/**", - "!path" // node modules (except path where we have our own impl) + "*" // node modules ] }, { "target": "**/{vs,sql}/platform/*/electron-main/**", "restrictions": [ - "vs/nls", - "azdata", + "vs/nls", + "azdata", "**/{vs,sql}/base/{common,node,electron-main}/**", "**/{vs,sql}/base/parts/*/{common,node,electron-main}/**", "**/{vs,sql}/platform/*/{common,node,electron-main}/**", - "!path" // node modules (except path where we have our own impl) + "**/{vs,sql}/code/**", + "*" // node modules ] }, { @@ -420,12 +417,6 @@ "assert" ] }, - { - "target": "**/{vs,sql}/workbench/workbench.desktop.main.ts", - "restrictions": [ - "**" - ] - }, { "target": "**/{vs,sql}/workbench/api/common/**", "restrictions": [ @@ -463,7 +454,7 @@ "**/{vs,sql}/editor/contrib/**", // editor/contrib is equivalent to /browser/ by convention "**/{vs,sql}/workbench/{common,browser,node,electron-browser,api}/**", "**/{vs,sql}/workbench/services/*/{common,browser,node,electron-browser}/**", - "!path" // node modules (except path where we have our own impl) + "*" // node modules ] }, { @@ -478,7 +469,7 @@ "vs/workbench/contrib/files/common/editors/fileEditorInput", "**/{vs,sql}/workbench/services/**", "**/{vs,sql}/workbench/test/**", - "!path" // node modules (except path where we have our own impl) + "*" // node modules ] }, { @@ -541,7 +532,7 @@ "**/{vs,sql}/workbench/{common,node}/**", "**/{vs,sql}/workbench/api/{common,node}/**", "**/{vs,sql}/workbench/services/**/{common,node}/**", - "!path" // node modules (except path where we have our own impl) + "*" // node modules ] }, { @@ -554,7 +545,23 @@ "**/{vs,sql}/editor/**", "**/{vs,sql}/workbench/{common,browser,node,electron-browser,api}/**", "**/{vs,sql}/workbench/services/**/{common,browser,node,electron-browser}/**", - "!path" // node modules (except path where we have our own impl) + "*" // node modules + ] + }, + { + "target": "**/{vs,sql}/workbench/contrib/**/test/**", + "restrictions": [ + "assert", + "vs/nls", + "vs/css!./**/*", + "**/{vs,sql}/base/**", + "**/{vs,sql}/platform/**", + "**/{vs,sql}/editor/**", + "**/{vs,sql}/workbench/{common,browser,node,electron-browser}/**", + "**/{vs,sql}/workbench/services/**", + "**/{vs,sql}/workbench/contrib/**", + "**/{vs,sql}/workbench/test/**", + "**" ] }, { @@ -563,21 +570,109 @@ // xterm and its addons are strictly browser-only components "xterm", "xterm-addon-*", - "**/{vs,sql}/**" + "vs/nls", + "vs/css!./**/*", + "**/{vs,sql}/base/**/{common,browser}/**", + "**/{vs,sql}/platform/**/{common,browser}/**", + "**/{vs,sql}/editor/**", + "**/{vs,sql}/workbench/{common,browser}/**", + "**/{vs,sql}/workbench/contrib/**/{common,browser}/**", + "**/{vs,sql}/workbench/services/**/{common,browser}/**" ] }, { "target": "**/{vs,sql}/workbench/contrib/extensions/browser/**", "restrictions": [ "semver-umd", - "**/{vs,sql}/**" + "vs/nls", + "vs/css!./**/*", + "**/{vs,sql}/base/**/{common,browser}/**", + "**/{vs,sql}/platform/**/{common,browser}/**", + "**/{vs,sql}/editor/**", + "**/{vs,sql}/workbench/{common,browser}/**", + "**/{vs,sql}/workbench/contrib/**/{common,browser}/**", + "**/{vs,sql}/workbench/services/**/{common,browser}/**" ] }, { "target": "**/{vs,sql}/workbench/contrib/update/browser/update.ts", "restrictions": [ "semver-umd", - "**/{vs,sql}/**" + "vs/nls", + "vs/css!./**/*", + "**/{vs,sql}/base/**/{common,browser}/**", + "**/{vs,sql}/platform/**/{common,browser}/**", + "**/{vs,sql}/editor/**", + "**/{vs,sql}/workbench/{common,browser}/**", + "**/{vs,sql}/workbench/contrib/**/{common,browser}/**", + "**/{vs,sql}/workbench/services/**/{common,browser}/**" + ] + }, + { + "target": "**/{vs,sql}/workbench/contrib/**/common/**", + "restrictions": [ + "vs/nls", + "vs/css!./**/*", + "azdata", + "**/{vs,sql}/base/**/common/**", + "**/{vs,sql}/platform/**/common/**", + "**/{vs,sql}/editor/**", + "**/{vs,sql}/workbench/common/**", + "**/{vs,sql}/workbench/api/common/**", + "**/{vs,sql}/workbench/services/**/common/**", + "**/{vs,sql}/workbench/contrib/**/common/**" + ] + }, + { + "target": "**/{vs,sql}/workbench/contrib/**/browser/**", + "restrictions": [ + "vs/nls", + "vs/css!./**/*", + "azdata", + "vscode", + "**/{vs,sql}/base/**/{common,browser}/**", + "**/{vs,sql}/platform/**/{common,browser}/**", + "**/{vs,sql}/editor/**", + "**/{vs,sql}/workbench/{common,browser}/**", + "**/{vs,sql}/workbench/api/{common,browser}/**", + "**/{vs,sql}/workbench/services/**/{common,browser}/**", + "**/{vs,sql}/workbench/contrib/**/{common,browser}/**", + "@angular/*", + "rxjs/**", + "ng2-charts", + "chart.js", + "plotly.js-dist", + "angular2-grid", + "html-query-plan" + ] + }, + { + "target": "**/{vs,sql}/workbench/contrib/**/node/**", + "restrictions": [ + "vs/nls", + "vs/css!./**/*", + "**/{vs,sql}/base/**/{common,node}/**", + "**/{vs,sql}/platform/**/{common,node}/**", + "**/{vs,sql}/editor/**/common/**", + "**/{vs,sql}/workbench/{common,node}/**", + "**/{vs,sql}/workbench/api/{common,node}/**", + "**/{vs,sql}/workbench/services/**/{common,node}/**", + "**/{vs,sql}/workbench/contrib/**/{common,node}/**", + "*" // node modules + ] + }, + { + "target": "**/{vs,sql}/workbench/contrib/**/electron-browser/**", + "restrictions": [ + "vs/nls", + "vs/css!./**/*", + "**/{vs,sql}/base/**/{common,browser,worker,node,electron-browser}/**", + "**/{vs,sql}/platform/**/{common,browser,node,electron-browser}/**", + "**/{vs,sql}/editor/**", + "**/{vs,sql}/workbench/{common,browser,node,electron-browser,api}/**", + "**/{vs,sql}/workbench/services/**/{common,browser,node,electron-browser}/**", + "**/{vs,sql}/workbench/contrib/**/{common,browser,node,electron-browser}/**", + "*" // node modules ] }, { @@ -588,7 +683,7 @@ "**/{vs,sql}/base/parts/**/{common,node}/**", "**/{vs,sql}/platform/**/{common,node}/**", "**/{vs,sql}/code/**/{common,node}/**", - "!path" // node modules (except path where we have our own impl) + "*" // node modules ] }, { @@ -600,7 +695,7 @@ "**/{vs,sql}/base/parts/**/{common,browser,node,electron-browser}/**", "**/{vs,sql}/platform/**/{common,browser,node,electron-browser}/**", "**/{vs,sql}/code/**/{common,browser,node,electron-browser}/**", - "!path" // node modules (except path where we have our own impl) + "*" // node modules ] }, { @@ -611,7 +706,7 @@ "**/{vs,sql}/base/parts/**/{common,node,electron-main}/**", "**/{vs,sql}/platform/**/{common,node,electron-main}/**", "**/{vs,sql}/code/**/{common,node,electron-main}/**", - "!path" // node modules (except path where we have our own impl) + "*" // node modules ] }, { @@ -624,13 +719,9 @@ "**/{vs,sql}/workbench/**/{common,node}/**", "**/{vs,sql}/server/**", "**/{vs,sql}/code/**/{common,node}/**", - "!path" // node modules (except path where we have our own impl) + "*" // node modules ] }, - { - "target": "**/{node,electron-browser,electron-main}/**", - "restrictions": "**/*" - }, { "target": "**/extensions/**", "restrictions": "**/*" @@ -639,49 +730,78 @@ "target": "**/test/smoke/**", "restrictions": [ "**/test/smoke/**", - "*" + "*" // node modules ] }, { "target": "**/test/automation/**", "restrictions": [ "**/test/automation/**", - "*" + "*" // node modules ] }, { "target": "**/test/integration/**", "restrictions": [ "**/test/integration/**", - "*" + "*" // node modules ] }, { - "target": "{**/api/**.test.ts,}", - "restrictions": "{**/{vs,sql}/**,assert,sinon,crypto,vscode,typemoq,azdata}" - }, - { - "target": "{**/**.test.ts,**/test/**}", - "restrictions": "{**/{vs,sql}/**,assert,sinon,crypto,xterm*,vscode,typemoq,azdata}" - }, - { - "target": "**/browser/**", + "target": "**/api/**.test.ts", "restrictions": [ "**/{vs,sql}/**", - "azdata", - "vscode", - "@angular/*", - "rxjs/**", - "chart.js", - "ng2-charts", - "angular2-grid", - "html-query-plan", - "plotly.js-dist" + "assert", + "sinon", + "crypto", + "vscode", + "typemoq", + "azdata" ] }, { - "target": "**/{common,browser,workbench}/**", - "restrictions": ["**/{vs,sql}/**","azdata","vscode"] + "target": "**/{node,electron-browser,electron-main}/**/*.test.ts", + "restrictions": [ + "**/{vs,sql}/**", + "**" // node modules + ] + }, + { + "target": "**/{node,electron-browser,electron-main}/**/test/**", + "restrictions": [ + "**/{vs,sql}/**", + "*" // node modules + ] + }, + { + "target": "**/test/{node,electron-browser,electron-main}/**", + "restrictions": [ + "**/{vs,sql}/**", + "*" // node modules + ] + }, + { + "target": "**/**.test.ts", + "restrictions": [ + "**/{vs,sql}/**", + "assert", + "typemoq", + "sinon", + "crypto", + "xterm*", + "azdata" + ] + }, + { + "target": "**/test/**", + "restrictions": [ + "**/{vs,sql}/**", + "assert", + "typemoq", + "sinon", + "crypto", + "xterm*" + ] } ] }, diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f82be05653..73f0b6a91d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,13 +2,16 @@ name: CI on: push: - branches: - - master - - release/* - pull_request: - branches: - - master - - release/* + branches-ignore: + - '**' + # push: + # branches: + # - master + # - release/* + # pull_request: + # branches: + # - master + # - release/* jobs: linux: diff --git a/build/builtInExtensions.json b/build/builtInExtensions.json deleted file mode 100644 index 6bc27e5ac1..0000000000 --- a/build/builtInExtensions.json +++ /dev/null @@ -1,7 +0,0 @@ -[ - { - "name": "Microsoft.sqlservernotebook", - "version": "0.3.5", - "repo": "https://github.com/Microsoft/azuredatastudio" - } -] diff --git a/build/builtin/browser-main.js b/build/builtin/browser-main.js index 9d52b75fb6..db335a363e 100644 --- a/build/builtin/browser-main.js +++ b/build/builtin/browser-main.js @@ -9,8 +9,7 @@ const os = require('os'); const { remote } = require('electron'); const dialog = remote.dialog; -const productJsonPath = path.join(__dirname, '..', '..', 'product.json'); -const builtInExtensionsPath = path.join(__dirname, '..', 'builtInExtensions.json'); +const builtInExtensionsPath = path.join(__dirname, '..', '..', 'product.json'); const controlFilePath = path.join(os.homedir(), '.vscode-oss-dev', 'extensions', 'control.json'); function readJson(filePath) { @@ -51,7 +50,6 @@ function render(el, state) { } const ul = document.createElement('ul'); - const { quality } = readJson(productJsonPath); const { builtin, control } = state; for (const ext of builtin) { @@ -62,10 +60,6 @@ function render(el, state) { const name = document.createElement('code'); name.textContent = ext.name; - if (quality && ext.forQualities && !ext.forQualities.includes(quality)) { - name.textContent += ` (only on ${ext.forQualities.join(', ')})`; - } - li.appendChild(name); const form = document.createElement('form'); @@ -116,7 +110,7 @@ function render(el, state) { function main() { const el = document.getElementById('extensions'); - const builtin = readJson(builtInExtensionsPath); + const builtin = readJson(builtInExtensionsPath).builtInExtensions; let control; try { diff --git a/build/gulpfile.hygiene.js b/build/gulpfile.hygiene.js index 95bcc0c57a..d86f486ad3 100644 --- a/build/gulpfile.hygiene.js +++ b/build/gulpfile.hygiene.js @@ -201,7 +201,7 @@ const tsHygieneFilter = [ '!extensions/big-data-cluster/src/bigDataCluster/controller/apiGenerated.ts', // {{SQL CARBON EDIT}}, '!extensions/big-data-cluster/src/bigDataCluster/controller/tokenApiGenerated.ts', // {{SQL CARBON EDIT}}, '!src/vs/workbench/services/themes/common/textMateScopeMatcher.ts', // {{SQL CARBON EDIT}} skip this because we have no plans on touching this and its not ours - '!src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts' // {{SQL CARBON EDIT}} skip this because known issue + '!src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts' // {{SQL CARBON EDIT}} skip this because known issue ]; const copyrightHeaderLines = [ diff --git a/build/lib/builtInExtensions.js b/build/lib/builtInExtensions.js index 1fa5b2c5a1..708bbf38fc 100644 --- a/build/lib/builtInExtensions.js +++ b/build/lib/builtInExtensions.js @@ -18,7 +18,7 @@ const fancyLog = require('fancy-log'); const ansiColors = require('ansi-colors'); const root = path.dirname(path.dirname(__dirname)); -const builtInExtensions = require('../builtInExtensions.json'); +const builtInExtensions = JSON.parse(fs.readFileSync(path.join(__dirname, '../../product.json'), 'utf8')).builtInExtensions; const controlFilePath = path.join(os.homedir(), '.vscode-oss-dev', 'extensions', 'control.json'); const ENABLE_LOGGING = !process.env['VSCODE_BUILD_BUILTIN_EXTENSIONS_SILENCE_PLEASE']; diff --git a/build/lib/extensions.js b/build/lib/extensions.js index eaccdb3f33..be3abd23f4 100644 --- a/build/lib/extensions.js +++ b/build/lib/extensions.js @@ -28,7 +28,6 @@ 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 product = require('../../product.json'); function fromLocal(extensionPath) { const webpackFilename = path.join(extensionPath, 'extension.webpack.config.js'); const input = fs.existsSync(webpackFilename) @@ -189,7 +188,6 @@ const excludedExtensions = [ 'ms-vscode.node-debug', 'ms-vscode.node-debug2', 'integration-tests', - 'ms.vscode.js-debug-nightly' ]; // {{SQL CARBON EDIT}} const externalExtensions = [ @@ -215,8 +213,7 @@ const rebuildExtensions = [ 'big-data-cluster', 'mssql' ]; -const builtInExtensions = require('../builtInExtensions.json') - .filter(({ forQualities }) => { var _a; return !product.quality || ((_a = forQualities === null || forQualities === void 0 ? void 0 : forQualities.includes) === null || _a === void 0 ? void 0 : _a.call(forQualities, product.quality)) !== false; }); +const builtInExtensions = JSON.parse(fs.readFileSync(path.join(__dirname, '../../product.json'), 'utf8')).builtInExtensions; function packageLocalExtensionsStream() { const localExtensionDescriptions = glob.sync('extensions/*/package.json') .map(manifestPath => { diff --git a/build/lib/extensions.ts b/build/lib/extensions.ts index 294e5a3100..7ae12f2764 100644 --- a/build/lib/extensions.ts +++ b/build/lib/extensions.ts @@ -27,7 +27,6 @@ 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 product = require('../../product.json'); function fromLocal(extensionPath: string): Stream { const webpackFilename = path.join(extensionPath, 'extension.webpack.config.js'); @@ -223,7 +222,6 @@ const excludedExtensions = [ 'ms-vscode.node-debug', 'ms-vscode.node-debug2', 'integration-tests', // {{SQL CARBON EDIT}} - 'ms.vscode.js-debug-nightly' ]; // {{SQL CARBON EDIT}} @@ -256,12 +254,10 @@ interface IBuiltInExtension { name: string; version: string; repo: string; - forQualities?: ReadonlyArray; metadata: any; } -const builtInExtensions = (require('../builtInExtensions.json')) - .filter(({ forQualities }) => !product.quality || forQualities?.includes?.(product.quality) !== false); +const builtInExtensions: IBuiltInExtension[] = JSON.parse(fs.readFileSync(path.join(__dirname, '../../product.json'), 'utf8')).builtInExtensions; export function packageLocalExtensionsStream(): NodeJS.ReadWriteStream { const localExtensionDescriptions = (glob.sync('extensions/*/package.json')) diff --git a/build/package.json b/build/package.json index 919f97a342..49c78e63e4 100644 --- a/build/package.json +++ b/build/package.json @@ -42,7 +42,7 @@ "iconv-lite": "0.4.23", "mime": "^1.3.4", "minimatch": "3.0.4", - "minimist": "^1.2.2", + "minimist": "^1.2.3", "request": "^2.85.0", "rollup": "^1.20.3", "rollup-plugin-commonjs": "^10.1.0", diff --git a/build/yarn.lock b/build/yarn.lock index 96ceca1441..ba4022b2f4 100644 --- a/build/yarn.lock +++ b/build/yarn.lock @@ -2450,10 +2450,10 @@ 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.2: - version "1.2.2" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.2.tgz#b00a00230a1108c48c169e69a291aafda3aacd63" - integrity sha512-rIqbOrKb8GJmx/5bc2M0QchhUouMXSpd1RTclXsB41JdL+VtnojfaJR+h7F9k18/4kHUsBFgk80Uk+q569vjPA== +minimist@^1.1.0, minimist@^1.2.0, minimist@^1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.3.tgz#3db5c0765545ab8637be71f333a104a965a9ca3f" + integrity sha512-+bMdgqjMN/Z77a6NlY/I3U5LlRDbnmaAk6lDveAPKwSpcPM4tKAuYsvYF8xjhOPXhOYGe/73vVLVez5PW+jqhw== minimist@~0.0.1: version "0.0.10" diff --git a/extensions/git/package.json b/extensions/git/package.json index 572fb1692a..9ff99819cd 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -1001,6 +1001,31 @@ "command": "git.ignore", "when": "scmProvider == git && scmResourceGroup == workingTree", "group": "1_modification@3" + }, + { + "command": "git.stage", + "when": "scmProvider == git && scmResourceGroup == untracked", + "group": "1_modification" + }, + { + "command": "git.stage", + "when": "scmProvider == git && scmResourceGroup == untracked", + "group": "inline" + }, + { + "command": "git.clean", + "when": "scmProvider == git && scmResourceGroup == untracked", + "group": "1_modification" + }, + { + "command": "git.clean", + "when": "scmProvider == git && scmResourceGroup == untracked", + "group": "inline" + }, + { + "command": "git.ignore", + "when": "scmProvider == git && scmResourceGroup == untracked", + "group": "1_modification@3" } ], "scm/resourceState/context": [ @@ -1237,17 +1262,17 @@ "timeline/item/context": [ { "command": "git.timeline.openDiff", - "group": "1_timeline", + "group": "1_actions", "when": "config.git.enabled && !git.missing && timelineItem =~ /git:file\\b/" }, { "command": "git.timeline.copyCommitId", - "group": "2_timeline@1", + "group": "5_copy@1", "when": "config.git.enabled && !git.missing && timelineItem =~ /git:file:commit\\b/" }, { "command": "git.timeline.copyCommitMessage", - "group": "2_timeline@2", + "group": "5_copy@2", "when": "config.git.enabled && !git.missing && timelineItem =~ /git:file:commit\\b/" } ] diff --git a/extensions/git/src/timelineProvider.ts b/extensions/git/src/timelineProvider.ts index 59d9186bf3..ad000b86a7 100644 --- a/extensions/git/src/timelineProvider.ts +++ b/extensions/git/src/timelineProvider.ts @@ -227,6 +227,6 @@ export class GitTimelineProvider implements TimelineProvider { @debounce(500) private fireChanged() { - this._onDidChange.fire({ reset: true }); + this._onDidChange.fire(undefined); } } diff --git a/extensions/merge-conflict/package.nls.json b/extensions/merge-conflict/package.nls.json index 3310dac7e2..3353f40b31 100644 --- a/extensions/merge-conflict/package.nls.json +++ b/extensions/merge-conflict/package.nls.json @@ -14,10 +14,10 @@ "command.compare": "Compare Current Conflict", "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.codeLensEnabled": "Create a CodeLens 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/python/cgmanifest.json b/extensions/python/cgmanifest.json index 37a21b2de5..97a9d1d111 100644 --- a/extensions/python/cgmanifest.json +++ b/extensions/python/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "MagicStack/MagicPython", "repositoryUrl": "https://github.com/MagicStack/MagicPython", - "commitHash": "c9b3409deb69acec31bbf7913830e93a046b30cc" + "commitHash": "0b09c1fca238d22e15ac5712d03f9bf6da626f9c" } }, "license": "MIT", diff --git a/extensions/python/syntaxes/MagicPython.tmLanguage.json b/extensions/python/syntaxes/MagicPython.tmLanguage.json index bf5277fdc1..a6920a0630 100644 --- a/extensions/python/syntaxes/MagicPython.tmLanguage.json +++ b/extensions/python/syntaxes/MagicPython.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/MagicStack/MagicPython/commit/c9b3409deb69acec31bbf7913830e93a046b30cc", + "version": "https://github.com/MagicStack/MagicPython/commit/0b09c1fca238d22e15ac5712d03f9bf6da626f9c", "name": "MagicPython", "scopeName": "source.python", "patterns": [ diff --git a/extensions/sql/cgmanifest.json b/extensions/sql/cgmanifest.json index 7abcea36e5..859d678224 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": "a79741f76fd33bd137a8c28172c9750b978309b6" + "commitHash": "a542fe96780e6b274adb281810d419a512fb5bb4" } }, "license": "MIT", - "version": "1.6.0" + "version": "1.9.0" } ], "version": 1 diff --git a/extensions/sql/syntaxes/sql.tmLanguage.json b/extensions/sql/syntaxes/sql.tmLanguage.json index b10b1a6605..2c12fd5076 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/a79741f76fd33bd137a8c28172c9750b978309b6", + "version": "https://github.com/Microsoft/vscode-mssql/commit/a542fe96780e6b274adb281810d419a512fb5bb4", "name": "SQL", "scopeName": "source.sql", "patterns": [ @@ -404,7 +404,7 @@ } }, "comment": "this is faster than the next begin/end rule since sub-pattern will match till end-of-line and SQL files tend to have very long lines.", - "match": "(N)?(')[^']*(')", + "match": "(N)?(')(?:[^'\\\\]|\\\\.)*(')", "name": "string.quoted.single.sql" }, { @@ -437,7 +437,7 @@ } }, "comment": "this is faster than the next begin/end rule since sub-pattern will match till end-of-line and SQL files tend to have very long lines.", - "match": "(`)[^`\\\\]*(`)", + "match": "(`)(?:[^`\\\\]|\\\\.)*(`)", "name": "string.quoted.other.backtick.sql" }, { @@ -470,7 +470,7 @@ } }, "comment": "this is faster than the next begin/end rule since sub-pattern will match till end-of-line and SQL files tend to have very long lines.", - "match": "(\")[^\"#]*(\")", + "match": "(\")(?:[^\"#\\\\]|\\\\.)*(\")", "name": "string.quoted.double.sql" }, { diff --git a/package.json b/package.json index e32ad49b2d..1d5353a6fc 100644 --- a/package.json +++ b/package.json @@ -62,7 +62,7 @@ "native-keymap": "2.1.1", "native-watchdog": "1.3.0", "ng2-charts": "^1.6.0", - "node-pty": "^0.10.0-beta2", + "node-pty": "0.10.0-beta7", "onigasm-umd": "2.2.5", "plotly.js-dist": "^1.48.3", "reflect-metadata": "^0.1.8", diff --git a/product.json b/product.json index 9784515ad3..7052477516 100644 --- a/product.json +++ b/product.json @@ -60,28 +60,16 @@ ] }, "extensionAllowedProposedApi": [ - "ms-vscode.references-view", - "ms-vsliveshare.vsliveshare", - "ms-vsliveshare.cloudenv", - "ms-vsliveshare.cloudenv-explorer", - "GitHub.vscode-pull-request-github", - "GitHub.vscode-pull-request-github-insiders", - "Microsoft.vscode-nmake-tools", - "ms-vscode-remote.remote-containers", - "ms-vscode-remote.remote-containers-nightly", - "ms-vscode-remote.remote-ssh", - "ms-vscode-remote.remote-ssh-nightly", - "ms-vscode-remote.remote-ssh-edit", - "ms-vscode-remote.remote-ssh-edit-nightly", - "ms-vscode-remote.remote-ssh-explorer", - "ms-vscode-remote.remote-ssh-explorer-nightly", - "ms-vscode-remote.remote-wsl", - "ms-vscode-remote.remote-wsl-nightly", - "ms-vscode-remote.vscode-remote-extensionpack", - "ms-vscode-remote.vscode-remote-extensionpack-nightly", - "ms-azuretools.vscode-docker" + "ms-vscode.references-view" ], "extensionsGallery": { "serviceUrl": "https://sqlopsextensions.blob.core.windows.net/marketplace/v1/extensionsGallery.json" - } + }, + "builtInExtensions": [ + { + "name": "Microsoft.sqlservernotebook", + "version": "0.3.5", + "repo": "https://github.com/Microsoft/azuredatastudio" + } + ] } diff --git a/remote/package.json b/remote/package.json index 1d81b26309..141dc0d7fc 100644 --- a/remote/package.json +++ b/remote/package.json @@ -26,7 +26,7 @@ "minimist": "^1.2.5", "native-watchdog": "1.3.0", "ng2-charts": "^1.6.0", - "node-pty": "^0.10.0-beta2", + "node-pty": "0.10.0-beta7", "onigasm-umd": "2.2.5", "reflect-metadata": "^0.1.8", "rxjs": "5.4.0", diff --git a/remote/yarn.lock b/remote/yarn.lock index 8dee1187bf..5bc4e357ee 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -498,10 +498,10 @@ node-addon-api@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.10.0-beta2: - version "0.10.0-beta2" - resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.10.0-beta2.tgz#6fd0d2fbbe881869e4e19795a05c557ac958da81" - integrity sha512-IU2lzlPUZ+gKG7pHJjzBHpnuwPTxWGgT3iyQicZfdL7dwLvP5cm00QxavAXCInBmRkOMhvM4aBSKvfzqQnCDBA== +node-pty@0.10.0-beta7: + version "0.10.0-beta7" + resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.10.0-beta7.tgz#7e383b2d1fe2f34509b57187f5a9a6ff90c46111" + integrity sha512-oC2VyIz9YaIvv6lWjAPZbUzmhLW1ouFmxOogNRNQrKeUzUi2yM/QRmybs+dW/Mhd3V89Yh61Ml0J5yuWiMIBbw== dependencies: nan "^2.14.0" diff --git a/src/main.js b/src/main.js index 287e693b36..f02b10901e 100644 --- a/src/main.js +++ b/src/main.js @@ -64,6 +64,12 @@ const nodeCachedDataDir = getNodeCachedDir(); // Configure static command line arguments const argvConfig = configureCommandlineSwitchesSync(args); +// Remove env set by snap https://github.com/microsoft/vscode/issues/85344 +if (process.env['SNAP']) { + delete process.env['GDK_PIXBUF_MODULE_FILE']; + delete process.env['GDK_PIXBUF_MODULEDIR']; +} + /** * Support user defined locale: load it early before app('ready') * to have more things running in parallel. diff --git a/src/sql/base/browser/ui/scrollableSplitview/heightMap.ts b/src/sql/base/browser/ui/scrollableSplitview/heightMap.ts index 48278b7d1d..1ae3701c4f 100644 --- a/src/sql/base/browser/ui/scrollableSplitview/heightMap.ts +++ b/src/sql/base/browser/ui/scrollableSplitview/heightMap.ts @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { INextIterator } from 'vs/base/common/iterator'; +import { INavigator } from 'vs/base/common/navigator'; export interface IView { id?: string; @@ -31,7 +31,7 @@ export class HeightMap { return !last ? 0 : last.top + last.height; } - public onInsertItems(iterator: INextIterator, afterItemId: string | null = null): number | undefined { + public onInsertItems(iterator: INavigator, afterItemId: string | null = null): number | undefined { let viewItem: IViewItem | null = null; let i: number, j: number; let totalSize: number; @@ -89,7 +89,7 @@ export class HeightMap { } // Contiguous items - public onRemoveItems(iterator: INextIterator): void { + public onRemoveItems(iterator: INavigator): void { let itemId: string | null = null; let viewItem: IViewItem; let startIndex: number | null = null; diff --git a/src/sql/base/browser/ui/scrollableSplitview/scrollableSplitview.ts b/src/sql/base/browser/ui/scrollableSplitview/scrollableSplitview.ts index 5490159d90..1beea6c142 100644 --- a/src/sql/base/browser/ui/scrollableSplitview/scrollableSplitview.ts +++ b/src/sql/base/browser/ui/scrollableSplitview/scrollableSplitview.ts @@ -14,7 +14,7 @@ import { clamp } from 'vs/base/common/numbers'; import { range, firstIndex, pushToStart } from 'vs/base/common/arrays'; import { Sash, Orientation, ISashEvent as IBaseSashEvent } from 'vs/base/browser/ui/sash/sash'; import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; -import { ArrayIterator } from 'vs/base/common/iterator'; +import { ArrayNavigator } from 'vs/base/common/navigator'; import { mixin } from 'vs/base/common/objects'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import { ISplitViewStyles, Sizing } from 'vs/base/browser/ui/splitview/splitview'; @@ -239,7 +239,7 @@ export class ScrollableSplitView extends HeightMap implements IDisposable { if (container.parentElement) { this.viewContainer.removeChild(container); } - this.onRemoveItems(new ArrayIterator([item.view.id!])); + this.onRemoveItems(new ArrayNavigator([item.view.id!])); }); const disposable = combinedDisposable(onChangeDisposable, containerDisposable); @@ -268,7 +268,7 @@ export class ScrollableSplitView extends HeightMap implements IDisposable { const item: IViewItem = { onAdd, onRemove, view, container, size: viewSize, layout, disposable, height: viewSize, top: 0, width: 0 }; this.viewItems.splice(currentIndex, 0, item); - this.onInsertItems(new ArrayIterator([item]), currentIndex > 0 ? this.viewItems[currentIndex - 1].view.id : undefined); + this.onInsertItems(new ArrayNavigator([item]), currentIndex > 0 ? this.viewItems[currentIndex - 1].view.id : undefined); // Add sash if (this.options.enableResizing && this.viewItems.length > 1) { @@ -342,7 +342,7 @@ export class ScrollableSplitView extends HeightMap implements IDisposable { if (container.parentElement) { this.viewContainer.removeChild(container); } - this.onRemoveItems(new ArrayIterator([item.view.id!])); + this.onRemoveItems(new ArrayNavigator([item.view.id!])); }); const disposable = combinedDisposable(onChangeDisposable, containerDisposable); @@ -371,7 +371,7 @@ export class ScrollableSplitView extends HeightMap implements IDisposable { const item: IViewItem = { onAdd, onRemove, view, container, size: viewSize, layout, disposable, height: viewSize, top: 0, width: 0 }; this.viewItems.splice(index, 0, item); - this.onInsertItems(new ArrayIterator([item]), index > 0 ? this.viewItems[index - 1].view.id : undefined); + this.onInsertItems(new ArrayNavigator([item]), index > 0 ? this.viewItems[index - 1].view.id : undefined); // Add sash if (this.options.enableResizing && this.viewItems.length > 1) { diff --git a/src/sql/base/common/navigator.ts b/src/sql/base/common/navigator.ts new file mode 100644 index 0000000000..a1b1eeb08e --- /dev/null +++ b/src/sql/base/common/navigator.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 { INavigator as vsINavigator } from 'vs/base/common/navigator'; + +export interface INavigator extends vsINavigator { + parent(): T | null; +} + +export class MappedNavigator implements INavigator { + + constructor(protected navigator: INavigator, private fn: (item: T | null) => R) { + } + + current() { return this.fn(this.navigator.current()); } + previous() { return this.fn(this.navigator.previous()); } + first() { return this.fn(this.navigator.first()); } + last() { return this.fn(this.navigator.last()); } + next() { return this.fn(this.navigator.next()); } + parent() { return this.fn(this.navigator.parent()); } +} diff --git a/src/sql/workbench/api/common/extHostRequireInterceptor.ts b/src/sql/workbench/api/common/extHostRequireInterceptor.ts index 1798ca6394..9433e14bbf 100644 --- a/src/sql/workbench/api/common/extHostRequireInterceptor.ts +++ b/src/sql/workbench/api/common/extHostRequireInterceptor.ts @@ -20,7 +20,7 @@ export class AzdataNodeModuleFactory implements INodeModuleFactory { constructor( private readonly _apiFactory: IAzdataExtensionApiFactory, - private readonly _extensionPaths: TernarySearchTree, + private readonly _extensionPaths: TernarySearchTree, private readonly _logService: ILogService ) { } diff --git a/src/sql/workbench/contrib/commandLine/electron-browser/commandLine.ts b/src/sql/workbench/contrib/commandLine/electron-browser/commandLine.ts index 19032e6ef4..db61fa59f0 100644 --- a/src/sql/workbench/contrib/commandLine/electron-browser/commandLine.ts +++ b/src/sql/workbench/contrib/commandLine/electron-browser/commandLine.ts @@ -10,7 +10,7 @@ import { ConnectionProfileGroup } from 'sql/platform/connection/common/connectio import { equalsIgnoreCase } from 'vs/base/common/strings'; import { IConnectionManagementService, IConnectionCompletionOptions, ConnectionType, RunQueryOnConnectionMode } from 'sql/platform/connection/common/connectionManagement'; import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService'; -import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment'; +import { ParsedArgs } from 'vs/platform/environment/node/argv'; import * as Constants from 'sql/platform/connection/common/constants'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { ICommandService } from 'vs/platform/commands/common/commands'; @@ -29,6 +29,8 @@ import { IURLService, IURLHandler } from 'vs/platform/url/common/url'; import { getErrorMessage } from 'vs/base/common/errors'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { find } from 'vs/base/common/arrays'; +import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; const connectAuthority = 'connect'; @@ -48,7 +50,7 @@ export class CommandLineWorkbenchContribution implements IWorkbenchContribution, constructor( @ICapabilitiesService private readonly _capabilitiesService: ICapabilitiesService, @IConnectionManagementService private readonly _connectionManagementService: IConnectionManagementService, - @IEnvironmentService environmentService: IEnvironmentService, + @IEnvironmentService environmentService: INativeEnvironmentService, @IEditorService private readonly _editorService: IEditorService, @ICommandService private readonly _commandService: ICommandService, @IConfigurationService private readonly _configurationService: IConfigurationService, diff --git a/src/sql/workbench/contrib/commandLine/test/electron-browser/commandLine.test.ts b/src/sql/workbench/contrib/commandLine/test/electron-browser/commandLine.test.ts index 85bc3133d4..175ce78a2d 100644 --- a/src/sql/workbench/contrib/commandLine/test/electron-browser/commandLine.test.ts +++ b/src/sql/workbench/contrib/commandLine/test/electron-browser/commandLine.test.ts @@ -10,7 +10,7 @@ import { ConnectionProfile } from 'sql/platform/connection/common/connectionProf import { ConnectionProfileGroup } from 'sql/platform/connection/common/connectionProfileGroup'; import { CommandLineWorkbenchContribution } from 'sql/workbench/contrib/commandLine/electron-browser/commandLine'; import * as Constants from 'sql/platform/connection/common/constants'; -import { ParsedArgs } from 'vs/platform/environment/common/environment'; +import { ParsedArgs } from 'vs/platform/environment/node/argv'; import { ICapabilitiesService } from 'sql/platform/capabilities/common/capabilitiesService'; import { TestCapabilitiesService } from 'sql/platform/capabilities/test/common/testCapabilitiesService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; diff --git a/src/sql/workbench/contrib/dashboard/browser/contents/dashboardWidgetWrapper.component.ts b/src/sql/workbench/contrib/dashboard/browser/contents/dashboardWidgetWrapper.component.ts index 7a5931c2f9..923834fee5 100644 --- a/src/sql/workbench/contrib/dashboard/browser/contents/dashboardWidgetWrapper.component.ts +++ b/src/sql/workbench/contrib/dashboard/browser/contents/dashboardWidgetWrapper.component.ts @@ -3,7 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!sql/media/icons/common-icons'; import 'vs/css!./dashboardWidgetWrapper'; import { diff --git a/src/sql/workbench/contrib/dashboard/browser/core/dashboardPage.component.ts b/src/sql/workbench/contrib/dashboard/browser/core/dashboardPage.component.ts index dcdcea3574..2694812395 100644 --- a/src/sql/workbench/contrib/dashboard/browser/core/dashboardPage.component.ts +++ b/src/sql/workbench/contrib/dashboard/browser/core/dashboardPage.component.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./dashboardPage'; -import 'vs/css!sql/media/icons/common-icons'; import 'sql/workbench/contrib/dashboard/browser/core/dashboardPanelStyles'; import { Component, Inject, forwardRef, ViewChild, ElementRef, ViewChildren, QueryList, ChangeDetectorRef } from '@angular/core'; diff --git a/src/sql/workbench/contrib/dashboard/browser/widgets/explorer/explorerWidget.component.ts b/src/sql/workbench/contrib/dashboard/browser/widgets/explorer/explorerWidget.component.ts index 470187e678..d31444bb42 100644 --- a/src/sql/workbench/contrib/dashboard/browser/widgets/explorer/explorerWidget.component.ts +++ b/src/sql/workbench/contrib/dashboard/browser/widgets/explorer/explorerWidget.component.ts @@ -3,7 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!sql/media/icons/common-icons'; import 'vs/css!./media/explorerWidget'; import { Component, Inject, forwardRef, OnInit, ViewChild, ElementRef, ChangeDetectorRef } from '@angular/core'; diff --git a/src/sql/workbench/contrib/dataExplorer/browser/dataExplorer.contribution.ts b/src/sql/workbench/contrib/dataExplorer/browser/dataExplorer.contribution.ts index 5b1e8a7c26..1258cc2732 100644 --- a/src/sql/workbench/contrib/dataExplorer/browser/dataExplorer.contribution.ts +++ b/src/sql/workbench/contrib/dataExplorer/browser/dataExplorer.contribution.ts @@ -3,7 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!sql/media/actionBarLabel'; import 'vs/css!./media/dataExplorer.contribution'; import { localize } from 'vs/nls'; import { ViewletRegistry, Extensions as ViewletExtensions } from 'vs/workbench/browser/viewlet'; diff --git a/src/sql/workbench/services/jobManagement/test/electron-browser/jobActions.test.ts b/src/sql/workbench/contrib/jobManagement/test/browser/jobActions.test.ts similarity index 100% rename from src/sql/workbench/services/jobManagement/test/electron-browser/jobActions.test.ts rename to src/sql/workbench/contrib/jobManagement/test/browser/jobActions.test.ts diff --git a/src/sql/workbench/contrib/notebook/find/notebookFindDecorations.ts b/src/sql/workbench/contrib/notebook/browser/find/notebookFindDecorations.ts similarity index 100% rename from src/sql/workbench/contrib/notebook/find/notebookFindDecorations.ts rename to src/sql/workbench/contrib/notebook/browser/find/notebookFindDecorations.ts diff --git a/src/sql/workbench/contrib/notebook/find/notebookFindModel.ts b/src/sql/workbench/contrib/notebook/browser/find/notebookFindModel.ts similarity index 99% rename from src/sql/workbench/contrib/notebook/find/notebookFindModel.ts rename to src/sql/workbench/contrib/notebook/browser/find/notebookFindModel.ts index f8c13d5a55..70a89c31cb 100644 --- a/src/sql/workbench/contrib/notebook/find/notebookFindModel.ts +++ b/src/sql/workbench/contrib/notebook/browser/find/notebookFindModel.ts @@ -8,7 +8,7 @@ import { ICellModel, INotebookModel } from 'sql/workbench/services/notebook/brow import { INotebookFindModel } from 'sql/workbench/contrib/notebook/browser/models/notebookFindModel'; import { Event, Emitter } from 'vs/base/common/event'; import * as types from 'vs/base/common/types'; -import { NotebookFindMatch, NotebookFindDecorations } from 'sql/workbench/contrib/notebook/find/notebookFindDecorations'; +import { NotebookFindMatch, NotebookFindDecorations } from 'sql/workbench/contrib/notebook/browser/find/notebookFindDecorations'; import * as model from 'vs/editor/common/model'; import { ModelDecorationOptions, DidChangeDecorationsEmitter, createTextBuffer } from 'vs/editor/common/model/textModel'; import { IModelDecorationsChangedEvent } from 'vs/editor/common/model/textModelEvents'; diff --git a/src/sql/workbench/contrib/notebook/find/notebookFindWidget.ts b/src/sql/workbench/contrib/notebook/browser/find/notebookFindWidget.ts similarity index 100% rename from src/sql/workbench/contrib/notebook/find/notebookFindWidget.ts rename to src/sql/workbench/contrib/notebook/browser/find/notebookFindWidget.ts diff --git a/src/sql/workbench/contrib/notebook/browser/models/notebookFindModel.ts b/src/sql/workbench/contrib/notebook/browser/models/notebookFindModel.ts index 8748becadf..a1033d184c 100644 --- a/src/sql/workbench/contrib/notebook/browser/models/notebookFindModel.ts +++ b/src/sql/workbench/contrib/notebook/browser/models/notebookFindModel.ts @@ -5,7 +5,7 @@ import { Event } from 'vs/base/common/event'; import { IModelDecorationsChangeAccessor } from 'vs/editor/common/model'; -import { NotebookFindMatch } from 'sql/workbench/contrib/notebook/find/notebookFindDecorations'; +import { NotebookFindMatch } from 'sql/workbench/contrib/notebook/browser/find/notebookFindDecorations'; import { NotebookRange } from 'sql/workbench/services/notebook/browser/notebookService'; export interface INotebookFindModel { diff --git a/src/sql/workbench/contrib/notebook/browser/models/notebookInput.ts b/src/sql/workbench/contrib/notebook/browser/models/notebookInput.ts index 67a7355331..b2b346d4f0 100644 --- a/src/sql/workbench/contrib/notebook/browser/models/notebookInput.ts +++ b/src/sql/workbench/contrib/notebook/browser/models/notebookInput.ts @@ -31,7 +31,7 @@ import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/u import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput'; import { BinaryEditorModel } from 'vs/workbench/common/editor/binaryEditorModel'; -import { NotebookFindModel } from 'sql/workbench/contrib/notebook/find/notebookFindModel'; +import { NotebookFindModel } from 'sql/workbench/contrib/notebook/browser/find/notebookFindModel'; import { onUnexpectedError } from 'vs/base/common/errors'; export type ModeViewSaveHandler = (handle: number) => Thenable; diff --git a/src/sql/workbench/contrib/notebook/browser/notebookActions.ts b/src/sql/workbench/contrib/notebook/browser/notebookActions.ts index cd2aa2c88f..595ea961bd 100644 --- a/src/sql/workbench/contrib/notebook/browser/notebookActions.ts +++ b/src/sql/workbench/contrib/notebook/browser/notebookActions.ts @@ -21,7 +21,7 @@ import { ICommandService } from 'vs/platform/commands/common/commands'; import { CellType } from 'sql/workbench/services/notebook/common/contracts'; import { getErrorMessage } from 'vs/base/common/errors'; import { IEditorAction } from 'vs/editor/common/editorCommon'; -import { IFindNotebookController } from 'sql/workbench/contrib/notebook/find/notebookFindWidget'; +import { IFindNotebookController } from 'sql/workbench/contrib/notebook/browser/find/notebookFindWidget'; import { INotebookModel } from 'sql/workbench/services/notebook/browser/models/modelInterfaces'; import { IObjectExplorerService } from 'sql/workbench/services/objectExplorer/browser/objectExplorerService'; import { TreeUpdateUtils } from 'sql/workbench/services/objectExplorer/browser/treeUpdateUtils'; diff --git a/src/sql/workbench/contrib/notebook/browser/notebookEditor.ts b/src/sql/workbench/contrib/notebook/browser/notebookEditor.ts index b75bf77ba3..a00e9f305b 100644 --- a/src/sql/workbench/contrib/notebook/browser/notebookEditor.ts +++ b/src/sql/workbench/contrib/notebook/browser/notebookEditor.ts @@ -16,7 +16,7 @@ import { NotebookModule } from 'sql/workbench/contrib/notebook/browser/notebook. import { NOTEBOOK_SELECTOR } from 'sql/workbench/contrib/notebook/browser/notebook.component'; import { INotebookParams, INotebookService, NotebookRange } from 'sql/workbench/services/notebook/browser/notebookService'; import { IStorageService } from 'vs/platform/storage/common/storage'; -import { ACTION_IDS, NOTEBOOK_MAX_MATCHES, IFindNotebookController, FindWidget, IConfigurationChangedEvent } from 'sql/workbench/contrib/notebook/find/notebookFindWidget'; +import { ACTION_IDS, NOTEBOOK_MAX_MATCHES, IFindNotebookController, FindWidget, IConfigurationChangedEvent } from 'sql/workbench/contrib/notebook/browser/find/notebookFindWidget'; import { IOverlayWidget } from 'vs/editor/browser/editorBrowser'; import { FindReplaceState, FindReplaceStateChangedEvent } from 'vs/editor/contrib/find/findState'; import { IEditorAction } from 'vs/editor/common/editorCommon'; @@ -30,7 +30,7 @@ import { INotebookModel } from 'sql/workbench/services/notebook/browser/models/m import { INotebookFindModel } from 'sql/workbench/contrib/notebook/browser/models/notebookFindModel'; import { IDisposable, DisposableStore, dispose } from 'vs/base/common/lifecycle'; import { IModelDecorationsChangeAccessor, IModelDeltaDecoration } from 'vs/editor/common/model'; -import { NotebookFindDecorations } from 'sql/workbench/contrib/notebook/find/notebookFindDecorations'; +import { NotebookFindDecorations } from 'sql/workbench/contrib/notebook/browser/find/notebookFindDecorations'; import { TimeoutTimer } from 'vs/base/common/async'; import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor'; import { onUnexpectedError } from 'vs/base/common/errors'; diff --git a/src/sql/workbench/contrib/notebook/browser/outputs/markdownOutput.component.ts b/src/sql/workbench/contrib/notebook/browser/outputs/markdownOutput.component.ts index 7ddf110c8f..e3b839f23b 100644 --- a/src/sql/workbench/contrib/notebook/browser/outputs/markdownOutput.component.ts +++ b/src/sql/workbench/contrib/notebook/browser/outputs/markdownOutput.component.ts @@ -3,10 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!../cellViews/textCell'; -import 'vs/css!../cellViews/media/markdown'; -import 'vs/css!../cellViews/media/highlight'; - import { OnInit, Component, Input, Inject, ElementRef, ViewChild } from '@angular/core'; import { AngularDisposable } from 'sql/base/browser/lifecycle'; import { IMimeComponent } from 'sql/workbench/contrib/notebook/browser/outputs/mimeRegistry'; diff --git a/src/sql/workbench/contrib/notebook/test/electron-browser/notebookFindModel.test.ts b/src/sql/workbench/contrib/notebook/test/electron-browser/notebookFindModel.test.ts index d441ad38ca..9a6af3e1ac 100644 --- a/src/sql/workbench/contrib/notebook/test/electron-browser/notebookFindModel.test.ts +++ b/src/sql/workbench/contrib/notebook/test/electron-browser/notebookFindModel.test.ts @@ -12,7 +12,7 @@ import { CellTypes } from 'sql/workbench/services/notebook/common/contracts'; import { IClientSession, INotebookModelOptions } from 'sql/workbench/services/notebook/browser/models/modelInterfaces'; import { NotebookModel } from 'sql/workbench/services/notebook/browser/models/notebookModel'; import { NullLogService } from 'vs/platform/log/common/log'; -import { NotebookFindModel } from 'sql/workbench/contrib/notebook/find/notebookFindModel'; +import { NotebookFindModel } from 'sql/workbench/contrib/notebook/browser/find/notebookFindModel'; import { TestConnectionManagementService } from 'sql/platform/connection/test/common/testConnectionManagementService'; import { Deferred } from 'sql/base/common/promise'; import { ModelFactory } from 'sql/workbench/services/notebook/browser/models/modelFactory'; diff --git a/src/sql/workbench/contrib/notebook/test/stubs.ts b/src/sql/workbench/contrib/notebook/test/stubs.ts index 06ecd6c44c..7832b80a6c 100644 --- a/src/sql/workbench/contrib/notebook/test/stubs.ts +++ b/src/sql/workbench/contrib/notebook/test/stubs.ts @@ -12,10 +12,10 @@ import { INotebookManager, INotebookService, INotebookEditor, ILanguageMagic, IN import { ISingleNotebookEditOperation } from 'sql/workbench/api/common/sqlExtHostTypes'; import { IStandardKernelWithProvider } from 'sql/workbench/services/notebook/browser/models/notebookUtils'; import { IModelDecorationsChangeAccessor } from 'vs/editor/common/model'; -import { NotebookFindMatch } from 'sql/workbench/contrib/notebook/find/notebookFindDecorations'; -import { URI } from 'vs/workbench/workbench.web.api'; +import { NotebookFindMatch } from 'sql/workbench/contrib/notebook/browser/find/notebookFindDecorations'; import { RenderMimeRegistry } from 'sql/workbench/services/notebook/browser/outputs/registry'; import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile'; +import { URI } from 'vs/base/common/uri'; export class NotebookModelStub implements INotebookModel { constructor(private _languageInfo?: nb.ILanguageInfo) { diff --git a/src/sql/workbench/contrib/objectExplorer/test/browser/treeMock.ts b/src/sql/workbench/contrib/objectExplorer/test/browser/treeMock.ts index cb89477b3b..5c4472051b 100644 --- a/src/sql/workbench/contrib/objectExplorer/test/browser/treeMock.ts +++ b/src/sql/workbench/contrib/objectExplorer/test/browser/treeMock.ts @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { Event } from 'vs/base/common/event'; -import { INavigator } from 'vs/base/common/iterator'; +import { INavigator } from 'sql/base/common/navigator'; import { ITree, IHighlightEvent, ISelectionEvent, IFocusEvent, ITreeStyles } from 'vs/base/parts/tree/browser/tree'; import { IItemExpandEvent, IItemCollapseEvent } from 'vs/base/parts/tree/browser/treeModel'; diff --git a/src/sql/workbench/contrib/query/browser/actions.ts b/src/sql/workbench/contrib/query/browser/actions.ts index 51134f532e..d6750501f4 100644 --- a/src/sql/workbench/contrib/query/browser/actions.ts +++ b/src/sql/workbench/contrib/query/browser/actions.ts @@ -8,7 +8,6 @@ import { localize } from 'vs/nls'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { ITree } from 'vs/base/parts/tree/browser/tree'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { IExtensionTipsService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { Table } from 'sql/base/browser/ui/table/table'; import { QueryEditor } from './queryEditor'; import { CellSelectionModel } from 'sql/base/browser/ui/table/plugins/cellSelectionModel.plugin'; @@ -23,6 +22,7 @@ import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry'; import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys'; import { getErrorMessage } from 'vs/base/common/errors'; import { SaveFormat } from 'sql/workbench/services/query/common/resultSerializer'; +import { IExtensionRecommendationsService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; export interface IGridActionContext { gridDataProvider: IGridDataProvider; @@ -212,7 +212,7 @@ export class ChartDataAction extends Action { constructor( @IEditorService private editorService: IEditorService, - @IExtensionTipsService private readonly extensionTipsService: IExtensionTipsService + @IExtensionRecommendationsService private readonly extensionTipsService: IExtensionRecommendationsService ) { super(ChartDataAction.ID, ChartDataAction.LABEL, ChartDataAction.ICON); } diff --git a/src/sql/workbench/contrib/query/browser/query.contribution.ts b/src/sql/workbench/contrib/query/browser/query.contribution.ts index dfa729a034..0ef2cbb1e3 100644 --- a/src/sql/workbench/contrib/query/browser/query.contribution.ts +++ b/src/sql/workbench/contrib/query/browser/query.contribution.ts @@ -3,7 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!sql/media/overwriteVsIcons'; import { Registry } from 'vs/platform/registry/common/platform'; import { EditorDescriptor, IEditorRegistry, Extensions as EditorExtensions } from 'vs/workbench/browser/editor'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; diff --git a/src/sql/workbench/contrib/webview/browser/webViewDialog.ts b/src/sql/workbench/contrib/webview/browser/webViewDialog.ts index 50e1e2aeaa..7506567f7a 100644 --- a/src/sql/workbench/contrib/webview/browser/webViewDialog.ts +++ b/src/sql/workbench/contrib/webview/browser/webViewDialog.ts @@ -3,7 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!sql/media/icons/common-icons'; import { Button } from 'sql/base/browser/ui/button/button'; import { Modal } from 'sql/workbench/browser/modal/modal'; import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys'; diff --git a/src/sql/workbench/contrib/welcome/page/browser/welcomePage.ts b/src/sql/workbench/contrib/welcome/page/browser/welcomePage.ts index eb04393d87..5dce6183fe 100644 --- a/src/sql/workbench/contrib/welcome/page/browser/welcomePage.ts +++ b/src/sql/workbench/contrib/welcome/page/browser/welcomePage.ts @@ -24,7 +24,7 @@ import { Schemas } from 'vs/base/common/network'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { getInstalledExtensions, IExtensionStatus, onExtensionChanged, isKeymapExtension } from 'vs/workbench/contrib/extensions/common/extensionsUtils'; import { IExtensionManagementService, IExtensionGalleryService, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionTipsService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionRecommendationsService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { ILifecycleService, StartupKind } from 'vs/platform/lifecycle/common/lifecycle'; import { Disposable } from 'vs/base/common/lifecycle'; import { splitName } from 'vs/base/common/labels'; @@ -204,7 +204,7 @@ class WelcomePage extends Disposable { @IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService, @IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService, @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, - @IExtensionTipsService private readonly tipsService: IExtensionTipsService, + @IExtensionRecommendationsService private readonly tipsService: IExtensionRecommendationsService, @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, @ILifecycleService lifecycleService: ILifecycleService, @ITelemetryService private readonly telemetryService: ITelemetryService, diff --git a/src/sql/workbench/services/insights/test/electron-browser/insightsUtils.test.ts b/src/sql/workbench/services/insights/test/electron-browser/insightsUtils.test.ts index 2e85db2a41..6980bdfc47 100644 --- a/src/sql/workbench/services/insights/test/electron-browser/insightsUtils.test.ts +++ b/src/sql/workbench/services/insights/test/electron-browser/insightsUtils.test.ts @@ -179,9 +179,8 @@ suite('Insights Utils tests', function () { const environmentService = new MockWorkbenchEnvironmentService({ TEST_PATH: queryFileDir }); // Create mock window service with env variable containing test folder for resolution - const configurationResolverService = new TestConfigurationResolverService(environmentService.userEnv, + const configurationResolverService = new TestConfigurationResolverService({ getExecPath: () => undefined }, environmentService.userEnv, undefined, - environmentService, undefined, undefined, undefined, @@ -209,9 +208,8 @@ suite('Insights Utils tests', function () { const environmentService = new MockWorkbenchEnvironmentService({ TEST_PATH: queryFileDir }); // Create mock window service with env variable containing test folder for resolution - const configurationResolverService = new TestConfigurationResolverService(environmentService.userEnv, + const configurationResolverService = new TestConfigurationResolverService({ getExecPath: () => undefined }, environmentService.userEnv, undefined, - environmentService, undefined, undefined, undefined, diff --git a/src/sql/workbench/services/objectExplorer/test/browser/objectExplorerService.test.ts b/src/sql/workbench/services/objectExplorer/test/browser/objectExplorerService.test.ts index 988100bd75..b5d1da749e 100644 --- a/src/sql/workbench/services/objectExplorer/test/browser/objectExplorerService.test.ts +++ b/src/sql/workbench/services/objectExplorer/test/browser/objectExplorerService.test.ts @@ -12,7 +12,6 @@ import { TreeNode, TreeItemCollapsibleState } from 'sql/workbench/services/objec import * as azdata from 'azdata'; import * as TypeMoq from 'typemoq'; import * as assert from 'assert'; -import { ConnectionOptionSpecialType, ServiceOptionType } from 'sql/workbench/api/common/sqlExtHostTypes'; import { Event } from 'vs/base/common/event'; import { mssqlProviderName } from 'sql/platform/connection/common/constants'; import { NullLogService } from 'vs/platform/log/common/log'; @@ -21,6 +20,7 @@ import { TestConnectionManagementService } from 'sql/platform/connection/test/co import { TestCapabilitiesService } from 'sql/platform/capabilities/test/common/testCapabilitiesService'; import { find } from 'vs/base/common/arrays'; import { NullAdsTelemetryService } from 'sql/platform/telemetry/common/adsTelemetryService'; +import { ConnectionOptionSpecialType, ServiceOptionType } from 'sql/platform/connection/common/interfaces'; suite('SQL Object Explorer Service tests', () => { let sqlOEProvider: TypeMoq.Mock; diff --git a/src/sql/workbench/services/restore/test/browser/restoreViewModel.test.ts b/src/sql/workbench/services/restore/test/browser/restoreViewModel.test.ts index 08fc0b8e5f..32f93e16e9 100644 --- a/src/sql/workbench/services/restore/test/browser/restoreViewModel.test.ts +++ b/src/sql/workbench/services/restore/test/browser/restoreViewModel.test.ts @@ -6,7 +6,7 @@ import * as azdata from 'azdata'; import * as assert from 'assert'; import { RestoreViewModel } from 'sql/workbench/services/restore/browser/restoreViewModel'; -import { ServiceOptionType } from 'sql/workbench/api/common/sqlExtHostTypes'; +import { ServiceOptionType } from 'sql/platform/connection/common/interfaces'; suite('Restore Dialog view model tests', () => { let option1String = 'option1'; diff --git a/src/vs/base/browser/canIUse.ts b/src/vs/base/browser/canIUse.ts index a1729e784b..2d2df8496c 100644 --- a/src/vs/base/browser/canIUse.ts +++ b/src/vs/base/browser/canIUse.ts @@ -51,6 +51,8 @@ export const BrowserFeatures = { return KeyboardSupport.None; })(), - touch: 'ontouchstart' in window || navigator.maxTouchPoints > 0 || window.navigator.msMaxTouchPoints > 0, - pointerEvents: window.PointerEvent && ('ontouchstart' in window || window.navigator.maxTouchPoints > 0 || navigator.maxTouchPoints > 0 || window.navigator.msMaxTouchPoints > 0) + // 'ontouchstart' in window always evaluates to true with typescript's modern typings. This causes `window` to be + // `never` later in `window.navigator`. That's why we need the explicit `window as Window` cast + touch: 'ontouchstart' in window || navigator.maxTouchPoints > 0 || (window as Window).navigator.msMaxTouchPoints > 0, + pointerEvents: window.PointerEvent && ('ontouchstart' in window || (window as Window).navigator.maxTouchPoints > 0 || navigator.maxTouchPoints > 0 || (window as Window).navigator.msMaxTouchPoints > 0) }; diff --git a/src/vs/base/browser/touch.ts b/src/vs/base/browser/touch.ts index ee3e7436da..fc0f20ca87 100644 --- a/src/vs/base/browser/touch.ts +++ b/src/vs/base/browser/touch.ts @@ -131,7 +131,9 @@ export class Gesture extends Disposable { @memoize private static isTouchDevice(): boolean { - return 'ontouchstart' in window || navigator.maxTouchPoints > 0 || window.navigator.msMaxTouchPoints > 0; + // `'ontouchstart' in window` always evaluates to true with typescript's modern typings. This causes `window` to be + // `never` later in `window.navigator`. That's why we need the explicit `window as Window` cast + return 'ontouchstart' in window || navigator.maxTouchPoints > 0 || (window as Window).navigator.msMaxTouchPoints > 0; } public dispose(): void { diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 379f8a0fcf..49dfcd9620 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -1041,9 +1041,10 @@ export class ListView implements ISpliceable, IDisposable { // Util private getItemIndexFromEventTarget(target: EventTarget | null): number | undefined { + const scrollableElement = this.scrollableElement.getDomNode(); let element: HTMLElement | null = target as (HTMLElement | null); - while (element instanceof HTMLElement && element !== this.rowsContainer) { + while (element instanceof HTMLElement && element !== this.rowsContainer && scrollableElement.contains(element)) { const rawIndex = element.getAttribute('data-index'); if (rawIndex) { diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index b247a9fcdd..d8dad4a731 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -11,7 +11,7 @@ import { IDisposable, dispose, DisposableStore } from 'vs/base/common/lifecycle' import { Emitter, Event } from 'vs/base/common/event'; import { timeout, CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; import { IListStyles } from 'vs/base/browser/ui/list/listWidget'; -import { Iterator } from 'vs/base/common/iterator'; +import { Iterable } from 'vs/base/common/iterator'; import { IDragAndDropData } from 'vs/base/browser/dnd'; import { ElementsDragAndDropData } from 'vs/base/browser/ui/list/listView'; import { isPromiseCanceledError, onUnexpectedError } from 'vs/base/common/errors'; @@ -313,7 +313,7 @@ export class AsyncDataTree implements IDisposable private readonly collapseByDefault?: { (e: T): boolean; }; private readonly subTreeRefreshPromises = new Map, Promise>(); - private readonly refreshPromises = new Map, CancelablePromise>(); + private readonly refreshPromises = new Map, CancelablePromise>>(); protected readonly identityProvider?: IIdentityProvider; private readonly autoExpandSingleChildren: boolean; @@ -740,10 +740,10 @@ export class AsyncDataTree implements IDisposable private async doRefreshNode(node: IAsyncDataTreeNode, recursive: boolean, viewStateContext?: IAsyncDataTreeViewStateContext): Promise[]> { node.hasChildren = !!this.dataSource.hasChildren(node.element!); - let childrenPromise: Promise; + let childrenPromise: Promise>; if (!node.hasChildren) { - childrenPromise = Promise.resolve([]); + childrenPromise = Promise.resolve(Iterable.empty()); } else { const slowTimeout = timeout(800); @@ -777,7 +777,7 @@ export class AsyncDataTree implements IDisposable } } - private doGetChildren(node: IAsyncDataTreeNode): Promise { + private doGetChildren(node: IAsyncDataTreeNode): Promise> { let result = this.refreshPromises.get(node); if (result) { @@ -809,7 +809,9 @@ export class AsyncDataTree implements IDisposable } } - private setChildren(node: IAsyncDataTreeNode, childrenElements: T[], recursive: boolean, viewStateContext?: IAsyncDataTreeViewStateContext): IAsyncDataTreeNode[] { + private setChildren(node: IAsyncDataTreeNode, childrenElementsIterable: Iterable, recursive: boolean, viewStateContext?: IAsyncDataTreeViewStateContext): IAsyncDataTreeNode[] { + const childrenElements = [...childrenElementsIterable]; + // perf: if the node was and still is a leaf, avoid all this hassle if (node.children.length === 0 && childrenElements.length === 0) { return []; @@ -943,15 +945,15 @@ export class AsyncDataTree implements IDisposable return { element: node, - children: node.hasChildren ? Iterator.map(Iterator.fromArray(node.children), child => this.asTreeElement(child, viewStateContext)) : [], + children: node.hasChildren ? Iterable.map(node.children, child => this.asTreeElement(child, viewStateContext)) : [], collapsible: node.hasChildren, collapsed }; } - protected processChildren(children: T[]): T[] { + protected processChildren(children: Iterable): Iterable { if (this.sorter) { - children.sort(this.sorter.compare.bind(this.sorter)); + children = [...children].sort(this.sorter.compare.bind(this.sorter)); } return children; @@ -1243,9 +1245,9 @@ export class CompressibleAsyncDataTree extends As // For compressed async data trees, `TreeVisibility.Recurse` doesn't currently work // and we have to filter everything beforehand // Related to #85193 and #85835 - protected processChildren(children: T[]): T[] { + protected processChildren(children: Iterable): Iterable { if (this.filter) { - children = children.filter(e => { + children = Iterable.filter(children, e => { const result = this.filter!.filter(e, TreeVisibility.Visible); const visibility = getVisibility(result); diff --git a/src/vs/base/browser/ui/tree/compressedObjectTreeModel.ts b/src/vs/base/browser/ui/tree/compressedObjectTreeModel.ts index e624ff634e..daf79ea061 100644 --- a/src/vs/base/browser/ui/tree/compressedObjectTreeModel.ts +++ b/src/vs/base/browser/ui/tree/compressedObjectTreeModel.ts @@ -4,14 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import { ISpliceable } from 'vs/base/common/sequence'; -import { Iterator, ISequence } from 'vs/base/common/iterator'; +import { Iterable } from 'vs/base/common/iterator'; import { Event } from 'vs/base/common/event'; import { ITreeModel, ITreeNode, ITreeElement, ICollapseStateChangeEvent, ITreeModelSpliceEvent, TreeError, TreeFilterResult, TreeVisibility, WeakMapper } from 'vs/base/browser/ui/tree/tree'; import { IObjectTreeModelOptions, ObjectTreeModel, IObjectTreeModel } from 'vs/base/browser/ui/tree/objectTreeModel'; // Exported only for test reasons, do not use directly export interface ICompressedTreeElement extends ITreeElement { - readonly children?: ISequence>; + readonly children?: Iterable>; readonly incompressible?: boolean; } @@ -27,7 +27,7 @@ function noCompress(element: ICompressedTreeElement): ITreeElement(element: ICompressedTreeElement): ITreeElement>; + let childrenIterator: Iterable>; let children: ITreeElement[]; while (true) { - childrenIterator = Iterator.from(element.children); - children = Iterator.collect(childrenIterator, 2); + [children, childrenIterator] = Iterable.consume(Iterable.from(element.children), 2); if (children.length !== 1) { break; @@ -60,19 +59,19 @@ export function compress(element: ICompressedTreeElement): ITreeElement(element: ITreeElement>, index = 0): ICompressedTreeElement { - let children: Iterator>; + let children: Iterable>; if (index < element.element.elements.length - 1) { - children = Iterator.single(_decompress(element, index + 1)); + children = [_decompress(element, index + 1)]; } else { - children = Iterator.map(Iterator.from(element.children), el => _decompress(el, 0)); + children = Iterable.map(Iterable.from(element.children), el => _decompress(el, 0)); } if (index === 0 && element.element.incompressible) { @@ -98,12 +97,12 @@ export function decompress(element: ITreeElement>): IC return _decompress(element, 0); } -function splice(treeElement: ICompressedTreeElement, element: T, children: Iterator>): ICompressedTreeElement { +function splice(treeElement: ICompressedTreeElement, element: T, children: Iterable>): ICompressedTreeElement { if (treeElement.element === element) { return { ...treeElement, children }; } - return { ...treeElement, children: Iterator.map(Iterator.from(treeElement.children), e => splice(e, element, children)) }; + return { ...treeElement, children: Iterable.map(Iterable.from(treeElement.children), e => splice(e, element, children)) }; } interface ICompressedObjectTreeModelOptions extends IObjectTreeModelOptions, TFilterData> { @@ -136,10 +135,10 @@ export class CompressedObjectTreeModel, TFilterData e setChildren( element: T | null, - children: ISequence> | undefined + children: Iterable> = Iterable.empty() ): void { if (element === null) { - const compressedChildren = Iterator.map(Iterator.from(children), this.enabled ? compress : noCompress); + const compressedChildren = Iterable.map(children, this.enabled ? compress : noCompress); this._setChildren(null, compressedChildren); return; } @@ -155,7 +154,7 @@ export class CompressedObjectTreeModel, TFilterData e const parent = this.model.getNode(compressedParentNode) as ITreeNode, TFilterData>; const decompressedElement = decompress(node); - const splicedElement = splice(decompressedElement, element, Iterator.from(children)); + const splicedElement = splice(decompressedElement, element, children); const recompressedElement = (this.enabled ? compress : noCompress)(splicedElement); const parentChildren = parent.children @@ -176,15 +175,15 @@ export class CompressedObjectTreeModel, TFilterData e this.enabled = enabled; const root = this.model.getNode(); - const rootChildren = Iterator.from((root.children as unknown) as ITreeNode>[]); // {{SQL CARBON EDIT}} strict-null-checks - const decompressedRootChildren = Iterator.map(rootChildren, decompress); - const recompressedRootChildren = Iterator.map(decompressedRootChildren, enabled ? compress : noCompress); + const rootChildren = root.children as ITreeNode>[]; + const decompressedRootChildren = Iterable.map(rootChildren, decompress); + const recompressedRootChildren = Iterable.map(decompressedRootChildren, enabled ? compress : noCompress); this._setChildren(null, recompressedRootChildren); } private _setChildren( node: ICompressedTreeNode | null, - children: ISequence>> | undefined + children: Iterable>> ): void { const insertedElements = new Set(); const _onDidCreateNode = (node: ITreeNode, TFilterData>) => { @@ -413,7 +412,7 @@ export class CompressibleObjectTreeModel, TFilterData this.model = new CompressedObjectTreeModel(user, mapList(this.nodeMapper, list), mapOptions(compressedNodeUnwrapper, options)); } - setChildren(element: T | null, children?: ISequence>): void { + setChildren(element: T | null, children: Iterable> = Iterable.empty()): void { this.model.setChildren(element, children); } diff --git a/src/vs/base/browser/ui/tree/dataTree.ts b/src/vs/base/browser/ui/tree/dataTree.ts index 1eaf31141c..12e28deaac 100644 --- a/src/vs/base/browser/ui/tree/dataTree.ts +++ b/src/vs/base/browser/ui/tree/dataTree.ts @@ -8,7 +8,7 @@ import { ISpliceable } from 'vs/base/common/sequence'; import { ITreeNode, ITreeModel, ITreeElement, ITreeRenderer, ITreeSorter, IDataSource, TreeError } from 'vs/base/browser/ui/tree/tree'; import { ObjectTreeModel } from 'vs/base/browser/ui/tree/objectTreeModel'; import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list'; -import { Iterator } from 'vs/base/common/iterator'; +import { Iterable } from 'vs/base/common/iterator'; export interface IDataTreeOptions extends IAbstractTreeOptions { readonly sorter?: ITreeSorter; @@ -158,9 +158,9 @@ export class DataTree extends AbstractTree boolean | undefined): { elements: Iterator>, size: number } { - const children = this.dataSource.getChildren(element); - const elements = Iterator.map>(Iterator.fromArray(children), element => { + private iterate(element: TInput | T, isCollapsed?: (el: T) => boolean | undefined): { elements: Iterable>, size: number } { + const children = [...this.dataSource.getChildren(element)]; + const elements = Iterable.map(children, element => { const { elements: children, size } = this.iterate(element, isCollapsed); const collapsible = this.dataSource.hasChildren ? this.dataSource.hasChildren(element) : undefined; const collapsed = size === 0 ? undefined : (isCollapsed && isCollapsed(element)); diff --git a/src/vs/base/browser/ui/tree/indexTree.ts b/src/vs/base/browser/ui/tree/indexTree.ts index 4df112db6b..06d309a566 100644 --- a/src/vs/base/browser/ui/tree/indexTree.ts +++ b/src/vs/base/browser/ui/tree/indexTree.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/tree'; -import { Iterator, ISequence } from 'vs/base/common/iterator'; +import { Iterable } from 'vs/base/common/iterator'; import { AbstractTree, IAbstractTreeOptions } from 'vs/base/browser/ui/tree/abstractTree'; import { ISpliceable } from 'vs/base/common/sequence'; import { IndexTreeModel } from 'vs/base/browser/ui/tree/indexTreeModel'; @@ -28,7 +28,7 @@ export class IndexTree extends AbstractTree> = Iterator.empty()): void { + splice(location: number[], deleteCount: number, toInsert: Iterable> = Iterable.empty()): void { this.model.splice(location, deleteCount, toInsert); } diff --git a/src/vs/base/browser/ui/tree/indexTreeModel.ts b/src/vs/base/browser/ui/tree/indexTreeModel.ts index 64947bf012..d901efae8b 100644 --- a/src/vs/base/browser/ui/tree/indexTreeModel.ts +++ b/src/vs/base/browser/ui/tree/indexTreeModel.ts @@ -6,7 +6,7 @@ import { ICollapseStateChangeEvent, ITreeElement, ITreeFilter, ITreeFilterDataResult, ITreeModel, ITreeNode, TreeVisibility, ITreeModelSpliceEvent, TreeError } from 'vs/base/browser/ui/tree/tree'; import { tail2 } from 'vs/base/common/arrays'; import { Emitter, Event, EventBufferer } from 'vs/base/common/event'; -import { ISequence, Iterator } from 'vs/base/common/iterator'; +import { Iterable } from 'vs/base/common/iterator'; import { ISpliceable } from 'vs/base/common/sequence'; // Exported for tests @@ -103,7 +103,7 @@ export class IndexTreeModel, TFilterData = voi splice( location: number[], deleteCount: number, - toInsert?: ISequence>, + toInsert: Iterable> = Iterable.empty(), onDidCreateNode?: (node: ITreeNode) => void, onDidDeleteNode?: (node: ITreeNode) => void ): void { @@ -113,7 +113,7 @@ export class IndexTreeModel, TFilterData = voi const { parentNode, listIndex, revealed, visible } = this.getParentNodeWithListIndex(location); const treeListElementsToInsert: ITreeNode[] = []; - const nodesToInsertIterator = Iterator.map(Iterator.from(toInsert), el => this.createTreeNode(el, parentNode, parentNode.visible ? TreeVisibility.Visible : TreeVisibility.Hidden, revealed, treeListElementsToInsert, onDidCreateNode)); + const nodesToInsertIterator = Iterable.map(toInsert, el => this.createTreeNode(el, parentNode, parentNode.visible ? TreeVisibility.Visible : TreeVisibility.Hidden, revealed, treeListElementsToInsert, onDidCreateNode)); const lastIndex = location[location.length - 1]; @@ -134,14 +134,14 @@ export class IndexTreeModel, TFilterData = voi let insertedVisibleChildrenCount = 0; let renderNodeCount = 0; - Iterator.forEach(nodesToInsertIterator, child => { + for (const child of nodesToInsertIterator) { nodesToInsert.push(child); renderNodeCount += child.renderNodeCount; if (child.visible) { child.visibleChildIndex = visibleChildStartIndex + insertedVisibleChildrenCount++; } - }); + } const deletedNodes = parentNode.children.splice(lastIndex, deleteCount, ...nodesToInsert); @@ -365,21 +365,21 @@ export class IndexTreeModel, TFilterData = voi treeListElements.push(node); } - const childElements = Iterator.from(treeElement.children); + const childElements = treeElement.children || Iterable.empty(); const childRevealed = revealed && visibility !== TreeVisibility.Hidden && !node.collapsed; - const childNodes = Iterator.map(childElements, el => this.createTreeNode(el, node, visibility, childRevealed, treeListElements, onDidCreateNode)); + const childNodes = Iterable.map(childElements, el => this.createTreeNode(el, node, visibility, childRevealed, treeListElements, onDidCreateNode)); let visibleChildrenCount = 0; let renderNodeCount = 1; - Iterator.forEach(childNodes, child => { + for (const child of childNodes) { node.children.push(child); renderNodeCount += child.renderNodeCount; if (child.visible) { child.visibleChildIndex = visibleChildrenCount++; } - }); + } node.collapsible = node.collapsible || node.children.length > 0; node.visibleChildrenCount = visibleChildrenCount; diff --git a/src/vs/base/browser/ui/tree/objectTree.ts b/src/vs/base/browser/ui/tree/objectTree.ts index be20a0181a..bd8f9211f3 100644 --- a/src/vs/base/browser/ui/tree/objectTree.ts +++ b/src/vs/base/browser/ui/tree/objectTree.ts @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ISequence } from 'vs/base/common/iterator'; +import { Iterable } from 'vs/base/common/iterator'; import { AbstractTree, IAbstractTreeOptions, IAbstractTreeOptionsUpdate } from 'vs/base/browser/ui/tree/abstractTree'; import { ISpliceable } from 'vs/base/common/sequence'; import { ITreeNode, ITreeModel, ITreeElement, ITreeRenderer, ITreeSorter, ICollapseStateChangeEvent } from 'vs/base/browser/ui/tree/tree'; @@ -33,7 +33,7 @@ export class ObjectTree, TFilterData = void> extends super(user, container, delegate, renderers, options as IObjectTreeOptions); } - setChildren(element: T | null, children?: ISequence>): void { + setChildren(element: T | null, children: Iterable> = Iterable.empty()): void { this.model.setChildren(element, children); } @@ -184,7 +184,7 @@ export class CompressibleObjectTree, TFilterData = vo super(user, container, delegate, compressibleRenderers, asObjectTreeOptions(compressedTreeNodeProvider, options)); } - setChildren(element: T | null, children?: ISequence>): void { + setChildren(element: T | null, children: Iterable> = Iterable.empty()): void { this.model.setChildren(element, children); } diff --git a/src/vs/base/browser/ui/tree/objectTreeModel.ts b/src/vs/base/browser/ui/tree/objectTreeModel.ts index d319ccc219..1897d46aa8 100644 --- a/src/vs/base/browser/ui/tree/objectTreeModel.ts +++ b/src/vs/base/browser/ui/tree/objectTreeModel.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { ISpliceable } from 'vs/base/common/sequence'; -import { Iterator, ISequence, getSequenceIterator } from 'vs/base/common/iterator'; +import { Iterable } from 'vs/base/common/iterator'; import { IndexTreeModel, IIndexTreeModelOptions } from 'vs/base/browser/ui/tree/indexTreeModel'; import { Event } from 'vs/base/common/event'; import { ITreeModel, ITreeNode, ITreeElement, ITreeSorter, ICollapseStateChangeEvent, ITreeModelSpliceEvent, TreeError } from 'vs/base/browser/ui/tree/tree'; @@ -14,7 +14,7 @@ import { mergeSort } from 'vs/base/common/arrays'; export type ITreeNodeCallback = (node: ITreeNode) => void; export interface IObjectTreeModel, TFilterData extends NonNullable = void> extends ITreeModel { - setChildren(element: T | null, children: ISequence> | undefined): void; + setChildren(element: T | null, children: Iterable> | undefined): void; resort(element?: T | null, recursive?: boolean): void; } @@ -62,7 +62,7 @@ export class ObjectTreeModel, TFilterData extends Non setChildren( element: T | null, - children: ISequence> | undefined, + children: Iterable> = Iterable.empty(), onDidCreateNode?: ITreeNodeCallback, onDidDeleteNode?: ITreeNodeCallback ): void { @@ -72,7 +72,7 @@ export class ObjectTreeModel, TFilterData extends Non private _setChildren( location: number[], - children: ISequence> | undefined, + children: Iterable> = Iterable.empty(), onDidCreateNode?: ITreeNodeCallback, onDidDeleteNode?: ITreeNodeCallback ): void { @@ -132,14 +132,12 @@ export class ObjectTreeModel, TFilterData extends Non ); } - private preserveCollapseState(elements: ISequence> | undefined): ISequence> { - let iterator = elements ? getSequenceIterator(elements) : Iterator.empty>(); - + private preserveCollapseState(elements: Iterable> = Iterable.empty()): Iterable> { if (this.sorter) { - iterator = Iterator.fromArray(mergeSort(Iterator.collect(iterator), this.sorter.compare.bind(this.sorter))); + elements = mergeSort([...elements], this.sorter.compare.bind(this.sorter)); } - return Iterator.map(iterator, treeElement => { + return Iterable.map(elements, treeElement => { let node = this.nodes.get(treeElement.element); if (!node && this.identityProvider) { @@ -182,14 +180,14 @@ export class ObjectTreeModel, TFilterData extends Non this._setChildren(location, this.resortChildren(node, recursive)); } - private resortChildren(node: ITreeNode, recursive: boolean, first = true): ISequence> { - let childrenNodes = Iterator.fromArray(node.children as ITreeNode[]); + private resortChildren(node: ITreeNode, recursive: boolean, first = true): Iterable> { + let childrenNodes = [...node.children] as ITreeNode[]; if (recursive || first) { - childrenNodes = Iterator.fromArray(Iterator.collect(childrenNodes).sort(this.sorter!.compare.bind(this.sorter))); + childrenNodes = mergeSort(childrenNodes, this.sorter!.compare.bind(this.sorter)) as ITreeNode[]; } - return Iterator.map, ITreeElement>(childrenNodes, node => ({ + return Iterable.map, ITreeElement>(childrenNodes, node => ({ element: node.element as T, collapsible: node.collapsible, collapsed: node.collapsed, diff --git a/src/vs/base/browser/ui/tree/tree.ts b/src/vs/base/browser/ui/tree/tree.ts index d9bcf6519f..896f70f183 100644 --- a/src/vs/base/browser/ui/tree/tree.ts +++ b/src/vs/base/browser/ui/tree/tree.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { Event } from 'vs/base/common/event'; -import { Iterator } from 'vs/base/common/iterator'; import { IListRenderer, IListDragOverReaction, IListDragAndDrop, ListDragOverEffect } from 'vs/base/browser/ui/list/list'; import { IDragAndDropData } from 'vs/base/browser/dnd'; @@ -74,7 +73,7 @@ export interface ITreeSorter { export interface ITreeElement { readonly element: T; - readonly children?: Iterator> | ITreeElement[]; + readonly children?: Iterable>; readonly collapsible?: boolean; readonly collapsed?: boolean; } @@ -167,12 +166,12 @@ export interface ITreeNavigator { export interface IDataSource { hasChildren?(element: TInput | T): boolean; - getChildren(element: TInput | T): T[]; + getChildren(element: TInput | T): Iterable; } export interface IAsyncDataSource { hasChildren(element: TInput | T): boolean; - getChildren(element: TInput | T): T[] | Promise; + getChildren(element: TInput | T): Iterable | Promise>; } export const enum TreeDragOverBubble { diff --git a/src/vs/base/common/date.ts b/src/vs/base/common/date.ts index 2cccf674a5..61ed928066 100644 --- a/src/vs/base/common/date.ts +++ b/src/vs/base/common/date.ts @@ -19,6 +19,10 @@ export function fromNow(date: number | Date, appendAgoLabel?: boolean): string { } const seconds = Math.round((new Date().getTime() - date) / 1000); + if (seconds < -30) { + return localize('date.fromNow.in', 'in {0}', fromNow(new Date().getTime() + seconds * 1000, false)); + } + if (seconds < 30) { return localize('date.fromNow.now', 'now'); } diff --git a/src/vs/base/common/filters.ts b/src/vs/base/common/filters.ts index 6bc7135d9c..053601db86 100644 --- a/src/vs/base/common/filters.ts +++ b/src/vs/base/common/filters.ts @@ -559,6 +559,8 @@ export function fuzzyScore(pattern: string, patternLow: string, patternStart: nu let patternPos = patternStart; let wordPos = wordStart; + let hasStrongFirstMatch = false; + // There will be a match, fill in tables for (row = 1, patternPos = patternStart; patternPos < patternLen; row++, patternPos++) { @@ -566,6 +568,10 @@ export function fuzzyScore(pattern: string, patternLow: string, patternStart: nu const score = _doScore(pattern, patternLow, patternPos, patternStart, word, wordLow, wordPos); + if (patternPos === patternStart && score > 1) { + hasStrongFirstMatch = true; + } + _scores[row][column] = score; const diag = _table[row - 1][column - 1] + (score > 1 ? 1 : score); @@ -604,6 +610,10 @@ export function fuzzyScore(pattern: string, patternLow: string, patternStart: nu printTables(pattern, patternStart, word, wordStart); } + if (!hasStrongFirstMatch && !firstMatchCanBeWeak) { + return undefined; + } + _matchesCount = 0; _topScore = -100; _wordStart = wordStart; diff --git a/src/vs/base/common/fuzzyScorer.ts b/src/vs/base/common/fuzzyScorer.ts index 44397e9ab3..581ffe99f4 100644 --- a/src/vs/base/common/fuzzyScorer.ts +++ b/src/vs/base/common/fuzzyScorer.ts @@ -526,15 +526,45 @@ function createMatches(offsets: number[] | undefined): IMatch[] { } function normalizeMatches(matches: IMatch[]): IMatch[] { - const positions = new Set(); - for (const match of matches) { - for (let i = match.start; i < match.end; i++) { - positions.add(i); + // sort matches by start to be able to normalize + const sortedMatches = matches.sort((matchA, matchB) => { + return matchA.start - matchB.start; + }); + + // merge matches that overlap + const normalizedMatches: IMatch[] = []; + let currentMatch: IMatch | undefined = undefined; + for (const match of sortedMatches) { + + // if we have no current match or the matches + // do not overlap, we take it as is and remember + // it for future merging + if (!currentMatch || !matchOverlaps(currentMatch, match)) { + currentMatch = match; + normalizedMatches.push(match); + } + + // otherwise we merge the matches + else { + currentMatch.start = Math.min(currentMatch.start, match.start); + currentMatch.end = Math.max(currentMatch.end, match.end); } } - return createMatches(Array.from(positions.values()).sort((a, b) => a - b)); + return normalizedMatches; +} + +function matchOverlaps(matchA: IMatch, matchB: IMatch): boolean { + if (matchA.end < matchB.start) { + return false; // A ends before B starts + } + + if (matchB.end < matchA.start) { + return false; // B ends before A starts + } + + return true; } //#endregion diff --git a/src/vs/base/common/history.ts b/src/vs/base/common/history.ts index 1f3e6d022d..1b01e1a14e 100644 --- a/src/vs/base/common/history.ts +++ b/src/vs/base/common/history.ts @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { INavigator, ArrayNavigator } from 'vs/base/common/iterator'; +import { INavigator, ArrayNavigator } from 'vs/base/common/navigator'; export class HistoryNavigator implements INavigator { @@ -45,10 +45,6 @@ export class HistoryNavigator implements INavigator { return this._navigator.current(); } - public parent(): null { - return null; - } - public first(): T | null { return this._navigator.first(); } diff --git a/src/vs/base/common/iterator.ts b/src/vs/base/common/iterator.ts index 72bc66275c..5c5b2e5b7f 100644 --- a/src/vs/base/common/iterator.ts +++ b/src/vs/base/common/iterator.ts @@ -3,39 +3,17 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -export interface IteratorDefinedResult { - readonly done: false; - readonly value: T; -} -export interface IteratorUndefinedResult { - readonly done: true; - readonly value: undefined; -} -export const FIN: IteratorUndefinedResult = { done: true, value: undefined }; -export type IteratorResult = IteratorDefinedResult | IteratorUndefinedResult; - -export interface Iterator { - next(): IteratorResult; -} - -interface NativeIteratorYieldResult { - done?: false; - value: TYield; -} - -interface NativeIteratorReturnResult { - done: true; - value: TReturn; -} - -type NativeIteratorResult = NativeIteratorYieldResult | NativeIteratorReturnResult; - -export interface NativeIterator { - next(): NativeIteratorResult; -} - export namespace Iterable { + const _empty: Iterable = Object.freeze([]); + export function empty(): Iterable { + return _empty; + } + + export function from(iterable: Iterable | undefined | null): Iterable { + return iterable || _empty; + } + export function first(iterable: Iterable): T | undefined { return iterable[Symbol.iterator]().next().value; } @@ -52,291 +30,48 @@ export namespace Iterable { export function* filter(iterable: Iterable, predicate: (t: T) => boolean): Iterable { for (const element of iterable) { if (predicate(element)) { - return yield element; + yield element; } } } export function* map(iterable: Iterable, fn: (t: T) => R): Iterable { for (const element of iterable) { - return yield fn(element); - } - } -} - -export module Iterator { - const _empty: Iterator = { - next() { - return FIN; - } - }; - - export function empty(): Iterator { - return _empty; - } - - export function single(value: T): Iterator { - let done = false; - - return { - next(): IteratorResult { - if (done) { - return FIN; - } - - done = true; - return { done: false, value }; - } - }; - } - - export function fromArray(array: ReadonlyArray, index = 0, length = array.length): Iterator { - return { - next(): IteratorResult { - if (index >= length) { - return FIN; - } - - return { done: false, value: array[index++] }; - } - }; - } - - export function fromNativeIterator(it: NativeIterator): Iterator { - return { - next(): IteratorResult { - const result = it.next(); - - if (result.done) { - return FIN; - } - - return { done: false, value: result.value }; - } - }; - } - - export function from(elements: Iterator | T[] | undefined): Iterator { - if (!elements) { - return Iterator.empty(); - } else if (Array.isArray(elements)) { - return Iterator.fromArray(elements); - } else { - return elements; + yield fn(element); } } - export function map(iterator: Iterator, fn: (t: T) => R): Iterator { - return { - next() { - const element = iterator.next(); - if (element.done) { - return FIN; - } else { - return { done: false, value: fn(element.value) }; - } - } - }; - } - - export function filter(iterator: Iterator, fn: (t: T) => boolean): Iterator { - return { - next() { - while (true) { - const element = iterator.next(); - if (element.done) { - return FIN; - } - if (fn(element.value)) { - return { done: false, value: element.value }; - } - } - } - }; - } - - export function some(iterator: Iterator | NativeIterator, fn: (t: T) => boolean): boolean { - while (true) { - const element = iterator.next(); - if (element.done) { - return false; - } - - if (fn(element.value)) { - return true; + export function* concat(...iterables: Iterable[]): Iterable { + for (const iterable of iterables) { + for (const element of iterable) { + yield element; } } } - export function forEach(iterator: Iterator, fn: (t: T) => void): void { - for (let next = iterator.next(); !next.done; next = iterator.next()) { - fn(next.value); - } - } - - export function collect(iterator: Iterator, atMost: number = Number.POSITIVE_INFINITY): T[] { - const result: T[] = []; + /** + * Consumes `atMost` elements from iterable and returns the consumed elements, + * and an iterable for the rest of the elements. + */ + export function consume(iterable: Iterable, atMost: number = Number.POSITIVE_INFINITY): [T[], Iterable] { + const consumed: T[] = []; if (atMost === 0) { - return result; + return [consumed, iterable]; } - let i = 0; + const iterator = iterable[Symbol.iterator](); - for (let next = iterator.next(); !next.done; next = iterator.next()) { - result.push(next.value); + for (let i = 0; i < atMost; i++) { + const next = iterator.next(); - if (++i >= atMost) { - break; + if (next.done) { + return [consumed, Iterable.empty()]; } + + consumed.push(next.value); } - return result; - } - - export function concat(...iterators: Iterator[]): Iterator { - let i = 0; - - return { - next() { - if (i >= iterators.length) { - return FIN; - } - - const iterator = iterators[i]; - const result = iterator.next(); - - if (result.done) { - i++; - return this.next(); - } - - return result; - } - }; - } - - export function chain(iterator: Iterator): ChainableIterator { - return new ChainableIterator(iterator); + return [consumed, { [Symbol.iterator]() { return iterator; } }]; } } - -export class ChainableIterator implements Iterator { - - constructor(private it: Iterator) { } - - next(): IteratorResult { return this.it.next(); } - map(fn: (t: T) => R): ChainableIterator { return new ChainableIterator(Iterator.map(this.it, fn)); } - filter(fn: (t: T) => boolean): ChainableIterator { return new ChainableIterator(Iterator.filter(this.it, fn)); } -} - -export type ISequence = Iterator | T[]; - -export function getSequenceIterator(arg: ISequence | undefined): Iterator { - if (Array.isArray(arg)) { - return Iterator.fromArray(arg); - } else if (!arg) { - return Iterator.empty(); - } else { - return arg; - } -} - -export interface INextIterator { - next(): T | null; -} - -export class ArrayIterator implements INextIterator { - - private readonly items: readonly T[]; - protected start: number; - protected end: number; - protected index: number; - - constructor(items: readonly T[], start: number = 0, end: number = items.length, index = start - 1) { - this.items = items; - this.start = start; - this.end = end; - this.index = index; - } - - public first(): T | null { - this.index = this.start; - return this.current(); - } - - public next(): T | null { - this.index = Math.min(this.index + 1, this.end); - return this.current(); - } - - protected current(): T | null { - if (this.index === this.start - 1 || this.index === this.end) { - return null; - } - - return this.items[this.index]; - } -} - -export class ArrayNavigator extends ArrayIterator implements INavigator { - - constructor(items: readonly T[], start: number = 0, end: number = items.length, index = start - 1) { - super(items, start, end, index); - } - - public current(): T | null { - return super.current(); - } - - public previous(): T | null { - this.index = Math.max(this.index - 1, this.start - 1); - return this.current(); - } - - public first(): T | null { - this.index = this.start; - return this.current(); - } - - public last(): T | null { - this.index = this.end - 1; - return this.current(); - } - - public parent(): T | null { - return null; - } -} - -export class MappedIterator implements INextIterator { - - constructor(protected iterator: INextIterator, protected fn: (item: T | null) => R) { - // noop - } - - next() { return this.fn(this.iterator.next()); } -} - -export interface INavigator extends INextIterator { - current(): T | null; - previous(): T | null; - parent(): T | null; - first(): T | null; - last(): T | null; - next(): T | null; -} - -export class MappedNavigator extends MappedIterator implements INavigator { - - constructor(protected navigator: INavigator, fn: (item: T | null) => R) { - super(navigator, fn); - } - - current() { return this.fn(this.navigator.current()); } - previous() { return this.fn(this.navigator.previous()); } - parent() { return this.fn(this.navigator.parent()); } - first() { return this.fn(this.navigator.first()); } - last() { return this.fn(this.navigator.last()); } - next() { return this.fn(this.navigator.next()); } -} diff --git a/src/vs/base/common/labels.ts b/src/vs/base/common/labels.ts index 56ca73d53c..951ba64d5e 100644 --- a/src/vs/base/common/labels.ts +++ b/src/vs/base/common/labels.ts @@ -18,7 +18,7 @@ export interface IWorkspaceFolderProvider { } export interface IUserHomeProvider { - userHome: string; + userHome?: URI; } /** @@ -63,8 +63,8 @@ export function getPathLabel(resource: URI | string, userHomeProvider?: IUserHom // normalize and tildify (macOS, Linux only) let res = normalize(resource.fsPath); - if (!isWindows && userHomeProvider) { - res = tildify(res, userHomeProvider.userHome); + if (!isWindows && userHomeProvider?.userHome) { + res = tildify(res, userHomeProvider.userHome.fsPath); } return res; diff --git a/src/vs/base/common/map.ts b/src/vs/base/common/map.ts index d226c1a55c..c3de99fbda 100644 --- a/src/vs/base/common/map.ts +++ b/src/vs/base/common/map.ts @@ -5,7 +5,7 @@ import { URI } from 'vs/base/common/uri'; import { CharCode } from 'vs/base/common/charCode'; -import { FIN } from './iterator'; +import { compareIgnoreCase, compare } from 'vs/base/common/strings'; /** * @deprecated ES6: use `[...SetOrMap.values()]` @@ -56,8 +56,8 @@ export function setToString(set: Set): string { return `Set(${set.size}) {${entries.join(', ')}}`; } -export interface IKeyIterator { - reset(key: string): this; +export interface IKeyIterator { + reset(key: K): this; next(): this; hasNext(): boolean; @@ -65,7 +65,7 @@ export interface IKeyIterator { value(): string; } -export class StringIterator implements IKeyIterator { +export class StringIterator implements IKeyIterator { private _value: string = ''; private _pos: number = 0; @@ -96,7 +96,7 @@ export class StringIterator implements IKeyIterator { } } -export class PathIterator implements IKeyIterator { +export class PathIterator implements IKeyIterator { private _value!: string; private _from!: number; @@ -163,33 +163,118 @@ export class PathIterator implements IKeyIterator { } } -class TernarySearchTreeNode { +const enum UriIteratorState { + Scheme = 1, Authority = 2, Path = 3, Query = 4, Fragment = 5 +} + +export class UriIterator implements IKeyIterator { + + private _pathIterator = new PathIterator(false); + private _value!: URI; + private _states: UriIteratorState[] = []; + private _stateIdx: number = 0; + + reset(key: URI): this { + this._value = key; + this._states = []; + if (this._value.scheme) { + this._states.push(UriIteratorState.Scheme); + } + if (this._value.authority) { + this._states.push(UriIteratorState.Authority); + } + if (this._value.path) { + this._pathIterator.reset(key.path); + if (this._pathIterator.value()) { + this._states.push(UriIteratorState.Path); + } + } + if (this._value.query) { + this._states.push(UriIteratorState.Query); + } + if (this._value.fragment) { + this._states.push(UriIteratorState.Fragment); + } + this._stateIdx = 0; + return this; + } + + next(): this { + if (this._states[this._stateIdx] === UriIteratorState.Path && this._pathIterator.hasNext()) { + this._pathIterator.next(); + } else { + this._stateIdx += 1; + } + return this; + } + + hasNext(): boolean { + return (this._states[this._stateIdx] === UriIteratorState.Path && this._pathIterator.hasNext()) + || this._stateIdx < this._states.length - 1; + } + + cmp(a: string): number { + if (this._states[this._stateIdx] === UriIteratorState.Scheme) { + return compareIgnoreCase(a, this._value.scheme); + } else if (this._states[this._stateIdx] === UriIteratorState.Authority) { + return compareIgnoreCase(a, this._value.authority); + } else if (this._states[this._stateIdx] === UriIteratorState.Path) { + return this._pathIterator.cmp(a); + } else if (this._states[this._stateIdx] === UriIteratorState.Query) { + return compare(a, this._value.query); + } else if (this._states[this._stateIdx] === UriIteratorState.Fragment) { + return compare(a, this._value.fragment); + } + throw new Error(); + } + + value(): string { + if (this._states[this._stateIdx] === UriIteratorState.Scheme) { + return this._value.scheme; + } else if (this._states[this._stateIdx] === UriIteratorState.Authority) { + return this._value.authority; + } else if (this._states[this._stateIdx] === UriIteratorState.Path) { + return this._pathIterator.value(); + } else if (this._states[this._stateIdx] === UriIteratorState.Query) { + return this._value.query; + } else if (this._states[this._stateIdx] === UriIteratorState.Fragment) { + return this._value.fragment; + } + throw new Error(); + } +} + +class TernarySearchTreeNode { segment!: string; - value: E | undefined; - key!: string; - left: TernarySearchTreeNode | undefined; - mid: TernarySearchTreeNode | undefined; - right: TernarySearchTreeNode | undefined; + value: V | undefined; + key!: K; + left: TernarySearchTreeNode | undefined; + mid: TernarySearchTreeNode | undefined; + right: TernarySearchTreeNode | undefined; isEmpty(): boolean { return !this.left && !this.mid && !this.right && !this.value; } } -export class TernarySearchTree { +export class TernarySearchTree { - static forPaths(): TernarySearchTree { - return new TernarySearchTree(new PathIterator()); + static forUris(): TernarySearchTree { + return new TernarySearchTree(new UriIterator()); } - static forStrings(): TernarySearchTree { - return new TernarySearchTree(new StringIterator()); + static forPaths(): TernarySearchTree { + return new TernarySearchTree(new PathIterator()); } - private _iter: IKeyIterator; - private _root: TernarySearchTreeNode | undefined; + static forStrings(): TernarySearchTree { + return new TernarySearchTree(new StringIterator()); + } - constructor(segments: IKeyIterator) { + private _iter: IKeyIterator; + private _root: TernarySearchTreeNode | undefined; + + constructor(segments: IKeyIterator) { this._iter = segments; } @@ -197,12 +282,12 @@ export class TernarySearchTree { this._root = undefined; } - set(key: string, element: E): E | undefined { + set(key: K, element: V): V | undefined { const iter = this._iter.reset(key); - let node: TernarySearchTreeNode; + let node: TernarySearchTreeNode; if (!this._root) { - this._root = new TernarySearchTreeNode(); + this._root = new TernarySearchTreeNode(); this._root.segment = iter.value(); } @@ -212,7 +297,7 @@ export class TernarySearchTree { if (val > 0) { // left if (!node.left) { - node.left = new TernarySearchTreeNode(); + node.left = new TernarySearchTreeNode(); node.left.segment = iter.value(); } node = node.left; @@ -220,7 +305,7 @@ export class TernarySearchTree { } else if (val < 0) { // right if (!node.right) { - node.right = new TernarySearchTreeNode(); + node.right = new TernarySearchTreeNode(); node.right.segment = iter.value(); } node = node.right; @@ -229,7 +314,7 @@ export class TernarySearchTree { // mid iter.next(); if (!node.mid) { - node.mid = new TernarySearchTreeNode(); + node.mid = new TernarySearchTreeNode(); node.mid.segment = iter.value(); } node = node.mid; @@ -243,7 +328,7 @@ export class TernarySearchTree { return oldElement; } - get(key: string): E | undefined { + get(key: K): V | undefined { const iter = this._iter.reset(key); let node = this._root; while (node) { @@ -265,10 +350,10 @@ export class TernarySearchTree { return node ? node.value : undefined; } - delete(key: string): void { + delete(key: K): void { const iter = this._iter.reset(key); - const stack: [-1 | 0 | 1, TernarySearchTreeNode][] = []; + const stack: [-1 | 0 | 1, TernarySearchTreeNode][] = []; let node = this._root; // find and unset node @@ -306,10 +391,10 @@ export class TernarySearchTree { } } - findSubstr(key: string): E | undefined { + findSubstr(key: K): V | undefined { const iter = this._iter.reset(key); let node = this._root; - let candidate: E | undefined = undefined; + let candidate: V | undefined = undefined; while (node) { const val = iter.cmp(node.segment); if (val > 0) { @@ -330,7 +415,7 @@ export class TernarySearchTree { return node && node.value || candidate; } - findSuperstr(key: string): Iterator | undefined { + findSuperstr(key: K): Iterator | undefined { const iter = this._iter.reset(key); let node = this._root; while (node) { @@ -357,11 +442,11 @@ export class TernarySearchTree { return undefined; } - private _nodeIterator(node: TernarySearchTreeNode): Iterator { - let res: { done: false; value: E; }; + private _nodeIterator(node: TernarySearchTreeNode): Iterator { + let res: { done: false; value: V; }; let idx: number; - let data: E[]; - const next = (): IteratorResult => { + let data: V[]; + const next = (): IteratorResult => { if (!data) { // lazy till first invocation data = []; @@ -369,7 +454,7 @@ export class TernarySearchTree { this._forEach(node, value => data.push(value)); } if (idx >= data.length) { - return FIN; + return { done: true, value: undefined }; } if (!res) { @@ -382,11 +467,11 @@ export class TernarySearchTree { return { next }; } - forEach(callback: (value: E, index: string) => any) { + forEach(callback: (value: V, index: K) => any) { this._forEach(this._root, callback); } - private _forEach(node: TernarySearchTreeNode | undefined, callback: (value: E, index: string) => any) { + private _forEach(node: TernarySearchTreeNode | undefined, callback: (value: V, index: K) => any) { if (node) { // left this._forEach(node.left, callback); diff --git a/src/vs/base/common/navigator.ts b/src/vs/base/common/navigator.ts new file mode 100644 index 0000000000..40980367e8 --- /dev/null +++ b/src/vs/base/common/navigator.ts @@ -0,0 +1,50 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export interface INavigator { + current(): T | null; + previous(): T | null; + first(): T | null; + last(): T | null; + next(): T | null; +} + +export class ArrayNavigator implements INavigator { + + constructor( + private readonly items: readonly T[], + protected start: number = 0, + protected end: number = items.length, + protected index = start - 1 + ) { } + + current(): T | null { + if (this.index === this.start - 1 || this.index === this.end) { + return null; + } + + return this.items[this.index]; + } + + next(): T | null { + this.index = Math.min(this.index + 1, this.end); + return this.current(); + } + + previous(): T | null { + this.index = Math.max(this.index - 1, this.start - 1); + return this.current(); + } + + first(): T | null { + this.index = this.start; + return this.current(); + } + + last(): T | null { + this.index = this.end - 1; + return this.current(); + } +} diff --git a/src/vs/base/common/resourceTree.ts b/src/vs/base/common/resourceTree.ts index d7433949ed..d22e3a5545 100644 --- a/src/vs/base/common/resourceTree.ts +++ b/src/vs/base/common/resourceTree.ts @@ -5,7 +5,6 @@ import { memoize } from 'vs/base/common/decorators'; import * as paths from 'vs/base/common/path'; -import { Iterator } from 'vs/base/common/iterator'; import { relativePath, joinPath } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { PathIterator, values } from 'vs/base/common/map'; @@ -15,7 +14,7 @@ export interface IResourceNode { readonly relativePath: string; readonly name: string; readonly element: T | undefined; - readonly children: Iterator>; + readonly children: Iterable>; readonly childrenCount: number; readonly parent: IResourceNode | undefined; readonly context: C; @@ -30,8 +29,8 @@ class Node implements IResourceNode { return this._children.size; } - get children(): Iterator> { - return Iterator.fromArray(values(this._children)); + get children(): Iterable> { + return [...values(this._children)]; } @memoize @@ -69,7 +68,9 @@ function collect(node: IResourceNode, result: T[]): T[] { result.push(node.element); } - Iterator.forEach(node.children, child => collect(child, result)); + for (const child of node.children) { + collect(child, result); + } return result; } diff --git a/src/vs/base/common/resources.ts b/src/vs/base/common/resources.ts index 730d88acd9..2d8d49823a 100644 --- a/src/vs/base/common/resources.ts +++ b/src/vs/base/common/resources.ts @@ -5,7 +5,7 @@ import * as extpath from 'vs/base/common/extpath'; import * as paths from 'vs/base/common/path'; -import { URI } from 'vs/base/common/uri'; +import { URI, uriToFsPath } from 'vs/base/common/uri'; import { equalsIgnoreCase } from 'vs/base/common/strings'; import { Schemas } from 'vs/base/common/network'; import { isLinux, isWindows } from 'vs/base/common/platform'; @@ -14,26 +14,7 @@ import { ParsedExpression, IExpression, parse } from 'vs/base/common/glob'; import { TernarySearchTree } from 'vs/base/common/map'; export function originalFSPath(uri: URI): string { - let value: string; - const uriPath = uri.path; - if (uri.authority && uriPath.length > 1 && uri.scheme === 'file') { - // unc path: file://shares/c$/far/boo - value = `//${uri.authority}${uriPath}`; - } else if ( - isWindows - && uriPath.charCodeAt(0) === CharCode.Slash - && extpath.isWindowsDriveLetter(uriPath.charCodeAt(1)) - && uriPath.charCodeAt(2) === CharCode.Colon - ) { - value = uriPath.substr(1); - } else { - // other path - value = uriPath; - } - if (isWindows) { - value = value.replace(/\//g, '\\'); - } - return value; + return uriToFsPath(uri, true); } /** @@ -336,7 +317,7 @@ export namespace DataUri { export class ResourceGlobMatcher { private readonly globalExpression: ParsedExpression; - private readonly expressionsByRoot: TernarySearchTree<{ root: URI, expression: ParsedExpression }> = TernarySearchTree.forPaths<{ root: URI, expression: ParsedExpression }>(); + private readonly expressionsByRoot: TernarySearchTree = TernarySearchTree.forPaths<{ root: URI, expression: ParsedExpression }>(); constructor( globalExpression: IExpression, diff --git a/src/vs/base/common/strings.ts b/src/vs/base/common/strings.ts index 20b1794e42..fddcae44ad 100644 --- a/src/vs/base/common/strings.ts +++ b/src/vs/base/common/strings.ts @@ -295,11 +295,12 @@ export function compare(a: string, b: string): number { } } -export function compareIgnoreCase(a: string, b: string): number { - const len = Math.min(a.length, b.length); - for (let i = 0; i < len; i++) { - let codeA = a.charCodeAt(i); - let codeB = b.charCodeAt(i); +export function compareIgnoreCase(a: string, b: string, aStart: number = 0, aEnd: number = a.length, bStart: number = 0, bEnd: number = b.length): number { + + for (; aStart < aEnd && bStart < bEnd; aStart++, bStart++) { + + let codeA = a.charCodeAt(aStart); + let codeB = b.charCodeAt(bStart); if (codeA === codeB) { // equal @@ -329,13 +330,16 @@ export function compareIgnoreCase(a: string, b: string): number { } } - if (a.length < b.length) { + const aLen = aEnd - aStart; + const bLen = bEnd - bStart; + + if (aLen < bLen) { return -1; - } else if (a.length > b.length) { + } else if (aLen > bLen) { return 1; - } else { - return 0; } + + return 0; } export function isLowerAsciiLetter(code: number): boolean { diff --git a/src/vs/base/common/uri.ts b/src/vs/base/common/uri.ts index 2dc91be53f..d4bdc3b4d7 100644 --- a/src/vs/base/common/uri.ts +++ b/src/vs/base/common/uri.ts @@ -205,7 +205,7 @@ export class URI implements UriComponents { // if (this.scheme !== 'file') { // console.warn(`[UriError] calling fsPath with scheme ${this.scheme}`); // } - return _makeFsPath(this, false); + return uriToFsPath(this, false); } // ---- modify to new ------------------------- @@ -349,7 +349,7 @@ export class URI implements UriComponents { } let newPath: string; if (isWindows && uri.scheme === 'file') { - newPath = URI.file(paths.win32.join(_makeFsPath(uri, true), ...pathFragment)).path; + newPath = URI.file(paths.win32.join(uriToFsPath(uri, true), ...pathFragment)).path; } else { newPath = paths.posix.join(uri.path, ...pathFragment); } @@ -421,7 +421,7 @@ class _URI extends URI { get fsPath(): string { if (!this._fsPath) { - this._fsPath = _makeFsPath(this, false); + this._fsPath = uriToFsPath(this, false); } return this._fsPath; } @@ -577,7 +577,7 @@ function encodeURIComponentMinimal(path: string): string { /** * Compute `fsPath` for the given uri */ -function _makeFsPath(uri: URI, keepDriveLetterCasing: boolean): string { +export function uriToFsPath(uri: URI, keepDriveLetterCasing: boolean): string { let value: string; if (uri.authority && uri.path.length > 1 && uri.scheme === 'file') { diff --git a/src/vs/base/node/id.ts b/src/vs/base/node/id.ts index 930abbda8b..3b4b0756ab 100644 --- a/src/vs/base/node/id.ts +++ b/src/vs/base/node/id.ts @@ -21,7 +21,7 @@ import { getMac } from 'vs/base/node/macAddress'; // Sun xVM VirtualBox 08-00-27 export const virtualMachineHint: { value(): number } = new class { - private _virtualMachineOUIs?: TernarySearchTree; + private _virtualMachineOUIs?: TernarySearchTree; private _value?: number; private _isVirtualMachineMacAdress(mac: string): boolean { diff --git a/src/vs/base/parts/quickinput/browser/media/quickInput.css b/src/vs/base/parts/quickinput/browser/media/quickInput.css index aca9fddc2d..bf01cc9722 100644 --- a/src/vs/base/parts/quickinput/browser/media/quickInput.css +++ b/src/vs/base/parts/quickinput/browser/media/quickInput.css @@ -201,7 +201,8 @@ flex: 1; /* make sure the icon label grows within the row */ } -.quick-input-list .quick-input-list-rows > .quick-input-list-row .codicon { +.quick-input-list .quick-input-list-rows > .quick-input-list-row .codicon[class*='codicon-'] { + color: currentColor !important; vertical-align: sub; } @@ -237,7 +238,7 @@ .quick-input-list .quick-input-list-entry-action-bar .action-label { /* * By default, actions in the quick input action bar are hidden - * until hovered over them or selected. + * until hovered over them or selected. */ display: none; } diff --git a/src/vs/base/parts/quickinput/browser/quickInput.ts b/src/vs/base/parts/quickinput/browser/quickInput.ts index 2832ed1710..8da38a7c6d 100644 --- a/src/vs/base/parts/quickinput/browser/quickInput.ts +++ b/src/vs/base/parts/quickinput/browser/quickInput.ts @@ -825,7 +825,16 @@ class QuickPick extends QuickInput implements IQuickPi if (!this.visible) { return; } - const hideInput = !!this._hideInput && this._items.length > 0; // do not allow to hide input without items + let hideInput = false; + let inputShownJustForScreenReader = false; + if (!!this._hideInput && this._items.length > 0) { + if (this.ui.isScreenReaderOptimized()) { + // Always show input if screen reader attached https://github.com/microsoft/vscode/issues/94360 + inputShownJustForScreenReader = true; + } else { + hideInput = true; + } + } dom.toggleClass(this.ui.container, 'hidden-input', hideInput); const visibilities: Visibilities = { title: !!this.title || !!this.step, @@ -852,7 +861,9 @@ class QuickPick extends QuickInput implements IQuickPi if (this.ui.inputBox.placeholder !== (this.placeholder || '')) { this.ui.inputBox.placeholder = (this.placeholder || ''); } - if (this.ui.inputBox.ariaLabel !== this.ariaLabel) { + if (inputShownJustForScreenReader) { + this.ui.inputBox.ariaLabel = ''; + } else if (this.ui.inputBox.ariaLabel !== this.ariaLabel) { this.ui.inputBox.ariaLabel = this.ariaLabel; } this.ui.list.matchOnDescription = this.matchOnDescription; @@ -1072,6 +1083,8 @@ export class QuickInputController extends Disposable { private onHideEmitter = new Emitter(); readonly onHide = this.onHideEmitter.event; + private previousFocusElement?: HTMLElement; + constructor(private options: IQuickInputOptions) { super(); this.idPrefix = options.idPrefix; @@ -1188,7 +1201,11 @@ export class QuickInputController extends Disposable { const focusTracker = dom.trackFocus(container); this._register(focusTracker); + this._register(dom.addDisposableListener(container, dom.EventType.FOCUS, e => { + this.previousFocusElement = e.relatedTarget instanceof HTMLElement ? e.relatedTarget : undefined; + }, true)); this._register(focusTracker.onDidBlur(() => { + this.previousFocusElement = undefined; if (!this.getUI().ignoreFocusOut && !this.options.ignoreFocusOut()) { this.hide(true); } @@ -1538,7 +1555,12 @@ export class QuickInputController extends Disposable { this.onHideEmitter.fire(); this.getUI().container.style.display = 'none'; if (!focusLost) { - this.options.returnFocus(); + if (this.previousFocusElement && this.previousFocusElement.offsetParent) { + this.previousFocusElement.focus(); + this.previousFocusElement = undefined; + } else { + this.options.returnFocus(); + } } controller.didHide(); } diff --git a/src/vs/base/parts/quickinput/browser/quickInputList.ts b/src/vs/base/parts/quickinput/browser/quickInputList.ts index 9d218cd6de..ed179d6690 100644 --- a/src/vs/base/parts/quickinput/browser/quickInputList.ts +++ b/src/vs/base/parts/quickinput/browser/quickInputList.ts @@ -692,6 +692,10 @@ function compareEntries(elementA: ListElement, elementB: ListElement, lookFor: s return 1; } + if (labelHighlightsA.length === 0 && labelHighlightsB.length === 0) { + return 0; + } + return compareAnything(elementA.saneLabel, elementB.saneLabel, lookFor); } diff --git a/src/vs/base/parts/tree/browser/tree.ts b/src/vs/base/parts/tree/browser/tree.ts index e13df1cdcb..fa745ef220 100644 --- a/src/vs/base/parts/tree/browser/tree.ts +++ b/src/vs/base/parts/tree/browser/tree.ts @@ -6,7 +6,7 @@ import * as Touch from 'vs/base/browser/touch'; import * as Mouse from 'vs/base/browser/mouseEvent'; import * as Keyboard from 'vs/base/browser/keyboardEvent'; -import { INavigator } from 'vs/base/common/iterator'; +import { INavigator } from 'sql/base/common/navigator'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import { Event } from 'vs/base/common/event'; import { IAction } from 'vs/base/common/actions'; diff --git a/src/vs/base/parts/tree/browser/treeImpl.ts b/src/vs/base/parts/tree/browser/treeImpl.ts index e0fbe7e749..3f6f80a582 100644 --- a/src/vs/base/parts/tree/browser/treeImpl.ts +++ b/src/vs/base/parts/tree/browser/treeImpl.ts @@ -8,7 +8,7 @@ import * as TreeDefaults from 'vs/base/parts/tree/browser/treeDefaults'; import * as Model from 'vs/base/parts/tree/browser/treeModel'; import * as View from './treeView'; import * as _ from 'vs/base/parts/tree/browser/tree'; -import { INavigator, MappedNavigator } from 'vs/base/common/iterator'; +import { INavigator, MappedNavigator } from 'sql/base/common/navigator'; import { Event, Emitter, Relay } from 'vs/base/common/event'; import { Color } from 'vs/base/common/color'; import { mixin } from 'vs/base/common/objects'; diff --git a/src/vs/base/parts/tree/browser/treeModel.ts b/src/vs/base/parts/tree/browser/treeModel.ts index dd1fb978f0..39deaeea2c 100644 --- a/src/vs/base/parts/tree/browser/treeModel.ts +++ b/src/vs/base/parts/tree/browser/treeModel.ts @@ -6,7 +6,7 @@ import * as Assert from 'vs/base/common/assert'; import { onUnexpectedError } from 'vs/base/common/errors'; import { IDisposable, combinedDisposable, Disposable } from 'vs/base/common/lifecycle'; -import { INavigator } from 'vs/base/common/iterator'; +import { INavigator } from 'sql/base/common/navigator'; import * as _ from './tree'; import { Event, Emitter, EventMultiplexer, Relay } from 'vs/base/common/event'; diff --git a/src/vs/base/parts/tree/browser/treeView.ts b/src/vs/base/parts/tree/browser/treeView.ts index 98815e17de..26a325d43e 100644 --- a/src/vs/base/parts/tree/browser/treeView.ts +++ b/src/vs/base/parts/tree/browser/treeView.ts @@ -14,7 +14,7 @@ import * as Mouse from 'vs/base/browser/mouseEvent'; import * as Keyboard from 'vs/base/browser/keyboardEvent'; import * as Model from 'vs/base/parts/tree/browser/treeModel'; import * as dnd from './treeDnd'; -import { ArrayIterator, MappedIterator } from 'vs/base/common/iterator'; +import { ArrayNavigator } from 'vs/base/common/navigator'; import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElement'; import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import { HeightMap, IViewItem } from 'vs/base/parts/tree/browser/treeViewModel'; @@ -24,6 +24,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { DataTransfers, StaticDND, IDragAndDropData } from 'vs/base/browser/dnd'; import { DefaultTreestyler } from './treeDefaults'; import { Delayer, timeout } from 'vs/base/common/async'; +import { MappedNavigator } from 'sql/base/common/navigator'; export interface IRow { element: HTMLElement | null; @@ -829,7 +830,7 @@ export class TreeView extends HeightMap { private onClearingInput(e: Model.IInputEvent): void { let item = e.item; if (item) { - this.onRemoveItems(new MappedIterator(item.getNavigator(), item => item && item.id)); + this.onRemoveItems(new MappedNavigator(item.getNavigator(), item => item && item.id)); this.onRowsChanged(); } } @@ -925,20 +926,20 @@ export class TreeView extends HeightMap { for (const diffChange of diff) { if (diffChange.originalLength > 0) { - this.onRemoveItems(new ArrayIterator(previousChildrenIds, diffChange.originalStart, diffChange.originalStart + diffChange.originalLength)); + this.onRemoveItems(new ArrayNavigator(previousChildrenIds, diffChange.originalStart, diffChange.originalStart + diffChange.originalLength)); } if (diffChange.modifiedLength > 0) { let beforeItem: Model.Item | null = afterModelItems[diffChange.modifiedStart - 1] || item; beforeItem = beforeItem.getDepth() > 0 ? beforeItem : null; - this.onInsertItems(new ArrayIterator(afterModelItems, diffChange.modifiedStart, diffChange.modifiedStart + diffChange.modifiedLength), beforeItem ? beforeItem.id : null); + this.onInsertItems(new ArrayNavigator(afterModelItems, diffChange.modifiedStart, diffChange.modifiedStart + diffChange.modifiedLength), beforeItem ? beforeItem.id : null); } } } else if (skipDiff || diff.length) { - this.onRemoveItems(new ArrayIterator(previousChildrenIds)); - this.onInsertItems(new ArrayIterator(afterModelItems), item.getDepth() > 0 ? item.id : null); + this.onRemoveItems(new ArrayNavigator(previousChildrenIds)); + this.onInsertItems(new ArrayNavigator(afterModelItems), item.getDepth() > 0 ? item.id : null); } if (skipDiff || diff.length) { @@ -985,7 +986,7 @@ export class TreeView extends HeightMap { let viewItem = this.items[item.id]; if (viewItem) { viewItem.expanded = false; - this.onRemoveItems(new MappedIterator(item.getNavigator(), item => item && item.id)); + this.onRemoveItems(new MappedNavigator(item.getNavigator(), item => item && item.id)); this.onRowsChanged(); } } diff --git a/src/vs/base/parts/tree/browser/treeViewModel.ts b/src/vs/base/parts/tree/browser/treeViewModel.ts index 876cc17588..434a790254 100644 --- a/src/vs/base/parts/tree/browser/treeViewModel.ts +++ b/src/vs/base/parts/tree/browser/treeViewModel.ts @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { INextIterator, ArrayIterator } from 'vs/base/common/iterator'; +import { ArrayNavigator, INavigator } from 'vs/base/common/navigator'; import { Item } from './treeModel'; export interface IViewItem { @@ -23,7 +23,7 @@ export class HeightMap { return !last ? 0 : last.top + last.height; } - onInsertItems(iterator: INextIterator, afterItemId: string | null = null): number | undefined { + onInsertItems(iterator: INavigator, afterItemId: string | null = null): number | undefined { let item: Item | null = null; let viewItem: IViewItem; let i: number, j: number; @@ -81,7 +81,7 @@ export class HeightMap { } // Contiguous items - onRemoveItems(iterator: INextIterator): void { + onRemoveItems(iterator: INavigator): void { let itemId: string | null = null; let viewItem: IViewItem; let startIndex: number | null = null; @@ -126,11 +126,11 @@ export class HeightMap { onRefreshItemSet(items: Item[]): void { let sortedItems = items.sort((a, b) => this.indexes[a.id] - this.indexes[b.id]); - this.onRefreshItems(new ArrayIterator(sortedItems)); + this.onRefreshItems(new ArrayNavigator(sortedItems)); } // Ordered, but not necessarily contiguous items - onRefreshItems(iterator: INextIterator): void { + onRefreshItems(iterator: INavigator): void { let item: Item | null = null; let viewItem: IViewItem; let newHeight: number; diff --git a/src/vs/base/parts/tree/test/browser/treeViewModel.test.ts b/src/vs/base/parts/tree/test/browser/treeViewModel.test.ts index 541660bea9..029bcb3150 100644 --- a/src/vs/base/parts/tree/test/browser/treeViewModel.test.ts +++ b/src/vs/base/parts/tree/test/browser/treeViewModel.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { ArrayIterator } from 'vs/base/common/iterator'; +import { ArrayNavigator } from 'vs/base/common/navigator'; import { HeightMap, IViewItem } from 'vs/base/parts/tree/browser/treeViewModel'; function makeItem(id: any, height: any): any { @@ -139,7 +139,7 @@ suite('TreeView - HeightMap', () => { }); test('onRemoveItems at beginning', () => { - rangeMap.onRemoveItems(new ArrayIterator(['a', 'b'])); + rangeMap.onRemoveItems(new ArrayNavigator(['a', 'b'])); assert.equal(rangeMap.itemAt(0), 'c'); assert.equal(rangeMap.itemAt(24), 'c'); @@ -149,7 +149,7 @@ suite('TreeView - HeightMap', () => { }); test('onRemoveItems in middle', () => { - rangeMap.onRemoveItems(new ArrayIterator(['c'])); + rangeMap.onRemoveItems(new ArrayNavigator(['c'])); assert.equal(rangeMap.itemAt(0), 'a'); assert.equal(rangeMap.itemAt(2), 'a'); @@ -161,7 +161,7 @@ suite('TreeView - HeightMap', () => { }); test('onRemoveItems at end', () => { - rangeMap.onRemoveItems(new ArrayIterator(['c', 'd'])); + rangeMap.onRemoveItems(new ArrayNavigator(['c', 'd'])); assert.equal(rangeMap.itemAt(0), 'a'); assert.equal(rangeMap.itemAt(2), 'a'); diff --git a/src/vs/base/test/browser/ui/tree/compressedObjectTreeModel.test.ts b/src/vs/base/test/browser/ui/tree/compressedObjectTreeModel.test.ts index 9601db91f0..3ca473e5bf 100644 --- a/src/vs/base/test/browser/ui/tree/compressedObjectTreeModel.test.ts +++ b/src/vs/base/test/browser/ui/tree/compressedObjectTreeModel.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import { compress, ICompressedTreeElement, ICompressedTreeNode, decompress, CompressedObjectTreeModel } from 'vs/base/browser/ui/tree/compressedObjectTreeModel'; -import { Iterator } from 'vs/base/common/iterator'; +import { Iterable } from 'vs/base/common/iterator'; import { ITreeNode } from 'vs/base/browser/ui/tree/tree'; import { ISpliceable } from 'vs/base/common/sequence'; @@ -16,7 +16,7 @@ interface IResolvedCompressedTreeElement extends ICompressedTreeElement { function resolve(treeElement: ICompressedTreeElement): IResolvedCompressedTreeElement { const result: any = { element: treeElement.element }; - const children = Iterator.collect(Iterator.map(Iterator.from(treeElement.children), resolve)); + const children = [...Iterable.map(Iterable.from(treeElement.children), resolve)]; if (treeElement.incompressible) { result.incompressible = true; @@ -315,25 +315,25 @@ suite('CompressedObjectTree', function () { const list: ITreeNode>[] = []; const model = new CompressedObjectTreeModel('test', toSpliceable(list)); - model.setChildren(null, Iterator.fromArray([ + model.setChildren(null, [ { element: 0 }, { element: 1 }, { element: 2 } - ])); + ]); assert.deepEqual(toArray(list), [[0], [1], [2]]); assert.equal(model.size, 3); - model.setChildren(null, Iterator.fromArray([ + model.setChildren(null, [ { element: 3 }, { element: 4 }, { element: 5 }, - ])); + ]); assert.deepEqual(toArray(list), [[3], [4], [5]]); assert.equal(model.size, 3); - model.setChildren(null, Iterator.empty()); + model.setChildren(null); assert.deepEqual(toArray(list), []); assert.equal(model.size, 0); }); @@ -342,34 +342,34 @@ suite('CompressedObjectTree', function () { const list: ITreeNode>[] = []; const model = new CompressedObjectTreeModel('test', toSpliceable(list)); - model.setChildren(null, Iterator.fromArray([ + model.setChildren(null, [ { - element: 0, children: Iterator.fromArray([ + element: 0, children: [ { element: 10 }, { element: 11 }, { element: 12 }, - ]) + ] }, { element: 1 }, { element: 2 } - ])); + ]); assert.deepEqual(toArray(list), [[0], [10], [11], [12], [1], [2]]); assert.equal(model.size, 6); - model.setChildren(12, Iterator.fromArray([ + model.setChildren(12, [ { element: 120 }, { element: 121 } - ])); + ]); assert.deepEqual(toArray(list), [[0], [10], [11], [12], [120], [121], [1], [2]]); assert.equal(model.size, 8); - model.setChildren(0, Iterator.empty()); + model.setChildren(0); assert.deepEqual(toArray(list), [[0], [1], [2]]); assert.equal(model.size, 3); - model.setChildren(null, Iterator.empty()); + model.setChildren(null); assert.deepEqual(toArray(list), []); assert.equal(model.size, 0); }); @@ -378,50 +378,50 @@ suite('CompressedObjectTree', function () { const list: ITreeNode>[] = []; const model = new CompressedObjectTreeModel('test', toSpliceable(list)); - model.setChildren(null, Iterator.fromArray([ + model.setChildren(null, [ { - element: 1, children: Iterator.fromArray([{ - element: 11, children: Iterator.fromArray([{ - element: 111, children: Iterator.fromArray([ + element: 1, children: [{ + element: 11, children: [{ + element: 111, children: [ { element: 1111 }, { element: 1112 }, { element: 1113 }, - ]) - }]) - }]) + ] + }] + }] } - ])); + ]); assert.deepEqual(toArray(list), [[1, 11, 111], [1111], [1112], [1113]]); assert.equal(model.size, 6); - model.setChildren(11, Iterator.fromArray([ + model.setChildren(11, [ { element: 111 }, { element: 112 }, { element: 113 }, - ])); + ]); assert.deepEqual(toArray(list), [[1, 11], [111], [112], [113]]); assert.equal(model.size, 5); - model.setChildren(113, Iterator.fromArray([ + model.setChildren(113, [ { element: 1131 } - ])); + ]); assert.deepEqual(toArray(list), [[1, 11], [111], [112], [113, 1131]]); assert.equal(model.size, 6); - model.setChildren(1131, Iterator.fromArray([ + model.setChildren(1131, [ { element: 1132 } - ])); + ]); assert.deepEqual(toArray(list), [[1, 11], [111], [112], [113, 1131, 1132]]); assert.equal(model.size, 7); - model.setChildren(1131, Iterator.fromArray([ + model.setChildren(1131, [ { element: 1132 }, { element: 1133 }, - ])); + ]); assert.deepEqual(toArray(list), [[1, 11], [111], [112], [113, 1131], [1132], [1133]]); assert.equal(model.size, 8); diff --git a/src/vs/base/test/browser/ui/tree/indexTreeModel.test.ts b/src/vs/base/test/browser/ui/tree/indexTreeModel.test.ts index c7a8db9776..e3d2eb5afc 100644 --- a/src/vs/base/test/browser/ui/tree/indexTreeModel.test.ts +++ b/src/vs/base/test/browser/ui/tree/indexTreeModel.test.ts @@ -6,7 +6,6 @@ import * as assert from 'assert'; import { ITreeNode, ITreeFilter, TreeVisibility } from 'vs/base/browser/ui/tree/tree'; import { ISpliceable } from 'vs/base/common/sequence'; -import { Iterator } from 'vs/base/common/iterator'; import { IndexTreeModel, IIndexTreeNode } from 'vs/base/browser/ui/tree/indexTreeModel'; function toSpliceable(arr: T[]): ISpliceable { @@ -34,11 +33,11 @@ suite('IndexTreeModel', function () { const list: ITreeNode[] = []; const model = new IndexTreeModel('test', toSpliceable(list), -1); - model.splice([0], 0, Iterator.fromArray([ + model.splice([0], 0, [ { element: 0 }, { element: 1 }, { element: 2 } - ])); + ]); assert.deepEqual(list.length, 3); assert.deepEqual(list[0].element, 0); @@ -56,17 +55,17 @@ suite('IndexTreeModel', function () { const list: ITreeNode[] = []; const model = new IndexTreeModel('test', toSpliceable(list), -1); - model.splice([0], 0, Iterator.fromArray([ + model.splice([0], 0, [ { - element: 0, children: Iterator.fromArray([ + element: 0, children: [ { element: 10 }, { element: 11 }, { element: 12 }, - ]) + ] }, { element: 1 }, { element: 2 } - ])); + ]); assert.deepEqual(list.length, 6); assert.deepEqual(list[0].element, 0); @@ -93,17 +92,17 @@ suite('IndexTreeModel', function () { const list: ITreeNode[] = []; const model = new IndexTreeModel('test', toSpliceable(list), -1); - model.splice([0], 0, Iterator.fromArray([ + model.splice([0], 0, [ { - element: 0, collapsed: true, children: Iterator.fromArray([ + element: 0, collapsed: true, children: [ { element: 10 }, { element: 11 }, { element: 12 }, - ]) + ] }, { element: 1 }, { element: 2 } - ])); + ]); assert.deepEqual(list.length, 3); assert.deepEqual(list[0].element, 0); @@ -121,11 +120,11 @@ suite('IndexTreeModel', function () { const list: ITreeNode[] = []; const model = new IndexTreeModel('test', toSpliceable(list), -1); - model.splice([0], 0, Iterator.fromArray([ + model.splice([0], 0, [ { element: 0 }, { element: 1 }, { element: 2 } - ])); + ]); assert.deepEqual(list.length, 3); @@ -146,17 +145,17 @@ suite('IndexTreeModel', function () { const list: ITreeNode[] = []; const model = new IndexTreeModel('test', toSpliceable(list), -1); - model.splice([0], 0, Iterator.fromArray([ + model.splice([0], 0, [ { - element: 0, children: Iterator.fromArray([ + element: 0, children: [ { element: 10 }, { element: 11 }, { element: 12 }, - ]) + ] }, { element: 1 }, { element: 2 } - ])); + ]); assert.deepEqual(list.length, 6); @@ -180,17 +179,17 @@ suite('IndexTreeModel', function () { const list: ITreeNode[] = []; const model = new IndexTreeModel('test', toSpliceable(list), -1); - model.splice([0], 0, Iterator.fromArray([ + model.splice([0], 0, [ { - element: 0, children: Iterator.fromArray([ + element: 0, children: [ { element: 10 }, { element: 11 }, { element: 12 }, - ]) + ] }, { element: 1 }, { element: 2 } - ])); + ]); assert.deepEqual(list.length, 6); @@ -208,17 +207,17 @@ suite('IndexTreeModel', function () { const list: ITreeNode[] = []; const model = new IndexTreeModel('test', toSpliceable(list), -1); - model.splice([0], 0, Iterator.fromArray([ + model.splice([0], 0, [ { - element: 0, collapsed: true, children: Iterator.fromArray([ + element: 0, collapsed: true, children: [ { element: 10 }, { element: 11 }, { element: 12 }, - ]) + ] }, { element: 1 }, { element: 2 } - ])); + ]); assert.deepEqual(list.length, 3); @@ -233,17 +232,17 @@ suite('IndexTreeModel', function () { const list: ITreeNode[] = []; const model = new IndexTreeModel('test', toSpliceable(list), -1); - model.splice([0], 0, Iterator.fromArray([ + model.splice([0], 0, [ { - element: 0, children: Iterator.fromArray([ + element: 0, children: [ { element: 10 }, { element: 11 }, { element: 12 }, - ]) + ] }, { element: 1 }, { element: 2 } - ])); + ]); assert.deepEqual(list.length, 6); @@ -264,17 +263,17 @@ suite('IndexTreeModel', function () { const list: ITreeNode[] = []; const model = new IndexTreeModel('test', toSpliceable(list), -1); - model.splice([0], 0, Iterator.fromArray([ + model.splice([0], 0, [ { - element: 0, collapsed: true, children: Iterator.fromArray([ + element: 0, collapsed: true, children: [ { element: 10 }, { element: 11 }, { element: 12 }, - ]) + ] }, { element: 1 }, { element: 2 } - ])); + ]); assert.deepEqual(list.length, 3); @@ -304,7 +303,7 @@ suite('IndexTreeModel', function () { const list: ITreeNode[] = []; const model = new IndexTreeModel('test', toSpliceable(list), -1); - model.splice([0], 0, Iterator.fromArray([ + model.splice([0], 0, [ { element: 1, children: [ { @@ -319,7 +318,7 @@ suite('IndexTreeModel', function () { { element: 21 } ] } - ])); + ]); assert.deepEqual(list.length, 5); assert.deepEqual(toArray(list), [1, 11, 111, 2, 21]); @@ -337,13 +336,13 @@ suite('IndexTreeModel', function () { const list: ITreeNode[] = []; const model = new IndexTreeModel('test', toSpliceable(list), -1); - model.splice([0], 0, Iterator.fromArray([ + model.splice([0], 0, [ { - element: 0, children: Iterator.fromArray([ + element: 0, children: [ { element: 10 } - ]) + ] } - ])); + ]); assert.deepEqual(list.length, 2); @@ -406,7 +405,7 @@ suite('IndexTreeModel', function () { const model = new IndexTreeModel('test', toSpliceable(list), -1, { filter }); - model.splice([0], 0, Iterator.fromArray([ + model.splice([0], 0, [ { element: 0, children: [ { element: 1 }, @@ -418,7 +417,7 @@ suite('IndexTreeModel', function () { { element: 7 } ] } - ])); + ]); assert.deepEqual(list.length, 4); assert.deepEqual(toArray(list), [0, 2, 4, 6]); @@ -440,14 +439,14 @@ suite('IndexTreeModel', function () { const model = new IndexTreeModel('test', toSpliceable(list), -1, { filter }); - model.splice([0], 0, Iterator.fromArray([ + model.splice([0], 0, [ { element: 0, children: [ { element: 1 }, { element: 2 } ] } - ])); + ]); assert.deepEqual(toArray(list), []); }); @@ -463,7 +462,7 @@ suite('IndexTreeModel', function () { const model = new IndexTreeModel('test', toSpliceable(list), -1, { filter }); - model.splice([0], 0, Iterator.fromArray([ + model.splice([0], 0, [ { element: 0, children: [ { element: 1 }, @@ -475,7 +474,7 @@ suite('IndexTreeModel', function () { { element: 7 } ] }, - ])); + ]); assert.deepEqual(toArray(list), [0, 1, 2, 3, 4, 5, 6, 7]); @@ -502,7 +501,7 @@ suite('IndexTreeModel', function () { const model = new IndexTreeModel('test', toSpliceable(list), 'root', { filter }); - model.splice([0], 0, Iterator.fromArray([ + model.splice([0], 0, [ { element: 'vscode', children: [ { element: '.build' }, @@ -522,7 +521,7 @@ suite('IndexTreeModel', function () { } ] }, - ])); + ]); assert.deepEqual(list.length, 10); @@ -548,7 +547,7 @@ suite('IndexTreeModel', function () { const model = new IndexTreeModel('test', toSpliceable(list), 'root', { filter }); - model.splice([0], 0, Iterator.fromArray([ + model.splice([0], 0, [ { element: 'vscode', children: [ { element: '.build' }, @@ -568,7 +567,7 @@ suite('IndexTreeModel', function () { } ] }, - ])); + ]); assert.deepEqual(list.length, 10); @@ -594,7 +593,7 @@ suite('IndexTreeModel', function () { const model = new IndexTreeModel('test', toSpliceable(list), 'root', { filter }); - model.splice([0], 0, Iterator.fromArray([ + model.splice([0], 0, [ { element: 'vscode', collapsed: true, children: [ { element: '.build' }, @@ -614,7 +613,7 @@ suite('IndexTreeModel', function () { } ] }, - ])); + ]); assert.deepEqual(toArray(list), ['vscode']); @@ -642,17 +641,17 @@ suite('IndexTreeModel', function () { const list: IIndexTreeNode[] = []; const model = new IndexTreeModel('test', toSpliceable(list), -1); - model.splice([0], 0, Iterator.fromArray([ + model.splice([0], 0, [ { - element: 0, children: Iterator.fromArray([ + element: 0, children: [ { element: 10 }, { element: 11 }, { element: 12 }, - ]) + ] }, { element: 1 }, { element: 2 } - ])); + ]); assert.deepEqual(model.getNodeLocation(list[0]), [0]); assert.deepEqual(model.getNodeLocation(list[1]), [0, 0]); @@ -672,7 +671,7 @@ suite('IndexTreeModel', function () { const model = new IndexTreeModel('test', toSpliceable(list), -1, { filter }); - model.splice([0], 0, Iterator.fromArray([ + model.splice([0], 0, [ { element: 0, children: [ { element: 1 }, @@ -684,7 +683,7 @@ suite('IndexTreeModel', function () { { element: 7 } ] } - ])); + ]); assert.deepEqual(model.getNodeLocation(list[0]), [0]); assert.deepEqual(model.getNodeLocation(list[1]), [0, 1]); @@ -704,11 +703,11 @@ suite('IndexTreeModel', function () { const model = new IndexTreeModel('test', toSpliceable(list), 'root', { filter }); - model.splice([0], 0, Iterator.fromArray([ + model.splice([0], 0, [ { element: 'silver' }, { element: 'gold' }, { element: 'platinum' } - ])); + ]); assert.deepEqual(toArray(list), ['silver', 'gold', 'platinum']); @@ -716,11 +715,11 @@ suite('IndexTreeModel', function () { model.refilter(); assert.deepEqual(toArray(list), ['platinum']); - model.splice([0], Number.POSITIVE_INFINITY, Iterator.fromArray([ + model.splice([0], Number.POSITIVE_INFINITY, [ { element: 'silver' }, { element: 'gold' }, { element: 'platinum' } - ])); + ]); assert.deepEqual(toArray(list), ['platinum']); model.refilter(); diff --git a/src/vs/base/test/browser/ui/tree/objectTree.test.ts b/src/vs/base/test/browser/ui/tree/objectTree.test.ts index 3a5635c27d..ea333c0748 100644 --- a/src/vs/base/test/browser/ui/tree/objectTree.test.ts +++ b/src/vs/base/test/browser/ui/tree/objectTree.test.ts @@ -7,7 +7,6 @@ import * as assert from 'assert'; import { ITreeNode, ITreeRenderer } from 'vs/base/browser/ui/tree/tree'; import { IListVirtualDelegate, IIdentityProvider } from 'vs/base/browser/ui/list/list'; import { ObjectTree, CompressibleObjectTree, ICompressibleTreeRenderer } from 'vs/base/browser/ui/tree/objectTree'; -import { Iterator } from 'vs/base/common/iterator'; import { ICompressedTreeNode } from 'vs/base/browser/ui/tree/compressedObjectTreeModel'; suite('ObjectTree', function () { @@ -46,17 +45,17 @@ suite('ObjectTree', function () { }); test('should be able to navigate', () => { - tree.setChildren(null, Iterator.fromArray([ + tree.setChildren(null, [ { - element: 0, children: Iterator.fromArray([ + element: 0, children: [ { element: 10 }, { element: 11 }, { element: 12 }, - ]) + ] }, { element: 1 }, { element: 2 } - ])); + ]); const navigator = tree.navigate(); @@ -87,17 +86,17 @@ suite('ObjectTree', function () { }); test('should skip collapsed nodes', () => { - tree.setChildren(null, Iterator.fromArray([ + tree.setChildren(null, [ { - element: 0, collapsed: true, children: Iterator.fromArray([ + element: 0, collapsed: true, children: [ { element: 10 }, { element: 11 }, { element: 12 }, - ]) + ] }, { element: 1 }, { element: 2 } - ])); + ]); const navigator = tree.navigate(); @@ -118,17 +117,17 @@ suite('ObjectTree', function () { test('should skip filtered elements', () => { filter = el => el % 2 === 0; - tree.setChildren(null, Iterator.fromArray([ + tree.setChildren(null, [ { - element: 0, children: Iterator.fromArray([ + element: 0, children: [ { element: 10 }, { element: 11 }, { element: 12 }, - ]) + ] }, { element: 1 }, { element: 2 } - ])); + ]); const navigator = tree.navigate(); @@ -150,17 +149,17 @@ suite('ObjectTree', function () { }); test('should be able to start from node', () => { - tree.setChildren(null, Iterator.fromArray([ + tree.setChildren(null, [ { - element: 0, children: Iterator.fromArray([ + element: 0, children: [ { element: 10 }, { element: 11 }, { element: 12 }, - ]) + ] }, { element: 1 }, { element: 2 } - ])); + ]); const navigator = tree.navigate(1); @@ -291,50 +290,50 @@ suite('CompressibleObjectTree', function () { const tree = new CompressibleObjectTree('test', container, new Delegate(), [new Renderer()]); tree.layout(200); - tree.setChildren(null, Iterator.fromArray([ + tree.setChildren(null, [ { - element: 1, children: Iterator.fromArray([{ - element: 11, children: Iterator.fromArray([{ - element: 111, children: Iterator.fromArray([ + element: 1, children: [{ + element: 11, children: [{ + element: 111, children: [ { element: 1111 }, { element: 1112 }, { element: 1113 }, - ]) - }]) - }]) + ] + }] + }] } - ])); + ]); let rows = toArray(container.querySelectorAll('.monaco-tl-contents')).map(row => row.textContent); assert.deepEqual(rows, ['1/11/111', '1111', '1112', '1113']); - tree.setChildren(11, Iterator.fromArray([ + tree.setChildren(11, [ { element: 111 }, { element: 112 }, { element: 113 }, - ])); + ]); rows = toArray(container.querySelectorAll('.monaco-tl-contents')).map(row => row.textContent); assert.deepEqual(rows, ['1/11', '111', '112', '113']); - tree.setChildren(113, Iterator.fromArray([ + tree.setChildren(113, [ { element: 1131 } - ])); + ]); rows = toArray(container.querySelectorAll('.monaco-tl-contents')).map(row => row.textContent); assert.deepEqual(rows, ['1/11', '111', '112', '113/1131']); - tree.setChildren(1131, Iterator.fromArray([ + tree.setChildren(1131, [ { element: 1132 } - ])); + ]); rows = toArray(container.querySelectorAll('.monaco-tl-contents')).map(row => row.textContent); assert.deepEqual(rows, ['1/11', '111', '112', '113/1131/1132']); - tree.setChildren(1131, Iterator.fromArray([ + tree.setChildren(1131, [ { element: 1132 }, { element: 1133 }, - ])); + ]); rows = toArray(container.querySelectorAll('.monaco-tl-contents')).map(row => row.textContent); assert.deepEqual(rows, ['1/11', '111', '112', '113/1131', '1132', '1133']); @@ -348,19 +347,19 @@ suite('CompressibleObjectTree', function () { const tree = new CompressibleObjectTree('test', container, new Delegate(), [new Renderer()]); tree.layout(200); - tree.setChildren(null, Iterator.fromArray([ + tree.setChildren(null, [ { - element: 1, children: Iterator.fromArray([{ - element: 11, children: Iterator.fromArray([{ - element: 111, children: Iterator.fromArray([ + element: 1, children: [{ + element: 11, children: [{ + element: 111, children: [ { element: 1111 }, { element: 1112 }, { element: 1113 }, - ]) - }]) - }]) + ] + }] + }] } - ])); + ]); let rows = toArray(container.querySelectorAll('.monaco-tl-contents')).map(row => row.textContent); assert.deepEqual(rows, ['1/11/111', '1111', '1112', '1113']); diff --git a/src/vs/base/test/browser/ui/tree/objectTreeModel.test.ts b/src/vs/base/test/browser/ui/tree/objectTreeModel.test.ts index f1fc8c4166..cd6c9fc389 100644 --- a/src/vs/base/test/browser/ui/tree/objectTreeModel.test.ts +++ b/src/vs/base/test/browser/ui/tree/objectTreeModel.test.ts @@ -7,7 +7,6 @@ import * as assert from 'assert'; import { ITreeNode } from 'vs/base/browser/ui/tree/tree'; import { ISpliceable } from 'vs/base/common/sequence'; import { ObjectTreeModel } from 'vs/base/browser/ui/tree/objectTreeModel'; -import { Iterator } from 'vs/base/common/iterator'; function toSpliceable(arr: T[]): ISpliceable { return { @@ -35,25 +34,25 @@ suite('ObjectTreeModel', function () { const list: ITreeNode[] = []; const model = new ObjectTreeModel('test', toSpliceable(list)); - model.setChildren(null, Iterator.fromArray([ + model.setChildren(null, [ { element: 0 }, { element: 1 }, { element: 2 } - ])); + ]); assert.deepEqual(toArray(list), [0, 1, 2]); assert.equal(model.size, 3); - model.setChildren(null, Iterator.fromArray([ + model.setChildren(null, [ { element: 3 }, { element: 4 }, { element: 5 }, - ])); + ]); assert.deepEqual(toArray(list), [3, 4, 5]); assert.equal(model.size, 3); - model.setChildren(null, Iterator.empty()); + model.setChildren(null); assert.deepEqual(toArray(list), []); assert.equal(model.size, 0); }); @@ -62,34 +61,34 @@ suite('ObjectTreeModel', function () { const list: ITreeNode[] = []; const model = new ObjectTreeModel('test', toSpliceable(list)); - model.setChildren(null, Iterator.fromArray([ + model.setChildren(null, [ { - element: 0, children: Iterator.fromArray([ + element: 0, children: [ { element: 10 }, { element: 11 }, { element: 12 }, - ]) + ] }, { element: 1 }, { element: 2 } - ])); + ]); assert.deepEqual(toArray(list), [0, 10, 11, 12, 1, 2]); assert.equal(model.size, 6); - model.setChildren(12, Iterator.fromArray([ + model.setChildren(12, [ { element: 120 }, { element: 121 } - ])); + ]); assert.deepEqual(toArray(list), [0, 10, 11, 12, 120, 121, 1, 2]); assert.equal(model.size, 8); - model.setChildren(0, Iterator.empty()); + model.setChildren(0); assert.deepEqual(toArray(list), [0, 1, 2]); assert.equal(model.size, 3); - model.setChildren(null, Iterator.empty()); + model.setChildren(null); assert.deepEqual(toArray(list), []); assert.equal(model.size, 0); }); @@ -98,16 +97,16 @@ suite('ObjectTreeModel', function () { const list: ITreeNode[] = []; const model = new ObjectTreeModel('test', toSpliceable(list)); - model.setChildren(null, Iterator.fromArray([ + model.setChildren(null, [ { element: 0, collapsed: true } - ])); + ]); assert.deepEqual(toArray(list), [0]); - model.setChildren(0, Iterator.fromArray([ + model.setChildren(0, [ { element: 1 }, { element: 2 } - ])); + ]); assert.deepEqual(toArray(list), [0]); diff --git a/src/vs/base/test/common/filters.test.ts b/src/vs/base/test/common/filters.test.ts index aeb8623984..0368ce04f8 100644 --- a/src/vs/base/test/common/filters.test.ts +++ b/src/vs/base/test/common/filters.test.ts @@ -343,6 +343,36 @@ suite('Filters', () => { ); }); + test('Freeze when fjfj -> jfjf, https://github.com/microsoft/vscode/issues/91807', function () { + assertMatches( + 'jfjfj', + 'fjfjfjfjfjfjfjfjfjfjfj', + undefined, fuzzyScore + ); + assertMatches( + 'jfjfjfjfjfjfjfjfjfj', + 'fjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfj', + undefined, fuzzyScore + ); + assertMatches( + 'jfjfjfjfjfjfjfjfjfjjfjfjfjfjfjfjfjfjfjjfjfjfjfjfjfjfjfjfjjfjfjfjfjfjfjfjfjfjjfjfjfjfjfjfjfjfjfjjfjfjfjfjfjfjfjfjfj', + 'fjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfj', + undefined, fuzzyScore + ); + assertMatches( + 'jfjfjfjfjfjfjfjfjfj', + 'fJfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfj', + 'f^J^f^j^f^j^f^j^f^j^f^j^f^j^f^j^f^j^f^jfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfj', // strong match + fuzzyScore + ); + assertMatches( + 'jfjfjfjfjfjfjfjfjfj', + 'fjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfj', + 'f^j^f^j^f^j^f^j^f^j^f^j^f^j^f^j^f^j^f^jfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfjfj', // any match + fuzzyScore, { firstMatchCanBeWeak: true } + ); + }); + test('fuzzyScore, issue #26423', function () { assertMatches('baba', 'abababab', undefined, fuzzyScore); diff --git a/src/vs/base/test/common/iterator.test.ts b/src/vs/base/test/common/iterator.test.ts index 41fb8b43ec..03b35db4ca 100644 --- a/src/vs/base/test/common/iterator.test.ts +++ b/src/vs/base/test/common/iterator.test.ts @@ -4,19 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { Iterator, Iterable } from 'vs/base/common/iterator'; - -suite('Iterator', () => { - test('concat', () => { - const first = Iterator.fromArray([1, 2, 3]); - const second = Iterator.fromArray([4, 5, 6]); - const third = Iterator.fromArray([7, 8, 9]); - const actualIterator = Iterator.concat(first, second, third); - const actual = Iterator.collect(actualIterator); - - assert.deepEqual(actual, [1, 2, 3, 4, 5, 6, 7, 8, 9]); - }); -}); +import { Iterable } from 'vs/base/common/iterator'; suite('Iterable', function () { diff --git a/src/vs/base/test/common/map.test.ts b/src/vs/base/test/common/map.test.ts index a53e145974..f331875048 100644 --- a/src/vs/base/test/common/map.test.ts +++ b/src/vs/base/test/common/map.test.ts @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ResourceMap, TernarySearchTree, PathIterator, StringIterator, LinkedMap, Touch, LRUCache } from 'vs/base/common/map'; +import { ResourceMap, TernarySearchTree, PathIterator, StringIterator, LinkedMap, Touch, LRUCache, UriIterator } from 'vs/base/common/map'; import * as assert from 'assert'; import { URI } from 'vs/base/common/uri'; @@ -312,7 +312,64 @@ suite('Map', () => { assert.equal(iter.hasNext(), false); }); - function assertTernarySearchTree(trie: TernarySearchTree, ...elements: [string, E][]) { + test('URIIterator', function () { + const iter = new UriIterator(); + iter.reset(URI.parse('file:///usr/bin/file.txt')); + + assert.equal(iter.value(), 'file'); + assert.equal(iter.cmp('FILE'), 0); + assert.equal(iter.hasNext(), true); + iter.next(); + + assert.equal(iter.value(), 'usr'); + assert.equal(iter.hasNext(), true); + iter.next(); + + assert.equal(iter.value(), 'bin'); + assert.equal(iter.hasNext(), true); + iter.next(); + + assert.equal(iter.value(), 'file.txt'); + assert.equal(iter.hasNext(), false); + + + iter.reset(URI.parse('file://share/usr/bin/file.txt?foo')); + + // scheme + assert.equal(iter.value(), 'file'); + assert.equal(iter.cmp('FILE'), 0); + assert.equal(iter.hasNext(), true); + iter.next(); + + // authority + assert.equal(iter.value(), 'share'); + assert.equal(iter.cmp('SHARe'), 0); + assert.equal(iter.hasNext(), true); + iter.next(); + + // path + assert.equal(iter.value(), 'usr'); + assert.equal(iter.hasNext(), true); + iter.next(); + + // path + assert.equal(iter.value(), 'bin'); + assert.equal(iter.hasNext(), true); + iter.next(); + + // path + assert.equal(iter.value(), 'file.txt'); + assert.equal(iter.hasNext(), true); + iter.next(); + + // query + assert.equal(iter.value(), 'foo'); + assert.equal(iter.cmp('z') > 0, true); + assert.equal(iter.cmp('a') < 0, true); + assert.equal(iter.hasNext(), false); + }); + + function assertTernarySearchTree(trie: TernarySearchTree, ...elements: [string, E][]) { const map = new Map(); for (const [key, value] of elements) { map.set(key, value); @@ -378,7 +435,7 @@ suite('Map', () => { }); test('TernarySearchTree - basics', function () { - let trie = new TernarySearchTree(new StringIterator()); + let trie = new TernarySearchTree(new StringIterator()); trie.set('foo', 1); trie.set('bar', 2); @@ -408,7 +465,7 @@ suite('Map', () => { }); test('TernarySearchTree - delete & cleanup', function () { - let trie = new TernarySearchTree(new StringIterator()); + let trie = new TernarySearchTree(new StringIterator()); trie.set('foo', 1); trie.set('foobar', 2); trie.set('bar', 3); @@ -418,7 +475,7 @@ suite('Map', () => { }); test('TernarySearchTree (PathSegments) - basics', function () { - let trie = new TernarySearchTree(new PathIterator()); + let trie = new TernarySearchTree(new PathIterator()); trie.set('/user/foo/bar', 1); trie.set('/user/foo', 2); @@ -442,7 +499,7 @@ suite('Map', () => { test('TernarySearchTree (PathSegments) - lookup', function () { - const map = new TernarySearchTree(new PathIterator()); + const map = new TernarySearchTree(new PathIterator()); map.set('/user/foo/bar', 1); map.set('/user/foo', 2); map.set('/user/foo/flip/flop', 3); @@ -456,7 +513,7 @@ suite('Map', () => { test('TernarySearchTree (PathSegments) - superstr', function () { - const map = new TernarySearchTree(new PathIterator()); + const map = new TernarySearchTree(new PathIterator()); map.set('/user/foo/bar', 1); map.set('/user/foo', 2); map.set('/user/foo/flip/flop', 3); @@ -493,6 +550,100 @@ suite('Map', () => { assert.equal(map.findSuperstr('/userr'), undefined); }); + + test('TernarySearchTree (URI) - basics', function () { + let trie = new TernarySearchTree(new UriIterator()); + + trie.set(URI.file('/user/foo/bar'), 1); + trie.set(URI.file('/user/foo'), 2); + trie.set(URI.file('/user/foo/flip/flop'), 3); + + assert.equal(trie.get(URI.file('/user/foo/bar')), 1); + assert.equal(trie.get(URI.file('/user/foo')), 2); + assert.equal(trie.get(URI.file('/user/foo/flip/flop')), 3); + + assert.equal(trie.findSubstr(URI.file('/user/bar')), undefined); + assert.equal(trie.findSubstr(URI.file('/user/foo')), 2); + assert.equal(trie.findSubstr(URI.file('/user/foo/ba')), 2); + assert.equal(trie.findSubstr(URI.file('/user/foo/far/boo')), 2); + assert.equal(trie.findSubstr(URI.file('/user/foo/bar')), 1); + assert.equal(trie.findSubstr(URI.file('/user/foo/bar/far/boo')), 1); + }); + + test('TernarySearchTree (URI) - lookup', function () { + + const map = new TernarySearchTree(new UriIterator()); + map.set(URI.parse('http://foo.bar/user/foo/bar'), 1); + map.set(URI.parse('http://foo.bar/user/foo?query'), 2); + map.set(URI.parse('http://foo.bar/user/foo?QUERY'), 3); + map.set(URI.parse('http://foo.bar/user/foo/flip/flop'), 3); + + assert.equal(map.get(URI.parse('http://foo.bar/foo')), undefined); + assert.equal(map.get(URI.parse('http://foo.bar/user')), undefined); + assert.equal(map.get(URI.parse('http://foo.bar/user/foo/bar')), 1); + assert.equal(map.get(URI.parse('http://foo.bar/user/foo?query')), 2); + assert.equal(map.get(URI.parse('http://foo.bar/user/foo?Query')), undefined); + assert.equal(map.get(URI.parse('http://foo.bar/user/foo?QUERY')), 3); + assert.equal(map.get(URI.parse('http://foo.bar/user/foo/bar/boo')), undefined); + }); + + test('TernarySearchTree (PathSegments) - superstr', function () { + + const map = new TernarySearchTree(new UriIterator()); + map.set(URI.file('/user/foo/bar'), 1); + map.set(URI.file('/user/foo'), 2); + map.set(URI.file('/user/foo/flip/flop'), 3); + map.set(URI.file('/usr/foo'), 4); + + let item: IteratorResult; + let iter = map.findSuperstr(URI.file('/user'))!; + + item = iter.next(); + assert.equal(item.value, 2); + assert.equal(item.done, false); + item = iter.next(); + assert.equal(item.value, 1); + assert.equal(item.done, false); + item = iter.next(); + assert.equal(item.value, 3); + assert.equal(item.done, false); + item = iter.next(); + assert.equal(item.value, undefined); + assert.equal(item.done, true); + + iter = map.findSuperstr(URI.file('/usr'))!; + item = iter.next(); + assert.equal(item.value, 4); + assert.equal(item.done, false); + + item = iter.next(); + assert.equal(item.value, undefined); + assert.equal(item.done, true); + + iter = map.findSuperstr(URI.file('/'))!; + item = iter.next(); + assert.equal(item.value, 2); + assert.equal(item.done, false); + item = iter.next(); + assert.equal(item.value, 1); + assert.equal(item.done, false); + item = iter.next(); + assert.equal(item.value, 3); + assert.equal(item.done, false); + item = iter.next(); + assert.equal(item.value, 4); + assert.equal(item.done, false); + item = iter.next(); + assert.equal(item.value, undefined); + assert.equal(item.done, true); + + assert.equal(map.findSuperstr(URI.file('/not')), undefined); + assert.equal(map.findSuperstr(URI.file('/us')), undefined); + assert.equal(map.findSuperstr(URI.file('/usrr')), undefined); + assert.equal(map.findSuperstr(URI.file('/userr')), undefined); + }); + + test('ResourceMap - basics', function () { const map = new ResourceMap(); diff --git a/src/vs/base/test/common/strings.test.ts b/src/vs/base/test/common/strings.test.ts index 0c0ef6bf8e..683015e3cf 100644 --- a/src/vs/base/test/common/strings.test.ts +++ b/src/vs/base/test/common/strings.test.ts @@ -81,6 +81,27 @@ suite('Strings', () => { assertCompareIgnoreCase('O', '/'); }); + test('compareIgnoreCase (substring)', () => { + + function assertCompareIgnoreCase(a: string, b: string, aStart: number, aEnd: number, bStart: number, bEnd: number, recurse = true): void { + let actual = strings.compareIgnoreCase(a, b, aStart, aEnd, bStart, bEnd); + actual = actual > 0 ? 1 : actual < 0 ? -1 : actual; + + let expected = strings.compare(a.toLowerCase().substring(aStart, aEnd), b.toLowerCase().substring(bStart, bEnd)); + expected = expected > 0 ? 1 : expected < 0 ? -1 : expected; + assert.equal(actual, expected, `${a} <> ${b}`); + + if (recurse) { + assertCompareIgnoreCase(b, a, bStart, bEnd, aStart, aEnd, false); + } + } + + assertCompareIgnoreCase('', '', 0, 0, 0, 0); + assertCompareIgnoreCase('abc', 'ABC', 0, 1, 0, 1); + assertCompareIgnoreCase('abc', 'Aabc', 0, 3, 1, 4); + assertCompareIgnoreCase('abcABc', 'ABcd', 3, 6, 0, 4); + }); + test('format', () => { assert.strictEqual(strings.format('Foo Bar'), 'Foo Bar'); assert.strictEqual(strings.format('Foo {0} Bar'), 'Foo {0} Bar'); diff --git a/src/vs/base/test/common/uri.test.ts b/src/vs/base/test/common/uri.test.ts index a20b13af55..f15093a58d 100644 --- a/src/vs/base/test/common/uri.test.ts +++ b/src/vs/base/test/common/uri.test.ts @@ -538,7 +538,7 @@ suite('URI', () => { assert.throws(() => assertJoined(('foo:'), 'bazz', '')); assert.throws(() => new URL('bazz', 'foo:')); assert.throws(() => assertJoined(('foo://bar'), 'bazz', '')); - // assert.throws(() => new URL('bazz', 'foo://bar')); Edge,Chrome => throw, Safari => foo://bar/bazz, Firefox ?? + // assert.throws(() => new URL('bazz', 'foo://bar')); Edge, Chrome => THROW, Firefox, Safari => foo://bar/bazz }); test('URI#joinPath (posix)', function () { @@ -549,8 +549,8 @@ suite('URI', () => { assertJoined(('file://server/share/c:/'), '../../bazz', 'file://server/bazz', false); assertJoined(('file://server/share/c:'), '../../bazz', 'file://server/bazz', false); - assertJoined(('file://ser/foo/'), '../../bazz', 'file://ser/bazz'); - assertJoined(('file://ser/foo'), '../../bazz', 'file://ser/bazz'); + assertJoined(('file://ser/foo/'), '../../bazz', 'file://ser/bazz', false); // Firefox -> Different, Edge, Chrome, Safar -> OK + assertJoined(('file://ser/foo'), '../../bazz', 'file://ser/bazz', false); // Firefox -> Different, Edge, Chrome, Safar -> OK }); test('URI#joinPath (windows)', function () { diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts index 1f4523f977..d414140029 100644 --- a/src/vs/code/browser/workbench/workbench.ts +++ b/src/vs/code/browser/workbench/workbench.ts @@ -322,7 +322,11 @@ class WorkspaceProvider implements IWorkspaceProvider { // Payload case WorkspaceProvider.QUERY_PARAM_PAYLOAD: - payload = JSON.parse(value); + try { + payload = JSON.parse(value); + } catch (error) { + console.error(error); // possible invalid JSON + } break; } }); diff --git a/src/vs/code/electron-browser/issue/issueReporterMain.ts b/src/vs/code/electron-browser/issue/issueReporterMain.ts index 918676c557..95bd9061c2 100644 --- a/src/vs/code/electron-browser/issue/issueReporterMain.ts +++ b/src/vs/code/electron-browser/issue/issueReporterMain.ts @@ -17,14 +17,13 @@ import { escape } from 'vs/base/common/strings'; import { getDelayedChannel } from 'vs/base/parts/ipc/common/ipc'; import { createChannelSender } from 'vs/base/parts/ipc/node/ipc'; import { connect as connectNet } from 'vs/base/parts/ipc/node/ipc.net'; -import { normalizeGitHubUrl } from 'vs/code/common/issue/issueReporterUtil'; +import { normalizeGitHubUrl } from 'vs/platform/issue/common/issueReporterUtil'; import { IssueReporterData as IssueReporterModelData, IssueReporterModel } from 'vs/code/electron-browser/issue/issueReporterModel'; import BaseHtml from 'vs/code/electron-browser/issue/issueReporterPage'; import 'vs/css!./media/issueReporter'; import { localize } from 'vs/nls'; import { isRemoteDiagnosticError, SystemInfo } from 'vs/platform/diagnostics/common/diagnostics'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; +import { EnvironmentService, INativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IMainProcessService, MainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; @@ -63,7 +62,7 @@ export function startup(configuration: IssueReporterConfiguration) { } export class IssueReporter extends Disposable { - private environmentService!: IEnvironmentService; + private environmentService!: INativeEnvironmentService; private telemetryService!: ITelemetryService; private logService!: ILogService; private readonly issueReporterModel: IssueReporterModel; diff --git a/src/vs/code/electron-browser/issue/issueReporterModel.ts b/src/vs/code/electron-browser/issue/issueReporterModel.ts index 6408274726..208e998a39 100644 --- a/src/vs/code/electron-browser/issue/issueReporterModel.ts +++ b/src/vs/code/electron-browser/issue/issueReporterModel.ts @@ -169,6 +169,13 @@ ${this.getInfos()} |Screen Reader|${this._data.systemInfo.screenReader}| |VM|${this._data.systemInfo.vmHint}|`; + if (this._data.systemInfo.linuxEnv) { + md += `\n|DESKTOP_SESSION|${this._data.systemInfo.linuxEnv.desktopSession}| +|XDG_CURRENT_DESKTOP|${this._data.systemInfo.linuxEnv.xdgCurrentDesktop}| +|XDG_SESSION_DESKTOP|${this._data.systemInfo.linuxEnv.xdgSessionDesktop}| +|XDG_SESSION_TYPE|${this._data.systemInfo.linuxEnv.xdgSessionType}|`; + } + this._data.systemInfo.remoteData.forEach(remote => { if (isRemoteDiagnosticError(remote)) { md += `\n\n${remote.errorMessage}`; diff --git a/src/vs/code/electron-browser/issue/test/testReporterModel.test.ts b/src/vs/code/electron-browser/issue/test/testReporterModel.test.ts index bf6bdc8add..9a645ae18c 100644 --- a/src/vs/code/electron-browser/issue/test/testReporterModel.test.ts +++ b/src/vs/code/electron-browser/issue/test/testReporterModel.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; import { IssueReporterModel } from 'vs/code/electron-browser/issue/issueReporterModel'; -import { normalizeGitHubUrl } from 'vs/code/common/issue/issueReporterUtil'; +import { normalizeGitHubUrl } from 'vs/platform/issue/common/issueReporterUtil'; import { IssueType } from 'vs/platform/issue/node/issue'; suite('IssueReporter', () => { @@ -81,7 +81,56 @@ OS version: undefined `); }); - test.skip('serializes remote information when data is provided', () => { //{{SQL CARBON EDIT}} skip test + test.skip('serializes Linux environment information when data is provided', () => { //{{SQL CARBON EDIT}} skip test + const issueReporterModel = new IssueReporterModel({ + issueType: 0, + systemInfo: { + os: 'Darwin', + cpus: 'Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz (8 x 2800)', + memory: '16.00GB', + vmHint: '0%', + processArgs: '', + screenReader: 'no', + remoteData: [], + gpuStatus: {}, + linuxEnv: { + desktopSession: 'ubuntu', + xdgCurrentDesktop: 'ubuntu', + xdgSessionDesktop: 'ubuntu:GNOME', + xdgSessionType: 'x11' + } + } + }); + assert.equal(issueReporterModel.serialize(), + ` +Issue Type: Bug + +undefined + +VS Code version: undefined +OS version: undefined + +
+System Info + +|Item|Value| +|---|---| +|CPUs|Intel(R) Core(TM) i7-7700HQ CPU @ 2.80GHz (8 x 2800)| +|GPU Status|| +|Load (avg)|undefined| +|Memory (System)|16.00GB| +|Process Argv|| +|Screen Reader|no| +|VM|0%| +|DESKTOP_SESSION|ubuntu| +|XDG_CURRENT_DESKTOP|ubuntu| +|XDG_SESSION_DESKTOP|ubuntu:GNOME| +|XDG_SESSION_TYPE|x11| +
Extensions: none +`); + }); + + test.skip('serializes remote information when data is provided', () => { //{{SQL CARBON EDIT}} skip test const issueReporterModel = new IssueReporterModel({ issueType: 0, systemInfo: { diff --git a/src/vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner.ts b/src/vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner.ts index c4d8930fc6..2a1353a48a 100644 --- a/src/vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner.ts +++ b/src/vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner.ts @@ -5,12 +5,12 @@ import * as path from 'vs/base/common/path'; import * as pfs from 'vs/base/node/pfs'; - import { IStringDictionary } from 'vs/base/common/collections'; import product from 'vs/platform/product/common/product'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { onUnexpectedError } from 'vs/base/common/errors'; import { ILogService } from 'vs/platform/log/common/log'; +import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; interface ExtensionEntry { @@ -33,7 +33,7 @@ interface LanguagePackFile { export class LanguagePackCachedDataCleaner extends Disposable { constructor( - @IEnvironmentService private readonly _environmentService: IEnvironmentService, + @IEnvironmentService private readonly _environmentService: INativeEnvironmentService, @ILogService private readonly _logService: ILogService ) { super(); @@ -102,4 +102,4 @@ export class LanguagePackCachedDataCleaner extends Disposable { } })); } -} \ 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 91ebed4dfd..68822853c9 100644 --- a/src/vs/code/electron-browser/sharedProcess/contrib/nodeCachedDataCleaner.ts +++ b/src/vs/code/electron-browser/sharedProcess/contrib/nodeCachedDataCleaner.ts @@ -8,6 +8,7 @@ import { onUnexpectedError } from 'vs/base/common/errors'; 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 { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; import product from 'vs/platform/product/common/product'; export class NodeCachedDataCleaner { @@ -19,7 +20,7 @@ export class NodeCachedDataCleaner { private readonly _disposables = new DisposableStore(); constructor( - @IEnvironmentService private readonly _environmentService: IEnvironmentService + @IEnvironmentService private readonly _environmentService: INativeEnvironmentService ) { this._manageCachedDataSoon(); } diff --git a/src/vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner.ts b/src/vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner.ts index 3b8981e238..79b973aef5 100644 --- a/src/vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner.ts +++ b/src/vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; import { join } from 'vs/base/common/path'; import { readdir, readFile, rimraf } from 'vs/base/node/pfs'; import { onUnexpectedError } from 'vs/base/common/errors'; @@ -16,7 +17,7 @@ export class StorageDataCleaner extends Disposable { private static readonly NON_EMPTY_WORKSPACE_ID_LENGTH = 128 / 4; constructor( - @IEnvironmentService private readonly environmentService: IEnvironmentService + @IEnvironmentService private readonly environmentService: INativeEnvironmentService ) { super(); diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index 30f927a65f..1bf5b414d3 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -10,10 +10,11 @@ import { serve, Server, connect } from 'vs/base/parts/ipc/node/ipc.net'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; -import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { ParsedArgs } from 'vs/platform/environment/node/argv'; import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; -import { ExtensionManagementChannel } from 'vs/platform/extensionManagement/common/extensionManagementIpc'; -import { IExtensionManagementService, IExtensionGalleryService, IGlobalExtensionEnablementService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { ExtensionManagementChannel, ExtensionTipsChannel } from 'vs/platform/extensionManagement/common/extensionManagementIpc'; +import { IExtensionManagementService, IExtensionGalleryService, IGlobalExtensionEnablementService, IExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; @@ -68,6 +69,7 @@ import { IAuthenticationTokenService, AuthenticationTokenService } from 'vs/plat import { AuthenticationTokenServiceChannel } from 'vs/platform/authentication/common/authenticationIpc'; import { UserDataSyncBackupStoreService } from 'vs/platform/userDataSync/common/userDataSyncBackupStoreService'; import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; +import { ExtensionTipsService } from 'vs/platform/extensionManagement/node/extensionTipsService'; export interface ISharedProcessConfiguration { readonly machineId: string; @@ -160,14 +162,13 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat let telemetryService: ITelemetryService; instantiationService.invokeFunction(accessor => { const services = new ServiceCollection(); - const environmentService = accessor.get(IEnvironmentService); const { appRoot, extensionsPath, extensionDevelopmentLocationURI, isBuilt, installSourcePath } = environmentService; const telemetryLogService = new FollowerLogService(loggerClient, 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('==========================================================='); let appInsightsAppender: ITelemetryAppender | null = NullAppender; - if (!extensionDevelopmentLocationURI && !environmentService.args['disable-telemetry'] && product.enableTelemetry) { + if (!extensionDevelopmentLocationURI && !environmentService.disableTelemetry && product.enableTelemetry) { if (product.aiConfig && product.aiConfig.asimovKey && isBuilt) { appInsightsAppender = new AppInsightsAppender(eventPrefix, null, product.aiConfig.asimovKey, telemetryLogService); disposables.add(toDisposable(() => appInsightsAppender!.flush())); // Ensure the AI appender is disposed so that it flushes remaining data @@ -190,6 +191,7 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService)); services.set(ILocalizationsService, new SyncDescriptor(LocalizationsService)); services.set(IDiagnosticsService, new SyncDescriptor(DiagnosticsService)); + services.set(IExtensionTipsService, new SyncDescriptor(ExtensionTipsService)); services.set(ICredentialsService, new SyncDescriptor(KeytarCredentialsService)); services.set(IAuthenticationTokenService, new SyncDescriptor(AuthenticationTokenService)); @@ -218,6 +220,10 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat const diagnosticsChannel = new DiagnosticsChannel(diagnosticsService); server.registerChannel('diagnostics', diagnosticsChannel); + const extensionTipsService = accessor.get(IExtensionTipsService); + const extensionTipsChannel = new ExtensionTipsChannel(extensionTipsService); + server.registerChannel('extensionTipsService', extensionTipsChannel); + const authTokenService = accessor.get(IAuthenticationTokenService); const authTokenChannel = new AuthenticationTokenServiceChannel(authTokenService); server.registerChannel('authToken', authTokenChannel); diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index f986dfd08a..415905965a 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -6,7 +6,8 @@ import { app, ipcMain as ipc, systemPreferences, shell, Event, contentTracing, protocol, powerMonitor, IpcMainEvent, BrowserWindow } from 'electron'; import { IProcessEnvironment, isWindows, isMacintosh } from 'vs/base/common/platform'; import { WindowsMainService } from 'vs/platform/windows/electron-main/windowsMainService'; -import { OpenContext, IWindowOpenable } from 'vs/platform/windows/common/windows'; +import { IWindowOpenable } from 'vs/platform/windows/common/windows'; +import { OpenContext } from 'vs/platform/windows/node/window'; import { ActiveWindowManager } from 'vs/code/node/activeWindowTracker'; import { ILifecycleMainService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; import { getShellEnvironment } from 'vs/code/node/shellEnv'; @@ -78,6 +79,7 @@ import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv'; import { coalesce } from 'vs/base/common/arrays'; import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; import { StorageKeysSyncRegistryChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc'; +import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; export class CodeApplication extends Disposable { private windowsMainService: IWindowsMainService | undefined; @@ -88,7 +90,7 @@ export class CodeApplication extends Disposable { private readonly userEnv: IProcessEnvironment, @IInstantiationService private readonly instantiationService: IInstantiationService, @ILogService private readonly logService: ILogService, - @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IEnvironmentService private readonly environmentService: INativeEnvironmentService, @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, @IConfigurationService private readonly configurationService: IConfigurationService, @IStateService private readonly stateService: IStateService @@ -588,12 +590,11 @@ export class CodeApplication extends Disposable { this.dialogMainService = accessor.get(IDialogMainService); // Check for initial URLs to handle from protocol link invocations - const environmentService = accessor.get(IEnvironmentService); const pendingWindowOpenablesFromProtocolLinks: IWindowOpenable[] = []; const pendingProtocolLinksToHandle = coalesce([ // Windows/Linux: protocol handler invokes CLI with --open-url - ...environmentService.args['open-url'] ? environmentService.args._urls || [] : [], + ...this.environmentService.args['open-url'] ? this.environmentService.args._urls || [] : [], // macOS: open-url events ...((global).getOpenUrls() || []) as string[] @@ -619,6 +620,7 @@ export class CodeApplication extends Disposable { // Create a URL handler to open file URIs in the active window const app = this; + const environmentService = this.environmentService; urlService.registerHandler({ async handleURL(uri: URI): Promise { diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index 0aa58b3a18..be54b9bf0c 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -5,7 +5,6 @@ import 'vs/platform/update/common/update.config.contribution'; import { app, dialog } from 'electron'; -import { assign } from 'vs/base/common/objects'; import { isWindows, IProcessEnvironment, isMacintosh } from 'vs/base/common/platform'; import product from 'vs/platform/product/common/product'; import { parseMainProcessArgv, addArg } from 'vs/platform/environment/node/argvHelper'; @@ -23,8 +22,9 @@ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { ILogService, ConsoleLogMainService, MultiplexLogService, getLogLevel } from 'vs/platform/log/common/log'; import { StateService } from 'vs/platform/state/node/stateService'; import { IStateService } from 'vs/platform/state/node/state'; -import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment'; -import { EnvironmentService, xdgRuntimeDir } from 'vs/platform/environment/node/environmentService'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { ParsedArgs } from 'vs/platform/environment/node/argv'; +import { EnvironmentService, xdgRuntimeDir, INativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ConfigurationService } from 'vs/platform/configuration/common/configurationService'; import { IRequestService } from 'vs/platform/request/common/request'; @@ -98,12 +98,11 @@ class CodeMain { // log file access on Windows (https://github.com/Microsoft/vscode/issues/41218) const bufferLogService = new BufferLogService(); - const [instantiationService, instanceEnvironment] = this.createServices(args, bufferLogService); + const [instantiationService, instanceEnvironment, environmentService] = this.createServices(args, bufferLogService); try { // Init services await instantiationService.invokeFunction(async accessor => { - const environmentService = accessor.get(IEnvironmentService); const configurationService = accessor.get(IConfigurationService); const stateService = accessor.get(IStateService); @@ -120,13 +119,12 @@ class CodeMain { // Startup await instantiationService.invokeFunction(async accessor => { - const environmentService = accessor.get(IEnvironmentService); const logService = accessor.get(ILogService); const lifecycleMainService = accessor.get(ILifecycleMainService); const fileService = accessor.get(IFileService); const configurationService = accessor.get(IConfigurationService); - const mainIpcServer = await this.doStartup(logService, environmentService, lifecycleMainService, instantiationService, true); + const mainIpcServer = await this.doStartup(args, logService, environmentService, lifecycleMainService, instantiationService, true); bufferLogService.logger = new SpdLogService('main', environmentService.logsPath, bufferLogService.getLevel()); once(lifecycleMainService.onWillShutdown)(() => { @@ -141,7 +139,7 @@ class CodeMain { } } - private createServices(args: ParsedArgs, bufferLogService: BufferLogService): [IInstantiationService, IProcessEnvironment] { + private createServices(args: ParsedArgs, bufferLogService: BufferLogService): [IInstantiationService, IProcessEnvironment, INativeEnvironmentService] { const services = new ServiceCollection(); const environmentService = new EnvironmentService(args, process.execPath); @@ -165,10 +163,10 @@ class CodeMain { services.set(ISignService, new SyncDescriptor(SignService)); services.set(IStorageKeysSyncRegistryService, new SyncDescriptor(StorageKeysSyncRegistryService)); - return [new InstantiationService(services, true), instanceEnvironment]; + return [new InstantiationService(services, true), instanceEnvironment, environmentService]; } - private initServices(environmentService: IEnvironmentService, configurationService: ConfigurationService, stateService: StateService): Promise { + private initServices(environmentService: INativeEnvironmentService, configurationService: ConfigurationService, stateService: StateService): Promise { // Environment service (paths) const environmentServiceInitialization = Promise.all([ @@ -189,7 +187,7 @@ class CodeMain { return Promise.all([environmentServiceInitialization, configurationServiceInitialization, stateServiceInitialization]); } - private patchEnvironment(environmentService: IEnvironmentService): IProcessEnvironment { + private patchEnvironment(environmentService: INativeEnvironmentService): IProcessEnvironment { const instanceEnvironment: IProcessEnvironment = { VSCODE_IPC_HOOK: environmentService.mainIPCHandle }; @@ -201,12 +199,12 @@ class CodeMain { } }); - assign(process.env, instanceEnvironment); + Object.assign(process.env, instanceEnvironment); return instanceEnvironment; } - private async doStartup(logService: ILogService, environmentService: IEnvironmentService, lifecycleMainService: ILifecycleMainService, instantiationService: IInstantiationService, retry: boolean): Promise { + private async doStartup(args: ParsedArgs, logService: ILogService, environmentService: INativeEnvironmentService, lifecycleMainService: ILifecycleMainService, 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 @@ -262,7 +260,7 @@ class CodeMain { throw error; } - return this.doStartup(logService, environmentService, lifecycleMainService, instantiationService, false); + return this.doStartup(args, logService, environmentService, lifecycleMainService, instantiationService, false); } // Tests from CLI require to be the only instance currently @@ -278,7 +276,7 @@ class CodeMain { // 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.args.wait && !environmentService.args.status) { + if (!args.wait && !args.status) { startupWarningDialogHandle = setTimeout(() => { this.showStartupWarningDialog( localize('secondInstanceNoResponse', "Another instance of {0} is running but not responding", product.nameShort), @@ -290,7 +288,7 @@ class CodeMain { const launchService = createChannelSender(client.getChannel('launch'), { disableMarshalling: true }); // Process Info - if (environmentService.args.status) { + if (args.status) { return instantiationService.invokeFunction(async accessor => { // Create a diagnostic service connected to the existing shared process const sharedProcessClient = await connect(environmentService.sharedIPCHandle, 'main'); @@ -312,7 +310,7 @@ class CodeMain { // Send environment over... logService.trace('Sending env to running instance...'); - await launchService.start(environmentService.args, process.env as IProcessEnvironment); + await launchService.start(args, process.env as IProcessEnvironment); // Cleanup client.dispose(); @@ -326,7 +324,7 @@ class CodeMain { } // Print --status usage info - if (environmentService.args.status) { + if (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...'); @@ -344,7 +342,7 @@ class CodeMain { return server; } - private handleStartupDataDirError(environmentService: IEnvironmentService, error: NodeJS.ErrnoException): void { + private handleStartupDataDirError(environmentService: INativeEnvironmentService, error: NodeJS.ErrnoException): void { if (error.code === 'EACCES' || error.code === 'EPERM') { const directories = [environmentService.userDataPath]; diff --git a/src/vs/code/electron-main/sharedProcess.ts b/src/vs/code/electron-main/sharedProcess.ts index a313d6fced..59df84ce4b 100644 --- a/src/vs/code/electron-main/sharedProcess.ts +++ b/src/vs/code/electron-main/sharedProcess.ts @@ -3,7 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { assign } from 'vs/base/common/objects'; import { memoize } from 'vs/base/common/decorators'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { BrowserWindow, ipcMain, WebContents, Event as ElectronEvent } from 'electron'; @@ -14,6 +13,7 @@ import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifec import { IThemeMainService } from 'vs/platform/theme/electron-main/themeMainService'; import { toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { Event } from 'vs/base/common/event'; +import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; export class SharedProcess implements ISharedProcess { @@ -26,7 +26,7 @@ export class SharedProcess implements ISharedProcess { constructor( private readonly machineId: string, private userEnv: NodeJS.ProcessEnv, - @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IEnvironmentService private readonly environmentService: INativeEnvironmentService, @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, @ILogService private readonly logService: ILogService, @IThemeMainService private readonly themeMainService: IThemeMainService @@ -47,13 +47,13 @@ export class SharedProcess implements ISharedProcess { disableBlinkFeatures: 'Auxclick' // do NOT change, allows us to identify this window as shared-process in the process explorer } }); - const config = assign({ + const config = { appRoot: this.environmentService.appRoot, machineId: this.machineId, nodeCachedDataDir: this.environmentService.nodeCachedDataDir, userEnv: this.userEnv, windowId: this.window.id - }); + }; const url = `${require.toUrl('vs/code/electron-browser/sharedProcess/sharedProcess.html')}?config=${encodeURIComponent(JSON.stringify(config))}`; this.window.loadURL(url); diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index ab86c4d913..db3ee692fa 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -9,12 +9,13 @@ import * as nls from 'vs/nls'; import { Emitter } from 'vs/base/common/event'; import { URI } from 'vs/base/common/uri'; import { screen, BrowserWindow, systemPreferences, app, TouchBar, nativeImage, Rectangle, Display, TouchBarSegmentedControl, NativeImage, BrowserWindowConstructorOptions, SegmentedControlSegment, nativeTheme } from 'electron'; -import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; import { ILogService } from 'vs/platform/log/common/log'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv'; +import { parseArgs, OPTIONS, ParsedArgs } from 'vs/platform/environment/node/argv'; import product from 'vs/platform/product/common/product'; -import { IWindowSettings, MenuBarVisibility, ReadyState, getTitleBarStyle, getMenuBarVisibility } from 'vs/platform/windows/common/windows'; +import { IWindowSettings, MenuBarVisibility, getTitleBarStyle, getMenuBarVisibility } 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'; @@ -60,6 +61,29 @@ const enum WindowError { CRASHED = 2 } +const enum ReadyState { + + /** + * This window has not loaded any HTML yet + */ + NONE, + + /** + * This window is loading HTML + */ + LOADING, + + /** + * This window is navigating to another HTML + */ + NAVIGATING, + + /** + * This window is done loading HTML + */ + READY +} + export class CodeWindow extends Disposable implements ICodeWindow { private static readonly MIN_WIDTH = 600; @@ -97,7 +121,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { constructor( config: IWindowCreationOptions, @ILogService private readonly logService: ILogService, - @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IEnvironmentService private readonly environmentService: INativeEnvironmentService, @IFileService private readonly fileService: IFileService, @IStorageMainService private readonly storageService: IStorageMainService, @IConfigurationService private readonly configurationService: IConfigurationService, @@ -471,7 +495,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { // 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 => cb({ cancel: false, requestHeaders: objects.assign(details.requestHeaders, headers) as Record }))); + this.marketplaceHeadersPromise.then(headers => cb({ cancel: false, requestHeaders: Object.assign(details.requestHeaders, headers) }))); } private onWindowError(error: WindowError): void { @@ -597,7 +621,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { // Add disable-extensions to the config, but do not preserve it on currentConfig or // pendingLoadConfig so that it is applied only on this load - const configuration = objects.assign({}, config); + const configuration = { ...config }; if (disableExtensions !== undefined) { configuration['disable-extensions'] = disableExtensions; } @@ -701,7 +725,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { // Config (combination of process.argv and window configuration) const environment = parseArgs(process.argv, OPTIONS); - const config = objects.assign(environment, windowConfiguration); + const config = Object.assign(environment, windowConfiguration); for (const key in config) { const configValue = (config as any)[key]; if (configValue === undefined || configValue === null || configValue === '' || configValue === false) { diff --git a/src/vs/code/node/cli.ts b/src/vs/code/node/cli.ts index c4f87715d6..589a17936d 100644 --- a/src/vs/code/node/cli.ts +++ b/src/vs/code/node/cli.ts @@ -6,10 +6,9 @@ import * as os from 'os'; import * as fs from 'fs'; import { spawn, ChildProcess, SpawnOptions } from 'child_process'; -import { buildHelpMessage, buildVersionMessage, OPTIONS } from 'vs/platform/environment/node/argv'; +import { buildHelpMessage, buildVersionMessage, OPTIONS, ParsedArgs } from 'vs/platform/environment/node/argv'; import { parseCLIProcessArgv, addArg } from 'vs/platform/environment/node/argvHelper'; import { createWaitMarkerFile } from 'vs/platform/environment/node/waitMarkerFile'; -import { ParsedArgs } from 'vs/platform/environment/common/environment'; import product from 'vs/platform/product/common/product'; import * as paths from 'vs/base/common/path'; import { whenDeleted, writeFileSync } from 'vs/base/node/pfs'; diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index dafc4affde..cd2a2aa44d 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -7,13 +7,13 @@ import { localize } from 'vs/nls'; import product from 'vs/platform/product/common/product'; import * as path from 'vs/base/common/path'; import * as semver from 'semver-umd'; - import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; -import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment'; -import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { ParsedArgs } from 'vs/platform/environment/node/argv'; +import { EnvironmentService, INativeEnvironmentService } 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/common/extensionGalleryService'; @@ -49,7 +49,7 @@ import { IProductService } from 'vs/platform/product/common/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, e.g.: {0}", 'ms-vscode.csharp'); +const useId = localize('useId', "Make sure you use the full extension ID, including the publisher, e.g.: {0}", 'ms-dotnettools.csharp'); function getId(manifest: IExtensionManifest, withVersion?: boolean): string { if (withVersion) { @@ -74,7 +74,7 @@ export class Main { constructor( @IInstantiationService private readonly instantiationService: IInstantiationService, - @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IEnvironmentService private readonly environmentService: INativeEnvironmentService, @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, @IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService ) { } @@ -328,21 +328,17 @@ export async function main(argv: ParsedArgs): Promise { const instantiationService: IInstantiationService = new InstantiationService(services); return instantiationService.invokeFunction(async accessor => { - const envService = accessor.get(IEnvironmentService); const stateService = accessor.get(IStateService); - const { appRoot, extensionsPath, extensionDevelopmentLocationURI, isBuilt, installSourcePath } = envService; + const { appRoot, extensionsPath, extensionDevelopmentLocationURI, isBuilt, installSourcePath } = environmentService; const services = new ServiceCollection(); - - services.set(IRequestService, new SyncDescriptor(RequestService)); services.set(IExtensionManagementService, new SyncDescriptor(ExtensionManagementService)); services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService)); const appenders: AppInsightsAppender[] = []; - if (isBuilt && !extensionDevelopmentLocationURI && !envService.args['disable-telemetry'] && product.enableTelemetry) { - + if (isBuilt && !extensionDevelopmentLocationURI && !environmentService.disableTelemetry && product.enableTelemetry) { if (product.aiConfig && product.aiConfig.asimovKey) { appenders.push(new AppInsightsAppender(eventPrefix, null, product.aiConfig.asimovKey, logService)); } diff --git a/src/vs/code/node/paths.ts b/src/vs/code/node/paths.ts index fc1a6ea8b3..10eefb07d9 100644 --- a/src/vs/code/node/paths.ts +++ b/src/vs/code/node/paths.ts @@ -9,7 +9,7 @@ import * as strings from 'vs/base/common/strings'; import * as extpath from 'vs/base/common/extpath'; import * as platform from 'vs/base/common/platform'; import * as types from 'vs/base/common/types'; -import { ParsedArgs } from 'vs/platform/environment/common/environment'; +import { ParsedArgs } from 'vs/platform/environment/node/argv'; export function validatePaths(args: ParsedArgs): ParsedArgs { diff --git a/src/vs/code/node/shellEnv.ts b/src/vs/code/node/shellEnv.ts index 1ca11320f3..f5b2144084 100644 --- a/src/vs/code/node/shellEnv.ts +++ b/src/vs/code/node/shellEnv.ts @@ -4,11 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import * as cp from 'child_process'; -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'; +import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; function getUnixShellEnvironment(logService: ILogService): Promise { const promise = new Promise((resolve, reject) => { @@ -21,10 +20,11 @@ function getUnixShellEnvironment(logService: ILogService): Promise; * This should only be done when Code itself is not launched * from within a shell. */ -export function getShellEnvironment(logService: ILogService, environmentService: IEnvironmentService): Promise { +export function getShellEnvironment(logService: ILogService, environmentService: INativeEnvironmentService): Promise { if (_shellEnv === undefined) { if (environmentService.args['disable-user-env-probe']) { logService.trace('getShellEnvironment: disable-user-env-probe set, skipping'); diff --git a/src/vs/editor/common/services/markerDecorationsServiceImpl.ts b/src/vs/editor/common/services/markerDecorationsServiceImpl.ts index f63805f3f4..8623983ba8 100644 --- a/src/vs/editor/common/services/markerDecorationsServiceImpl.ts +++ b/src/vs/editor/common/services/markerDecorationsServiceImpl.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IMarkerService, IMarker, MarkerSeverity, MarkerTag } from 'vs/platform/markers/common/markers'; -import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; +import { Disposable, toDisposable, IDisposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; import { IModelDeltaDecoration, ITextModel, IModelDecorationOptions, TrackedRangeStickiness, OverviewRulerLane, IModelDecoration, MinimapPosition, IModelDecorationMinimapOptions } from 'vs/editor/common/model'; import { ClassName } from 'vs/editor/common/model/intervalTree'; @@ -37,6 +37,10 @@ class MarkerDecorations extends Disposable { })); } + register(t: T): T { + return super._register(t); + } + public update(markers: IMarker[], newDecorations: IModelDeltaDecoration[]): void { const oldIds = keys(this._markersData); this._markersData.clear(); @@ -110,6 +114,7 @@ export class MarkerDecorationsService extends Disposable implements IMarkerDecor private _onModelAdded(model: ITextModel): void { const markerDecorations = new MarkerDecorations(model); this._markerDecorations.set(MODEL_ID(model.uri), markerDecorations); + markerDecorations.register(model.onDidChangeContent(() => this._updateDecorations(markerDecorations))); this._updateDecorations(markerDecorations); } diff --git a/src/vs/editor/common/view/editorColorRegistry.ts b/src/vs/editor/common/view/editorColorRegistry.ts index 76afd2655d..1edfd97668 100644 --- a/src/vs/editor/common/view/editorColorRegistry.ts +++ b/src/vs/editor/common/view/editorColorRegistry.ts @@ -30,7 +30,7 @@ export const editorActiveLineNumber = registerColor('editorLineNumber.activeFore export const editorRuler = registerColor('editorRuler.foreground', { dark: '#5A5A5A', light: Color.lightgrey, hc: Color.white }, nls.localize('editorRuler', 'Color of the editor rulers.')); -export const editorCodeLensForeground = registerColor('editorCodeLens.foreground', { dark: '#999999', light: '#999999', hc: '#999999' }, nls.localize('editorCodeLensForeground', 'Foreground color of editor code lenses')); +export const editorCodeLensForeground = registerColor('editorCodeLens.foreground', { dark: '#999999', light: '#999999', hc: '#999999' }, nls.localize('editorCodeLensForeground', 'Foreground color of editor CodeLens')); export const editorBracketMatchBackground = registerColor('editorBracketMatch.background', { dark: '#0064001a', light: '#0064001a', hc: '#0064001a' }, nls.localize('editorBracketMatchBackground', 'Background color behind matching brackets')); export const editorBracketMatchBorder = registerColor('editorBracketMatch.border', { dark: '#888', light: '#B9B9B9', hc: contrastBorder }, nls.localize('editorBracketMatchBorder', 'Color for matching brackets boxes')); diff --git a/src/vs/editor/contrib/codelens/codelensController.ts b/src/vs/editor/contrib/codelens/codelensController.ts index 6a592019a7..786d642acd 100644 --- a/src/vs/editor/contrib/codelens/codelensController.ts +++ b/src/vs/editor/contrib/codelens/codelensController.ts @@ -419,8 +419,8 @@ registerEditorAction(class ShowLensesInCurrentLine extends EditorAction { super({ id: 'codelens.showLensesInCurrentLine', precondition: EditorContextKeys.hasCodeLensProvider, - label: localize('showLensOnLine', "Show Code Lens Commands For Current Line"), - alias: 'Show Code Lens Commands For Current Line', + label: localize('showLensOnLine', "Show CodeLens Commands For Current Line"), + alias: 'Show CodeLens Commands For Current Line', }); } diff --git a/src/vs/editor/contrib/documentSymbols/outlineTree.ts b/src/vs/editor/contrib/documentSymbols/outlineTree.ts index 8f5be59175..899fe494f5 100644 --- a/src/vs/editor/contrib/documentSymbols/outlineTree.ts +++ b/src/vs/editor/contrib/documentSymbols/outlineTree.ts @@ -540,168 +540,168 @@ registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) = const symbolIconArrayColor = theme.getColor(SYMBOL_ICON_ARRAY_FOREGROUND); if (symbolIconArrayColor) { - collector.addRule(`.codicon-symbol-array { color: ${symbolIconArrayColor} !important; }`); + collector.addRule(`.monaco-workbench .codicon-symbol-array { color: ${symbolIconArrayColor}; }`); } const symbolIconBooleanColor = theme.getColor(SYMBOL_ICON_BOOLEAN_FOREGROUND); if (symbolIconBooleanColor) { - collector.addRule(`.codicon-symbol-boolean { color: ${symbolIconBooleanColor} !important; }`); + collector.addRule(`.monaco-workbench .codicon-symbol-boolean { color: ${symbolIconBooleanColor}; }`); } const symbolIconClassColor = theme.getColor(SYMBOL_ICON_CLASS_FOREGROUND); if (symbolIconClassColor) { - collector.addRule(`.codicon-symbol-class { color: ${symbolIconClassColor} !important; }`); + collector.addRule(`.monaco-workbench .codicon-symbol-class { color: ${symbolIconClassColor}; }`); } const symbolIconMethodColor = theme.getColor(SYMBOL_ICON_METHOD_FOREGROUND); if (symbolIconMethodColor) { - collector.addRule(`.codicon-symbol-method { color: ${symbolIconMethodColor} !important; }`); + collector.addRule(`.monaco-workbench .codicon-symbol-method { color: ${symbolIconMethodColor}; }`); } const symbolIconColorColor = theme.getColor(SYMBOL_ICON_COLOR_FOREGROUND); if (symbolIconColorColor) { - collector.addRule(`.codicon-symbol-color { color: ${symbolIconColorColor} !important; }`); + collector.addRule(`.monaco-workbench .codicon-symbol-color { color: ${symbolIconColorColor}; }`); } const symbolIconConstantColor = theme.getColor(SYMBOL_ICON_CONSTANT_FOREGROUND); if (symbolIconConstantColor) { - collector.addRule(`.codicon-symbol-constant { color: ${symbolIconConstantColor} !important; }`); + collector.addRule(`.monaco-workbench .codicon-symbol-constant { color: ${symbolIconConstantColor}; }`); } const symbolIconConstructorColor = theme.getColor(SYMBOL_ICON_CONSTRUCTOR_FOREGROUND); if (symbolIconConstructorColor) { - collector.addRule(`.codicon-symbol-constructor { color: ${symbolIconConstructorColor} !important; }`); + collector.addRule(`.monaco-workbench .codicon-symbol-constructor { color: ${symbolIconConstructorColor}; }`); } const symbolIconEnumeratorColor = theme.getColor(SYMBOL_ICON_ENUMERATOR_FOREGROUND); if (symbolIconEnumeratorColor) { collector.addRule(` - .codicon-symbol-value,.codicon-symbol-enum { color: ${symbolIconEnumeratorColor} !important; }`); + .monaco-workbench .codicon-symbol-value,.monaco-workbench .codicon-symbol-enum { color: ${symbolIconEnumeratorColor}; }`); } const symbolIconEnumeratorMemberColor = theme.getColor(SYMBOL_ICON_ENUMERATOR_MEMBER_FOREGROUND); if (symbolIconEnumeratorMemberColor) { - collector.addRule(`.codicon-symbol-enum-member { color: ${symbolIconEnumeratorMemberColor} !important; }`); + collector.addRule(`.monaco-workbench .codicon-symbol-enum-member { color: ${symbolIconEnumeratorMemberColor}; }`); } const symbolIconEventColor = theme.getColor(SYMBOL_ICON_EVENT_FOREGROUND); if (symbolIconEventColor) { - collector.addRule(`.codicon-symbol-event { color: ${symbolIconEventColor} !important; }`); + collector.addRule(`.monaco-workbench .codicon-symbol-event { color: ${symbolIconEventColor}; }`); } const symbolIconFieldColor = theme.getColor(SYMBOL_ICON_FIELD_FOREGROUND); if (symbolIconFieldColor) { - collector.addRule(`.codicon-symbol-field { color: ${symbolIconFieldColor} !important; }`); + collector.addRule(`.monaco-workbench .codicon-symbol-field { color: ${symbolIconFieldColor}; }`); } const symbolIconFileColor = theme.getColor(SYMBOL_ICON_FILE_FOREGROUND); if (symbolIconFileColor) { - collector.addRule(`.codicon-symbol-file { color: ${symbolIconFileColor} !important; }`); + collector.addRule(`.monaco-workbench .codicon-symbol-file { color: ${symbolIconFileColor}; }`); } const symbolIconFolderColor = theme.getColor(SYMBOL_ICON_FOLDER_FOREGROUND); if (symbolIconFolderColor) { - collector.addRule(`.codicon-symbol-folder { color: ${symbolIconFolderColor} !important; }`); + collector.addRule(`.monaco-workbench .codicon-symbol-folder { color: ${symbolIconFolderColor}; }`); } const symbolIconFunctionColor = theme.getColor(SYMBOL_ICON_FUNCTION_FOREGROUND); if (symbolIconFunctionColor) { - collector.addRule(`.codicon-symbol-function { color: ${symbolIconFunctionColor} !important; }`); + collector.addRule(`.monaco-workbench .codicon-symbol-function { color: ${symbolIconFunctionColor}; }`); } const symbolIconInterfaceColor = theme.getColor(SYMBOL_ICON_INTERFACE_FOREGROUND); if (symbolIconInterfaceColor) { - collector.addRule(`.codicon-symbol-interface { color: ${symbolIconInterfaceColor} !important; }`); + collector.addRule(`.monaco-workbench .codicon-symbol-interface { color: ${symbolIconInterfaceColor}; }`); } const symbolIconKeyColor = theme.getColor(SYMBOL_ICON_KEY_FOREGROUND); if (symbolIconKeyColor) { - collector.addRule(`.codicon-symbol-key { color: ${symbolIconKeyColor} !important; }`); + collector.addRule(`.monaco-workbench .codicon-symbol-key { color: ${symbolIconKeyColor}; }`); } const symbolIconKeywordColor = theme.getColor(SYMBOL_ICON_KEYWORD_FOREGROUND); if (symbolIconKeywordColor) { - collector.addRule(`.codicon-symbol-keyword { color: ${symbolIconKeywordColor} !important; }`); + collector.addRule(`.monaco-workbench .codicon-symbol-keyword { color: ${symbolIconKeywordColor}; }`); } const symbolIconModuleColor = theme.getColor(SYMBOL_ICON_MODULE_FOREGROUND); if (symbolIconModuleColor) { - collector.addRule(`.codicon-symbol-module { color: ${symbolIconModuleColor} !important; }`); + collector.addRule(`.monaco-workbench .codicon-symbol-module { color: ${symbolIconModuleColor}; }`); } const outlineNamespaceColor = theme.getColor(SYMBOL_ICON_NAMESPACE_FOREGROUND); if (outlineNamespaceColor) { - collector.addRule(`.codicon-symbol-namespace { color: ${outlineNamespaceColor} !important; }`); + collector.addRule(`.monaco-workbench .codicon-symbol-namespace { color: ${outlineNamespaceColor}; }`); } const symbolIconNullColor = theme.getColor(SYMBOL_ICON_NULL_FOREGROUND); if (symbolIconNullColor) { - collector.addRule(`.codicon-symbol-null { color: ${symbolIconNullColor} !important; }`); + collector.addRule(`.monaco-workbench .codicon-symbol-null { color: ${symbolIconNullColor}; }`); } const symbolIconNumberColor = theme.getColor(SYMBOL_ICON_NUMBER_FOREGROUND); if (symbolIconNumberColor) { - collector.addRule(`.codicon-symbol-number { color: ${symbolIconNumberColor} !important; }`); + collector.addRule(`.monaco-workbench .codicon-symbol-number { color: ${symbolIconNumberColor}; }`); } const symbolIconObjectColor = theme.getColor(SYMBOL_ICON_OBJECT_FOREGROUND); if (symbolIconObjectColor) { - collector.addRule(`.codicon-symbol-object { color: ${symbolIconObjectColor} !important; }`); + collector.addRule(`.monaco-workbench .codicon-symbol-object { color: ${symbolIconObjectColor}; }`); } const symbolIconOperatorColor = theme.getColor(SYMBOL_ICON_OPERATOR_FOREGROUND); if (symbolIconOperatorColor) { - collector.addRule(`.codicon-symbol-operator { color: ${symbolIconOperatorColor} !important; }`); + collector.addRule(`.monaco-workbench .codicon-symbol-operator { color: ${symbolIconOperatorColor}; }`); } const symbolIconPackageColor = theme.getColor(SYMBOL_ICON_PACKAGE_FOREGROUND); if (symbolIconPackageColor) { - collector.addRule(`.codicon-symbol-package { color: ${symbolIconPackageColor} !important; }`); + collector.addRule(`.monaco-workbench .codicon-symbol-package { color: ${symbolIconPackageColor}; }`); } const symbolIconPropertyColor = theme.getColor(SYMBOL_ICON_PROPERTY_FOREGROUND); if (symbolIconPropertyColor) { - collector.addRule(`.codicon-symbol-property { color: ${symbolIconPropertyColor} !important; }`); + collector.addRule(`.monaco-workbench .codicon-symbol-property { color: ${symbolIconPropertyColor}; }`); } const symbolIconReferenceColor = theme.getColor(SYMBOL_ICON_REFERENCE_FOREGROUND); if (symbolIconReferenceColor) { - collector.addRule(`.codicon-symbol-reference { color: ${symbolIconReferenceColor} !important; }`); + collector.addRule(`.monaco-workbench .codicon-symbol-reference { color: ${symbolIconReferenceColor}; }`); } const symbolIconSnippetColor = theme.getColor(SYMBOL_ICON_SNIPPET_FOREGROUND); if (symbolIconSnippetColor) { - collector.addRule(`.codicon-symbol-snippet { color: ${symbolIconSnippetColor} !important; }`); + collector.addRule(`.monaco-workbench .codicon-symbol-snippet { color: ${symbolIconSnippetColor}; }`); } const symbolIconStringColor = theme.getColor(SYMBOL_ICON_STRING_FOREGROUND); if (symbolIconStringColor) { - collector.addRule(`.codicon-symbol-string { color: ${symbolIconStringColor} !important; }`); + collector.addRule(`.monaco-workbench .codicon-symbol-string { color: ${symbolIconStringColor}; }`); } const symbolIconStructColor = theme.getColor(SYMBOL_ICON_STRUCT_FOREGROUND); if (symbolIconStructColor) { - collector.addRule(`.codicon-symbol-struct { color: ${symbolIconStructColor} !important; }`); + collector.addRule(`.monaco-workbench .codicon-symbol-struct { color: ${symbolIconStructColor}; }`); } const symbolIconTextColor = theme.getColor(SYMBOL_ICON_TEXT_FOREGROUND); if (symbolIconTextColor) { - collector.addRule(`.codicon-symbol-text { color: ${symbolIconTextColor} !important; }`); + collector.addRule(`.monaco-workbench .codicon-symbol-text { color: ${symbolIconTextColor}; }`); } const symbolIconTypeParameterColor = theme.getColor(SYMBOL_ICON_TYPEPARAMETER_FOREGROUND); if (symbolIconTypeParameterColor) { - collector.addRule(`.codicon-symbol-type-parameter { color: ${symbolIconTypeParameterColor} !important; }`); + collector.addRule(`.monaco-workbench .codicon-symbol-type-parameter { color: ${symbolIconTypeParameterColor}; }`); } const symbolIconUnitColor = theme.getColor(SYMBOL_ICON_UNIT_FOREGROUND); if (symbolIconUnitColor) { - collector.addRule(`.codicon-symbol-unit { color: ${symbolIconUnitColor} !important; }`); + collector.addRule(`.monaco-workbench .codicon-symbol-unit { color: ${symbolIconUnitColor}; }`); } const symbolIconVariableColor = theme.getColor(SYMBOL_ICON_VARIABLE_FOREGROUND); if (symbolIconVariableColor) { - collector.addRule(`.codicon-symbol-variable { color: ${symbolIconVariableColor} !important; }`); + collector.addRule(`.monaco-workbench .codicon-symbol-variable { color: ${symbolIconVariableColor}; }`); } }); diff --git a/src/vs/editor/contrib/hover/modesContentHover.ts b/src/vs/editor/contrib/hover/modesContentHover.ts index 2124cf85f6..5d8c461307 100644 --- a/src/vs/editor/contrib/hover/modesContentHover.ts +++ b/src/vs/editor/contrib/hover/modesContentHover.ts @@ -504,34 +504,33 @@ export class ModesContentHoverWidget extends ContentHoverWidget { messageElement.innerText = message; if (source || code) { - if (typeof code === 'string') { + // Code has link + if (code && typeof code !== 'string') { + const sourceAndCodeElement = $('span'); + if (source) { + const sourceElement = dom.append(sourceAndCodeElement, $('span')); + sourceElement.innerText = source; + } + this._codeLink = dom.append(sourceAndCodeElement, $('a.code-link')); + this._codeLink.setAttribute('href', code.target.toString()); + + this._codeLink.onclick = (e) => { + this._openerService.open(code.target); + e.preventDefault(); + e.stopPropagation(); + }; + + const codeElement = dom.append(this._codeLink, $('span')); + codeElement.innerText = code.value; + + const detailsElement = dom.append(markerElement, sourceAndCodeElement); + detailsElement.style.opacity = '0.6'; + detailsElement.style.paddingLeft = '6px'; + } else { const detailsElement = dom.append(markerElement, $('span')); detailsElement.style.opacity = '0.6'; detailsElement.style.paddingLeft = '6px'; detailsElement.innerText = source && code ? `${source}(${code})` : source ? source : `(${code})`; - } else { - if (code) { - const sourceAndCodeElement = $('span'); - if (source) { - const sourceElement = dom.append(sourceAndCodeElement, $('span')); - sourceElement.innerText = source; - } - this._codeLink = dom.append(sourceAndCodeElement, $('a.code-link')); - this._codeLink.setAttribute('href', code.target.toString()); - - this._codeLink.onclick = (e) => { - this._openerService.open(code.target); - e.preventDefault(); - e.stopPropagation(); - }; - - const codeElement = dom.append(this._codeLink, $('span')); - codeElement.innerText = code.value; - - const detailsElement = dom.append(markerElement, sourceAndCodeElement); - detailsElement.style.opacity = '0.6'; - detailsElement.style.paddingLeft = '6px'; - } } } diff --git a/src/vs/platform/backup/electron-main/backupMainService.ts b/src/vs/platform/backup/electron-main/backupMainService.ts index 5ec5b9e54a..fb5fe23d51 100644 --- a/src/vs/platform/backup/electron-main/backupMainService.ts +++ b/src/vs/platform/backup/electron-main/backupMainService.ts @@ -11,6 +11,7 @@ import { writeFileSync, writeFile, readFile, readdir, exists, rimraf, rename, Ri import { IBackupMainService, IWorkspaceBackupInfo, isWorkspaceBackupInfo } from 'vs/platform/backup/electron-main/backup'; import { IBackupWorkspacesFormat, IEmptyWindowBackupInfo } from 'vs/platform/backup/node/backup'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IFilesConfiguration, HotExitConfiguration } from 'vs/platform/files/common/files'; import { ILogService } from 'vs/platform/log/common/log'; @@ -32,7 +33,7 @@ export class BackupMainService implements IBackupMainService { private emptyWindows: IEmptyWindowBackupInfo[] = []; constructor( - @IEnvironmentService environmentService: IEnvironmentService, + @IEnvironmentService environmentService: INativeEnvironmentService, @IConfigurationService private readonly configurationService: IConfigurationService, @ILogService private readonly logService: ILogService ) { diff --git a/src/vs/platform/configuration/test/common/testConfigurationService.ts b/src/vs/platform/configuration/test/common/testConfigurationService.ts index 6879dc8f49..8590fc9e62 100644 --- a/src/vs/platform/configuration/test/common/testConfigurationService.ts +++ b/src/vs/platform/configuration/test/common/testConfigurationService.ts @@ -18,7 +18,7 @@ export class TestConfigurationService implements IConfigurationService { this.configuration = configuration || Object.create(null); } - private configurationByRoot: TernarySearchTree = TernarySearchTree.forPaths(); + private configurationByRoot: TernarySearchTree = TernarySearchTree.forPaths(); public reloadConfiguration(): Promise { return Promise.resolve(this.getValue()); diff --git a/src/vs/platform/diagnostics/common/diagnostics.ts b/src/vs/platform/diagnostics/common/diagnostics.ts index 9b17f7c701..57e86fc826 100644 --- a/src/vs/platform/diagnostics/common/diagnostics.ts +++ b/src/vs/platform/diagnostics/common/diagnostics.ts @@ -13,6 +13,14 @@ export interface IMachineInfo { cpus?: string; memory: string; vmHint: string; + linuxEnv?: ILinuxEnv; +} + +export interface ILinuxEnv { + desktopSession?: string; + xdgSessionDesktop?: string; + xdgCurrentDesktop?: string; + xdgSessionType?: string; } export interface IDiagnosticInfo { diff --git a/src/vs/platform/diagnostics/node/diagnosticsService.ts b/src/vs/platform/diagnostics/node/diagnosticsService.ts index 7bd6075d32..4e19409def 100644 --- a/src/vs/platform/diagnostics/node/diagnosticsService.ts +++ b/src/vs/platform/diagnostics/node/diagnosticsService.ts @@ -11,7 +11,7 @@ import { parse, ParseError, getNodeType } from 'vs/base/common/json'; import { listProcesses } from 'vs/base/node/ps'; import product from 'vs/platform/product/common/product'; import { repeat, pad } from 'vs/base/common/strings'; -import { isWindows } from 'vs/base/common/platform'; +import { isWindows, isLinux } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; import { ProcessItem } from 'vs/base/common/processes'; import { IMainProcessInfo } from 'vs/platform/launch/common/launch'; @@ -336,11 +336,19 @@ export class DiagnosticsService implements IDiagnosticsService { remoteData }; - if (!isWindows) { systemInfo.load = `${osLib.loadavg().map(l => Math.round(l)).join(', ')}`; } + if (isLinux) { + systemInfo.linuxEnv = { + desktopSession: process.env.DESKTOP_SESSION, + xdgSessionDesktop: process.env.XDG_SESSION_DESKTOP, + xdgCurrentDesktop: process.env.XDG_CURRENT_DESKTOP, + xdgSessionType: process.env.XDG_SESSION_TYPE + }; + } + return Promise.resolve(systemInfo); } diff --git a/src/vs/platform/driver/electron-main/driver.ts b/src/vs/platform/driver/electron-main/driver.ts index 9ef9ab6fdb..06e65bb8ad 100644 --- a/src/vs/platform/driver/electron-main/driver.ts +++ b/src/vs/platform/driver/electron-main/driver.ts @@ -13,7 +13,7 @@ import { SimpleKeybinding, KeyCode } from 'vs/base/common/keyCodes'; import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding'; import { OS } from 'vs/base/common/platform'; import { Emitter, Event } from 'vs/base/common/event'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; import { ScanCodeBinding } from 'vs/base/common/scanCode'; import { KeybindingParser } from 'vs/base/common/keybindingParser'; import { timeout } from 'vs/base/common/async'; @@ -207,7 +207,7 @@ export class Driver implements IDriver, IWindowDriverRegistry { export async function serve( windowServer: IPCServer, handle: string, - environmentService: IEnvironmentService, + environmentService: INativeEnvironmentService, instantiationService: IInstantiationService ): Promise { const verbose = environmentService.driverVerbose; diff --git a/src/vs/platform/electron/electron-main/electronMainService.ts b/src/vs/platform/electron/electron-main/electronMainService.ts index 2a9da7611e..eb77f1ed42 100644 --- a/src/vs/platform/electron/electron-main/electronMainService.ts +++ b/src/vs/platform/electron/electron-main/electronMainService.ts @@ -6,9 +6,9 @@ import { Event } from 'vs/base/common/event'; import { IWindowsMainService, ICodeWindow } from 'vs/platform/windows/electron-main/windows'; import { MessageBoxOptions, MessageBoxReturnValue, shell, OpenDevToolsOptions, SaveDialogOptions, SaveDialogReturnValue, OpenDialogOptions, OpenDialogReturnValue, CrashReporterStartOptions, crashReporter, Menu, BrowserWindow, app } from 'electron'; -import { INativeOpenWindowOptions } from 'vs/platform/windows/node/window'; +import { INativeOpenWindowOptions, IOpenedWindow, OpenContext } from 'vs/platform/windows/node/window'; import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; -import { IOpenedWindow, OpenContext, IWindowOpenable, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows'; +import { IWindowOpenable, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows'; import { INativeOpenDialogOptions } from 'vs/platform/dialogs/node/dialogs'; import { isMacintosh } from 'vs/base/common/platform'; import { IElectronService } from 'vs/platform/electron/node/electron'; @@ -21,6 +21,7 @@ import { URI } from 'vs/base/common/uri'; import { ITelemetryData, ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { ILogService } from 'vs/platform/log/common/log'; +import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; export interface IElectronMainService extends AddFirstParameterToFunctions /* only methods, not events */, number | undefined /* window ID */> { } @@ -34,7 +35,7 @@ export class ElectronMainService implements IElectronMainService { @IWindowsMainService private readonly windowsMainService: IWindowsMainService, @IDialogMainService private readonly dialogMainService: IDialogMainService, @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, - @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IEnvironmentService private readonly environmentService: INativeEnvironmentService, @ITelemetryService private readonly telemetryService: ITelemetryService, @ILogService private readonly logService: ILogService ) { diff --git a/src/vs/platform/electron/node/electron.ts b/src/vs/platform/electron/node/electron.ts index 4f7f0c9590..5dd4f5fcd0 100644 --- a/src/vs/platform/electron/node/electron.ts +++ b/src/vs/platform/electron/node/electron.ts @@ -6,10 +6,10 @@ import { Event } from 'vs/base/common/event'; import { MessageBoxOptions, MessageBoxReturnValue, OpenDevToolsOptions, SaveDialogOptions, OpenDialogOptions, OpenDialogReturnValue, SaveDialogReturnValue, CrashReporterStartOptions } from 'electron'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IWindowOpenable, IOpenEmptyWindowOptions, IOpenedWindow } from 'vs/platform/windows/common/windows'; +import { IWindowOpenable, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows'; import { INativeOpenDialogOptions } from 'vs/platform/dialogs/node/dialogs'; import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; -import { INativeOpenWindowOptions } from 'vs/platform/windows/node/window'; +import { INativeOpenWindowOptions, IOpenedWindow } from 'vs/platform/windows/node/window'; export const IElectronService = createDecorator('electronService'); diff --git a/src/vs/platform/environment/common/environment.ts b/src/vs/platform/environment/common/environment.ts index 9640e98158..5347cc470a 100644 --- a/src/vs/platform/environment/common/environment.ts +++ b/src/vs/platform/environment/common/environment.ts @@ -5,99 +5,6 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { URI } from 'vs/base/common/uri'; -import { IUserHomeProvider } from 'vs/base/common/labels'; - -export interface ParsedArgs { - _: string[]; - 'folder-uri'?: string[]; // undefined or array of 1 or more - 'file-uri'?: string[]; // undefined or array of 1 or more - _urls?: string[]; - help?: boolean; - version?: boolean; - telemetry?: boolean; - status?: boolean; - wait?: boolean; - waitMarkerFilePath?: string; - diff?: boolean; - add?: boolean; - goto?: boolean; - 'new-window'?: boolean; - 'unity-launch'?: boolean; // Always open a new window, except if opening the first window or opening a file or folder as part of the launch. - 'reuse-window'?: boolean; - locale?: string; - 'user-data-dir'?: string; - 'prof-startup'?: boolean; - 'prof-startup-prefix'?: string; - 'prof-append-timers'?: string; - verbose?: boolean; - trace?: boolean; - 'trace-category-filter'?: string; - 'trace-options'?: string; - log?: string; - logExtensionHostCommunication?: boolean; - 'extensions-dir'?: string; - 'builtin-extensions-dir'?: string; - extensionDevelopmentPath?: string[]; // // undefined or array of 1 or more local paths or URIs - extensionTestsPath?: string; // either a local path or a URI - 'inspect-extensions'?: string; - 'inspect-brk-extensions'?: string; - debugId?: string; - 'inspect-search'?: string; - 'inspect-brk-search'?: string; - 'disable-extensions'?: boolean; - 'disable-extension'?: string[]; // undefined or array of 1 or more - 'list-extensions'?: boolean; - 'show-versions'?: boolean; - 'category'?: string; - 'install-extension'?: string[]; // undefined or array of 1 or more - 'uninstall-extension'?: string[]; // undefined or array of 1 or more - 'locate-extension'?: string[]; // undefined or array of 1 or more - 'enable-proposed-api'?: string[]; // undefined or array of 1 or more - 'open-url'?: boolean; - 'skip-getting-started'?: boolean; - 'skip-release-notes'?: boolean; - 'sticky-quickinput'?: boolean; - 'disable-restore-windows'?: boolean; - 'disable-telemetry'?: boolean; - 'export-default-configuration'?: string; - 'install-source'?: string; - 'disable-updates'?: boolean; - 'disable-crash-reporter'?: boolean; - 'skip-add-to-recently-opened'?: boolean; - 'max-memory'?: string; - 'file-write'?: boolean; - 'file-chmod'?: boolean; - 'driver'?: string; - 'driver-verbose'?: boolean; - remote?: string; - 'disable-user-env-probe'?: boolean; - 'force'?: boolean; - 'force-user-env'?: boolean; - // {{SQL CARBON EDIT}} Start - aad?: boolean; - database?: string; - integrated?: boolean; - server?: string; - user?: string; - command?: string; - // {{SQL CARBON EDIT}} End - 'sync'?: 'on' | 'off'; - - // chromium command line args: https://electronjs.org/docs/all#supported-chrome-command-line-switches - 'no-proxy-server'?: boolean; - 'proxy-server'?: string; - 'proxy-bypass-list'?: string; - 'proxy-pac-url'?: string; - 'inspect'?: string; - 'inspect-brk'?: string; - 'js-flags'?: string; - 'disable-gpu'?: boolean; - 'nolazy'?: boolean; - 'force-device-scale-factor'?: string; - 'force-renderer-accessibility'?: boolean; - 'ignore-certificate-errors'?: boolean; - 'allow-insecure-localhost'?: boolean; -} export const IEnvironmentService = createDecorator('environmentService'); @@ -112,21 +19,16 @@ export interface IExtensionHostDebugParams extends IDebugParams { export const BACKUPS = 'Backups'; -export interface IEnvironmentService extends IUserHomeProvider { +export interface IEnvironmentService { + + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // NOTE: DO NOT ADD ANY OTHER PROPERTY INTO THE COLLECTION HERE + // UNLESS THIS PROPERTY IS SUPPORTED BOTH IN WEB AND DESKTOP!!!! + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! _serviceBrand: undefined; - args: ParsedArgs; - - execPath: string; - appRoot: string; - - userHome: string; - userDataPath: string; - - appSettingsHome: URI; - - // user roaming data + // --- user roaming data userRoamingDataHome: URI; settingsResource: URI; keybindingsResource: URI; @@ -134,46 +36,42 @@ export interface IEnvironmentService extends IUserHomeProvider { argvResource: URI; snippetsHome: URI; - // sync resources + // --- settings sync userDataSyncLogResource: URI; userDataSyncHome: URI; + sync: 'on' | 'off'; - machineSettingsResource: URI; - - globalStorageHome: string; - workspaceStorageHome: string; - - backupHome: URI; - backupWorkspacesPath: string; - - untitledWorkspacesHome: URI; - + // --- extension development + debugExtensionHost: IExtensionHostDebugParams; isExtensionDevelopment: boolean; disableExtensions: boolean | string[]; - builtinExtensionsPath: string; - extensionsPath?: string; extensionDevelopmentLocationURI?: URI[]; extensionTestsLocationURI?: URI; - extensionEnabledProposedApi?: string[] | undefined; + extensionEnabledProposedApi?: string[]; logExtensionHostCommunication?: boolean; - debugExtensionHost: IExtensionHostDebugParams; - + // --- logging + logsPath: string; + logLevel?: string; + verbose: boolean; isBuilt: boolean; - logsPath: string; - verbose: boolean; + // --- data paths + backupHome: URI; + untitledWorkspacesHome: URI; - mainIPCHandle: string; - sharedIPCHandle: string; + // --- misc + disableTelemetry: boolean; - nodeCachedDataDir?: string; + serviceMachineIdResource: URI; - installSourcePath: string; - disableUpdates: boolean; + /** + * @deprecated use IRemotePathService#userHome instead (https://github.com/microsoft/vscode/issues/94506) + */ + userHome?: URI; - driverHandle?: string; - driverVerbose: boolean; - - serviceMachineIdResource?: URI; + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + // NOTE: DO NOT ADD ANY OTHER PROPERTY INTO THE COLLECTION HERE + // UNLESS THIS PROPERTY IS SUPPORTED BOTH IN WEB AND DESKTOP!!!! + // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! } diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index 8a7026b5a6..8e24c02db9 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -6,7 +6,97 @@ import * as minimist from 'minimist'; import * as os from 'os'; import { localize } from 'vs/nls'; -import { ParsedArgs } from 'vs/platform/environment/common/environment'; + +export interface ParsedArgs { + _: string[]; + 'folder-uri'?: string[]; // undefined or array of 1 or more + 'file-uri'?: string[]; // undefined or array of 1 or more + _urls?: string[]; + help?: boolean; + version?: boolean; + telemetry?: boolean; + status?: boolean; + wait?: boolean; + waitMarkerFilePath?: string; + diff?: boolean; + add?: boolean; + goto?: boolean; + 'new-window'?: boolean; + 'unity-launch'?: boolean; // Always open a new window, except if opening the first window or opening a file or folder as part of the launch. + 'reuse-window'?: boolean; + locale?: string; + 'user-data-dir'?: string; + 'prof-startup'?: boolean; + 'prof-startup-prefix'?: string; + 'prof-append-timers'?: string; + verbose?: boolean; + trace?: boolean; + 'trace-category-filter'?: string; + 'trace-options'?: string; + log?: string; + logExtensionHostCommunication?: boolean; + 'extensions-dir'?: string; + 'builtin-extensions-dir'?: string; + extensionDevelopmentPath?: string[]; // // undefined or array of 1 or more local paths or URIs + extensionTestsPath?: string; // either a local path or a URI + 'inspect-extensions'?: string; + 'inspect-brk-extensions'?: string; + debugId?: string; + 'inspect-search'?: string; + 'inspect-brk-search'?: string; + 'disable-extensions'?: boolean; + 'disable-extension'?: string[]; // undefined or array of 1 or more + 'list-extensions'?: boolean; + 'show-versions'?: boolean; + 'category'?: string; + 'install-extension'?: string[]; // undefined or array of 1 or more + 'uninstall-extension'?: string[]; // undefined or array of 1 or more + 'locate-extension'?: string[]; // undefined or array of 1 or more + 'enable-proposed-api'?: string[]; // undefined or array of 1 or more + 'open-url'?: boolean; + 'skip-getting-started'?: boolean; + 'disable-restore-windows'?: boolean; + 'disable-telemetry'?: boolean; + 'export-default-configuration'?: string; + 'install-source'?: string; + 'disable-updates'?: boolean; + 'disable-crash-reporter'?: boolean; + 'skip-add-to-recently-opened'?: boolean; + 'max-memory'?: string; + 'file-write'?: boolean; + 'file-chmod'?: boolean; + 'driver'?: string; + 'driver-verbose'?: boolean; + remote?: string; + 'disable-user-env-probe'?: boolean; + 'force'?: boolean; + 'force-user-env'?: boolean; + 'sync'?: 'on' | 'off'; + + // {{SQL CARBON EDIT}} Start + aad?: boolean; + database?: string; + integrated?: boolean; + server?: string; + user?: string; + command?: string; + // {{SQL CARBON EDIT}} End + + // chromium command line args: https://electronjs.org/docs/all#supported-chrome-command-line-switches + 'no-proxy-server'?: boolean; + 'proxy-server'?: string; + 'proxy-bypass-list'?: string; + 'proxy-pac-url'?: string; + 'inspect'?: string; + 'inspect-brk'?: string; + 'js-flags'?: string; + 'disable-gpu'?: boolean; + 'nolazy'?: boolean; + 'force-device-scale-factor'?: string; + 'force-renderer-accessibility'?: boolean; + 'ignore-certificate-errors'?: boolean; + 'allow-insecure-localhost'?: boolean; +} /** * This code is also used by standalone cli's. Avoid adding any other dependencies. @@ -89,8 +179,6 @@ export const OPTIONS: OptionDescriptions> = { 'driver': { type: 'string' }, 'logExtensionHostCommunication': { type: 'boolean' }, 'skip-getting-started': { type: 'boolean' }, - 'skip-release-notes': { type: 'boolean' }, - 'sticky-quickinput': { type: 'boolean' }, 'disable-restore-windows': { type: 'boolean' }, 'disable-telemetry': { type: 'boolean' }, 'disable-updates': { type: 'boolean' }, diff --git a/src/vs/platform/environment/node/argvHelper.ts b/src/vs/platform/environment/node/argvHelper.ts index b8ac38c898..09c68a312e 100644 --- a/src/vs/platform/environment/node/argvHelper.ts +++ b/src/vs/platform/environment/node/argvHelper.ts @@ -6,9 +6,8 @@ import * as assert from 'assert'; import { firstIndex } from 'vs/base/common/arrays'; import { localize } from 'vs/nls'; -import { ParsedArgs } from '../common/environment'; import { MIN_MAX_MEMORY_SIZE_MB } from 'vs/platform/files/node/files'; -import { parseArgs, ErrorReporter, OPTIONS } from 'vs/platform/environment/node/argv'; +import { parseArgs, ErrorReporter, OPTIONS, ParsedArgs } from 'vs/platform/environment/node/argv'; function parseAndValidate(cmdLineArgs: string[], reportWarnings: boolean): ParsedArgs { const errorReporter: ErrorReporter = { diff --git a/src/vs/platform/environment/node/environmentService.ts b/src/vs/platform/environment/node/environmentService.ts index 4ba758598b..89f5def643 100644 --- a/src/vs/platform/environment/node/environmentService.ts +++ b/src/vs/platform/environment/node/environmentService.ts @@ -3,7 +3,8 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IEnvironmentService, ParsedArgs, IDebugParams, IExtensionHostDebugParams, BACKUPS } from 'vs/platform/environment/common/environment'; +import { IEnvironmentService, IDebugParams, IExtensionHostDebugParams, BACKUPS } from 'vs/platform/environment/common/environment'; +import { ParsedArgs } from 'vs/platform/environment/node/argv'; import * as crypto from 'crypto'; import * as paths from 'vs/base/node/paths'; import * as os from 'os'; @@ -16,64 +17,37 @@ import { isWindows, isLinux } from 'vs/base/common/platform'; import { getPathFromAmdModule } from 'vs/base/common/amd'; import { URI } from 'vs/base/common/uri'; -// Read this before there's any chance it is overwritten -// Related to https://github.com/Microsoft/vscode/issues/30624 -export const xdgRuntimeDir = process.env['XDG_RUNTIME_DIR']; +export interface INativeEnvironmentService extends IEnvironmentService { + args: ParsedArgs; -function getNixIPCHandle(userDataPath: string, type: string): string { - const vscodePortable = process.env['VSCODE_PORTABLE']; + appRoot: string; + execPath: string; - if (xdgRuntimeDir && !vscodePortable) { - const scope = crypto.createHash('md5').update(userDataPath).digest('hex').substr(0, 8); - return path.join(xdgRuntimeDir, `vscode-${scope}-${product.version}-${type}.sock`); - } + appSettingsHome: URI; + userDataPath: string; + userHome: URI; + machineSettingsResource: URI; + backupWorkspacesPath: string; + nodeCachedDataDir?: string; - return path.join(userDataPath, `${product.version}-${type}.sock`); + mainIPCHandle: string; + sharedIPCHandle: string; + + installSourcePath: string; + + extensionsPath?: string; + builtinExtensionsPath: string; + + globalStorageHome: string; + workspaceStorageHome: string; + + driverHandle?: string; + driverVerbose: boolean; + + disableUpdates: boolean; } -function getWin32IPCHandle(userDataPath: string, type: string): string { - const scope = crypto.createHash('md5').update(userDataPath).digest('hex'); - - return `\\\\.\\pipe\\${scope}-${product.version}-${type}-sock`; -} - -function getIPCHandle(userDataPath: string, type: string): string { - if (isWindows) { - return getWin32IPCHandle(userDataPath, type); - } - - return getNixIPCHandle(userDataPath, type); -} - -function getCLIPath(execPath: string, appRoot: string, isBuilt: boolean): string { - - // Windows - if (isWindows) { - if (isBuilt) { - return path.join(path.dirname(execPath), 'bin', `${product.applicationName}.cmd`); - } - - return path.join(appRoot, 'scripts', 'code-cli.bat'); - } - - // Linux - if (isLinux) { - if (isBuilt) { - return path.join(path.dirname(execPath), 'bin', `${product.applicationName}`); - } - - return path.join(appRoot, 'scripts', 'code-cli.sh'); - } - - // macOS - if (isBuilt) { - return path.join(appRoot, 'bin', 'code'); - } - - return path.join(appRoot, 'scripts', 'code-cli.sh'); -} - -export class EnvironmentService implements IEnvironmentService { +export class EnvironmentService implements INativeEnvironmentService { _serviceBrand: undefined; @@ -90,7 +64,7 @@ export class EnvironmentService implements IEnvironmentService { readonly logsPath: string; @memoize - get userHome(): string { return os.homedir(); } + get userHome(): URI { return URI.file(os.homedir()); } @memoize get userDataPath(): string { @@ -117,6 +91,9 @@ export class EnvironmentService implements IEnvironmentService { @memoize get userDataSyncLogResource(): URI { return URI.file(path.join(this.logsPath, 'userDataSync.log')); } + @memoize + get sync(): 'on' | 'off' { return this.args.sync === 'off' ? 'off' : 'on'; } + @memoize get machineSettingsResource(): URI { return resources.joinPath(URI.file(path.join(this.userDataPath, 'Machine')), 'settings.json'); } @@ -139,7 +116,7 @@ export class EnvironmentService implements IEnvironmentService { return URI.file(path.join(vscodePortable, 'argv.json')); } - return URI.file(path.join(this.userHome, product.dataFolderName, 'argv.json')); + return resources.joinPath(this.userHome, product.dataFolderName, 'argv.json'); } @memoize @@ -188,7 +165,7 @@ export class EnvironmentService implements IEnvironmentService { return path.join(vscodePortable, 'extensions'); } - return path.join(this.userHome, product.dataFolderName, 'extensions'); + return resources.joinPath(this.userHome, product.dataFolderName, 'extensions').fsPath; } @memoize @@ -252,7 +229,7 @@ export class EnvironmentService implements IEnvironmentService { get isBuilt(): boolean { return !process.env['VSCODE_DEV']; } get verbose(): boolean { return !!this._args.verbose; } - get log(): string | undefined { return this._args.log; } + get logLevel(): string | undefined { return this._args.log; } @memoize get mainIPCHandle(): string { return getIPCHandle(this.userDataPath, 'main'); } @@ -272,6 +249,8 @@ export class EnvironmentService implements IEnvironmentService { get driverHandle(): string | undefined { return this._args['driver']; } get driverVerbose(): boolean { return !!this._args['driver-verbose']; } + get disableTelemetry(): boolean { return !!this._args['disable-telemetry']; } + constructor(private _args: ParsedArgs, private _execPath: string) { if (!process.env['VSCODE_LOGS']) { const key = toLocalISOString(new Date()).replace(/-|:|\.\d+Z$/g, ''); @@ -288,6 +267,63 @@ export class EnvironmentService implements IEnvironmentService { } } +// Read this before there's any chance it is overwritten +// Related to https://github.com/Microsoft/vscode/issues/30624 +export const xdgRuntimeDir = process.env['XDG_RUNTIME_DIR']; + +function getNixIPCHandle(userDataPath: string, type: string): string { + const vscodePortable = process.env['VSCODE_PORTABLE']; + + if (xdgRuntimeDir && !vscodePortable) { + const scope = crypto.createHash('md5').update(userDataPath).digest('hex').substr(0, 8); + return path.join(xdgRuntimeDir, `vscode-${scope}-${product.version}-${type}.sock`); + } + + return path.join(userDataPath, `${product.version}-${type}.sock`); +} + +function getWin32IPCHandle(userDataPath: string, type: string): string { + const scope = crypto.createHash('md5').update(userDataPath).digest('hex'); + + return `\\\\.\\pipe\\${scope}-${product.version}-${type}-sock`; +} + +function getIPCHandle(userDataPath: string, type: string): string { + if (isWindows) { + return getWin32IPCHandle(userDataPath, type); + } + + return getNixIPCHandle(userDataPath, type); +} + +function getCLIPath(execPath: string, appRoot: string, isBuilt: boolean): string { + + // Windows + if (isWindows) { + if (isBuilt) { + return path.join(path.dirname(execPath), 'bin', `${product.applicationName}.cmd`); + } + + return path.join(appRoot, 'scripts', 'code-cli.bat'); + } + + // Linux + if (isLinux) { + if (isBuilt) { + return path.join(path.dirname(execPath), 'bin', `${product.applicationName}`); + } + + return path.join(appRoot, 'scripts', 'code-cli.sh'); + } + + // macOS + if (isBuilt) { + return path.join(appRoot, 'bin', 'code'); + } + + return path.join(appRoot, 'scripts', 'code-cli.sh'); +} + export function parseExtensionHostPort(args: ParsedArgs, isBuild: boolean): IExtensionHostDebugParams { return parseDebugPort(args['inspect-extensions'], args['inspect-brk-extensions'], 5870, isBuild, args.debugId); } diff --git a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts index 592cee48ce..80cb767ffb 100644 --- a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts +++ b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts @@ -935,8 +935,6 @@ export async function resolveMarketplaceHeaders(version: string, environmentServ 'User-Agent': `VSCode ${version}` }; const uuid = await getServiceMachineId(environmentService, fileService, storageService); - if (uuid) { - headers['X-Market-User-Id'] = uuid; - } + headers['X-Market-User-Id'] = uuid; return headers; } diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts index afdcd4ea14..911e5f3302 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts @@ -10,6 +10,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' import { URI } from 'vs/base/common/uri'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IExtensionManifest, IExtension, ExtensionType } from 'vs/platform/extensions/common/extensions'; +import { IExeBasedExtensionTip } from 'vs/platform/product/common/productService'; export const EXTENSION_IDENTIFIER_PATTERN = '^([a-z0-9A-Z][a-z0-9\-A-Z]*)\\.([a-z0-9A-Z][a-z0-9\-A-Z]*)$'; export const EXTENSION_IDENTIFIER_REGEX = new RegExp(EXTENSION_IDENTIFIER_PATTERN); @@ -225,6 +226,20 @@ export interface IGlobalExtensionEnablementService { } +export type IExecutableBasedExtensionTip = { extensionId: string } & Omit, 'important'>; +export type IWorkspaceTips = { readonly remoteSet: string[]; readonly recommendations: string[]; }; + +export const IExtensionTipsService = createDecorator('IExtensionTipsService'); +export interface IExtensionTipsService { + _serviceBrand: undefined; + + getImportantExecutableBasedTips(): Promise; + getOtherExecutableBasedTips(): Promise; + getAllWorkspacesTips(): Promise; +} + + + export const ExtensionsLabel = localize('extensions', "Extensions"); export const ExtensionsChannelId = 'extensions'; export const PreferencesLabel = localize('preferences', "Preferences"); \ No newline at end of file diff --git a/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts b/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts index 64d6043fcd..a53c4eda1c 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagementIpc.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; -import { IExtensionManagementService, ILocalExtension, InstallExtensionEvent, DidInstallExtensionEvent, IGalleryExtension, DidUninstallExtensionEvent, IExtensionIdentifier, IGalleryMetadata, IReportedExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionManagementService, ILocalExtension, InstallExtensionEvent, DidInstallExtensionEvent, IGalleryExtension, DidUninstallExtensionEvent, IExtensionIdentifier, IGalleryMetadata, IReportedExtension, IExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { Event } from 'vs/base/common/event'; import { URI, UriComponents } from 'vs/base/common/uri'; import { IURITransformer, DefaultURITransformer, transformAndReviveIncomingURIs } from 'vs/base/common/uriIpc'; @@ -130,3 +130,23 @@ export class ExtensionManagementChannelClient implements IExtensionManagementSer return Promise.resolve(this.channel.call('getExtensionsReport')); } } + +export class ExtensionTipsChannel implements IServerChannel { + + constructor(private service: IExtensionTipsService) { + } + + listen(context: any, event: string): Event { + throw new Error('Invalid listen'); + } + + call(context: any, command: string, args?: any): Promise { + switch (command) { + case 'getImportantExecutableBasedTips': return this.service.getImportantExecutableBasedTips(); + case 'getOtherExecutableBasedTips': return this.service.getOtherExecutableBasedTips(); + case 'getAllWorkspacesTips': return this.service.getAllWorkspacesTips(); + } + + throw new Error('Invalid call'); + } +} diff --git a/src/vs/platform/extensionManagement/common/extensionManagementUtil.ts b/src/vs/platform/extensionManagement/common/extensionManagementUtil.ts index 7d9db3da2a..59077e4336 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagementUtil.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagementUtil.ts @@ -120,25 +120,4 @@ export function getMaliciousExtensionsSet(report: IReportedExtension[]): Set; - metadata: any; -} - -/** - * Parses the built-in extension JSON data and filters it down to the - * extensions built into this product quality. - */ -export function parseBuiltInExtensions(rawJson: string, productQuality: string | undefined) { - const parsed: IBuiltInExtension[] = JSON.parse(rawJson); - if (!productQuality) { - return parsed; - } - - return parsed.filter(ext => ext.forQualities?.indexOf?.(productQuality) !== -1); -} +} \ No newline at end of file diff --git a/src/vs/platform/extensionManagement/node/extensionLifecycle.ts b/src/vs/platform/extensionManagement/node/extensionLifecycle.ts index 01f8997409..7d600a7bff 100644 --- a/src/vs/platform/extensionManagement/node/extensionLifecycle.ts +++ b/src/vs/platform/extensionManagement/node/extensionLifecycle.ts @@ -12,7 +12,7 @@ import { join } from 'vs/base/common/path'; import { Limiter } from 'vs/base/common/async'; import { Event } from 'vs/base/common/event'; import { Schemas } from 'vs/base/common/network'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; import { rimraf } from 'vs/base/node/pfs'; export class ExtensionsLifecycle extends Disposable { @@ -20,7 +20,7 @@ export class ExtensionsLifecycle extends Disposable { private processesLimiter: Limiter = new Limiter(5); // Run max 5 processes in parallel constructor( - private environmentService: IEnvironmentService, + private environmentService: INativeEnvironmentService, private logService: ILogService ) { super(); diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index 62af51bc63..e73f9d560e 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -21,9 +21,10 @@ import { INSTALL_ERROR_MALICIOUS, INSTALL_ERROR_INCOMPATIBLE } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { areSameExtensions, getGalleryExtensionId, groupByExtension, getMaliciousExtensionsSet, getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData, ExtensionIdentifierWithVersion, parseBuiltInExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; +import { areSameExtensions, getGalleryExtensionId, groupByExtension, getMaliciousExtensionsSet, getGalleryExtensionTelemetryData, getLocalExtensionTelemetryData, ExtensionIdentifierWithVersion } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { localizeManifest } from '../common/extensionNls'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; import { Limiter, createCancelablePromise, CancelablePromise, Queue } from 'vs/base/common/async'; import { Event, Emitter } from 'vs/base/common/event'; import * as semver from 'semver-umd'; @@ -45,7 +46,6 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { getPathFromAmdModule } from 'vs/base/common/amd'; import { getManifest } from 'vs/platform/extensionManagement/node/extensionManagementUtil'; import { IExtensionManifest, ExtensionType } from 'vs/platform/extensions/common/extensions'; -import { IProductService } from 'vs/platform/product/common/productService'; const ERROR_SCANNING_SYS_EXTENSIONS = 'scanningSystem'; const ERROR_SCANNING_USER_EXTENSIONS = 'scanningUser'; @@ -128,12 +128,11 @@ export class ExtensionManagementService extends Disposable implements IExtension onDidUninstallExtension: Event = this._onDidUninstallExtension.event; constructor( - @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IEnvironmentService private readonly environmentService: INativeEnvironmentService, @IExtensionGalleryService private readonly galleryService: IExtensionGalleryService, @ILogService private readonly logService: ILogService, @optional(IDownloadService) private downloadService: IDownloadService, @ITelemetryService private readonly telemetryService: ITelemetryService, - @IProductService private readonly productService: IProductService, ) { super(); this.systemExtensionsPath = environmentService.builtinExtensionsPath; @@ -772,6 +771,7 @@ export class ExtensionManagementService extends Disposable implements IExtension // Scan other system extensions during development const devSystemExtensionsPromise = this.getDevSystemExtensionsList() .then(devSystemExtensionsList => { + console.log(devSystemExtensionsList); if (devSystemExtensionsList.length) { return this.scanExtensions(this.devSystemExtensionsPath, ExtensionType.System) .then(result => { @@ -963,17 +963,8 @@ export class ExtensionManagementService extends Disposable implements IExtension return this._devSystemExtensionsPath; } - private _devSystemExtensionsFilePath: string | null = null; - private get devSystemExtensionsFilePath(): string { - if (!this._devSystemExtensionsFilePath) { - this._devSystemExtensionsFilePath = path.normalize(path.join(getPathFromAmdModule(require, ''), '..', 'build', 'builtInExtensions.json')); - } - return this._devSystemExtensionsFilePath; - } - private getDevSystemExtensionsList(): Promise { - return pfs.readFile(this.devSystemExtensionsFilePath, 'utf8') - .then(data => parseBuiltInExtensions(data, this.productService.quality).map(ext => ext.name)); + return Promise.resolve(product.builtInExtensions ? product.builtInExtensions.map(e => e.name) : []); } private toNonCancellablePromise(promise: Promise): Promise { diff --git a/src/vs/platform/extensionManagement/node/extensionTipsService.ts b/src/vs/platform/extensionManagement/node/extensionTipsService.ts new file mode 100644 index 0000000000..5dac6d212d --- /dev/null +++ b/src/vs/platform/extensionManagement/node/extensionTipsService.ts @@ -0,0 +1,122 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { URI } from 'vs/base/common/uri'; +import { join, } from 'vs/base/common/path'; +import { IProductService, IExeBasedExtensionTip } from 'vs/platform/product/common/productService'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { env as processEnv } from 'vs/base/common/process'; +import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; +import { IFileService } from 'vs/platform/files/common/files'; +import { isWindows } from 'vs/base/common/platform'; +import { isNonEmptyArray } from 'vs/base/common/arrays'; +import { IExtensionTipsService, IExecutableBasedExtensionTip, IWorkspaceTips } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IStringDictionary, forEach } from 'vs/base/common/collections'; +import { IRequestService, asJson } from 'vs/platform/request/common/request'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { ILogService } from 'vs/platform/log/common/log'; + +export class ExtensionTipsService implements IExtensionTipsService { + + _serviceBrand: any; + + private readonly allImportantExecutableTips: IStringDictionary = {}; + private readonly allOtherExecutableTips: IStringDictionary = {}; + + constructor( + @IFileService private readonly fileService: IFileService, + @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IProductService private readonly productService: IProductService, + @IRequestService private readonly requestService: IRequestService, + @ILogService private readonly logService: ILogService, + ) { + if (this.productService.exeBasedExtensionTips) { + forEach(this.productService.exeBasedExtensionTips, ({ key, value }) => { + if (value.important) { + this.allImportantExecutableTips[key] = value; + } else { + this.allOtherExecutableTips[key] = value; + } + }); + } + } + + getAllWorkspacesTips(): Promise { + return this.fetchWorkspacesTips(); + } + + getImportantExecutableBasedTips(): Promise { + return this.getValidExecutableBasedExtensionTips(this.allImportantExecutableTips); + } + + getOtherExecutableBasedTips(): Promise { + return this.getValidExecutableBasedExtensionTips(this.allOtherExecutableTips); + } + + private async getValidExecutableBasedExtensionTips(executableTips: IStringDictionary): Promise { + const result: IExecutableBasedExtensionTip[] = []; + + const checkedExecutables: Map = new Map(); + for (const exeName of Object.keys(executableTips)) { + const extensionTip = executableTips[exeName]; + if (!isNonEmptyArray(extensionTip?.recommendations)) { + continue; + } + + const exePaths: string[] = []; + if (isWindows) { + if (extensionTip.windowsPath) { + exePaths.push(extensionTip.windowsPath.replace('%USERPROFILE%', processEnv['USERPROFILE']!) + .replace('%ProgramFiles(x86)%', processEnv['ProgramFiles(x86)']!) + .replace('%ProgramFiles%', processEnv['ProgramFiles']!) + .replace('%APPDATA%', processEnv['APPDATA']!) + .replace('%WINDIR%', processEnv['WINDIR']!)); + } + } else { + exePaths.push(join('/usr/local/bin', exeName)); + exePaths.push(join((this.environmentService as INativeEnvironmentService).userHome.fsPath, exeName)); + } + + for (const exePath of exePaths) { + let exists = checkedExecutables.get(exePath); + if (exists === undefined) { + exists = await this.fileService.exists(URI.file(exePath)); + checkedExecutables.set(exePath, exists); + } + if (exists) { + extensionTip.recommendations.forEach(recommendation => result.push({ + extensionId: recommendation, + friendlyName: extensionTip.friendlyName, + exeFriendlyName: extensionTip.exeFriendlyName, + windowsPath: extensionTip.windowsPath, + })); + } + } + } + + return result; + } + + private async fetchWorkspacesTips(): Promise { + if (!this.productService.extensionsGallery?.recommendationsUrl) { + return []; + } + try { + const context = await this.requestService.request({ type: 'GET', url: this.productService.extensionsGallery?.recommendationsUrl }, CancellationToken.None); + if (context.res.statusCode !== 200) { + return []; + } + const result = await asJson<{ workspaceRecommendations?: IWorkspaceTips[] }>(context); + if (!result) { + return []; + } + return result.workspaceRecommendations || []; + } catch (error) { + this.logService.error(error); + return []; + } + } + +} diff --git a/src/vs/platform/extensionManagement/node/extensionsManifestCache.ts b/src/vs/platform/extensionManagement/node/extensionsManifestCache.ts index e160016700..1ac396baa8 100644 --- a/src/vs/platform/extensionManagement/node/extensionsManifestCache.ts +++ b/src/vs/platform/extensionManagement/node/extensionsManifestCache.ts @@ -5,7 +5,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { join } from 'vs/base/common/path'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; import { IExtensionManagementService, DidInstallExtensionEvent, DidUninstallExtensionEvent } from 'vs/platform/extensionManagement/common/extensionManagement'; import { MANIFEST_CACHE_FOLDER, USER_MANIFEST_CACHE_FILE } from 'vs/platform/extensions/common/extensions'; import * as pfs from 'vs/base/node/pfs'; @@ -15,7 +15,7 @@ export class ExtensionsManifestCache extends Disposable { private extensionsManifestCache = join(this.environmentService.userDataPath, MANIFEST_CACHE_FOLDER, USER_MANIFEST_CACHE_FILE); constructor( - private readonly environmentService: IEnvironmentService, + private readonly environmentService: INativeEnvironmentService, extensionsManagementService: IExtensionManagementService ) { super(); diff --git a/src/vs/platform/files/common/fileService.ts b/src/vs/platform/files/common/fileService.ts index 333b2f93f5..37af5b3d85 100644 --- a/src/vs/platform/files/common/fileService.ts +++ b/src/vs/platform/files/common/fileService.ts @@ -18,7 +18,6 @@ import { isReadableStream, transform, ReadableStreamEvents, consumeReadableWithL import { Queue } from 'vs/base/common/async'; import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation'; import { Schemas } from 'vs/base/common/network'; -import { assign } from 'vs/base/common/objects'; import { createReadStream } from 'vs/platform/files/common/io'; export class FileService extends Disposable implements IFileService { @@ -183,7 +182,7 @@ export class FileService extends Disposable implements IFileService { const stat = await provider.stat(resource); - let trie: TernarySearchTree | undefined; + let trie: TernarySearchTree | undefined; return this.toFileStat(provider, resource, stat, undefined, !!resolveMetadata, (stat, siblings) => { @@ -384,14 +383,15 @@ export class FileService extends Disposable implements IFileService { async readFile(resource: URI, options?: IReadFileOptions): Promise { const provider = await this.withReadProvider(resource); - const stream = await this.doReadAsFileStream(provider, resource, assign({ + const stream = await this.doReadAsFileStream(provider, resource, { + ...options, // optimization: since we know that the caller does not // care about buffering, we indicate this to the reader. // this reduces all the overhead the buffered reading // has (open, read, close) if the provider supports // unbuffered reading. preferUnbuffered: true - }, options || Object.create(null))); + }); return { ...stream, diff --git a/src/vs/code/common/issue/issueReporterUtil.ts b/src/vs/platform/issue/common/issueReporterUtil.ts similarity index 100% rename from src/vs/code/common/issue/issueReporterUtil.ts rename to src/vs/platform/issue/common/issueReporterUtil.ts diff --git a/src/vs/platform/issue/electron-main/issueMainService.ts b/src/vs/platform/issue/electron-main/issueMainService.ts index 0fe9fbb372..6279251e43 100644 --- a/src/vs/platform/issue/electron-main/issueMainService.ts +++ b/src/vs/platform/issue/electron-main/issueMainService.ts @@ -12,6 +12,7 @@ import { ILaunchMainService } from 'vs/platform/launch/electron-main/launchMainS import { PerformanceInfo, isRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnostics'; import { IDiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsService'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; import { isMacintosh, IProcessEnvironment } from 'vs/base/common/platform'; import { ILogService } from 'vs/platform/log/common/log'; import { IWindowState } from 'vs/platform/windows/electron-main/windows'; @@ -30,7 +31,7 @@ export class IssueMainService implements IIssueService { constructor( private machineId: string, private userEnv: IProcessEnvironment, - @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IEnvironmentService private readonly environmentService: INativeEnvironmentService, @ILaunchMainService private readonly launchMainService: ILaunchMainService, @ILogService private readonly logService: ILogService, @IDiagnosticsService private readonly diagnosticsService: IDiagnosticsService, diff --git a/src/vs/platform/launch/electron-main/launchMainService.ts b/src/vs/platform/launch/electron-main/launchMainService.ts index 1cf3e7f43a..289f02f9c4 100644 --- a/src/vs/platform/launch/electron-main/launchMainService.ts +++ b/src/vs/platform/launch/electron-main/launchMainService.ts @@ -6,9 +6,11 @@ import { ILogService } from 'vs/platform/log/common/log'; import { IURLService } from 'vs/platform/url/common/url'; import { IProcessEnvironment, isMacintosh } from 'vs/base/common/platform'; -import { ParsedArgs, IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { ParsedArgs } from 'vs/platform/environment/node/argv'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { OpenContext, IWindowSettings } from 'vs/platform/windows/common/windows'; +import { IWindowSettings } from 'vs/platform/windows/common/windows'; +import { OpenContext } from 'vs/platform/windows/node/window'; import { IWindowsMainService, ICodeWindow } from 'vs/platform/windows/electron-main/windows'; import { whenDeleted } from 'vs/base/node/pfs'; import { IWorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService'; diff --git a/src/vs/platform/lifecycle/common/lifecycleService.ts b/src/vs/platform/lifecycle/common/lifecycleService.ts index 4ad35a49d8..9fd995dad5 100644 --- a/src/vs/platform/lifecycle/common/lifecycleService.ts +++ b/src/vs/platform/lifecycle/common/lifecycleService.ts @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event, Emitter } from 'vs/base/common/event'; +import { Emitter } from 'vs/base/common/event'; import { Barrier } from 'vs/base/common/async'; import { Disposable } from 'vs/base/common/lifecycle'; import { ILifecycleService, BeforeShutdownEvent, WillShutdownEvent, StartupKind, LifecyclePhase, LifecyclePhaseToString } from 'vs/platform/lifecycle/common/lifecycle'; @@ -15,13 +15,13 @@ export abstract class AbstractLifecycleService extends Disposable implements ILi _serviceBrand: undefined; protected readonly _onBeforeShutdown = this._register(new Emitter()); - readonly onBeforeShutdown: Event = this._onBeforeShutdown.event; + readonly onBeforeShutdown = this._onBeforeShutdown.event; protected readonly _onWillShutdown = this._register(new Emitter()); - readonly onWillShutdown: Event = this._onWillShutdown.event; + readonly onWillShutdown = this._onWillShutdown.event; protected readonly _onShutdown = this._register(new Emitter()); - readonly onShutdown: Event = this._onShutdown.event; + readonly onShutdown = this._onShutdown.event; protected _startupKind: StartupKind = StartupKind.NewWindow; get startupKind(): StartupKind { return this._startupKind; } @@ -29,7 +29,7 @@ export abstract class AbstractLifecycleService extends Disposable implements ILi private _phase: LifecyclePhase = LifecyclePhase.Starting; get phase(): LifecyclePhase { return this._phase; } - private phaseWhen = new Map(); + private readonly phaseWhen = new Map(); constructor( @ILogService protected readonly logService: ILogService diff --git a/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts b/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts index d72eea3654..1a3f58f8ee 100644 --- a/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts +++ b/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts @@ -13,7 +13,7 @@ import { handleVetos } from 'vs/platform/lifecycle/common/lifecycle'; import { isMacintosh, isWindows } from 'vs/base/common/platform'; import { Disposable } from 'vs/base/common/lifecycle'; import { Barrier, timeout } from 'vs/base/common/async'; -import { ParsedArgs } from 'vs/platform/environment/common/environment'; +import { ParsedArgs } from 'vs/platform/environment/node/argv'; export const ILifecycleMainService = createDecorator('lifecycleMainService'); @@ -141,7 +141,28 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe private static readonly QUIT_FROM_RESTART_MARKER = 'quit.from.restart'; // use a marker to find out if the session was restarted - private windowToCloseRequest: Set = new Set(); + private readonly _onBeforeShutdown = this._register(new Emitter()); + readonly onBeforeShutdown = this._onBeforeShutdown.event; + + private readonly _onWillShutdown = this._register(new Emitter()); + readonly onWillShutdown = this._onWillShutdown.event; + + private readonly _onBeforeWindowClose = this._register(new Emitter()); + readonly onBeforeWindowClose = this._onBeforeWindowClose.event; + + private readonly _onBeforeWindowUnload = this._register(new Emitter()); + readonly onBeforeWindowUnload = this._onBeforeWindowUnload.event; + + private _quitRequested = false; + get quitRequested(): boolean { return this._quitRequested; } + + private _wasRestarted: boolean = false; + get wasRestarted(): boolean { return this._wasRestarted; } + + private _phase = LifecycleMainPhase.Starting; + get phase(): LifecycleMainPhase { return this._phase; } + + private readonly windowToCloseRequest = new Set(); private oneTimeListenerTokenGenerator = 0; private windowCounter = 0; @@ -150,28 +171,7 @@ export class LifecycleMainService extends Disposable implements ILifecycleMainSe private pendingWillShutdownPromise: Promise | null = null; - private _quitRequested = false; - get quitRequested(): boolean { return this._quitRequested; } - - private _wasRestarted: boolean = false; - get wasRestarted(): boolean { return this._wasRestarted; } - - private readonly _onBeforeShutdown = this._register(new Emitter()); - readonly onBeforeShutdown: Event = this._onBeforeShutdown.event; - - private readonly _onWillShutdown = this._register(new Emitter()); - readonly onWillShutdown: Event = this._onWillShutdown.event; - - private readonly _onBeforeWindowClose = this._register(new Emitter()); - readonly onBeforeWindowClose: Event = this._onBeforeWindowClose.event; - - private readonly _onBeforeWindowUnload = this._register(new Emitter()); - readonly onBeforeWindowUnload: Event = this._onBeforeWindowUnload.event; - - private _phase: LifecycleMainPhase = LifecycleMainPhase.Starting; - get phase(): LifecycleMainPhase { return this._phase; } - - private phaseWhen = new Map(); + private readonly phaseWhen = new Map(); constructor( @ILogService private readonly logService: ILogService, diff --git a/src/vs/platform/localizations/node/localizations.ts b/src/vs/platform/localizations/node/localizations.ts index c5e1987b8c..94662663c4 100644 --- a/src/vs/platform/localizations/node/localizations.ts +++ b/src/vs/platform/localizations/node/localizations.ts @@ -8,6 +8,7 @@ import { createHash } from 'crypto'; import { IExtensionManagementService, ILocalExtension, IExtensionIdentifier } from 'vs/platform/extensionManagement/common/extensionManagement'; import { Disposable } from 'vs/base/common/lifecycle'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; import { Queue } from 'vs/base/common/async'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { ILogService } from 'vs/platform/log/common/log'; @@ -43,7 +44,7 @@ export class LocalizationsService extends Disposable implements ILocalizationsSe constructor( @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, - @IEnvironmentService environmentService: IEnvironmentService, + @IEnvironmentService environmentService: INativeEnvironmentService, @ILogService private readonly logService: ILogService ) { super(); @@ -96,7 +97,7 @@ class LanguagePacksCache extends Disposable { private initializedCache: boolean | undefined; constructor( - @IEnvironmentService environmentService: IEnvironmentService, + @IEnvironmentService environmentService: INativeEnvironmentService, @ILogService private readonly logService: ILogService ) { super(); diff --git a/src/vs/platform/log/common/log.ts b/src/vs/platform/log/common/log.ts index 5a3264834b..916700adc0 100644 --- a/src/vs/platform/log/common/log.ts +++ b/src/vs/platform/log/common/log.ts @@ -418,8 +418,8 @@ export function getLogLevel(environmentService: IEnvironmentService): LogLevel { if (environmentService.verbose) { return LogLevel.Trace; } - if (typeof environmentService.args.log === 'string') { - const logLevel = environmentService.args.log.toLowerCase(); + if (typeof environmentService.logLevel === 'string') { + const logLevel = environmentService.logLevel.toLowerCase(); switch (logLevel) { case 'trace': return LogLevel.Trace; diff --git a/src/vs/platform/menubar/electron-main/menubar.ts b/src/vs/platform/menubar/electron-main/menubar.ts index 909cf98768..54668b3902 100644 --- a/src/vs/platform/menubar/electron-main/menubar.ts +++ b/src/vs/platform/menubar/electron-main/menubar.ts @@ -7,7 +7,8 @@ import * as nls from 'vs/nls'; import { isMacintosh, language } from 'vs/base/common/platform'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { app, shell, Menu, MenuItem, BrowserWindow, MenuItemConstructorOptions, WebContents, Event, KeyboardEvent } from 'electron'; -import { OpenContext, IRunActionInWindowRequest, getTitleBarStyle, IRunKeybindingInWindowRequest, IWindowOpenable } from 'vs/platform/windows/common/windows'; +import { getTitleBarStyle, IWindowOpenable } from 'vs/platform/windows/common/windows'; +import { OpenContext, IRunActionInWindowRequest, IRunKeybindingInWindowRequest } from 'vs/platform/windows/node/window'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IUpdateService, StateType } from 'vs/platform/update/common/update'; @@ -23,6 +24,7 @@ import { IStateService } from 'vs/platform/state/node/state'; import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; import { WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification } from 'vs/base/common/actions'; import { IElectronMainService } from 'vs/platform/electron/electron-main/electronMainService'; +import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; const telemetryFrom = 'menu'; @@ -65,7 +67,7 @@ export class Menubar { @IUpdateService private readonly updateService: IUpdateService, @IConfigurationService private readonly configurationService: IConfigurationService, @IWindowsMainService private readonly windowsMainService: IWindowsMainService, - @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IEnvironmentService private readonly environmentService: INativeEnvironmentService, @ITelemetryService private readonly telemetryService: ITelemetryService, @IWorkspacesHistoryMainService private readonly workspacesHistoryMainService: IWorkspacesHistoryMainService, @IStateService private readonly stateService: IStateService, diff --git a/src/vs/platform/product/common/product.ts b/src/vs/platform/product/common/product.ts index 4def0fe1ae..c16eef09cc 100644 --- a/src/vs/platform/product/common/product.ts +++ b/src/vs/platform/product/common/product.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { IProductConfiguration } from 'vs/platform/product/common/productService'; -import { assign } from 'vs/base/common/objects'; import { isWeb } from 'vs/base/common/platform'; import * as path from 'vs/base/common/path'; import { getPathFromAmdModule } from 'vs/base/common/amd'; @@ -20,9 +19,9 @@ if (isWeb) { // Running out of sources if (Object.keys(product).length === 0) { - assign(product, { + Object.assign(product, { version: '1.17.0-dev', - vscodeVersion: '1.44.0-dev', + vscodeVersion: '1.45.0-dev', nameLong: 'Azure Data Studio Web Dev', nameShort: 'Azure Data Studio Web Dev', urlProtocol: 'azuredatastudio-oss' @@ -36,19 +35,19 @@ else if (typeof require !== 'undefined' && typeof require.__$__nodeRequire === ' // Obtain values from product.json and package.json const rootPath = path.dirname(getPathFromAmdModule(require, '')); - product = assign({}, require.__$__nodeRequire(path.join(rootPath, 'product.json')) as IProductConfiguration); + product = require.__$__nodeRequire(path.join(rootPath, 'product.json')); const pkg = require.__$__nodeRequire(path.join(rootPath, 'package.json')) as { version: string; }; // Running out of sources if (env['VSCODE_DEV']) { - assign(product, { + Object.assign(product, { nameShort: `${product.nameShort} Dev`, nameLong: `${product.nameLong} Dev`, dataFolderName: `${product.dataFolderName}-dev` }); } - assign(product, { + Object.assign(product, { version: pkg.version }); } diff --git a/src/vs/platform/product/common/productService.ts b/src/vs/platform/product/common/productService.ts index 5f22694105..73194805af 100644 --- a/src/vs/platform/product/common/productService.ts +++ b/src/vs/platform/product/common/productService.ts @@ -14,6 +14,13 @@ export interface IProductService extends Readonly { } +export interface IBuiltInExtension { + readonly name: string; + readonly version: string; + readonly repo: string; + readonly metadata: any; +} + export interface IProductConfiguration { readonly version: string; readonly date?: string; @@ -30,6 +37,8 @@ export interface IProductConfiguration { readonly urlProtocol: string; readonly dataFolderName: string; + readonly builtInExtensions?: IBuiltInExtension[]; + readonly downloadUrl?: string; readonly updateUrl?: string; readonly target?: string; diff --git a/src/vs/platform/serviceMachineId/common/serviceMachineId.ts b/src/vs/platform/serviceMachineId/common/serviceMachineId.ts index a738060883..08c838465c 100644 --- a/src/vs/platform/serviceMachineId/common/serviceMachineId.ts +++ b/src/vs/platform/serviceMachineId/common/serviceMachineId.ts @@ -12,30 +12,28 @@ import { VSBuffer } from 'vs/base/common/buffer'; export async function getServiceMachineId(environmentService: IEnvironmentService, fileService: IFileService, storageService: { get: (key: string, scope: StorageScope, fallbackValue?: string | undefined) => string | undefined, store: (key: string, value: string, scope: StorageScope) => void -} | undefined): Promise { +} | undefined): Promise { let uuid: string | null = storageService ? storageService.get('storage.serviceMachineId', StorageScope.GLOBAL) || null : null; if (uuid) { return uuid; } - if (environmentService.serviceMachineIdResource) { - try { - const contents = await fileService.readFile(environmentService.serviceMachineIdResource); - const value = contents.value.toString(); - uuid = isUUID(value) ? value : null; - } catch (e) { - uuid = null; - } + try { + const contents = await fileService.readFile(environmentService.serviceMachineIdResource); + const value = contents.value.toString(); + uuid = isUUID(value) ? value : null; + } catch (e) { + uuid = null; + } - if (!uuid) { - uuid = generateUuid(); - try { - await fileService.writeFile(environmentService.serviceMachineIdResource, VSBuffer.fromString(uuid)); - } catch (error) { - //noop - } + if (!uuid) { + uuid = generateUuid(); + try { + await fileService.writeFile(environmentService.serviceMachineIdResource, VSBuffer.fromString(uuid)); + } catch (error) { + //noop } } - if (uuid && storageService) { + if (storageService) { storageService.store('storage.serviceMachineId', uuid, StorageScope.GLOBAL); } return uuid; diff --git a/src/vs/platform/state/node/stateService.ts b/src/vs/platform/state/node/stateService.ts index d0a8945f65..0f81b29f23 100644 --- a/src/vs/platform/state/node/stateService.ts +++ b/src/vs/platform/state/node/stateService.ts @@ -6,6 +6,7 @@ import * as path from 'vs/base/common/path'; import * as fs from 'fs'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; import { writeFileSync, readFile } from 'vs/base/node/pfs'; import { isUndefined, isUndefinedOrNull } from 'vs/base/common/types'; import { IStateService } from 'vs/platform/state/node/state'; @@ -132,7 +133,7 @@ export class StateService implements IStateService { private fileStorage: FileStorage; constructor( - @IEnvironmentService environmentService: IEnvironmentService, + @IEnvironmentService environmentService: INativeEnvironmentService, @ILogService logService: ILogService ) { this.fileStorage = new FileStorage(path.join(environmentService.userDataPath, StateService.STATE_FILE), error => logService.error(error)); diff --git a/src/vs/platform/storage/node/storageMainService.ts b/src/vs/platform/storage/node/storageMainService.ts index c34fd5cbdd..112e21bf0e 100644 --- a/src/vs/platform/storage/node/storageMainService.ts +++ b/src/vs/platform/storage/node/storageMainService.ts @@ -8,6 +8,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; import { ILogService, LogLevel } from 'vs/platform/log/common/log'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; import { SQLiteStorageDatabase, ISQLiteStorageDatabaseLoggingOptions } from 'vs/base/parts/storage/node/storage'; import { Storage, IStorage, InMemoryStorageDatabase } from 'vs/base/parts/storage/common/storage'; import { join } from 'vs/base/common/path'; @@ -103,7 +104,7 @@ export class StorageMainService extends Disposable implements IStorageMainServic constructor( @ILogService private readonly logService: ILogService, - @IEnvironmentService private readonly environmentService: IEnvironmentService + @IEnvironmentService private readonly environmentService: INativeEnvironmentService ) { super(); diff --git a/src/vs/platform/storage/node/storageService.ts b/src/vs/platform/storage/node/storageService.ts index 19203845a2..d43e4d3abb 100644 --- a/src/vs/platform/storage/node/storageService.ts +++ b/src/vs/platform/storage/node/storageService.ts @@ -13,6 +13,7 @@ import { mark } from 'vs/base/common/performance'; import { join } from 'vs/base/common/path'; import { copy, exists, mkdirp, writeFile } from 'vs/base/node/pfs'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; import { IWorkspaceInitializationPayload, isWorkspaceIdentifier, isSingleFolderWorkspaceInitializationPayload } from 'vs/platform/workspaces/common/workspaces'; import { assertIsDefined } from 'vs/base/common/types'; import { RunOnceScheduler, runWhenIdle } from 'vs/base/common/async'; @@ -44,7 +45,7 @@ export class NativeStorageService extends Disposable implements IStorageService constructor( private globalStorageDatabase: IStorageDatabase, @ILogService private readonly logService: ILogService, - @IEnvironmentService private readonly environmentService: IEnvironmentService + @IEnvironmentService private readonly environmentService: INativeEnvironmentService ) { super(); diff --git a/src/vs/platform/update/electron-main/abstractUpdateService.ts b/src/vs/platform/update/electron-main/abstractUpdateService.ts index 38dd9e5766..ee9bbfac46 100644 --- a/src/vs/platform/update/electron-main/abstractUpdateService.ts +++ b/src/vs/platform/update/electron-main/abstractUpdateService.ts @@ -10,6 +10,7 @@ import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifec import product from 'vs/platform/product/common/product'; import { IUpdateService, State, StateType, AvailableForDownload, UpdateType } from 'vs/platform/update/common/update'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; import { ILogService } from 'vs/platform/log/common/log'; import { IRequestService } from 'vs/platform/request/common/request'; import { CancellationToken } from 'vs/base/common/cancellation'; @@ -46,7 +47,7 @@ export abstract class AbstractUpdateService implements IUpdateService { constructor( @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, @IConfigurationService protected configurationService: IConfigurationService, - @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IEnvironmentService private readonly environmentService: INativeEnvironmentService, @IRequestService protected requestService: IRequestService, @ILogService protected logService: ILogService, ) { } diff --git a/src/vs/platform/update/electron-main/updateService.darwin.ts b/src/vs/platform/update/electron-main/updateService.darwin.ts index 41a56d79fb..2a743f4b67 100644 --- a/src/vs/platform/update/electron-main/updateService.darwin.ts +++ b/src/vs/platform/update/electron-main/updateService.darwin.ts @@ -12,6 +12,7 @@ import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifec import { State, IUpdate, StateType, UpdateType } from 'vs/platform/update/common/update'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; import { ILogService } from 'vs/platform/log/common/log'; import { AbstractUpdateService, createUpdateURL, UpdateNotAvailableClassification } from 'vs/platform/update/electron-main/abstractUpdateService'; import { IRequestService } from 'vs/platform/request/common/request'; @@ -31,7 +32,7 @@ export class DarwinUpdateService extends AbstractUpdateService { @ILifecycleMainService lifecycleMainService: ILifecycleMainService, @IConfigurationService configurationService: IConfigurationService, @ITelemetryService private readonly telemetryService: ITelemetryService, - @IEnvironmentService environmentService: IEnvironmentService, + @IEnvironmentService environmentService: INativeEnvironmentService, @IRequestService requestService: IRequestService, @ILogService logService: ILogService ) { diff --git a/src/vs/platform/update/electron-main/updateService.linux.ts b/src/vs/platform/update/electron-main/updateService.linux.ts index 05e7cac5a5..ea57d513da 100644 --- a/src/vs/platform/update/electron-main/updateService.linux.ts +++ b/src/vs/platform/update/electron-main/updateService.linux.ts @@ -9,6 +9,7 @@ import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifec import { State, IUpdate, AvailableForDownload, UpdateType } from 'vs/platform/update/common/update'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; import { ILogService } from 'vs/platform/log/common/log'; import { createUpdateURL, AbstractUpdateService, UpdateNotAvailableClassification } from 'vs/platform/update/electron-main/abstractUpdateService'; import { IRequestService, asJson } from 'vs/platform/request/common/request'; @@ -23,7 +24,7 @@ export class LinuxUpdateService extends AbstractUpdateService { @ILifecycleMainService lifecycleMainService: ILifecycleMainService, @IConfigurationService configurationService: IConfigurationService, @ITelemetryService private readonly telemetryService: ITelemetryService, - @IEnvironmentService environmentService: IEnvironmentService, + @IEnvironmentService environmentService: INativeEnvironmentService, @IRequestService requestService: IRequestService, @ILogService logService: ILogService ) { diff --git a/src/vs/platform/update/electron-main/updateService.snap.ts b/src/vs/platform/update/electron-main/updateService.snap.ts index 5ba11e146a..0358d78e2c 100644 --- a/src/vs/platform/update/electron-main/updateService.snap.ts +++ b/src/vs/platform/update/electron-main/updateService.snap.ts @@ -8,6 +8,7 @@ import { timeout } from 'vs/base/common/async'; import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; import { IUpdateService, State, StateType, AvailableForDownload, UpdateType } from 'vs/platform/update/common/update'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; import { ILogService } from 'vs/platform/log/common/log'; import * as path from 'vs/base/common/path'; import { realpath, watch } from 'fs'; @@ -36,7 +37,7 @@ abstract class AbstractUpdateService2 implements IUpdateService { constructor( @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, - @IEnvironmentService environmentService: IEnvironmentService, + @IEnvironmentService environmentService: INativeEnvironmentService, @ILogService protected logService: ILogService, ) { if (environmentService.disableUpdates) { @@ -140,7 +141,7 @@ export class SnapUpdateService extends AbstractUpdateService2 { private snap: string, private snapRevision: string, @ILifecycleMainService lifecycleMainService: ILifecycleMainService, - @IEnvironmentService environmentService: IEnvironmentService, + @IEnvironmentService environmentService: INativeEnvironmentService, @ILogService logService: ILogService, @ITelemetryService private readonly telemetryService: ITelemetryService ) { diff --git a/src/vs/platform/update/electron-main/updateService.win32.ts b/src/vs/platform/update/electron-main/updateService.win32.ts index 98113d6f9c..8764d827dd 100644 --- a/src/vs/platform/update/electron-main/updateService.win32.ts +++ b/src/vs/platform/update/electron-main/updateService.win32.ts @@ -13,6 +13,7 @@ import product from 'vs/platform/product/common/product'; import { State, IUpdate, StateType, AvailableForDownload, UpdateType } from 'vs/platform/update/common/update'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; import { ILogService } from 'vs/platform/log/common/log'; import { createUpdateURL, AbstractUpdateService, UpdateNotAvailableClassification } from 'vs/platform/update/electron-main/abstractUpdateService'; import { IRequestService, asJson } from 'vs/platform/request/common/request'; @@ -64,7 +65,7 @@ export class Win32UpdateService extends AbstractUpdateService { @ILifecycleMainService lifecycleMainService: ILifecycleMainService, @IConfigurationService configurationService: IConfigurationService, @ITelemetryService private readonly telemetryService: ITelemetryService, - @IEnvironmentService environmentService: IEnvironmentService, + @IEnvironmentService environmentService: INativeEnvironmentService, @IRequestService requestService: IRequestService, @ILogService logService: ILogService, @IFileService private readonly fileService: IFileService diff --git a/src/vs/platform/url/electron-main/electronUrlListener.ts b/src/vs/platform/url/electron-main/electronUrlListener.ts index 20fb468900..11931bdc7d 100644 --- a/src/vs/platform/url/electron-main/electronUrlListener.ts +++ b/src/vs/platform/url/electron-main/electronUrlListener.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Event } from 'vs/base/common/event'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; import { IURLService } from 'vs/platform/url/common/url'; import product from 'vs/platform/product/common/product'; import { app, Event as ElectronEvent } from 'electron'; @@ -43,7 +43,7 @@ export class ElectronURLListener { initialUrisToHandle: URI[], private readonly urlService: IURLService, windowsMainService: IWindowsMainService, - environmentService: IEnvironmentService + environmentService: INativeEnvironmentService ) { // the initial set of URIs we need to handle once the window is ready diff --git a/src/vs/platform/userDataSync/common/snippetsSync.ts b/src/vs/platform/userDataSync/common/snippetsSync.ts index 9cc5ab9f35..6fdc52e2e4 100644 --- a/src/vs/platform/userDataSync/common/snippetsSync.ts +++ b/src/vs/platform/userDataSync/common/snippetsSync.ts @@ -416,7 +416,8 @@ export class SnippetsSynchroniser extends AbstractSynchroniser implements IUserD } for (const entry of stat.children || []) { const resource = entry.resource; - if (extname(resource) === '.json') { + const extension = extname(resource); + if (extension === '.json' || extension === '.code-snippet') { const key = relativePath(this.snippetsFolder, resource)!; const content = await this.fileService.readFile(resource); snippets[key] = content; diff --git a/src/vs/platform/userDataSync/common/userDataSyncEnablementService.ts b/src/vs/platform/userDataSync/common/userDataSyncEnablementService.ts index 30e80620ed..f0eb3a9902 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncEnablementService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncEnablementService.ts @@ -33,7 +33,7 @@ export class UserDataSyncEnablementService extends Disposable implements IUserDa @IEnvironmentService environmentService: IEnvironmentService, ) { super(); - switch (environmentService.args['sync']) { + switch (environmentService.sync) { case 'on': this.setEnablement(true); break; diff --git a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts index 4373f81ee1..e1d3cb4b69 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts @@ -43,9 +43,7 @@ export class UserDataSyncStoreService extends Disposable implements IUserDataSyn const headers: IHeaders = { 'X-Sync-Client-Id': productService.version, }; - if (uuid) { - headers['X-Sync-Machine-Id'] = uuid; - } + headers['X-Sync-Machine-Id'] = uuid; return headers; }); } diff --git a/src/vs/platform/userDataSync/test/common/snippetsSync.test.ts b/src/vs/platform/userDataSync/test/common/snippetsSync.test.ts index 6343a1b96b..5a85b1d29c 100644 --- a/src/vs/platform/userDataSync/test/common/snippetsSync.test.ts +++ b/src/vs/platform/userDataSync/test/common/snippetsSync.test.ts @@ -127,6 +127,25 @@ const htmlSnippet3 = `{ } }`; +const globalSnippet = `{ + // Place your global snippets here. Each snippet is defined under a snippet name and has a scope, prefix, body and + // description. Add comma separated ids of the languages where the snippet is applicable in the scope field. If scope + // is left empty or omitted, the snippet gets applied to all languages. The prefix is what is + // used to trigger the snippet and the body will be expanded and inserted. Possible variables are: + // $1, $2 for tab stops, $0 for the final cursor position, and {1: label}, { 2: another } for placeholders. + // Placeholders with the same ids are connected. + // Example: + // "Print to console": { + // "scope": "javascript,typescript", + // "prefix": "log", + // "body": [ + // "console.log('$1');", + // "$2" + // ], + // "description": "Log output to console" + // } +}`; + suite('SnippetsSync', () => { const disposableStore = new DisposableStore(); @@ -577,6 +596,49 @@ suite('SnippetsSync', () => { assert.equal(actual2, tsSnippet1); }); + test('sync global and language snippet', async () => { + await updateSnippet('global.code-snippet', globalSnippet, client2); + await updateSnippet('html.json', htmlSnippet1, client2); + await client2.sync(); + + await testObject.sync(); + assert.equal(testObject.status, SyncStatus.Idle); + assert.deepEqual(testObject.conflicts, []); + + const actual1 = await readSnippet('html.json', testClient); + assert.equal(actual1, htmlSnippet1); + const actual2 = await readSnippet('global.code-snippet', testClient); + assert.equal(actual2, globalSnippet); + + const { content } = await testClient.read(testObject.resource); + assert.ok(content !== null); + const actual = parseSnippets(content!); + assert.deepEqual(actual, { 'html.json': htmlSnippet1, 'global.code-snippet': globalSnippet }); + }); + + test('sync should ignore non snippets', async () => { + await updateSnippet('global.code-snippet', globalSnippet, client2); + await updateSnippet('html.html', htmlSnippet1, client2); + await updateSnippet('typescript.json', tsSnippet1, client2); + await client2.sync(); + + await testObject.sync(); + assert.equal(testObject.status, SyncStatus.Idle); + assert.deepEqual(testObject.conflicts, []); + + const actual1 = await readSnippet('typescript.json', testClient); + assert.equal(actual1, tsSnippet1); + const actual2 = await readSnippet('global.code-snippet', testClient); + assert.equal(actual2, globalSnippet); + const actual3 = await readSnippet('html.html', testClient); + assert.equal(actual3, null); + + const { content } = await testClient.read(testObject.resource); + assert.ok(content !== null); + const actual = parseSnippets(content!); + assert.deepEqual(actual, { 'typescript.json': tsSnippet1, 'global.code-snippet': globalSnippet }); + }); + function parseSnippets(content: string): IStringDictionary { const syncData: ISyncData = JSON.parse(content); return JSON.parse(syncData.content); diff --git a/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts index 5a0e97c386..2fe3763037 100644 --- a/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts +++ b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts @@ -55,8 +55,7 @@ export class UserDataSyncClient extends Disposable { settingsResource: joinPath(userDataDirectory, 'settings.json'), keybindingsResource: joinPath(userDataDirectory, 'keybindings.json'), snippetsHome: joinPath(userDataDirectory, 'snippets'), - argvResource: joinPath(userDataDirectory, 'argv.json'), - args: {} + argvResource: joinPath(userDataDirectory, 'argv.json') }); const logService = new NullLogService(); diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index df2aa2b44a..04ac89b6fe 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -4,20 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import { isMacintosh, isLinux, isWeb } from 'vs/base/common/platform'; -import { ParsedArgs, IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { URI, UriComponents } from 'vs/base/common/uri'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -export interface IOpenedWindow { - id: number; - workspace?: IWorkspaceIdentifier; - folderUri?: ISingleFolderWorkspaceIdentifier; - title: string; - filename?: string; - dirty: boolean; -} - export interface IBaseOpenWindowsOptions { forceReuseWindow?: boolean; } @@ -130,66 +120,12 @@ export function getTitleBarStyle(configurationService: IConfigurationService, en return isLinux ? 'native' : 'custom'; // default to custom on all macOS and Windows } -export const enum OpenContext { - - // opening when running from the command line - CLI, - - // macOS only: opening from the dock (also when opening files to a running instance from desktop) - DOCK, - - // opening from the main application window - MENU, - - // opening from a file or folder dialog - DIALOG, - - // opening from the OS's UI - DESKTOP, - - // opening through the API - API -} - -export const enum ReadyState { - - /** - * This window has not loaded any HTML yet - */ - NONE, - - /** - * This window is loading HTML - */ - LOADING, - - /** - * This window is navigating to another HTML - */ - NAVIGATING, - - /** - * This window is done loading HTML - */ - READY -} - export interface IPath extends IPathData { // the file path to open within the instance fileUri?: URI; } -export interface IPathsToWaitFor extends IPathsToWaitForData { - paths: IPath[]; - waitMarkerFileUri: URI; -} - -export interface IPathsToWaitForData { - paths: IPathData[]; - waitMarkerFileUri: UriComponents; -} - export interface IPathData { // the file path to open within the instance @@ -210,34 +146,15 @@ export interface IPathData { export interface IOpenFileRequest { filesToOpenOrCreate?: IPathData[]; filesToDiff?: IPathData[]; - filesToWait?: IPathsToWaitForData; - termProgram?: string; } -export interface IAddFoldersRequest { - foldersToAdd: UriComponents[]; -} - -export interface IWindowConfiguration extends ParsedArgs { +export interface IWindowConfiguration { sessionId: string; - backupWorkspaceResource?: URI; - remoteAuthority?: string; - connectionToken?: string; highContrast?: boolean; filesToOpenOrCreate?: IPath[]; filesToDiff?: IPath[]; } - -export interface IRunActionInWindowRequest { - id: string; - from: 'menu' | 'touchbar' | 'mouse'; - args?: any[]; -} - -export interface IRunKeybindingInWindowRequest { - userSettingsLabel: string; -} diff --git a/src/vs/platform/windows/electron-main/windows.ts b/src/vs/platform/windows/electron-main/windows.ts index 656ae1e1e3..de11c1fae1 100644 --- a/src/vs/platform/windows/electron-main/windows.ts +++ b/src/vs/platform/windows/electron-main/windows.ts @@ -3,9 +3,9 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { OpenContext, IWindowOpenable, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows'; -import { INativeWindowConfiguration } from 'vs/platform/windows/node/window'; -import { ParsedArgs } from 'vs/platform/environment/common/environment'; +import { IWindowOpenable, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows'; +import { INativeWindowConfiguration, OpenContext } from 'vs/platform/windows/node/window'; +import { ParsedArgs } from 'vs/platform/environment/node/argv'; import { Event } from 'vs/base/common/event'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IProcessEnvironment } from 'vs/base/common/platform'; diff --git a/src/vs/platform/windows/electron-main/windowsMainService.ts b/src/vs/platform/windows/electron-main/windowsMainService.ts index 0bb50862f8..5dd19d8ae0 100644 --- a/src/vs/platform/windows/electron-main/windowsMainService.ts +++ b/src/vs/platform/windows/electron-main/windowsMainService.ts @@ -7,10 +7,12 @@ import * as fs from 'fs'; import { basename, normalize, join, posix } from 'vs/base/common/path'; import { localize } from 'vs/nls'; import * as arrays from 'vs/base/common/arrays'; -import { assign, mixin } from 'vs/base/common/objects'; +import { mixin } from 'vs/base/common/objects'; import { IBackupMainService } from 'vs/platform/backup/electron-main/backup'; import { IEmptyWindowBackupInfo } from 'vs/platform/backup/node/backup'; -import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { ParsedArgs } from 'vs/platform/environment/node/argv'; +import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; import { IStateService } from 'vs/platform/state/node/state'; import { CodeWindow, defaultWindowState } from 'vs/code/electron-main/window'; import { ipcMain as ipc, screen, BrowserWindow, MessageBoxOptions, Display, app, nativeTheme } from 'electron'; @@ -18,8 +20,8 @@ import { parseLineAndColumnAware } from 'vs/code/node/paths'; import { ILifecycleMainService, UnloadReason, LifecycleMainService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ILogService } from 'vs/platform/log/common/log'; -import { IWindowSettings, OpenContext, IPath, IPathsToWaitFor, isFileToOpen, isWorkspaceToOpen, isFolderToOpen, IWindowOpenable, IOpenEmptyWindowOptions, IAddFoldersRequest } from 'vs/platform/windows/common/windows'; -import { getLastActiveWindow, findBestWindowOrFolderForFile, findWindowOnWorkspace, findWindowOnExtensionDevelopmentPath, findWindowOnWorkspaceOrFolderUri, INativeWindowConfiguration } from 'vs/platform/windows/node/window'; +import { IWindowSettings, IPath, isFileToOpen, isWorkspaceToOpen, isFolderToOpen, IWindowOpenable, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows'; +import { getLastActiveWindow, findBestWindowOrFolderForFile, findWindowOnWorkspace, findWindowOnExtensionDevelopmentPath, findWindowOnWorkspaceOrFolderUri, INativeWindowConfiguration, OpenContext, IAddFoldersRequest, IPathsToWaitFor } from 'vs/platform/windows/node/window'; import { Emitter } from 'vs/base/common/event'; import product from 'vs/platform/product/common/product'; import { IWindowsMainService, IOpenConfiguration, IWindowsCountChangedEvent, ICodeWindow, IWindowState as ISingleWindowState, WindowMode } from 'vs/platform/windows/electron-main/windows'; @@ -176,7 +178,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic private readonly initialUserEnv: IProcessEnvironment, @ILogService private readonly logService: ILogService, @IStateService private readonly stateService: IStateService, - @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IEnvironmentService private readonly environmentService: INativeEnvironmentService, @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, @IBackupMainService private readonly backupMainService: IBackupMainService, @IConfigurationService private readonly configurationService: IConfigurationService, @@ -1358,7 +1360,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic configuration.nodeCachedDataDir = this.environmentService.nodeCachedDataDir; configuration.mainPid = process.pid; configuration.execPath = process.execPath; - configuration.userEnv = assign({}, this.initialUserEnv, options.userEnv || {}); + configuration.userEnv = { ...this.initialUserEnv, ...options.userEnv }; configuration.isInitialStartup = options.initialStartup; configuration.workspace = options.workspace; configuration.folderUri = options.folderUri; diff --git a/src/vs/platform/windows/node/window.ts b/src/vs/platform/windows/node/window.ts index 65ca4e3da3..a8b25c88cc 100644 --- a/src/vs/platform/windows/node/window.ts +++ b/src/vs/platform/windows/node/window.ts @@ -3,16 +3,61 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { OpenContext, IOpenWindowOptions, IWindowConfiguration, IPathsToWaitFor } from 'vs/platform/windows/common/windows'; -import { URI } from 'vs/base/common/uri'; +import { IOpenWindowOptions, IWindowConfiguration, IPath, IOpenFileRequest, IPathData } from 'vs/platform/windows/common/windows'; +import { URI, UriComponents } from 'vs/base/common/uri'; import * as platform from 'vs/base/common/platform'; import * as extpath from 'vs/base/common/extpath'; import { IWorkspaceIdentifier, IResolvedWorkspace, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, isWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { isEqual, isEqualOrParent } from 'vs/base/common/resources'; import { LogLevel } from 'vs/platform/log/common/log'; import { ExportData } from 'vs/base/common/performance'; +import { ParsedArgs } from 'vs/platform/environment/node/argv'; -export interface INativeWindowConfiguration extends IWindowConfiguration { +export interface IOpenedWindow { + id: number; + workspace?: IWorkspaceIdentifier; + folderUri?: ISingleFolderWorkspaceIdentifier; + title: string; + filename?: string; + dirty: boolean; +} + +export const enum OpenContext { + + // opening when running from the command line + CLI, + + // macOS only: opening from the dock (also when opening files to a running instance from desktop) + DOCK, + + // opening from the main application window + MENU, + + // opening from a file or folder dialog + DIALOG, + + // opening from the OS's UI + DESKTOP, + + // opening through the API + API +} + +export interface IRunActionInWindowRequest { + id: string; + from: 'menu' | 'touchbar' | 'mouse'; + args?: any[]; +} + +export interface IRunKeybindingInWindowRequest { + userSettingsLabel: string; +} + +export interface IAddFoldersRequest { + foldersToAdd: UriComponents[]; +} + +export interface INativeWindowConfiguration extends IWindowConfiguration, ParsedArgs { mainPid: number; windowId: number; @@ -40,6 +85,21 @@ export interface INativeWindowConfiguration extends IWindowConfiguration { filesToWait?: IPathsToWaitFor; } +export interface INativeOpenFileRequest extends IOpenFileRequest { + termProgram?: string; + filesToWait?: IPathsToWaitForData; +} + +export interface IPathsToWaitFor extends IPathsToWaitForData { + paths: IPath[]; + waitMarkerFileUri: URI; +} + +export interface IPathsToWaitForData { + paths: IPathData[]; + waitMarkerFileUri: UriComponents; +} + export interface INativeOpenWindowOptions extends IOpenWindowOptions { diffMode?: boolean; addMode?: boolean; diff --git a/src/vs/platform/windows/test/node/window.test.ts b/src/vs/platform/windows/test/node/window.test.ts index 6bb4e636ca..8942600108 100644 --- a/src/vs/platform/windows/test/node/window.test.ts +++ b/src/vs/platform/windows/test/node/window.test.ts @@ -4,8 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; import * as path from 'vs/base/common/path'; -import { IBestWindowOrFolderOptions, IWindowContext, findBestWindowOrFolderForFile } from 'vs/platform/windows/node/window'; -import { OpenContext } from 'vs/platform/windows/common/windows'; +import { IBestWindowOrFolderOptions, IWindowContext, findBestWindowOrFolderForFile, OpenContext } from 'vs/platform/windows/node/window'; import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { toWorkspaceFolders } from 'vs/platform/workspace/common/workspace'; import { URI } from 'vs/base/common/uri'; diff --git a/src/vs/platform/workspace/common/workspace.ts b/src/vs/platform/workspace/common/workspace.ts index 8d7e988a81..06403dfbe0 100644 --- a/src/vs/platform/workspace/common/workspace.ts +++ b/src/vs/platform/workspace/common/workspace.ts @@ -144,7 +144,7 @@ export interface IWorkspaceFolder extends IWorkspaceFolderData { export class Workspace implements IWorkspace { - private _foldersMap: TernarySearchTree = TernarySearchTree.forPaths(); + private _foldersMap: TernarySearchTree = TernarySearchTree.forPaths(); private _folders!: WorkspaceFolder[]; constructor( diff --git a/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts b/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts index b1bc83909b..336000b79a 100644 --- a/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts +++ b/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts @@ -18,6 +18,7 @@ import { isEqual as areResourcesEqual, dirname, originalFSPath, basename } from import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; import { getSimpleWorkspaceLabel } from 'vs/platform/label/common/label'; import { exists } from 'vs/base/node/pfs'; import { ILifecycleMainService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; @@ -67,7 +68,7 @@ export class WorkspacesHistoryMainService extends Disposable implements IWorkspa @IStateService private readonly stateService: IStateService, @ILogService private readonly logService: ILogService, @IWorkspacesMainService private readonly workspacesMainService: IWorkspacesMainService, - @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IEnvironmentService private readonly environmentService: INativeEnvironmentService, @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService ) { super(); diff --git a/src/vs/platform/workspaces/electron-main/workspacesMainService.ts b/src/vs/platform/workspaces/electron-main/workspacesMainService.ts index 2565f4195e..747e12894e 100644 --- a/src/vs/platform/workspaces/electron-main/workspacesMainService.ts +++ b/src/vs/platform/workspaces/electron-main/workspacesMainService.ts @@ -5,6 +5,7 @@ import { IWorkspaceIdentifier, hasWorkspaceFileExtension, UNTITLED_WORKSPACE_NAME, IResolvedWorkspace, IStoredWorkspaceFolder, isStoredWorkspaceFolder, IWorkspaceFolderCreationData, IUntitledWorkspaceInfo, getStoredWorkspaceFolder, IEnterWorkspaceResult, isUntitledWorkspace } from 'vs/platform/workspaces/common/workspaces'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; import { join, dirname } from 'vs/base/common/path'; import { mkdirp, writeFile, rimrafSync, readdirSync, writeFileSync } from 'vs/base/node/pfs'; import { readFileSync, existsSync, mkdirSync } from 'fs'; @@ -75,7 +76,7 @@ export class WorkspacesMainService extends Disposable implements IWorkspacesMain readonly onWorkspaceEntered: Event = this._onWorkspaceEntered.event; constructor( - @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IEnvironmentService private readonly environmentService: INativeEnvironmentService, @ILogService private readonly logService: ILogService, @IBackupMainService private readonly backupMainService: IBackupMainService, @IDialogMainService private readonly dialogMainService: IDialogMainService diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 7b0aaa49fe..1f05db8313 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -7869,7 +7869,7 @@ declare module 'vscode' { /** * The icon path or [ThemeIcon](#ThemeIcon) for the tree item. * When `falsy`, [Folder Theme Icon](#ThemeIcon.Folder) is assigned, if item is collapsible otherwise [File Theme Icon](#ThemeIcon.File). - * When a [ThemeIcon](#ThemeIcon) is specified, icon is derived from the current file icon theme for the specified theme icon using [resourceUri](#TreeItem.resourceUri) (if provided). + * When a file or folder [ThemeIcon](#ThemeIcon) is specified, icon is derived from the current file icon theme for the specified theme icon using [resourceUri](#TreeItem.resourceUri) (if provided). */ iconPath?: string | Uri | { light: string | Uri; dark: string | Uri } | ThemeIcon; @@ -7883,7 +7883,7 @@ declare module 'vscode' { * The [uri](#Uri) of the resource representing this item. * * Will be used to derive the [label](#TreeItem.label), when it is not provided. - * Will be used to derive the icon from current icon theme, when [iconPath](#TreeItem.iconPath) has [ThemeIcon](#ThemeIcon) value. + * Will be used to derive the icon from current file icon theme, when [iconPath](#TreeItem.iconPath) has [ThemeIcon](#ThemeIcon) value. */ resourceUri?: Uri; diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index fd82905e6d..1f60fdb1fb 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -1716,7 +1716,7 @@ declare module 'vscode' { * @returns HTML fragment. We can probably return `CellOutput` instead of string ? * */ - render(document: NotebookDocument, output: CellOutput, mimeType: string): string; + render(document: NotebookDocument, output: CellDisplayOutput, mimeType: string): string; preloads?: Uri[]; } @@ -1890,9 +1890,8 @@ declare module 'vscode' { export interface TimelineChangeEvent { /** * The [uri](#Uri) of the resource for which the timeline changed. - * If the [uri](#Uri) is `undefined` that signals that the timeline source for all resources changed. */ - uri?: Uri; + uri: Uri; /** * A flag which indicates whether the entire timeline should be reset. @@ -1933,7 +1932,7 @@ declare module 'vscode' { * An optional event to signal that the timeline for a source has changed. * To signal that the timeline for all resources (uris) has changed, do not pass any argument or pass `undefined`. */ - onDidChange?: Event; + onDidChange?: Event; /** * An identifier of the source of the timeline items. This can be used to filter sources. @@ -2059,18 +2058,25 @@ declare module 'vscode' { /** * Create a new uri which path is the result of joining - * the path of the base uri with the provided path fragments. + * the path of the base uri with the provided path segments. * - * * Note that `joinPath` only affects the path component + * - Note 1: `joinPath` only affects the path component * and all other components (scheme, authority, query, and fragment) are * left as they are. - * * Note that the base uri must have a path; an error is thrown otherwise. + * - Note 2: The base uri must have a path; an error is thrown otherwise. + * + * The path segments are normalized in the following ways: + * - sequences of path separators (`/` or `\`) are replaced with a single separator + * - for `file`-uris on windows, the backslash-character (`\`) is considered a path-separator + * - the `..`-segment denotes the parent segment, the `.` denotes the current segement + * - paths have a root which always remains, for instance on windows drive-letters are roots + * so that is true: `joinPath(Uri.file('file:///c:/root'), '../../other').fsPath === 'c:/other'` * * @param base An uri. Must have a path. - * @param pathFragments One more more path fragments + * @param pathSegments One more more path fragments * @returns A new uri which path is joined with the given fragments */ - export function joinPath(base: Uri, ...pathFragments: string[]): Uri; + export function joinPath(base: Uri, ...pathSegments: string[]): Uri; } //#endregion diff --git a/src/vs/workbench/api/browser/mainThreadAuthentication.ts b/src/vs/workbench/api/browser/mainThreadAuthentication.ts index 71d5cd0816..1fae88ce02 100644 --- a/src/vs/workbench/api/browser/mainThreadAuthentication.ts +++ b/src/vs/workbench/api/browser/mainThreadAuthentication.ts @@ -35,7 +35,6 @@ const BUILT_IN_AUTH_DEPENDENTS: AuthDependent[] = [ interface AllowedExtension { id: string; name: string; - sessionIds?: string[]; } function readAllowedExtensions(storageService: IStorageService, providerId: string, accountName: string): AllowedExtension[] { @@ -87,15 +86,6 @@ export class MainThreadAuthenticationProvider extends Disposable { const updatedAllowedList = quickPick.selectedItems.map(item => item.extension); storageService.store(`${this.id}-${accountName}`, JSON.stringify(updatedAllowedList), StorageScope.GLOBAL); - // Remove sessions of untrusted extensions - const deselectedItems = items.filter(item => !quickPick.selectedItems.includes(item)); - deselectedItems.forEach(item => { - const extensionData = allowedExtensions.find(extension => item.extension.id === extension.id); - extensionData?.sessionIds?.forEach(sessionId => { - this.logout(sessionId); - }); - }); - quickPick.dispose(); }); @@ -286,19 +276,10 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu this.authenticationService.sessionsUpdate(id, event); } - async $getSessionsPrompt(providerId: string, accountName: string, sessionId: string, providerName: string, extensionId: string, extensionName: string): Promise { + async $getSessionsPrompt(providerId: string, accountName: string, providerName: string, extensionId: string, extensionName: string): Promise { const allowList = readAllowedExtensions(this.storageService, providerId, accountName); const extensionData = allowList.find(extension => extension.id === extensionId); if (extensionData) { - if (!extensionData.sessionIds) { - extensionData.sessionIds = []; - } - - if (!extensionData.sessionIds.find(id => id === sessionId)) { - extensionData.sessionIds.push(sessionId); - this.storageService.store(`${providerId}-${accountName}`, JSON.stringify(allowList), StorageScope.GLOBAL); - } - return true; } @@ -313,7 +294,7 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu const allow = choice === 1; if (allow) { - allowList.push({ id: extensionId, name: extensionName, sessionIds: [sessionId] }); + allowList.push({ id: extensionId, name: extensionName }); this.storageService.store(`${providerId}-${accountName}`, JSON.stringify(allowList), StorageScope.GLOBAL); } @@ -332,12 +313,4 @@ export class MainThreadAuthentication extends Disposable implements MainThreadAu return choice === 1; } - - async $setTrustedExtension(providerId: string, accountName: string, extensionId: string, extensionName: string): Promise { - const allowList = readAllowedExtensions(this.storageService, providerId, accountName); - if (!allowList.find(allowed => allowed.id === extensionId)) { - allowList.push({ id: extensionId, name: extensionName, sessionIds: [] }); - this.storageService.store(`${providerId}-${accountName}`, JSON.stringify(allowList), StorageScope.GLOBAL); - } - } } diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index da05065c4e..58e0770f54 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -134,7 +134,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const extHostLabelService = rpcProtocol.set(ExtHostContext.ExtHosLabelService, new ExtHostLabelService(rpcProtocol)); const extHostNotebook = rpcProtocol.set(ExtHostContext.ExtHostNotebook, new ExtHostNotebookController(rpcProtocol, extHostCommands, extHostDocumentsAndEditors)); const extHostTheming = rpcProtocol.set(ExtHostContext.ExtHostTheming, new ExtHostTheming(rpcProtocol)); - const extHostAuthentication = rpcProtocol.set(ExtHostContext.ExtHostAuthentication, new ExtHostAuthentication(rpcProtocol, extHostStorage)); + const extHostAuthentication = rpcProtocol.set(ExtHostContext.ExtHostAuthentication, new ExtHostAuthentication(rpcProtocol)); const extHostTimeline = rpcProtocol.set(ExtHostContext.ExtHostTimeline, new ExtHostTimeline(rpcProtocol, extHostCommands)); const extHostWebviews = rpcProtocol.set(ExtHostContext.ExtHostWebviews, new ExtHostWebviews(rpcProtocol, initData.environment, extHostWorkspace, extHostLogService, extHostApiDeprecation, extHostDocuments)); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 5e397c3acb..531bb98cf7 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -162,9 +162,8 @@ export interface MainThreadAuthenticationShape extends IDisposable { $registerAuthenticationProvider(id: string, displayName: string): void; $unregisterAuthenticationProvider(id: string): void; $onDidChangeSessions(providerId: string, event: modes.AuthenticationSessionsChangeEvent): void; - $getSessionsPrompt(providerId: string, accountName: string, sessionId: string, providerName: string, extensionId: string, extensionName: string): Promise; + $getSessionsPrompt(providerId: string, accountName: string, providerName: string, extensionId: string, extensionName: string): Promise; $loginPrompt(providerName: string, extensionName: string): Promise; - $setTrustedExtension(providerId: string, accountName: string, extensionId: string, extensionName: string): Promise; } export interface MainThreadConfigurationShape extends IDisposable { @@ -890,7 +889,7 @@ export interface MainThreadTunnelServiceShape extends IDisposable { export interface MainThreadTimelineShape extends IDisposable { $registerTimelineProvider(provider: TimelineProviderDescriptor): void; $unregisterTimelineProvider(source: string): void; - $emitTimelineChangeEvent(e: TimelineChangeEvent): void; + $emitTimelineChangeEvent(e: TimelineChangeEvent | undefined): void; } // -- extension host diff --git a/src/vs/workbench/api/common/extHostAuthentication.ts b/src/vs/workbench/api/common/extHostAuthentication.ts index 620303ff99..3788c70195 100644 --- a/src/vs/workbench/api/common/extHostAuthentication.ts +++ b/src/vs/workbench/api/common/extHostAuthentication.ts @@ -9,7 +9,6 @@ import { Emitter, Event } from 'vs/base/common/event'; import { IMainContext, MainContext, MainThreadAuthenticationShape, ExtHostAuthenticationShape } from 'vs/workbench/api/common/extHost.protocol'; import { Disposable } from 'vs/workbench/api/common/extHostTypes'; import { IExtensionDescription, ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; -import { IExtHostStorage } from 'vs/workbench/api/common/extHostStorage'; export class ExtHostAuthentication implements ExtHostAuthenticationShape { private _proxy: MainThreadAuthenticationShape; @@ -21,8 +20,7 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape { private _onDidChangeSessions = new Emitter<{ [providerId: string]: vscode.AuthenticationSessionsChangeEvent }>(); readonly onDidChangeSessions: Event<{ [providerId: string]: vscode.AuthenticationSessionsChangeEvent }> = this._onDidChangeSessions.event; - constructor(mainContext: IMainContext, - @IExtHostStorage private readonly storageService: IExtHostStorage) { + constructor(mainContext: IMainContext) { this._proxy = mainContext.getProxy(MainContext.MainThreadAuthentication); } @@ -35,20 +33,6 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape { return ids; } - private async hasNotBeenReadByOtherExtension(providerId: string, session: vscode.AuthenticationSession, extensionId: string): Promise { - const readerId = await this.storageService.getValue(true, `${providerId}-${session.accountName}-${session.id}`); - if (!readerId) { - await this.storageService.setValue(true, `${providerId}-${session.accountName}-${session.id}`, extensionId as any); - return true; - } - - return readerId === extensionId; - } - - private async isMatchingSession(session: vscode.AuthenticationSession, scopes: string, providerId: string, extensionId: string): Promise { - return session.scopes.sort().join(' ') === scopes && (await this.hasNotBeenReadByOtherExtension(providerId, session, extensionId)); - } - async getSessions(requestingExtension: IExtensionDescription, providerId: string, scopes: string[]): Promise { const provider = this._authenticationProviders.get(providerId); if (!provider) { @@ -58,11 +42,8 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape { const extensionId = ExtensionIdentifier.toKey(requestingExtension.identifier); const orderedScopes = scopes.sort().join(' '); - const sessions = await provider.getSessions(); - const filteredSessions = await Promise.all(sessions.map(session => this.isMatchingSession(session, orderedScopes, providerId, extensionId))); - - return sessions - .filter((_, i) => { return filteredSessions[i]; }) + return (await provider.getSessions()) + .filter(session => session.scopes.sort().join(' ') === orderedScopes) .map(session => { return { id: session.id, @@ -72,7 +53,6 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape { const isAllowed = await this._proxy.$getSessionsPrompt( provider.id, session.accountName, - session.id, provider.displayName, extensionId, requestingExtension.displayName || requestingExtension.name); @@ -100,7 +80,6 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape { } const session = await provider.login(scopes); - await this._proxy.$setTrustedExtension(provider.id, session.accountName, ExtensionIdentifier.toKey(requestingExtension.identifier), extensionName); return { id: session.id, accountName: session.accountName, @@ -109,7 +88,6 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape { const isAllowed = await this._proxy.$getSessionsPrompt( provider.id, session.accountName, - session.id, provider.displayName, ExtensionIdentifier.toKey(requestingExtension.identifier), requestingExtension.displayName || requestingExtension.name); diff --git a/src/vs/workbench/api/common/extHostExtensionService.ts b/src/vs/workbench/api/common/extHostExtensionService.ts index 0a7adc9bdd..18a57a9e2d 100644 --- a/src/vs/workbench/api/common/extHostExtensionService.ts +++ b/src/vs/workbench/api/common/extHostExtensionService.ts @@ -90,7 +90,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio private readonly _storage: ExtHostStorage; private readonly _storagePath: IExtensionStoragePaths; private readonly _activator: ExtensionsActivator; - private _extensionPathIndex: Promise> | null; + private _extensionPathIndex: Promise> | null; private readonly _resolvers: { [authorityPrefix: string]: vscode.RemoteAuthorityResolver; }; @@ -236,7 +236,7 @@ export abstract class AbstractExtHostExtensionService implements ExtHostExtensio } // create trie to enable fast 'filename -> extension id' look up - public getExtensionPathIndex(): Promise> { + public getExtensionPathIndex(): Promise> { if (!this._extensionPathIndex) { const tree = TernarySearchTree.forPaths(); const extensions = this._registry.getAllExtensionDescriptions().map(ext => { @@ -792,6 +792,6 @@ export interface IExtHostExtensionService extends AbstractExtHostExtensionServic deactivateAll(): Promise; getExtensionExports(extensionId: ExtensionIdentifier): IExtensionAPI | null | undefined; getExtensionRegistry(): Promise; - getExtensionPathIndex(): Promise>; + getExtensionPathIndex(): Promise>; registerRemoteAuthorityResolver(authorityPrefix: string, resolver: vscode.RemoteAuthorityResolver): vscode.Disposable; } diff --git a/src/vs/workbench/api/common/extHostNotebook.ts b/src/vs/workbench/api/common/extHostNotebook.ts index 3c4cb8b11d..69ebc9afe2 100644 --- a/src/vs/workbench/api/common/extHostNotebook.ts +++ b/src/vs/workbench/api/common/extHostNotebook.ts @@ -184,11 +184,17 @@ export class ExtHostNotebookDocument extends Disposable implements vscode.Notebo ...notebookDocumentMetadataDefaults, ...newMetadata }; + if (this._metadataChangeListener) { + this._metadataChangeListener.dispose(); + } + const observableMetadata = getObservable(newMetadata); this._metadata = observableMetadata.proxy; this._metadataChangeListener = this._register(observableMetadata.onDidChange(() => { this.updateMetadata(); })); + + this.updateMetadata(); } private _displayOrder: string[] = []; @@ -567,7 +573,7 @@ export class ExtHostNotebookOutputRenderer { return false; } - render(document: ExtHostNotebookDocument, output: vscode.CellOutput, mimeType: string): string { + render(document: ExtHostNotebookDocument, output: vscode.CellDisplayOutput, mimeType: string): string { let html = this.renderer.render(document, output, mimeType); return html; diff --git a/src/vs/workbench/api/common/extHostRequireInterceptor.ts b/src/vs/workbench/api/common/extHostRequireInterceptor.ts index 8f51cb8668..fc598e91ab 100644 --- a/src/vs/workbench/api/common/extHostRequireInterceptor.ts +++ b/src/vs/workbench/api/common/extHostRequireInterceptor.ts @@ -95,7 +95,7 @@ class VSCodeNodeModuleFactory implements INodeModuleFactory { constructor( private readonly _apiFactory: IExtensionApiFactory, - private readonly _extensionPaths: TernarySearchTree, + private readonly _extensionPaths: TernarySearchTree, private readonly _extensionRegistry: ExtensionDescriptionRegistry, private readonly _configProvider: ExtHostConfigProvider, private readonly _logService: ILogService, @@ -234,7 +234,7 @@ class OpenNodeModuleFactory implements INodeModuleFactory { private _mainThreadTelemetry: MainThreadTelemetryShape; constructor( - private readonly _extensionPaths: TernarySearchTree, + private readonly _extensionPaths: TernarySearchTree, private readonly _appUriScheme: string, @IExtHostRpcService rpcService: IExtHostRpcService, ) { diff --git a/src/vs/workbench/api/common/extHostTerminalService.ts b/src/vs/workbench/api/common/extHostTerminalService.ts index 33d74f8652..e17e455257 100644 --- a/src/vs/workbench/api/common/extHostTerminalService.ts +++ b/src/vs/workbench/api/common/extHostTerminalService.ts @@ -645,6 +645,8 @@ export abstract class BaseExtHostTerminalService implements IExtHostTerminalServ export class EnvironmentVariableCollection implements vscode.EnvironmentVariableCollection { readonly map: Map = new Map(); + private _disposed = false; + protected readonly _onDidChangeCollection: Emitter = new Emitter(); get onDidChangeCollection(): Event { return this._onDidChangeCollection && this._onDidChangeCollection.event; } @@ -656,18 +658,22 @@ export class EnvironmentVariableCollection implements vscode.EnvironmentVariable } get size(): number { + this._checkDisposed(); return this.map.size; } replace(variable: string, value: string): void { + this._checkDisposed(); this._setIfDiffers(variable, { value, type: EnvironmentVariableMutatorType.Replace }); } append(variable: string, value: string): void { + this._checkDisposed(); this._setIfDiffers(variable, { value, type: EnvironmentVariableMutatorType.Append }); } prepend(variable: string, value: string): void { + this._checkDisposed(); this._setIfDiffers(variable, { value, type: EnvironmentVariableMutatorType.Prepend }); } @@ -680,26 +686,39 @@ export class EnvironmentVariableCollection implements vscode.EnvironmentVariable } get(variable: string): vscode.EnvironmentVariableMutator | undefined { + this._checkDisposed(); return this.map.get(variable); } forEach(callback: (variable: string, mutator: vscode.EnvironmentVariableMutator, collection: vscode.EnvironmentVariableCollection) => any, thisArg?: any): void { + this._checkDisposed(); this.map.forEach((value, key) => callback(key, value, this)); } delete(variable: string): void { + this._checkDisposed(); this.map.delete(variable); this._onDidChangeCollection.fire(); } clear(): void { + this._checkDisposed(); this.map.clear(); this._onDidChangeCollection.fire(); } dispose(): void { - this.map.clear(); - this._onDidChangeCollection.fire(); + if (!this._disposed) { + this._disposed = true; + this.map.clear(); + this._onDidChangeCollection.fire(); + } + } + + protected _checkDisposed() { + if (this._disposed) { + throw new Error('EnvironmentVariableCollection has already been disposed'); + } } } diff --git a/src/vs/workbench/api/common/extHostTimeline.ts b/src/vs/workbench/api/common/extHostTimeline.ts index ee2b553f55..953f206402 100644 --- a/src/vs/workbench/api/common/extHostTimeline.ts +++ b/src/vs/workbench/api/common/extHostTimeline.ts @@ -60,7 +60,7 @@ export class ExtHostTimeline implements IExtHostTimeline { let disposable: IDisposable | undefined; if (provider.onDidChange) { - disposable = provider.onDidChange(e => this._proxy.$emitTimelineChangeEvent({ ...e, id: provider.id }), this); + disposable = provider.onDidChange(e => this._proxy.$emitTimelineChangeEvent({ uri: undefined, reset: true, ...e, id: provider.id }), this); } const itemsBySourceAndUriMap = this._itemsBySourceAndUriMap; diff --git a/src/vs/workbench/api/common/shared/semanticTokensDto.ts b/src/vs/workbench/api/common/shared/semanticTokensDto.ts index 1f83f183e6..338de8e495 100644 --- a/src/vs/workbench/api/common/shared/semanticTokensDto.ts +++ b/src/vs/workbench/api/common/shared/semanticTokensDto.ts @@ -55,12 +55,12 @@ function fromLittleEndianBuffer(buff: VSBuffer): Uint32Array { reverseEndianness(uint8Arr); } if (uint8Arr.byteOffset % 4 === 0) { - return new Uint32Array(uint8Arr.buffer, uint8Arr.byteOffset); + return new Uint32Array(uint8Arr.buffer, uint8Arr.byteOffset, uint8Arr.length / 4); } else { // unaligned memory access doesn't work on all platforms const data = new Uint8Array(uint8Arr.byteLength); data.set(uint8Arr); - return new Uint32Array(data.buffer, data.byteOffset); + return new Uint32Array(data.buffer, data.byteOffset, data.length / 4); } } diff --git a/src/vs/workbench/browser/actions/media/actions.css b/src/vs/workbench/browser/actions/media/actions.css index 58ff36df63..0972d3c047 100644 --- a/src/vs/workbench/browser/actions/media/actions.css +++ b/src/vs/workbench/browser/actions/media/actions.css @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.quick-input-list .quick-input-list-entry.has-actions:hover .quick-input-list-entry-action-bar .action-label.dirty-workspace::before { +.monaco-workbench .quick-input-list .quick-input-list-entry.has-actions:hover .quick-input-list-entry-action-bar .action-label.dirty-workspace::before { content: "\ea76"; /* Close icon flips between black dot and "X" for dirty workspaces */ } diff --git a/src/vs/workbench/browser/media/part.css b/src/vs/workbench/browser/media/part.css index 7828280cfd..c266c5efd4 100644 --- a/src/vs/workbench/browser/media/part.css +++ b/src/vs/workbench/browser/media/part.css @@ -8,7 +8,6 @@ overflow: hidden; } - .monaco-workbench .part > .drop-block-overlay.visible { display: block; backdrop-filter: brightness(97%) blur(2px); diff --git a/src/vs/workbench/browser/panecomposite.ts b/src/vs/workbench/browser/panecomposite.ts index 2f93022b9e..1e34e861a3 100644 --- a/src/vs/workbench/browser/panecomposite.ts +++ b/src/vs/workbench/browser/panecomposite.ts @@ -18,7 +18,9 @@ import { IPaneComposite } from 'vs/workbench/common/panecomposite'; import { IAction, IActionViewItem } from 'vs/base/common/actions'; export class PaneComposite extends Composite implements IPaneComposite { - constructor(id: string, + + constructor( + id: string, protected readonly viewPaneContainer: ViewPaneContainer, @ITelemetryService telemetryService: ITelemetryService, @@ -33,24 +35,30 @@ export class PaneComposite extends Composite implements IPaneComposite { @IExtensionService protected extensionService: IExtensionService, @IWorkspaceContextService - protected contextService: IWorkspaceContextService) { + protected contextService: IWorkspaceContextService + ) { super(id, telemetryService, themeService, storageService); this._register(this.viewPaneContainer.onTitleAreaUpdate(() => this.updateTitleArea())); } + create(parent: HTMLElement): void { this.viewPaneContainer.create(parent); } + setVisible(visible: boolean): void { super.setVisible(visible); this.viewPaneContainer.setVisible(visible); } + layout(dimension: Dimension): void { this.viewPaneContainer.layout(dimension); } + getOptimalWidth(): number { return this.viewPaneContainer.getOptimalWidth(); } + openView(id: string, focus?: boolean): IView { return this.viewPaneContainer.openView(id, focus); } diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts index 9eaa156638..10fb67eace 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarActions.ts @@ -196,8 +196,8 @@ export class GlobalActivityActionViewItem extends ActivityActionViewItem { colors: (theme: IColorTheme) => ICompositeBarColors, @IThemeService themeService: IThemeService, @IMenuService private readonly menuService: IMenuService, - @IContextMenuService protected contextMenuService: IContextMenuService, - @IContextKeyService private readonly contextKeyService: IContextKeyService, + @IContextMenuService protected readonly contextMenuService: IContextMenuService, + @IContextKeyService private readonly contextKeyService: IContextKeyService ) { super(action, { draggable: false, colors, icon: true }, themeService); } diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts index fea6887606..15ffa6e11e 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts @@ -87,18 +87,18 @@ export class ActivitybarPart extends Part implements IActivityBarService { private globalActivityAction: ActivityAction | undefined; private globalActivityActionBar: ActionBar | undefined; - private globalActivity: ICompositeActivity[] = []; + private readonly globalActivity: ICompositeActivity[] = []; private customMenubar: CustomMenubarControl | undefined; private menubar: HTMLElement | undefined; private content: HTMLElement | undefined; - private cachedViewlets: ICachedViewlet[] = []; + private readonly cachedViewlets: ICachedViewlet[] = []; private compositeBar: CompositeBar; - private readonly compositeActions: Map = new Map(); + private readonly compositeActions = new Map(); - private readonly viewletDisposables: Map = new Map(); + private readonly viewletDisposables = new Map(); constructor( @IViewletService private readonly viewletService: IViewletService, @@ -116,6 +116,7 @@ export class ActivitybarPart extends Part implements IActivityBarService { @IProductService private readonly productService: IProductService ) { super(Parts.ACTIVITYBAR_PART, { hasTitle: false }, themeService, storageService, layoutService); + storageKeysSyncRegistryService.registerStorageKey({ key: ActivitybarPart.PINNED_VIEWLETS, version: 1 }); this.migrateFromOldCachedViewletsValue(); diff --git a/src/vs/workbench/browser/parts/compositeBar.ts b/src/vs/workbench/browser/parts/compositeBar.ts index 7e4e233fe5..f89555af37 100644 --- a/src/vs/workbench/browser/parts/compositeBar.ts +++ b/src/vs/workbench/browser/parts/compositeBar.ts @@ -41,6 +41,7 @@ export class CompositeDragAndDrop implements ICompositeDragAndDrop { private openComposite: (id: string, focus?: boolean) => Promise, private moveComposite: (from: string, to: string, before?: Before2D) => void, ) { } + drop(data: CompositeDragAndDropData, targetCompositeId: string | undefined, originalEvent: DragEvent, before?: Before2D): void { const dragData = data.getData(); const viewContainerRegistry = Registry.as(ViewContainerExtensions.ViewContainersRegistry); @@ -150,6 +151,7 @@ export class CompositeDragAndDrop implements ICompositeDragAndDrop { } export interface ICompositeBarOptions { + readonly icon: boolean; readonly orientation: ActionsOrientation; readonly colors: (theme: IColorTheme) => ICompositeBarColors; @@ -169,6 +171,9 @@ export interface ICompositeBarOptions { export class CompositeBar extends Widget implements ICompositeBar { + private readonly _onDidChange = this._register(new Emitter()); + readonly onDidChange = this._onDidChange.event; + private dimension: Dimension | undefined; private compositeSwitcherBar: ActionBar | undefined; @@ -179,12 +184,9 @@ export class CompositeBar extends Widget implements ICompositeBar { private visibleComposites: string[]; private compositeSizeInBar: Map; - private readonly _onDidChange: Emitter = this._register(new Emitter()); - readonly onDidChange = this._onDidChange.event; - constructor( items: ICompositeBarItem[], - private options: ICompositeBarOptions, + private readonly options: ICompositeBarOptions, @IInstantiationService private readonly instantiationService: IInstantiationService, @IContextMenuService private readonly contextMenuService: IContextMenuService ) { diff --git a/src/vs/workbench/browser/parts/compositeBarActions.ts b/src/vs/workbench/browser/parts/compositeBarActions.ts index d2bc6f741c..b368ca7218 100644 --- a/src/vs/workbench/browser/parts/compositeBarActions.ts +++ b/src/vs/workbench/browser/parts/compositeBarActions.ts @@ -138,7 +138,7 @@ export class ActivityActionViewItem extends BaseActionViewItem { constructor( action: ActivityAction, options: IActivityActionViewItemOptions, - @IThemeService protected themeService: IThemeService + @IThemeService protected readonly themeService: IThemeService ) { super(null, action, options); diff --git a/src/vs/workbench/browser/parts/compositePart.ts b/src/vs/workbench/browser/parts/compositePart.ts index e1ee451e85..6b7a5145f6 100644 --- a/src/vs/workbench/browser/parts/compositePart.ts +++ b/src/vs/workbench/browser/parts/compositePart.ts @@ -60,11 +60,11 @@ export abstract class CompositePart extends Part { protected toolBar: ToolBar | undefined; protected titleLabelElement: HTMLElement | undefined; - private mapCompositeToCompositeContainer = new Map(); - private mapActionsBindingToComposite = new Map void>(); + private readonly mapCompositeToCompositeContainer = new Map(); + private readonly mapActionsBindingToComposite = new Map void>(); private activeComposite: Composite | undefined; private lastActiveCompositeId: string; - private instantiatedCompositeItems: Map; + private readonly instantiatedCompositeItems = new Map(); private titleLabel: ICompositeTitleLabel | undefined; private progressBar: ProgressBar | undefined; private contentAreaSize: Dimension | undefined; @@ -72,26 +72,25 @@ export abstract class CompositePart extends Part { private currentCompositeOpenToken: string | undefined; constructor( - private notificationService: INotificationService, - protected storageService: IStorageService, - private telemetryService: ITelemetryService, - protected contextMenuService: IContextMenuService, - protected layoutService: IWorkbenchLayoutService, - protected keybindingService: IKeybindingService, - protected instantiationService: IInstantiationService, + private readonly notificationService: INotificationService, + protected readonly storageService: IStorageService, + private readonly telemetryService: ITelemetryService, + protected readonly contextMenuService: IContextMenuService, + protected readonly layoutService: IWorkbenchLayoutService, + protected readonly keybindingService: IKeybindingService, + protected readonly instantiationService: IInstantiationService, themeService: IThemeService, protected readonly registry: CompositeRegistry, - private activeCompositeSettingsKey: string, - private defaultCompositeId: string, - private nameForTelemetry: string, - private compositeCSSClass: string, - private titleForegroundColor: string | undefined, + private readonly activeCompositeSettingsKey: string, + private readonly defaultCompositeId: string, + private readonly nameForTelemetry: string, + private readonly compositeCSSClass: string, + private readonly titleForegroundColor: string | undefined, id: string, options: IPartOptions ) { super(id, options, themeService, storageService, layoutService); - this.instantiatedCompositeItems = new Map(); this.lastActiveCompositeId = storageService.get(activeCompositeSettingsKey, StorageScope.WORKSPACE, this.defaultCompositeId); } diff --git a/src/vs/workbench/browser/parts/editor/editor.ts b/src/vs/workbench/browser/parts/editor/editor.ts index 4199d1707a..0288744411 100644 --- a/src/vs/workbench/browser/parts/editor/editor.ts +++ b/src/vs/workbench/browser/parts/editor/editor.ts @@ -9,7 +9,6 @@ import { IEditorGroup, GroupDirection, IAddGroupOptions, IMergeGroupOptions, Gro import { IDisposable } from 'vs/base/common/lifecycle'; import { Dimension } from 'vs/base/browser/dom'; import { Event } from 'vs/base/common/event'; -import { assign } from 'vs/base/common/objects'; import { IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; import { ISerializableView } from 'vs/base/browser/ui/grid/grid'; import { getCodeEditor } from 'vs/editor/browser/editorBrowser'; @@ -47,7 +46,7 @@ export function impactsEditorPartOptions(event: IConfigurationChangeEvent): bool } export function getEditorPartOptions(config: IWorkbenchEditorConfiguration): IEditorPartOptions { - const options: IEditorPartOptions = assign(Object.create(null), DEFAULT_EDITOR_PART_OPTIONS); + const options = { ...DEFAULT_EDITOR_PART_OPTIONS }; if (!config || !config.workbench) { return options; @@ -58,7 +57,7 @@ export function getEditorPartOptions(config: IWorkbenchEditorConfiguration): IEd } if (config.workbench.editor) { - assign(options, config.workbench.editor); + Object.assign(options, config.workbench.editor); } return options; diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index c74222f535..c126c133b4 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -19,7 +19,6 @@ import { IEditorGroupsAccessor, IEditorGroupView, getEditorPartOptions, impactsE import { EditorGroupView } from 'vs/workbench/browser/parts/editor/editorGroupView'; import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; import { IDisposable, dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; -import { assign } from 'vs/base/common/objects'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { ISerializedEditorGroup, isSerializedEditorGroup } from 'vs/workbench/common/editor/editorGroup'; import { EditorDropTarget, EditorDropTargetDelegate } from 'vs/workbench/browser/parts/editor/editorDropTarget'; @@ -175,7 +174,7 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro const newPartOptions = getEditorPartOptions(this.configurationService.getValue()); this.enforcedPartOptions.forEach(enforcedPartOptions => { - assign(newPartOptions, enforcedPartOptions); // check for overrides + Object.assign(newPartOptions, enforcedPartOptions); // check for overrides }); this._partOptions = newPartOptions; diff --git a/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts index 2a57a990e3..2a348f1ef1 100644 --- a/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/noTabsTitleControl.ts @@ -99,7 +99,6 @@ export class NoTabsTitleControl extends TitleControl { } private onTitleClick(e: MouseEvent | GestureEvent): void { - if (e instanceof MouseEvent) { // Close editor on middle mouse click if (e.button === 1 /* Middle Button */) { diff --git a/src/vs/workbench/browser/parts/editor/textEditor.ts b/src/vs/workbench/browser/parts/editor/textEditor.ts index 5c1c4e0c52..969ee2776a 100644 --- a/src/vs/workbench/browser/parts/editor/textEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textEditor.ts @@ -5,7 +5,7 @@ import { localize } from 'vs/nls'; import { URI } from 'vs/base/common/uri'; -import { distinct, deepClone, assign } from 'vs/base/common/objects'; +import { distinct, deepClone } from 'vs/base/common/objects'; import { Event } from 'vs/base/common/event'; import { isObject, assertIsDefined, withNullAsUndefined, isFunction } from 'vs/base/common/types'; import { Dimension } from 'vs/base/browser/dom'; @@ -93,7 +93,7 @@ export abstract class BaseTextEditor extends BaseEditor implements ITextEditorPa // Specific editor options always overwrite user configuration const editorConfiguration: IEditorOptions = isObject(configuration.editor) ? deepClone(configuration.editor) : Object.create(null); - assign(editorConfiguration, this.getConfigurationOverrides()); + Object.assign(editorConfiguration, this.getConfigurationOverrides()); // ARIA label editorConfiguration.ariaLabel = this.computeAriaLabel(); diff --git a/src/vs/workbench/browser/parts/notifications/notificationsList.ts b/src/vs/workbench/browser/parts/notifications/notificationsList.ts index 4e78bc91d0..3c1459a34a 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsList.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsList.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/notificationsList'; +import { localize } from 'vs/nls'; import { addClass, isAncestor, trackFocus } from 'vs/base/browser/dom'; import { WorkbenchList } from 'vs/platform/list/browser/listService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -87,6 +88,15 @@ export class NotificationsList extends Themable { horizontalScrolling: false, overrideStyles: { listBackground: NOTIFICATIONS_BACKGROUND + }, + accessibilityProvider: { + getAriaLabel(element: INotificationViewItem): string { + if (!element.source) { + return localize('notificationAriaLabel', "{0}, notification", element.message.raw); + } + + return localize('notificationWithSourceAriaLabel', "{0}, source: {1}, notification", element.message.raw, element.source); + } } } )); diff --git a/src/vs/workbench/browser/parts/panel/panelPart.ts b/src/vs/workbench/browser/parts/panel/panelPart.ts index f735ecac8f..0f4a95963c 100644 --- a/src/vs/workbench/browser/parts/panel/panelPart.ts +++ b/src/vs/workbench/browser/parts/panel/panelPart.ts @@ -85,12 +85,12 @@ export class PanelPart extends CompositePart implements IPanelService { private panelFocusContextKey: IContextKey; private compositeBar: CompositeBar; - private compositeActions: Map = new Map(); + private readonly compositeActions = new Map(); private readonly panelDisposables: Map = new Map(); private blockOpeningPanel = false; - private _contentDimension: Dimension | undefined; + private contentDimension: Dimension | undefined; private panelRegistry: PanelRegistry; @@ -320,7 +320,7 @@ export class PanelPart extends CompositePart implements IPanelService { const viewContainer = this.getViewContainer(panelDescriptor.id); if (viewContainer?.hideIfEmpty) { const viewDescriptors = this.viewDescriptorService.getViewDescriptors(viewContainer); - if (viewDescriptors.activeViewDescriptors.length === 0) { + if (viewDescriptors.activeViewDescriptors.length === 0 && this.compositeBar.getPinnedComposites().length > 1) { this.hideComposite(panelDescriptor.id); // Update the composite bar by hiding } } @@ -473,21 +473,21 @@ export class PanelPart extends CompositePart implements IPanelService { } if (this.layoutService.getPanelPosition() === Position.RIGHT) { - this._contentDimension = new Dimension(width - 1, height); // Take into account the 1px border when layouting + this.contentDimension = new Dimension(width - 1, height); // Take into account the 1px border when layouting } else { - this._contentDimension = new Dimension(width, height); + this.contentDimension = new Dimension(width, height); } // Layout contents - super.layout(this._contentDimension.width, this._contentDimension.height); + super.layout(this.contentDimension.width, this.contentDimension.height); // Layout composite bar this.layoutCompositeBar(); } private layoutCompositeBar(): void { - if (this._contentDimension && this.dimension) { - let availableWidth = this._contentDimension.width - 40; // take padding into account + if (this.contentDimension && this.dimension) { + let availableWidth = this.contentDimension.width - 40; // take padding into account if (this.toolBar) { availableWidth = Math.max(PanelPart.MIN_COMPOSITE_BAR_WIDTH, availableWidth - this.getToolbarWidth()); // adjust height for global actions showing } diff --git a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts index 25dbf8a363..65b838e40e 100644 --- a/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts +++ b/src/vs/workbench/browser/parts/sidebar/sidebarPart.ts @@ -27,7 +27,7 @@ import { SIDE_BAR_TITLE_FOREGROUND, SIDE_BAR_BACKGROUND, SIDE_BAR_FOREGROUND, SI import { INotificationService } from 'vs/platform/notification/common/notification'; import { EventType, addDisposableListener, trackFocus } from 'vs/base/browser/dom'; import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; -import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { AnchorAlignment } from 'vs/base/browser/ui/contextview/contextview'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; @@ -77,9 +77,11 @@ export class SidebarPart extends CompositePart implements IViewletServi get onDidViewletOpen(): Event { return Event.map(this.onDidCompositeOpen.event, compositeEvent => compositeEvent.composite); } get onDidViewletClose(): Event { return this.onDidCompositeClose.event as Event; } - private viewletRegistry: ViewletRegistry; - private sideBarFocusContextKey: IContextKey; - private activeViewletContextKey: IContextKey; + private readonly viewletRegistry = Registry.as(ViewletExtensions.Viewlets); + + private readonly sideBarFocusContextKey = SidebarFocusContext.bindTo(this.contextKeyService); + private readonly activeViewletContextKey = ActiveViewletContext.bindTo(this.contextKeyService); + private blockOpeningViewlet = false; constructor( @@ -91,7 +93,7 @@ export class SidebarPart extends CompositePart implements IViewletServi @IKeybindingService keybindingService: IKeybindingService, @IInstantiationService instantiationService: IInstantiationService, @IThemeService themeService: IThemeService, - @IContextKeyService contextKeyService: IContextKeyService, + @IContextKeyService private readonly contextKeyService: IContextKeyService, @IExtensionService private readonly extensionService: IExtensionService ) { super( @@ -113,11 +115,6 @@ export class SidebarPart extends CompositePart implements IViewletServi { hasTitle: true, borderWidth: () => (this.getColor(SIDE_BAR_BORDER) || this.getColor(contrastBorder)) ? 1 : 0 } ); - this.viewletRegistry = Registry.as(ViewletExtensions.Viewlets); - - this.sideBarFocusContextKey = SidebarFocusContext.bindTo(contextKeyService); - this.activeViewletContextKey = ActiveViewletContext.bindTo(contextKeyService); - this.registerListeners(); } diff --git a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts index 66b18c977a..eda13f0baf 100644 --- a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts +++ b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts @@ -32,7 +32,7 @@ import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; import { ToggleStatusbarVisibilityAction } from 'vs/workbench/browser/actions/layoutActions'; import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; import { assertIsDefined } from 'vs/base/common/types'; -import { Emitter, Event } from 'vs/base/common/event'; +import { Emitter } from 'vs/base/common/event'; import { Command } from 'vs/editor/common/modes'; import { IStorageKeysSyncRegistryService } from 'vs/platform/userDataSync/common/storageKeys'; @@ -65,7 +65,7 @@ class StatusbarViewModel extends Disposable { private readonly _onDidChangeEntryVisibility = this._register(new Emitter<{ id: string, visible: boolean }>()); readonly onDidChangeEntryVisibility = this._onDidChangeEntryVisibility.event; - constructor(private storageService: IStorageService) { + constructor(private readonly storageService: IStorageService) { super(); this.restoreState(); @@ -341,18 +341,18 @@ export class StatusbarPart extends Part implements IStatusbarService { private pendingEntries: IPendingStatusbarEntry[] = []; - private readonly viewModel: StatusbarViewModel; + private readonly viewModel = this._register(new StatusbarViewModel(this.storageService)); + + readonly onDidChangeEntryVisibility = this.viewModel.onDidChangeEntryVisibility; private leftItemsContainer: HTMLElement | undefined; private rightItemsContainer: HTMLElement | undefined; - readonly onDidChangeEntryVisibility: Event<{ id: string, visible: boolean }>; - constructor( @IInstantiationService private readonly instantiationService: IInstantiationService, @IThemeService themeService: IThemeService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, - @IStorageService storageService: IStorageService, + @IStorageService private readonly storageService: IStorageService, @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService, @IContextMenuService private contextMenuService: IContextMenuService, @IStorageKeysSyncRegistryService storageKeysSyncRegistryService: IStorageKeysSyncRegistryService, @@ -360,8 +360,6 @@ export class StatusbarPart extends Part implements IStatusbarService { super(Parts.STATUSBAR_PART, { hasTitle: false }, themeService, storageService, layoutService); storageKeysSyncRegistryService.registerStorageKey({ key: StatusbarViewModel.HIDDEN_ENTRIES_KEY, version: 1 }); - this.viewModel = this._register(new StatusbarViewModel(storageService)); - this.onDidChangeEntryVisibility = this.viewModel.onDidChangeEntryVisibility; this.registerListeners(); } @@ -639,6 +637,7 @@ export class StatusbarPart extends Part implements IStatusbarService { } class StatusbarEntryItem extends Disposable { + private entry!: IStatusbarEntry; private labelContainer!: HTMLElement; diff --git a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts index c108610a31..7b6203bb3f 100644 --- a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts @@ -29,7 +29,6 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ import { MenuBar, IMenuBarOptions } from 'vs/base/browser/ui/menu/menubar'; import { SubmenuAction, Direction } from 'vs/base/browser/ui/menu/menu'; import { attachMenuStyler } from 'vs/platform/theme/common/styler'; -import { assign } from 'vs/base/common/objects'; import { mnemonicMenuLabel, unmnemonicLabel } from 'vs/base/common/labels'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; @@ -232,7 +231,7 @@ export abstract class MenubarControl extends Disposable { }); }); - return assign(ret, { uri: uri }); + return Object.assign(ret, { uri }); } private notifyUserOfCustomMenubarAccessibility(): void { diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 6c74dd0d26..8f55ab0868 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -47,7 +47,6 @@ export class TitlebarPart extends Part implements ITitleService { private static readonly NLS_USER_IS_ADMIN = isWindows ? nls.localize('userIsAdmin', "[Administrator]") : nls.localize('userIsSudo', "[Superuser]"); private static readonly NLS_EXTENSION_HOST = nls.localize('devExtensionWindowTitlePrefix', "[Extension Development Host]"); private static readonly TITLE_DIRTY = '\u25cf '; - private static readonly TITLE_SEPARATOR = isMacintosh ? ' — ' : ' - '; // macOS uses special - separator //#region IView @@ -126,7 +125,7 @@ export class TitlebarPart extends Part implements ITitleService { } protected onConfigurationChanged(event: IConfigurationChangeEvent): void { - if (event.affectsConfiguration('window.title')) { + if (event.affectsConfiguration('window.title') || event.affectsConfiguration('window.titleSeparator')) { this.titleUpdater.schedule(); } @@ -204,6 +203,9 @@ export class TitlebarPart extends Part implements ITitleService { title = `${TitlebarPart.NLS_EXTENSION_HOST} - ${title || this.productService.nameLong}`; } + // Replace non-space whitespace + title = title.replace(/[^\S ]/g, ' '); + return title; } @@ -283,7 +285,7 @@ export class TitlebarPart extends Part implements ITitleService { const dirty = editor?.isDirty() && !editor.isSaving() ? TitlebarPart.TITLE_DIRTY : ''; const appName = this.productService.nameLong; const remoteName = this.labelService.getHostLabel(REMOTE_HOST_SCHEME, this.environmentService.configuration.remoteAuthority); - const separator = TitlebarPart.TITLE_SEPARATOR; + const separator = this.configurationService.getValue('window.titleSeparator'); const titleTemplate = this.configurationService.getValue('window.title'); return template(titleTemplate, { diff --git a/src/vs/workbench/browser/parts/views/media/views.css b/src/vs/workbench/browser/parts/views/media/views.css index a8dca1c5f8..179dad4923 100644 --- a/src/vs/workbench/browser/parts/views/media/views.css +++ b/src/vs/workbench/browser/parts/views/media/views.css @@ -74,8 +74,11 @@ box-sizing: border-box; } -.monaco-workbench .pane > .pane-body .welcome-view-content > *:last-child { - margin-bottom: 1em; +.monaco-workbench .pane > .pane-body .welcome-view-content > * { + margin-block-start: 1em; + margin-block-end: 1em; + margin-inline-start: 0px; + margin-inline-end: 0px; } .customview-tree .monaco-list-row .monaco-tl-contents.align-icon-with-twisty::before { diff --git a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts index 7ef7062a71..7eeeb40a3e 100644 --- a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts +++ b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts @@ -1264,7 +1264,7 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { return; } - overlay = new ViewPaneDropOverlay(pane.dropTargetElement, this.options.orientation ?? Orientation.VERTICAL, this.themeService); + overlay = new ViewPaneDropOverlay(pane.dropTargetElement, this.orientation ?? Orientation.VERTICAL, this.themeService); } if (dropData.type === 'composite' && dropData.id !== this.viewContainer.id) { @@ -1274,7 +1274,7 @@ export class ViewPaneContainer extends Component implements IViewPaneContainer { const viewsToMove = this.viewDescriptorService.getViewDescriptors(container).allViewDescriptors; if (viewsToMove.length === 1 && viewsToMove[0].canMoveView) { - overlay = new ViewPaneDropOverlay(pane.dropTargetElement, this.options.orientation ?? Orientation.VERTICAL, this.themeService); + overlay = new ViewPaneDropOverlay(pane.dropTargetElement, this.orientation ?? Orientation.VERTICAL, this.themeService); } } diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index 68c7132f48..e3457446f1 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -179,7 +179,7 @@ class BrowserMain extends Disposable { serviceCollection.set(IRemoteAuthorityResolverService, remoteAuthorityResolverService); // Signing - const signService = new SignService(environmentService.configuration.connectionToken); + const signService = new SignService(environmentService.options.connectionToken || this.getCookieValue('vscode-tkn')); serviceCollection.set(ISignService, signService); // Remote Agent @@ -327,6 +327,12 @@ class BrowserMain extends Disposable { return undefined; } + + private getCookieValue(name: string): string | undefined { + const match = document.cookie.match('(^|[^;]+)\\s*' + name + '\\s*=\\s*([^;]+)'); // See https://stackoverflow.com/a/25490531 + + return match ? match.pop() : undefined; + } } export function main(domElement: HTMLElement, options: IWorkbenchConstructionOptions): Promise { diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index b0fbf83571..224d149560 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -98,7 +98,7 @@ import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuratio }, 'workbench.editor.showIcons': { 'type': 'boolean', - 'description': nls.localize('showIcons', "Controls whether opened editors should show with an icon or not. This requires an icon theme to be enabled as well."), + 'description': nls.localize('showIcons', "Controls whether opened editors should show with an icon or not. This requires an file icon theme to be enabled as well."), 'default': true }, 'workbench.editor.enablePreview': { @@ -301,6 +301,11 @@ import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuratio })(), 'markdownDescription': windowTitleDescription }, + 'window.titleSeparator': { + 'type': 'string', + 'default': isMacintosh ? ' — ' : ' - ', + 'markdownDescription': nls.localize("window.titleSeparator", "Separator used by `window.title`.") + }, 'window.menuBarVisibility': { 'type': 'string', 'enum': ['default', 'visible', 'toggle', 'hidden', 'compact'], diff --git a/src/vs/workbench/common/component.ts b/src/vs/workbench/common/component.ts index 0989454037..9a496c8989 100644 --- a/src/vs/workbench/common/component.ts +++ b/src/vs/workbench/common/component.ts @@ -8,6 +8,7 @@ import { IThemeService, Themable } from 'vs/platform/theme/common/themeService'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; export class Component extends Themable { + private readonly memento: Memento; constructor( diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index 4863539364..869665caba 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -5,7 +5,6 @@ import { localize } from 'vs/nls'; import { Event, Emitter } from 'vs/base/common/event'; -import { assign } from 'vs/base/common/objects'; import { withNullAsUndefined, assertIsDefined } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { IDisposable, Disposable, toDisposable } from 'vs/base/common/lifecycle'; @@ -890,7 +889,7 @@ export class SideBySideEditorInput extends EditorInput { getTelemetryDescriptor(): { [key: string]: unknown } { const descriptor = this.master.getTelemetryDescriptor(); - return assign(descriptor, super.getTelemetryDescriptor()); + return Object.assign(descriptor, super.getTelemetryDescriptor()); } private registerListeners(): void { diff --git a/src/vs/workbench/common/memento.ts b/src/vs/workbench/common/memento.ts index de98520e7d..8b31927ebe 100644 --- a/src/vs/workbench/common/memento.ts +++ b/src/vs/workbench/common/memento.ts @@ -61,6 +61,7 @@ export class Memento { } class ScopedMemento { + private readonly mementoObj: MementoObject; constructor(private id: string, private scope: StorageScope, private storageService: IStorageService) { diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts b/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts index 3115a659ec..291700c8e2 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadWidget.ts @@ -496,7 +496,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget // If there are no existing comments, place focus on the text area. This must be done after show, which also moves focus. // if this._commentThread.comments is undefined, it doesn't finish initialization yet, so we don't focus the editor immediately. - if (this._commentThread.comments && !this._commentThread.comments.length) { + if (!this._commentThread.comments || !this._commentThread.comments.length) { this._commentEditor.focus(); } else if (this._commentEditor.getModel()!.getValueLength() > 0) { this.expandReplyArea(); @@ -734,6 +734,10 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget this._viewZone.afterLineNumber = currentPosition.lineNumber; } + if (!this._commentThread.comments || !this._commentThread.comments.length) { + this._commentEditor.focus(); + } + this._relayout(computedLinesNumber); } } diff --git a/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts b/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts index ee5aed4ff5..430b860ff4 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsTreeViewer.ts @@ -21,6 +21,7 @@ import { WorkbenchAsyncDataTree, IListService } from 'vs/platform/list/browser/l import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IColorMapping } from 'vs/platform/theme/common/styler'; +import { basename } from 'vs/base/common/resources'; export const COMMENTS_VIEW_ID = 'workbench.panel.comments'; export const COMMENTS_VIEW_TITLE = 'Comments'; @@ -181,6 +182,27 @@ export class CommentsList extends WorkbenchAsyncDataTree { renderers, dataSource, { + accessibilityProvider: { + getAriaLabel(element: any): string { + if (element instanceof CommentsModel) { + return nls.localize('rootCommentsLabel', "Comments for current workspace"); + } + if (element instanceof ResourceWithCommentThreads) { + return nls.localize('resourceWithCommentThreadsLabel', "Comments in {0}, full path {1}", basename(element.resource), element.resource.fsPath); + } + if (element instanceof CommentNode) { + return nls.localize('resourceWithCommentLabel', + "Comment from ${0} at line {1} column {2} in {3}, source: {4}", + element.comment.userName, + element.range.startLineNumber, + element.range.startColumn, + basename(element.resource), + element.comment.body.value + ); + } + return ''; + } + }, ariaLabel: COMMENTS_VIEW_TITLE, keyboardSupport: true, identityProvider: { diff --git a/src/vs/workbench/contrib/configExporter/node/configurationExportHelper.contribution.ts b/src/vs/workbench/contrib/configExporter/electron-browser/configurationExportHelper.contribution.ts similarity index 83% rename from src/vs/workbench/contrib/configExporter/node/configurationExportHelper.contribution.ts rename to src/vs/workbench/contrib/configExporter/electron-browser/configurationExportHelper.contribution.ts index b22afc7496..2bac2b9d99 100644 --- a/src/vs/workbench/contrib/configExporter/node/configurationExportHelper.contribution.ts +++ b/src/vs/workbench/contrib/configExporter/electron-browser/configurationExportHelper.contribution.ts @@ -8,13 +8,14 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { DefaultConfigurationExportHelper } from 'vs/workbench/contrib/configExporter/node/configurationExportHelper'; +import { DefaultConfigurationExportHelper } from 'vs/workbench/contrib/configExporter/electron-browser/configurationExportHelper'; +import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-browser/environmentService'; export class ExtensionPoints implements IWorkbenchContribution { constructor( @IInstantiationService instantiationService: IInstantiationService, - @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService + @IWorkbenchEnvironmentService environmentService: INativeWorkbenchEnvironmentService ) { // Config Exporter if (environmentService.configuration['export-default-configuration']) { diff --git a/src/vs/workbench/contrib/configExporter/node/configurationExportHelper.ts b/src/vs/workbench/contrib/configExporter/electron-browser/configurationExportHelper.ts similarity index 91% rename from src/vs/workbench/contrib/configExporter/node/configurationExportHelper.ts rename to src/vs/workbench/contrib/configExporter/electron-browser/configurationExportHelper.ts index eccadbcfed..513db614a9 100644 --- a/src/vs/workbench/contrib/configExporter/node/configurationExportHelper.ts +++ b/src/vs/workbench/contrib/configExporter/electron-browser/configurationExportHelper.ts @@ -5,7 +5,8 @@ import { writeFile } from 'vs/base/node/pfs'; import product from 'vs/platform/product/common/product'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-browser/environmentService'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { Registry } from 'vs/platform/registry/common/platform'; import { IConfigurationNode, IConfigurationRegistry, Extensions, IConfigurationPropertySchema } from 'vs/platform/configuration/common/configurationRegistry'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @@ -30,7 +31,7 @@ interface IConfigurationExport { export class DefaultConfigurationExportHelper { constructor( - @IEnvironmentService environmentService: IEnvironmentService, + @IWorkbenchEnvironmentService environmentService: INativeWorkbenchEnvironmentService, @IExtensionService private readonly extensionService: IExtensionService, @ICommandService private readonly commandService: ICommandService) { if (environmentService.args['export-default-configuration']) { diff --git a/src/vs/workbench/contrib/customEditor/browser/webviewEditor.contribution.ts b/src/vs/workbench/contrib/customEditor/browser/webviewEditor.contribution.ts index 1e83d4eaf4..c167a3ea53 100644 --- a/src/vs/workbench/contrib/customEditor/browser/webviewEditor.contribution.ts +++ b/src/vs/workbench/contrib/customEditor/browser/webviewEditor.contribution.ts @@ -46,21 +46,23 @@ Registry.as(ConfigurationExtensions.Configuration) 'properties': { [customEditorsAssociationsKey]: { type: 'array', - markdownDescription: nls.localize('editor.editorAssociations', "Configure which editor to use for a resource."), + markdownDescription: nls.localize('editor.editorAssociations', "Configure which editor to use for specific file types."), items: { type: 'object', + defaultSnippets: [{ + body: { + 'viewType': '$1', + 'filenamePattern': '$2' + } + }], properties: { 'viewType': { type: 'string', - description: nls.localize('editor.editorAssociations.viewType', "Editor view type."), - }, - 'mime': { - type: 'string', - description: nls.localize('editor.editorAssociations.mime', "Mime type the editor should be used for. This is used for binary files."), + description: nls.localize('editor.editorAssociations.viewType', "The unique id of the editor to use."), }, 'filenamePattern': { type: 'string', - description: nls.localize('editor.editorAssociations.filenamePattern', "Glob pattern the editor should be used for."), + description: nls.localize('editor.editorAssociations.filenamePattern', "Glob pattern specifying which files the editor should be used for."), } } } diff --git a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts index 7b51ecb24a..4b80518338 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts @@ -37,6 +37,7 @@ import { TextEditorSelectionRevealType } from 'vs/platform/editor/common/editor' import { IOpenerService } from 'vs/platform/opener/common/opener'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { Orientation } from 'vs/base/browser/ui/splitview/splitview'; +import { IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; const $ = dom.$; @@ -50,14 +51,15 @@ function createCheckbox(): HTMLInputElement { } const MAX_VISIBLE_BREAKPOINTS = 9; -export function getExpandedBodySize(model: IDebugModel): number { +export function getExpandedBodySize(model: IDebugModel, countLimit: number): number { const length = model.getBreakpoints().length + model.getExceptionBreakpoints().length + model.getFunctionBreakpoints().length + model.getDataBreakpoints().length; - return Math.min(MAX_VISIBLE_BREAKPOINTS, length) * 22; + return Math.min(countLimit, length) * 22; } +type BreakpointItem = IBreakpoint | IFunctionBreakpoint | IDataBreakpoint | IExceptionBreakpoint; export class BreakpointsView extends ViewPane { - private list!: WorkbenchList; + private list!: WorkbenchList; private needsRefresh = false; constructor( @@ -88,7 +90,7 @@ export class BreakpointsView extends ViewPane { dom.addClass(container, 'debug-breakpoints'); const delegate = new BreakpointsDelegate(this.debugService); - this.list = >this.instantiationService.createInstance(WorkbenchList, 'Breakpoints', container, delegate, [ + this.list = >this.instantiationService.createInstance(WorkbenchList, 'Breakpoints', container, delegate, [ this.instantiationService.createInstance(BreakpointsRenderer), new ExceptionBreakpointsRenderer(this.debugService), this.instantiationService.createInstance(FunctionBreakpointsRenderer), @@ -104,6 +106,7 @@ export class BreakpointsView extends ViewPane { getRole: (breakpoint: IEnablement) => 'checkbox', isChecked: (breakpoint: IEnablement) => breakpoint.enabled }, + accessibilityProvider: new BreakpointsAccessibilityProvider(this.debugService), overrideStyles: { listBackground: this.getBackgroundColor() } @@ -230,8 +233,8 @@ export class BreakpointsView extends ViewPane { private updateSize(): void { // Adjust expanded body size - this.minimumBodySize = this.orientation === Orientation.VERTICAL ? getExpandedBodySize(this.debugService.getModel()) : 170; - this.maximumBodySize = this.orientation === Orientation.VERTICAL ? this.minimumBodySize : Number.POSITIVE_INFINITY; + this.minimumBodySize = this.orientation === Orientation.VERTICAL ? getExpandedBodySize(this.debugService.getModel(), MAX_VISIBLE_BREAKPOINTS) : 170; + this.maximumBodySize = this.orientation === Orientation.VERTICAL ? getExpandedBodySize(this.debugService.getModel(), Number.POSITIVE_INFINITY) : Number.POSITIVE_INFINITY; } private onBreakpointsChange(): void { @@ -246,25 +249,25 @@ export class BreakpointsView extends ViewPane { } } - private get elements(): IEnablement[] { + private get elements(): BreakpointItem[] { const model = this.debugService.getModel(); const elements = (>model.getExceptionBreakpoints()).concat(model.getFunctionBreakpoints()).concat(model.getDataBreakpoints()).concat(model.getBreakpoints()); - return elements; + return elements as BreakpointItem[]; } } -class BreakpointsDelegate implements IListVirtualDelegate { +class BreakpointsDelegate implements IListVirtualDelegate { constructor(private debugService: IDebugService) { // noop } - getHeight(element: IEnablement): number { + getHeight(_element: BreakpointItem): number { return 22; } - getTemplateId(element: IEnablement): string { + getTemplateId(element: BreakpointItem): string { if (element instanceof Breakpoint) { return BreakpointsRenderer.ID; } @@ -291,7 +294,7 @@ interface IBaseBreakpointTemplateData { breakpoint: HTMLElement; name: HTMLElement; checkbox: HTMLInputElement; - context: IEnablement; + context: BreakpointItem; toDispose: IDisposable[]; } @@ -621,6 +624,22 @@ class FunctionBreakpointInputRenderer implements IListRenderer { + + constructor(private readonly debugService: IDebugService) { } + + getAriaLabel(element: BreakpointItem): string | null { + if (element instanceof ExceptionBreakpoint) { + return element.toString(); + } + + const { message } = getBreakpointMessageAndClassName(this.debugService.state, this.debugService.getModel().areBreakpointsActivated(), element as IBreakpoint | IDataBreakpoint | IFunctionBreakpoint); + const toString = element.toString(); + + return message ? `${toString} ${message}` : toString; + } +} + export function openBreakpointSource(breakpoint: IBreakpoint, sideBySide: boolean, preserveFocus: boolean, debugService: IDebugService, editorService: IEditorService): Promise { if (breakpoint.uri.scheme === DEBUG_SCHEME && debugService.state === State.Inactive) { return Promise.resolve(undefined); diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index db3974c008..122b1554ef 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -3,8 +3,8 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!../browser/media/debug.contribution'; -import 'vs/css!../browser/media/debugHover'; +import 'vs/css!./media/debug.contribution'; +import 'vs/css!./media/debugHover'; import * as nls from 'vs/nls'; import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; diff --git a/src/vs/workbench/contrib/debug/browser/debugHover.ts b/src/vs/workbench/contrib/debug/browser/debugHover.ts index 935833a87c..0a7b3a863c 100644 --- a/src/vs/workbench/contrib/debug/browser/debugHover.ts +++ b/src/vs/workbench/contrib/debug/browser/debugHover.ts @@ -177,7 +177,6 @@ export class DebugHoverWidget implements IContentWidget { } async showAt(range: Range, focus: boolean): Promise { - const session = this.debugService.getViewModel().focusedSession; if (!session || !this.editor.hasModel()) { @@ -285,12 +284,12 @@ export class DebugHoverWidget implements IContentWidget { this.valueContainer.hidden = true; this.complexValueContainer.hidden = false; - await this.tree.setInput(expression); this.complexValueTitle.textContent = expression.value; this.complexValueTitle.title = expression.value; this.layoutTreeAndContainer(); this.editor.layoutContentWidget(this); this.scrollbar.scanDomNode(); + await this.tree.setInput(expression); this.tree.scrollTop = 0; this.tree.scrollLeft = 0; diff --git a/src/vs/workbench/contrib/debug/browser/linkDetector.ts b/src/vs/workbench/contrib/debug/browser/linkDetector.ts index cd380278f7..08a052b031 100644 --- a/src/vs/workbench/contrib/debug/browser/linkDetector.ts +++ b/src/vs/workbench/contrib/debug/browser/linkDetector.ts @@ -7,12 +7,12 @@ import * as osPath from 'vs/base/common/path'; import * as platform from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; import * as nls from 'vs/nls'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IFileService } from 'vs/platform/files/common/files'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { IRemotePathService } from 'vs/workbench/services/path/common/remotePathService'; const CONTROL_CODES = '\\u0000-\\u0020\\u007f-\\u009f'; const WEB_LINK_REGEX = new RegExp('(?:[a-zA-Z][a-zA-Z0-9+.-]{2,}:\\/\\/|data:|www\\.)[^\\s' + CONTROL_CODES + '"]{2,}[^\\s' + CONTROL_CODES + '"\')}\\],:;.!?]', 'ug'); @@ -38,7 +38,7 @@ export class LinkDetector { @IEditorService private readonly editorService: IEditorService, @IFileService private readonly fileService: IFileService, @IOpenerService private readonly openerService: IOpenerService, - @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IRemotePathService private readonly remotePathService: IRemotePathService, @IWorkbenchEnvironmentService private readonly workbenchEnvironmentService: IWorkbenchEnvironmentService ) { // noop @@ -120,7 +120,10 @@ export class LinkDetector { } if (path[0] === '~') { - path = osPath.join(this.environmentService.userHome, path.substring(1)); + const userHome = this.remotePathService.userHomeSync; + if (userHome) { + path = osPath.join(userHome.fsPath, path.substring(1)); + } } const link = this.createLink(text); diff --git a/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts b/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts index 870e1033bc..40a2ae44ac 100644 --- a/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts +++ b/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts @@ -17,7 +17,6 @@ import { IDebugSession, IDebugService, CONTEXT_LOADED_SCRIPTS_ITEM_TYPE } from ' import { Source } from 'vs/workbench/contrib/debug/common/debugSource'; import { IWorkspaceContextService, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { tildify } from 'vs/base/common/labels'; import { isWindows } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; @@ -40,6 +39,7 @@ import { IViewDescriptorService } from 'vs/workbench/common/views'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IRemotePathService } from 'vs/workbench/services/path/common/remotePathService'; const NEW_STYLE_COMPRESS = true; @@ -241,12 +241,12 @@ class RootFolderTreeItem extends BaseTreeItem { class RootTreeItem extends BaseTreeItem { - constructor(private _environmentService: IEnvironmentService, private _contextService: IWorkspaceContextService, private _labelService: ILabelService) { + constructor(private _remotePathService: IRemotePathService, private _contextService: IWorkspaceContextService, private _labelService: ILabelService) { super(undefined, 'Root'); } add(session: IDebugSession): SessionTreeItem { - return this.createIfNeeded(session.getId(), () => new SessionTreeItem(this._labelService, this, session, this._environmentService, this._contextService)); + return this.createIfNeeded(session.getId(), () => new SessionTreeItem(this._labelService, this, session, this._remotePathService, this._contextService)); } find(session: IDebugSession): SessionTreeItem { @@ -262,7 +262,7 @@ class SessionTreeItem extends BaseTreeItem { private _map = new Map(); private _labelService: ILabelService; - constructor(labelService: ILabelService, parent: BaseTreeItem, session: IDebugSession, private _environmentService: IEnvironmentService, private rootProvider: IWorkspaceContextService) { + constructor(labelService: ILabelService, parent: BaseTreeItem, session: IDebugSession, private _remotePathService: IRemotePathService, private rootProvider: IWorkspaceContextService) { super(parent, session.getLabel(), true); this._labelService = labelService; this._session = session; @@ -347,8 +347,9 @@ class SessionTreeItem extends BaseTreeItem { } else { // on unix try to tildify absolute paths path = normalize(path); - if (!isWindows) { - path = tildify(path, this._environmentService.userHome); + const userHome = this._remotePathService.userHomeSync; + if (userHome && !isWindows) { + path = tildify(path, userHome.fsPath); } } } @@ -423,9 +424,9 @@ export class LoadedScriptsView extends ViewPane { @IEditorService private readonly editorService: IEditorService, @IContextKeyService readonly contextKeyService: IContextKeyService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, - @IEnvironmentService private readonly environmentService: IEnvironmentService, @IDebugService private readonly debugService: IDebugService, @ILabelService private readonly labelService: ILabelService, + @IRemotePathService private readonly remotePathService: IRemotePathService, @IOpenerService openerService: IOpenerService, @IThemeService themeService: IThemeService, @ITelemetryService telemetryService: ITelemetryService, @@ -445,7 +446,7 @@ export class LoadedScriptsView extends ViewPane { this.filter = new LoadedScriptsFilter(); - const root = new RootTreeItem(this.environmentService, this.contextService, this.labelService); + const root = new RootTreeItem(this.remotePathService, this.contextService, this.labelService); this.treeLabels = this.instantiationService.createInstance(ResourceLabels, { onDidChangeVisibility: this.onDidChangeBodyVisibility }); this._register(this.treeLabels); diff --git a/src/vs/workbench/contrib/debug/browser/media/repl.css b/src/vs/workbench/contrib/debug/browser/media/repl.css index a914f8d07b..6cd788aee0 100644 --- a/src/vs/workbench/contrib/debug/browser/media/repl.css +++ b/src/vs/workbench/contrib/debug/browser/media/repl.css @@ -79,7 +79,8 @@ } .repl .repl-input-wrapper { - padding-left: 20px; + display: flex; + align-items: center; border-top: 1px solid rgba(128, 128, 128, 0.35); } @@ -92,15 +93,14 @@ border-top-color: #6FC3DF; } -.repl .repl-input-wrapper:before { - left: 8px; - position: absolute; - content: '\276f'; - line-height: 18px; -} - -.monaco-workbench.linux .repl .repl-input-wrapper:before { - font-size: 9px; +.repl .repl-input-wrapper .repl-input-chevron { + padding: 0 6px 0 8px; + width: 16px; + height: 100%; + display: flex; + flex-shrink: 0; + justify-content: center; + font-weight: 600; } /* Output coloring and styling */ diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index 41bc442c79..f2c0a57435 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!vs/workbench/contrib/debug/browser/media/repl'; +import 'vs/css!./media/repl'; import { URI as uri } from 'vs/base/common/uri'; import { IAction, IActionViewItem, Action } from 'vs/base/common/actions'; import * as dom from 'vs/base/browser/dom'; @@ -58,6 +58,7 @@ import { IViewsService, IViewDescriptorService } from 'vs/workbench/common/views import { IOpenerService } from 'vs/platform/opener/common/opener'; import { ReplGroup } from 'vs/workbench/contrib/debug/common/replModel'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { EDITOR_FONT_DEFAULTS, EditorOption } from 'vs/editor/common/config/editorOptions'; const $ = dom.$; @@ -75,7 +76,6 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { _serviceBrand: undefined; private static readonly REFRESH_DELAY = 100; // delay in ms to refresh the repl for new elements to show - private static readonly REPL_INPUT_LINE_HEIGHT = 19; private static readonly URI = uri.parse(`${DEBUG_SCHEME}:replinput`); private history: HistoryNavigator; @@ -284,6 +284,14 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { const lineHeight = debugConsole.lineHeight ? `${debugConsole.lineHeight}px` : '1.4em'; const backgroundColor = this.themeService.getColorTheme().getColor(this.getBackgroundColor()); + this.replInput.updateOptions({ + fontSize, + lineHeight: debugConsole.lineHeight, + fontFamily: debugConsole.fontFamily === 'default' ? EDITOR_FONT_DEFAULTS.fontFamily : debugConsole.fontFamily + }); + + const replInputLineHeight = this.replInput.getOption(EditorOption.lineHeight); + // Set the font size, font family, line height and align the twistie to be centered, and input theme color this.styleElement.innerHTML = ` .repl .repl-tree .expression { @@ -299,12 +307,20 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { background-position-y: calc(100% - ${fontSize * 1.4 / 2 - 8}px); } + .repl .repl-input-wrapper .repl-input-chevron { + line-height: ${replInputLineHeight}px + } + .repl .repl-input-wrapper .monaco-editor .lines-content { background-color: ${backgroundColor}; } `; this.tree.rerender(); + + if (this.dimension) { + this.layoutBody(this.dimension.height, this.dimension.width); + } } } @@ -396,7 +412,7 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { protected layoutBody(height: number, width: number): void { this.dimension = new dom.Dimension(width, height); - const replInputHeight = Repl.REPL_INPUT_LINE_HEIGHT * this.replInputLineCount; + const replInputHeight = Math.min(this.replInput.getContentHeight(), height); if (this.tree) { const lastElementVisible = this.tree.scrollTop + this.tree.renderHeight >= this.tree.scrollHeight; const treeHeight = height - replInputHeight; @@ -408,7 +424,7 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { } this.replInputContainer.style.height = `${replInputHeight}px`; - this.replInput.layout({ width: width - 20, height: replInputHeight }); + this.replInput.layout({ width: width - 30, height: replInputHeight }); } focus(): void { @@ -543,6 +559,7 @@ export class Repl extends ViewPane implements IHistoryNavigationWidget { private createReplInput(container: HTMLElement): void { this.replInputContainer = dom.append(container, $('.repl-input-wrapper')); + dom.append(this.replInputContainer, $('.repl-input-chevron.codicon.codicon-chevron-right')); const { scopedContextKeyService, historyNavigationEnablement } = createAndBindHistoryNavigationWidgetScopedContextKeyService(this.contextKeyService, { target: this.replInputContainer, historyNavigator: this }); this.historyNavigationEnablement = historyNavigationEnablement; diff --git a/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts b/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts index 4a5add60c8..97c9e5f98c 100644 --- a/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts +++ b/src/vs/workbench/contrib/debug/test/browser/breakpoints.test.ts @@ -98,10 +98,10 @@ suite('Debug - Breakpoints', () => { const modelUri1 = uri.file('/myfolder/my file first.js'); const modelUri2 = uri.file('/secondfolder/second/second file.js'); addBreakpointsAndCheckEvents(model, modelUri1, [{ lineNumber: 5, enabled: true }, { lineNumber: 10, enabled: false }]); - assert.equal(getExpandedBodySize(model), 44); + assert.equal(getExpandedBodySize(model, 9), 44); addBreakpointsAndCheckEvents(model, modelUri2, [{ lineNumber: 1, enabled: true }, { lineNumber: 2, enabled: true }, { lineNumber: 3, enabled: false }]); - assert.equal(getExpandedBodySize(model), 110); + assert.equal(getExpandedBodySize(model, 9), 110); assert.equal(model.getBreakpoints().length, 5); assert.equal(model.getBreakpoints({ uri: modelUri1 }).length, 2); @@ -135,7 +135,7 @@ suite('Debug - Breakpoints', () => { assert.equal(bp.enabled, true); model.removeBreakpoints(model.getBreakpoints({ uri: modelUri1 })); - assert.equal(getExpandedBodySize(model), 66); + assert.equal(getExpandedBodySize(model, 9), 66); assert.equal(model.getBreakpoints().length, 3); }); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index c0405b4102..ff55d2f8d7 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -19,7 +19,7 @@ import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { IExtensionTipsService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IExtensionRecommendationsService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IExtensionManifest, IKeyBinding, IView, IViewContainer, ExtensionType } from 'vs/platform/extensions/common/extensions'; import { ResolvedKeybinding, KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { ExtensionsInput } from 'vs/workbench/contrib/extensions/common/extensionsInput'; @@ -150,6 +150,7 @@ interface IExtensionEditorTemplate { preview: HTMLElement; builtin: HTMLElement; license: HTMLElement; + version: HTMLElement; publisher: HTMLElement; // installCount: HTMLElement; // {{SQL CARBON EDIT}} remove install count widget // rating: HTMLElement; // {{SQL CARBON EDIT}} remove rating widget @@ -189,7 +190,7 @@ export class ExtensionEditor extends BaseEditor { @IKeybindingService private readonly keybindingService: IKeybindingService, @INotificationService private readonly notificationService: INotificationService, @IOpenerService private readonly openerService: IOpenerService, - @IExtensionTipsService private readonly extensionTipsService: IExtensionTipsService, + @IExtensionRecommendationsService private readonly extensionRecommendationsService: IExtensionRecommendationsService, @IStorageService storageService: IStorageService, @IExtensionService private readonly extensionService: IExtensionService, @IWorkbenchThemeService private readonly workbenchThemeService: IWorkbenchThemeService, @@ -241,6 +242,9 @@ export class ExtensionEditor extends BaseEditor { license.style.display = 'none'; license.tabIndex = 0; + const version = append(subtitle, $('span.version')); + version.textContent = localize('version', 'Version'); + const description = append(details, $('.description')); const extensionActions = append(details, $('.actions')); @@ -282,6 +286,7 @@ export class ExtensionEditor extends BaseEditor { icon, iconContainer, identifier, + version, ignoreActionbar, // installCount, // {{SQL CARBON EDIT}} remove install count widget license, @@ -341,9 +346,10 @@ export class ExtensionEditor extends BaseEditor { template.builtin.style.display = extension.type === ExtensionType.System ? 'inherit' : 'none'; template.publisher.textContent = extension.publisherDisplayName; + template.version.textContent = extension.version; template.description.textContent = extension.description; - const extRecommendations = this.extensionTipsService.getAllRecommendationsWithReason(); + const extRecommendations = this.extensionRecommendationsService.getAllRecommendationsWithReason(); let recommendationsData = {}; if (extRecommendations[extension.identifier.id.toLowerCase()]) { recommendationsData = { recommendationReason: extRecommendations[extension.identifier.id.toLowerCase()].reasonId }; @@ -488,12 +494,12 @@ export class ExtensionEditor extends BaseEditor { this.transientDisposables.add(ignoreAction); this.transientDisposables.add(undoIgnoreAction); - const extRecommendations = this.extensionTipsService.getAllRecommendationsWithReason(); + const extRecommendations = this.extensionRecommendationsService.getAllRecommendationsWithReason(); if (extRecommendations[extension.identifier.id.toLowerCase()]) { ignoreAction.enabled = true; template.subtext.textContent = extRecommendations[extension.identifier.id.toLowerCase()].reasonText; show(template.subtextContainer); - } else if (this.extensionTipsService.getAllIgnoredRecommendations().global.indexOf(extension.identifier.id.toLowerCase()) !== -1) { + } else if (this.extensionRecommendationsService.getAllIgnoredRecommendations().global.indexOf(extension.identifier.id.toLowerCase()) !== -1) { undoIgnoreAction.enabled = true; template.subtext.textContent = localize('recommendationHasBeenIgnored', "You have chosen not to receive recommendations for this extension."); show(template.subtextContainer); @@ -502,11 +508,11 @@ export class ExtensionEditor extends BaseEditor { template.subtext.textContent = ''; } - this.extensionTipsService.onRecommendationChange(change => { + this.extensionRecommendationsService.onRecommendationChange(change => { if (change.extensionId.toLowerCase() === extension.identifier.id.toLowerCase()) { if (change.isRecommended) { undoIgnoreAction.enabled = false; - const extRecommendations = this.extensionTipsService.getAllRecommendationsWithReason(); + const extRecommendations = this.extensionRecommendationsService.getAllRecommendationsWithReason(); if (extRecommendations[extension.identifier.id.toLowerCase()]) { ignoreAction.enabled = true; template.subtext.textContent = extRecommendations[extension.identifier.id.toLowerCase()].reasonText; @@ -1189,7 +1195,7 @@ export class ExtensionEditor extends BaseEditor { } const details = $('details', { open: true, ontoggle: onDetailsToggle }, - $('summary', { tabindex: '0' }, localize('iconThemes', "Icon Themes ({0})", contrib.length)), + $('summary', { tabindex: '0' }, localize('iconThemes', "File Icon Themes ({0})", contrib.length)), $('ul', undefined, ...contrib.map(theme => $('li', undefined, theme.label))) ); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts similarity index 55% rename from src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts rename to src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts index 98be8e2b87..4caa4af433 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionRecommendationsService.ts @@ -4,14 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; -import { join, basename } from 'vs/base/common/path'; -import { forEach } from 'vs/base/common/collections'; +import { forEach, IStringDictionary } from 'vs/base/common/collections'; import { Disposable } from 'vs/base/common/lifecycle'; // import { match } from 'vs/base/common/glob'; import * as json from 'vs/base/common/json'; -import { IExtensionManagementService, IExtensionGalleryService, EXTENSION_IDENTIFIER_PATTERN, InstallOperation, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IExtensionTipsService, ExtensionRecommendationReason, IExtensionsConfigContent, RecommendationChangeNotification, IExtensionRecommendation, ExtensionRecommendationSource, IWorkbenchExtensionEnablementService, EnablementState } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; -// import { IModelService } from 'vs/editor/common/services/modelService'; +import { IExtensionManagementService, IExtensionGalleryService, EXTENSION_IDENTIFIER_PATTERN, InstallOperation, ILocalExtension, IExecutableBasedExtensionTip, IExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionManagement'; +import { IExtensionRecommendationsService, ExtensionRecommendationReason, IExtensionsConfigContent, RecommendationChangeNotification, IExtensionRecommendation, ExtensionRecommendationSource, IWorkbenchExtensionEnablementService, EnablementState } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IModelService } from 'vs/editor/common/services/modelService'; // import { ITextModel } from 'vs/editor/common/model'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -23,43 +22,65 @@ import * as Constants from 'sql/workbench/contrib/extensions/common/constants'; import Severity from 'vs/base/common/severity'; import { IWorkspaceContextService, IWorkspaceFolder, IWorkspace, IWorkspaceFoldersChangeEvent, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IFileService } from 'vs/platform/files/common/files'; -import { IExtensionsConfiguration, ConfigurationKey, ShowRecommendationsOnlyOnDemandKey/*, IExtensionsViewPaneContainer, IExtensionsWorkbenchService*/, EXTENSIONS_CONFIG } from 'vs/workbench/contrib/extensions/common/extensions'; +import { IExtensionsConfiguration, ConfigurationKey, IExtensionsWorkbenchService, EXTENSIONS_CONFIG, ShowRecommendationsOnlyOnDemandKey } from 'vs/workbench/contrib/extensions/common/extensions'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { flatten, distinct, shuffle, coalesce, firstIndex } from 'vs/base/common/arrays'; +import { flatten, distinct, coalesce, isNonEmptyArray, shuffle } from 'vs/base/common/arrays'; // import { guessMimeTypes, MIME_UNKNOWN } from 'vs/base/common/mime'; -// import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { IRequestService, asJson } from 'vs/platform/request/common/request'; -import { isNumber } from 'vs/base/common/types'; -// import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { Emitter, Event } from 'vs/base/common/event'; import { assign } from 'vs/base/common/objects'; -import { URI } from 'vs/base/common/uri'; +// import { URI } from 'vs/base/common/uri'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; -// import { IExperimentService, ExperimentActionType, ExperimentState } from 'vs/workbench/contrib/experiments/common/experimentService'; +import { IExperimentService } from 'vs/workbench/contrib/experiments/common/experimentService'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { ExtensionType, ExtensionsPolicy, ExtensionsPolicyKey } from 'vs/platform/extensions/common/extensions'; // {{SQL CARBON EDIT}} +import { ExtensionType, ExtensionsPolicyKey, ExtensionsPolicy } from 'vs/platform/extensions/common/extensions'; // import { extname } from 'vs/base/common/resources'; -import { IExeBasedExtensionTip, IProductService } from 'vs/platform/product/common/productService'; -import { timeout } from 'vs/base/common/async'; -import { IWorkspaceTagsService } from 'vs/workbench/contrib/tags/common/workspaceTags'; -import { /*setImmediate,*/ isWeb } from 'vs/base/common/platform'; -import { platform, env as processEnv } from 'vs/base/common/process'; -import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys'; // {{SQL CARBON EDIT}} -import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry'; +import { IProductService } from 'vs/platform/product/common/productService'; +// import { setImmediate } from 'vs/base/common/platform'; // import { Schemas } from 'vs/base/common/network'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry'; +import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys'; +import { timeout } from 'vs/base/common/async'; +import { LifecyclePhase, ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { IWorkspaceTagsService } from 'vs/workbench/contrib/tags/common/workspaceTags'; +import { isNumber } from 'vs/base/common/types'; +import { basename } from 'vs/base/common/path'; +type ExtensionRecommendationsNotificationClassification = { + userReaction: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; + extensionId: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' }; +}; + +/*type FileExtensionSuggestionClassification = { + userReaction: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; + fileExtension: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' }; +};*/ + +type ExtensionWorkspaceRecommendationsNotificationClassification = { + userReaction: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; +}; + +type DynamicWorkspaceRecommendationsClassification = { + count: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; + cache: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; +}; + +type ExeExtensionRecommendationsClassification = { + extensionId: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' }; + exeName: { classification: 'PublicNonPersonalData', purpose: 'FeatureInsight' }; +}; + +type IStoredDynamicWorkspaceRecommendations = { recommendations: string[], timestamp: number }; + +const dynamicWorkspaceRecommendationsStorageKey = 'extensionsAssistant/dynamicWorkspaceRecommendations'; const milliSecondsInADay = 1000 * 60 * 60 * 24; const choiceNever = localize('neverShowAgain', "Don't Show Again"); -// const searchMarketplace = localize('searchMarketplace', "Search Marketplace"); {{SQL CARBON EDIT}} comment out for no unused -// const processedFileExtensions: string[] = []; {{SQL CARBON EDIT}} comment out for no unused - -interface IDynamicWorkspaceRecommendations { - remoteSet: string[]; - recommendations: string[]; -} +// const searchMarketplace = localize('searchMarketplace', "Search Marketplace"); +// const processedFileExtensions: string[] = []; function caseInsensitiveGet(obj: { [key: string]: T }, key: string): T | undefined { if (!obj) { @@ -73,32 +94,33 @@ function caseInsensitiveGet(obj: { [key: string]: T }, key: string): T | unde return undefined; } -export class ExtensionTipsService extends Disposable implements IExtensionTipsService { +export class ExtensionRecommendationsService extends Disposable implements IExtensionRecommendationsService { _serviceBrand: undefined; - private _fileBasedRecommendations: { [id: string]: { recommendedTime: number, sources: ExtensionRecommendationSource[] }; } = Object.create(null); - private _recommendations: string[] = []; // {{SQL CARBON EDIT}} - private _exeBasedRecommendations: { [id: string]: IExeBasedExtensionTip; } = Object.create(null); - private _importantExeBasedRecommendations: { [id: string]: IExeBasedExtensionTip; } = Object.create(null); - private _availableRecommendations: { [pattern: string]: string[] } = Object.create(null); - private _allWorkspaceRecommendedExtensions: IExtensionRecommendation[] = []; - private _dynamicWorkspaceRecommendations: string[] = []; - private _experimentalRecommendations: { [id: string]: string } = Object.create(null); - private _allIgnoredRecommendations: string[] = []; - private _globallyIgnoredRecommendations: string[] = []; - private _workspaceIgnoredRecommendations: string[] = []; - private _extensionsRecommendationsUrl: string | undefined; + // Recommendations + private fileBasedRecommendations: { [id: string]: { recommendedTime: number, sources: ExtensionRecommendationSource[] }; } = Object.create(null); + private availableRecommendations: { [pattern: string]: string[] } = Object.create(null); + private allWorkspaceRecommendedExtensions: IExtensionRecommendation[] = []; + private experimentalRecommendations: { [id: string]: string } = Object.create(null); + private exeBasedRecommendations: { [id: string]: IExecutableBasedExtensionTip; } = Object.create(null); + private dynamicWorkspaceRecommendations: string[] = []; + private recommendations: { [id: string]: string } = Object.create(null); // {{SQL CARBON EDIT}} add our recommendations + + // Ignored Recommendations + private allIgnoredRecommendations: string[] = []; + private globallyIgnoredRecommendations: string[] = []; + private workspaceIgnoredRecommendations: string[] = []; + public loadWorkspaceConfigPromise: Promise; - private proactiveRecommendationsFetched: boolean = false; + private sessionSeed: number; private readonly _onRecommendationChange = this._register(new Emitter()); onRecommendationChange: Event = this._onRecommendationChange.event; - private sessionSeed: number; constructor( - @IExtensionGalleryService private readonly _galleryService: IExtensionGalleryService, - // @IModelService private readonly _modelService: IModelService, {{SQL CARBON EDIT}} comment out for no unused + @IExtensionGalleryService private readonly galleryService: IExtensionGalleryService, + @IModelService modelService: IModelService, @IStorageService private readonly storageService: IStorageService, @IExtensionManagementService private readonly extensionsService: IExtensionManagementService, @IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService, @@ -107,51 +129,51 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @IConfigurationService private readonly configurationService: IConfigurationService, @ITelemetryService private readonly telemetryService: ITelemetryService, - @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, - // @IExtensionService private readonly extensionService: IExtensionService, {{SQL CARBON EDIT}} comment out for no unused - @IRequestService private readonly requestService: IRequestService, - // @IViewletService private readonly viewletService: IViewletService, {{SQL CARBON EDIT}} comment out for no unused + @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IExtensionService extensionService: IExtensionService, + @IViewletService viewletService: IViewletService, @INotificationService private readonly notificationService: INotificationService, @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, - @IAdsTelemetryService private readonly adsTelemetryService: IAdsTelemetryService, // {{SQL CARBON EDIT}} - // @IExtensionsWorkbenchService private readonly extensionWorkbenchService: IExtensionsWorkbenchService, {{SQL CARBON EDIT}} comment out for no unused - // @IExperimentService private readonly experimentService: IExperimentService, {{SQL CARBON EDIT}} comment out for no unused + @IExtensionsWorkbenchService extensionWorkbenchService: IExtensionsWorkbenchService, + @IExperimentService experimentService: IExperimentService, + @IProductService private readonly productService: IProductService, @IWorkspaceTagsService private readonly workspaceTagsService: IWorkspaceTagsService, - @IProductService private readonly productService: IProductService + @IExtensionTipsService private readonly extensionTipsService: IExtensionTipsService, + @ILifecycleService private readonly lifecycleService: ILifecycleService, + @IAdsTelemetryService private readonly adsTelemetryService: IAdsTelemetryService ) { super(); - const extensionPolicy = this.configurationService.getValue(ExtensionsPolicyKey); // {{SQL CARBON EDIT}} - if (!this.isEnabled() || extensionPolicy === ExtensionsPolicy.allowNone) { + if (!this.isEnabled()) { this.sessionSeed = 0; this.loadWorkspaceConfigPromise = Promise.resolve(); return; } - if (this.productService.extensionsGallery && this.productService.extensionsGallery.recommendationsUrl) { - this._extensionsRecommendationsUrl = this.productService.extensionsGallery.recommendationsUrl; - } - this.sessionSeed = +new Date(); - let globallyIgnored = JSON.parse(this.storageService.get('extensionsAssistant/ignored_recommendations', StorageScope.GLOBAL, '[]')); - this._globallyIgnoredRecommendations = globallyIgnored.map(id => id.toLowerCase()); + const globallyIgnored = JSON.parse(this.storageService.get('extensionsAssistant/ignored_recommendations', StorageScope.GLOBAL, '[]')); + this.globallyIgnoredRecommendations = globallyIgnored.map(id => id.toLowerCase()); - this.fetchCachedDynamicWorkspaceRecommendations(); this.fetchFileBasedRecommendations(); this.fetchExperimentalRecommendations(); + + /* 3s has come out to be the good number to fetch and prompt important exe based recommendations */ + /* Fetch important exe based recommendations always for reporting telemetry */ + timeout(3000).then(() => this.fetchAndPromptImportantExeBasedRecommendations()); + if (!this.configurationService.getValue(ShowRecommendationsOnlyOnDemandKey)) { - this.fetchProactiveRecommendations(true); + this.lifecycleService.when(LifecyclePhase.Eventually).then(() => this.fetchProactiveRecommendations()); } - this.loadWorkspaceConfigPromise = this.getWorkspaceRecommendations().then(); // {{SQL CARBON EDIT}} disable workspace recommandations + this.loadWorkspaceConfigPromise = this.getWorkspaceRecommendations().then(() => { + // this.promptWorkspaceRecommendations(); // {{SQL CARBON EDIT}} disable workspace recommandations + // this._register(this.modelService.onModelAdded(this.promptFiletypeBasedRecommendations, this)); + // this.modelService.getModels().forEach(model => this.promptFiletypeBasedRecommendations(model)); + }); this._register(this.contextService.onDidChangeWorkspaceFolders(e => this.onWorkspaceFoldersChanged(e))); - this._register(this.configurationService.onDidChangeConfiguration(e => { - if (!this.proactiveRecommendationsFetched && !this.configurationService.getValue(ShowRecommendationsOnlyOnDemandKey)) { - this.fetchProactiveRecommendations(); - } - })); + this._register(this.extensionManagementService.onDidInstallExtension(e => { if (e.gallery && e.operation === InstallOperation.Install) { const extRecommendations = this.getAllRecommendationsWithReason() || {}; @@ -172,54 +194,60 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe } private isEnabled(): boolean { - return this._galleryService.isEnabled() && !this.environmentService.extensionDevelopmentLocationURI; + return this.galleryService.isEnabled() && !this.environmentService.extensionDevelopmentLocationURI && this.configurationService.getValue(ExtensionsPolicyKey) !== ExtensionsPolicy.allowNone; } getAllRecommendationsWithReason(): { [id: string]: { reasonId: ExtensionRecommendationReason, reasonText: string }; } { - let output: { [id: string]: { reasonId: ExtensionRecommendationReason, reasonText: string }; } = Object.create(null); + /* Trigger fetching proactive recommendations */ + this.fetchProactiveRecommendations(); - if (!this.proactiveRecommendationsFetched) { - return output; - } - - forEach(this._experimentalRecommendations, entry => output[entry.key.toLowerCase()] = { - reasonId: ExtensionRecommendationReason.Experimental, - reasonText: entry.value - }); + const output: { [id: string]: { reasonId: ExtensionRecommendationReason, reasonText: string }; } = Object.create(null); if (this.contextService.getWorkspace().folders && this.contextService.getWorkspace().folders.length === 1) { const currentRepo = this.contextService.getWorkspace().folders[0].name; - this._dynamicWorkspaceRecommendations.forEach(id => output[id.toLowerCase()] = { + this.dynamicWorkspaceRecommendations.forEach(id => output[id.toLowerCase()] = { reasonId: ExtensionRecommendationReason.DynamicWorkspace, reasonText: localize('dynamicWorkspaceRecommendation', "This extension may interest you because it's popular among users of the {0} repository.", currentRepo) }); } - forEach(this._exeBasedRecommendations, entry => output[entry.key.toLowerCase()] = { + forEach(this.exeBasedRecommendations, entry => output[entry.key.toLowerCase()] = { reasonId: ExtensionRecommendationReason.Executable, reasonText: localize('exeBasedRecommendation', "This extension is recommended because you have {0} installed.", entry.value.friendlyName) }); - forEach(this._fileBasedRecommendations, entry => output[entry.key.toLowerCase()] = { + for (const id of Object.keys(output)) { + if (!this.isExtensionAllowedToBeRecommended(id)) { + delete output[id]; + } + } + + forEach(this.experimentalRecommendations, entry => output[entry.key.toLowerCase()] = { + reasonId: ExtensionRecommendationReason.Experimental, + reasonText: entry.value + }); + + forEach(this.fileBasedRecommendations, entry => output[entry.key.toLowerCase()] = { reasonId: ExtensionRecommendationReason.File, reasonText: localize('fileBasedRecommendation', "This extension is recommended based on the files you recently opened.") }); - - this._allWorkspaceRecommendedExtensions.forEach(({ extensionId }) => output[extensionId.toLowerCase()] = { + this.allWorkspaceRecommendedExtensions.forEach(({ extensionId }) => output[extensionId.toLowerCase()] = { reasonId: ExtensionRecommendationReason.Workspace, reasonText: localize('workspaceRecommendation', "This extension is recommended by users of the current workspace.") }); // {{SQL CARBON EDIT}} - this._recommendations.forEach(x => output[x.toLowerCase()] = { + forEach(this.recommendations, entry => output[entry.key.toLowerCase()] = { reasonId: ExtensionRecommendationReason.Executable, reasonText: localize('defaultRecommendations', "This extension is recommended by Azure Data Studio.") }); - for (const id of this._allIgnoredRecommendations) { - delete output[id]; + for (const id of Object.keys(output)) { + if (!this.isExtensionAllowedToBeRecommended(id)) { + delete output[id]; + } } return output; @@ -227,8 +255,8 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe getAllIgnoredRecommendations(): { global: string[], workspace: string[] } { return { - global: this._globallyIgnoredRecommendations, - workspace: this._workspaceIgnoredRecommendations + global: this.globallyIgnoredRecommendations, + workspace: this.workspaceIgnoredRecommendations }; } @@ -247,12 +275,12 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe } } - this._globallyIgnoredRecommendations = shouldIgnore ? - distinct([...this._globallyIgnoredRecommendations, lowerId].map(id => id.toLowerCase())) : - this._globallyIgnoredRecommendations.filter(id => id !== lowerId); + this.globallyIgnoredRecommendations = shouldIgnore ? + distinct([...this.globallyIgnoredRecommendations, lowerId].map(id => id.toLowerCase())) : + this.globallyIgnoredRecommendations.filter(id => id !== lowerId); - this.storageService.store('extensionsAssistant/ignored_recommendations', JSON.stringify(this._globallyIgnoredRecommendations), StorageScope.GLOBAL); - this._allIgnoredRecommendations = distinct([...this._globallyIgnoredRecommendations, ...this._workspaceIgnoredRecommendations]); + this.storageService.store('extensionsAssistant/ignored_recommendations', JSON.stringify(this.globallyIgnoredRecommendations), StorageScope.GLOBAL); + this.allIgnoredRecommendations = distinct([...this.globallyIgnoredRecommendations, ...this.workspaceIgnoredRecommendations]); this._onRecommendationChange.fire({ extensionId: extensionId, isRecommended: !shouldIgnore }); } @@ -268,7 +296,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe getWorkspaceRecommendations(): Promise { if (!this.isEnabled()) { return Promise.resolve([]); } return this.fetchWorkspaceRecommendations() - .then(() => this._allWorkspaceRecommendedExtensions.filter(rec => this.isExtensionAllowedToBeRecommended(rec.extensionId))); + .then(() => this.allWorkspaceRecommendedExtensions.filter(rec => this.isExtensionAllowedToBeRecommended(rec.extensionId))); } /** @@ -288,15 +316,15 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe const seenUnWantedRecommendations: { [id: string]: boolean } = {}; - this._allWorkspaceRecommendedExtensions = []; - this._workspaceIgnoredRecommendations = []; + this.allWorkspaceRecommendedExtensions = []; + this.workspaceIgnoredRecommendations = []; for (const contentsBySource of result) { if (contentsBySource.contents.unwantedRecommendations) { for (const r of contentsBySource.contents.unwantedRecommendations) { const unwantedRecommendation = r.toLowerCase(); if (!seenUnWantedRecommendations[unwantedRecommendation] && invalidExtensions.indexOf(unwantedRecommendation) === -1) { - this._workspaceIgnoredRecommendations.push(unwantedRecommendation); + this.workspaceIgnoredRecommendations.push(unwantedRecommendation); seenUnWantedRecommendations[unwantedRecommendation] = true; } } @@ -306,10 +334,10 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe for (const r of contentsBySource.contents.recommendations) { const extensionId = r.toLowerCase(); if (invalidExtensions.indexOf(extensionId) === -1) { - let recommendation = this._allWorkspaceRecommendedExtensions.filter(r => r.extensionId === extensionId)[0]; + let recommendation = this.allWorkspaceRecommendedExtensions.filter(r => r.extensionId === extensionId)[0]; if (!recommendation) { recommendation = { extensionId, sources: [] }; - this._allWorkspaceRecommendedExtensions.push(recommendation); + this.allWorkspaceRecommendedExtensions.push(recommendation); } if (recommendation.sources.indexOf(contentsBySource.source) === -1) { recommendation.sources.push(contentsBySource.source); @@ -318,7 +346,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe } } } - this._allIgnoredRecommendations = distinct([...this._globallyIgnoredRecommendations, ...this._workspaceIgnoredRecommendations]); + this.allIgnoredRecommendations = distinct([...this.globallyIgnoredRecommendations, ...this.workspaceIgnoredRecommendations]); })); } @@ -389,7 +417,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe if (filteredWanted.length) { try { - let validRecommendations = (await this._galleryService.query({ names: filteredWanted, pageSize: filteredWanted.length }, CancellationToken.None)).firstPage + let validRecommendations = (await this.galleryService.query({ names: filteredWanted, pageSize: filteredWanted.length }, CancellationToken.None)).firstPage .map(extension => extension.identifier.id.toLowerCase()); if (validRecommendations.length !== filteredWanted.length) { @@ -409,7 +437,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe private onWorkspaceFoldersChanged(event: IWorkspaceFoldersChangeEvent): void { if (event.added.length) { - const oldWorkspaceRecommended = this._allWorkspaceRecommendedExtensions; + const oldWorkspaceRecommended = this.allWorkspaceRecommendedExtensions; this.getWorkspaceRecommendations() .then(currentWorkspaceRecommended => { // Suggest only if at least one of the newly added recommendations was not suggested before @@ -418,7 +446,6 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe } }); } - this._dynamicWorkspaceRecommendations = []; } /** @@ -426,13 +453,9 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe */ private promptWorkspaceRecommendations(): void { const storageKey = 'extensionsAssistant/workspaceRecommendationsIgnore'; - const config = this.configurationService.getValue(ConfigurationKey); - const filteredRecs = this._allWorkspaceRecommendedExtensions.filter(rec => this.isExtensionAllowedToBeRecommended(rec.extensionId)); + const filteredRecs = this.allWorkspaceRecommendedExtensions.filter(rec => this.isExtensionAllowedToBeRecommended(rec.extensionId)); - if (filteredRecs.length === 0 - || config.ignoreRecommendations - || config.showRecommendationsOnlyOnDemand - || this.storageService.getBoolean(storageKey, StorageScope.WORKSPACE, false)) { + if (filteredRecs.length === 0 || this.hasToIgnoreWorkspaceRecommendationNotifications()) { return; } @@ -451,12 +474,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe [{ label: localize('installAll', "Install All"), run: () => { - /* __GDPR__ - "extensionWorkspaceRecommendations:popup" : { - "userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - this.telemetryService.publicLog('extensionWorkspaceRecommendations:popup', { userReaction: 'install' }); + this.telemetryService.publicLog2<{ userReaction: string }, ExtensionWorkspaceRecommendationsNotificationClassification>('extensionWorkspaceRecommendations:popup', { userReaction: 'install' }); const installAllAction = this.instantiationService.createInstance(InstallWorkspaceRecommendedExtensionsAction, InstallWorkspaceRecommendedExtensionsAction.ID, localize('installAll', "Install All"), recommendations); installAllAction.run(); @@ -467,12 +485,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe }, { label: localize('showRecommendations', "Show Recommendations"), run: () => { - /* __GDPR__ - "extensionWorkspaceRecommendations:popup" : { - "userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - this.telemetryService.publicLog('extensionWorkspaceRecommendations:popup', { userReaction: 'show' }); + this.telemetryService.publicLog2<{ userReaction: string }, ExtensionWorkspaceRecommendationsNotificationClassification>('extensionWorkspaceRecommendations:popup', { userReaction: 'show' }); const showAction = this.instantiationService.createInstance(ShowRecommendedExtensionsAction, ShowRecommendedExtensionsAction.ID, localize('showRecommendations', "Show Recommendations")); showAction.run(); @@ -484,12 +497,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe label: choiceNever, isSecondary: true, run: () => { - /* __GDPR__ - "extensionWorkspaceRecommendations:popup" : { - "userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - this.telemetryService.publicLog('extensionWorkspaceRecommendations:popup', { userReaction: 'neverShowAgain' }); + this.telemetryService.publicLog2<{ userReaction: string }, ExtensionWorkspaceRecommendationsNotificationClassification>('extensionWorkspaceRecommendations:popup', { userReaction: 'neverShowAgain' }); this.storageService.store(storageKey, true, StorageScope.WORKSPACE); c(undefined); @@ -498,13 +506,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe { sticky: true, onCancel: () => { - /* __GDPR__ - "extensionWorkspaceRecommendations:popup" : { - "userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" } - } - */ - this.telemetryService.publicLog('extensionWorkspaceRecommendations:popup', { userReaction: 'cancelled' }); - + this.telemetryService.publicLog2<{ userReaction: string }, ExtensionWorkspaceRecommendationsNotificationClassification>('extensionWorkspaceRecommendations:popup', { userReaction: 'cancelled' }); c(undefined); } } @@ -515,124 +517,12 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe //#endregion - //#region important exe based extension - - private async promptForImportantExeBasedExtension(): Promise { - - let recommendationsToSuggest = Object.keys(this._importantExeBasedRecommendations); - - const installed = await this.extensionManagementService.getInstalled(ExtensionType.User); - recommendationsToSuggest = this.filterInstalled(recommendationsToSuggest, installed, (extensionId) => { - const tip = this._importantExeBasedRecommendations[extensionId]; - - /* __GDPR__ - "exeExtensionRecommendations:alreadyInstalled" : { - "extensionId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" }, - "exeName": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" } - } - */ - this.telemetryService.publicLog('exeExtensionRecommendations:alreadyInstalled', { extensionId, exeName: basename(tip.windowsPath!) }); - - }); - - if (recommendationsToSuggest.length === 0) { - return false; - } - - const storageKey = 'extensionsAssistant/workspaceRecommendationsIgnore'; - const config = this.configurationService.getValue(ConfigurationKey); - - if (config.ignoreRecommendations - || config.showRecommendationsOnlyOnDemand - || this.storageService.getBoolean(storageKey, StorageScope.WORKSPACE, false)) { - return false; - } - - recommendationsToSuggest = this.filterIgnoredOrNotAllowed(recommendationsToSuggest); - if (recommendationsToSuggest.length === 0) { - return false; - } - - const extensionId = recommendationsToSuggest[0]; - const tip = this._importantExeBasedRecommendations[extensionId]; - const message = localize('exeRecommended', "The '{0}' extension is recommended as you have {1} installed on your system.", tip.friendlyName!, tip.exeFriendlyName || basename(tip.windowsPath!)); - - this.notificationService.prompt(Severity.Info, message, - [{ - label: localize('install', 'Install'), - run: () => { - /* __GDPR__ - "exeExtensionRecommendations:popup" : { - "userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "extensionId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" } - } - */ - this.telemetryService.publicLog('exeExtensionRecommendations:popup', { userReaction: 'install', extensionId }); - this.instantiationService.createInstance(InstallRecommendedExtensionAction, extensionId).run(); - } - }, { - label: localize('showRecommendations', "Show Recommendations"), - run: () => { - /* __GDPR__ - "exeExtensionRecommendations:popup" : { - "userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "extensionId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" } - } - */ - this.telemetryService.publicLog('exeExtensionRecommendations:popup', { userReaction: 'show', extensionId }); - - const recommendationsAction = this.instantiationService.createInstance(ShowRecommendedExtensionsAction, ShowRecommendedExtensionsAction.ID, localize('showRecommendations', "Show Recommendations")); - recommendationsAction.run(); - recommendationsAction.dispose(); - } - }, { - label: choiceNever, - isSecondary: true, - run: () => { - this.addToImportantRecommendationsIgnore(extensionId); - /* __GDPR__ - "exeExtensionRecommendations:popup" : { - "userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "extensionId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" } - } - */ - this.telemetryService.publicLog('exeExtensionRecommendations:popup', { userReaction: 'neverShowAgain', extensionId }); - this.notificationService.prompt( - Severity.Info, - localize('ignoreExtensionRecommendations', "Do you want to ignore all extension recommendations?"), - [{ - label: localize('ignoreAll', "Yes, Ignore All"), - run: () => this.setIgnoreRecommendationsConfig(true) - }, { - label: localize('no', "No"), - run: () => this.setIgnoreRecommendationsConfig(false) - }] - ); - } - }], - { - sticky: true, - onCancel: () => { - /* __GDPR__ - "exeExtensionRecommendations:popup" : { - "userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "extensionId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" } - } - */ - this.telemetryService.publicLog('exeExtensionRecommendations:popup', { userReaction: 'cancelled', extensionId }); - } - } - ); - - return true; - } - //#region fileBasedRecommendations getFileBasedRecommendations(): IExtensionRecommendation[] { - return Object.keys(this._fileBasedRecommendations) + return Object.keys(this.fileBasedRecommendations) .sort((a, b) => { - if (this._fileBasedRecommendations[a].recommendedTime === this._fileBasedRecommendations[b].recommendedTime) { + if (this.fileBasedRecommendations[a].recommendedTime === this.fileBasedRecommendations[b].recommendedTime) { if (!this.productService.extensionImportantTips || caseInsensitiveGet(this.productService.extensionImportantTips, a)) { return -1; } @@ -640,10 +530,10 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe return 1; } } - return this._fileBasedRecommendations[a].recommendedTime > this._fileBasedRecommendations[b].recommendedTime ? -1 : 1; + return this.fileBasedRecommendations[a].recommendedTime > this.fileBasedRecommendations[b].recommendedTime ? -1 : 1; }) .filter(extensionId => this.isExtensionAllowedToBeRecommended(extensionId)) - .map(extensionId => ({ extensionId, sources: this._fileBasedRecommendations[extensionId].sources })); + .map(extensionId => ({ extensionId, sources: this.fileBasedRecommendations[extensionId].sources })); } /** @@ -652,19 +542,17 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe */ private fetchFileBasedRecommendations() { const extensionTips = this.productService.extensionTips; - // {{SQL CARBON EDIT}} - this._recommendations = this.productService.recommendedExtensions; if (!extensionTips) { return; } // group ids by pattern, like {**/*.md} -> [ext.foo1, ext.bar2] - this._availableRecommendations = Object.create(null); + this.availableRecommendations = Object.create(null); forEach(extensionTips, entry => { let { key: id, value: pattern } = entry; - let ids = this._availableRecommendations[pattern]; + let ids = this.availableRecommendations[pattern]; if (!ids) { - this._availableRecommendations[pattern] = [id.toLowerCase()]; + this.availableRecommendations[pattern] = [id.toLowerCase()]; } else { ids.push(id.toLowerCase()); } @@ -674,16 +562,16 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe forEach(this.productService.extensionImportantTips, entry => { let { key: id, value } = entry; const { pattern } = value; - let ids = this._availableRecommendations[pattern]; + let ids = this.availableRecommendations[pattern]; if (!ids) { - this._availableRecommendations[pattern] = [id.toLowerCase()]; + this.availableRecommendations[pattern] = [id.toLowerCase()]; } else { ids.push(id.toLowerCase()); } }); } - const allRecommendations: string[] = flatten((Object.keys(this._availableRecommendations).map(key => this._availableRecommendations[key]))); + const allRecommendations: string[] = flatten((Object.keys(this.availableRecommendations).map(key => this.availableRecommendations[key]))); // retrieve ids of previous recommendations const storedRecommendationsJson = JSON.parse(this.storageService.get('extensionsAssistant/recommendations', StorageScope.GLOBAL, '[]')); @@ -691,7 +579,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe if (Array.isArray(storedRecommendationsJson)) { for (let id of storedRecommendationsJson) { if (allRecommendations.indexOf(id) > -1) { - this._fileBasedRecommendations[id.toLowerCase()] = { recommendedTime: Date.now(), sources: ['cached'] }; + this.fileBasedRecommendations[id.toLowerCase()] = { recommendedTime: Date.now(), sources: ['cached'] }; } } } else { @@ -700,7 +588,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe if (typeof entry.value === 'number') { const diff = (now - entry.value) / milliSecondsInADay; if (diff <= 7 && allRecommendations.indexOf(entry.key) > -1) { - this._fileBasedRecommendations[entry.key.toLowerCase()] = { recommendedTime: entry.value, sources: ['cached'] }; + this.fileBasedRecommendations[entry.key.toLowerCase()] = { recommendedTime: entry.value, sources: ['cached'] }; } } }); @@ -731,26 +619,26 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe let recommendationsToSuggest: string[] = []; const now = Date.now(); - forEach(this._availableRecommendations, entry => { + forEach(this.availableRecommendations, entry => { let { key: pattern, value: ids } = entry; if (match(pattern, model.uri.toString())) { for (let id of ids) { if (this.productService.extensionImportantTips && caseInsensitiveGet(this.productService.extensionImportantTips, id)) { recommendationsToSuggest.push(id); } - const filedBasedRecommendation = this._fileBasedRecommendations[id.toLowerCase()] || { recommendedTime: now, sources: [] }; + const filedBasedRecommendation = this.fileBasedRecommendations[id.toLowerCase()] || { recommendedTime: now, sources: [] }; filedBasedRecommendation.recommendedTime = now; if (!filedBasedRecommendation.sources.some(s => s instanceof URI && s.toString() === model.uri.toString())) { filedBasedRecommendation.sources.push(model.uri); } - this._fileBasedRecommendations[id.toLowerCase()] = filedBasedRecommendation; + this.fileBasedRecommendations[id.toLowerCase()] = filedBasedRecommendation; } } }); this.storageService.store( 'extensionsAssistant/recommendations', - JSON.stringify(Object.keys(this._fileBasedRecommendations).reduce((result, key) => { result[key] = this._fileBasedRecommendations[key].recommendedTime; return result; }, {} as { [key: string]: any })), + JSON.stringify(Object.keys(this.fileBasedRecommendations).reduce((result, key) => { result[key] = this.fileBasedRecommendations[key].recommendedTime; return result; }, {} as { [key: string]: any })), StorageScope.GLOBAL ); @@ -793,86 +681,61 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe return false; } - const id = recommendationsToSuggest[0]; - const entry = this.productService.extensionImportantTips ? caseInsensitiveGet(this.productService.extensionImportantTips, id) : undefined; + const extensionId = recommendationsToSuggest[0]; + const entry = this.productService.extensionImportantTips ? caseInsensitiveGet(this.productService.extensionImportantTips, extensionId) : undefined; if (!entry) { return false; } - const name = entry.name; - let message = localize('reallyRecommended2', "The '{0}' extension is recommended for this file type.", name); + const extensionName = entry.name; + let message = localize('reallyRecommended2', "The '{0}' extension is recommended for this file type.", extensionName); if (entry.isExtensionPack) { - message = localize('reallyRecommendedExtensionPack', "The '{0}' extension pack is recommended for this file type.", name); + message = localize('reallyRecommendedExtensionPack', "The '{0}' extension pack is recommended for this file type.", extensionName); } - this.notificationService.prompt(Severity.Info, message, - [{ - label: localize('install', 'Install'), - run: () => { - *//* __GDPR__ - "extensionRecommendations:popup" : { - "userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "extensionId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" } - } - *//* - this.telemetryService.publicLog('extensionRecommendations:popup', { userReaction: 'install', extensionId: name }); - this.instantiationService.createInstance(InstallRecommendedExtensionAction, id).run(); - } - }, { - label: localize('showRecommendations', "Show Recommendations"), - run: () => { - *//* __GDPR__ - "extensionRecommendations:popup" : { - "userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "extensionId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" } - } - *//* - this.telemetryService.publicLog('extensionRecommendations:popup', { userReaction: 'show', extensionId: name }); - - const recommendationsAction = this.instantiationService.createInstance(ShowRecommendedExtensionsAction, ShowRecommendedExtensionsAction.ID, localize('showRecommendations', "Show Recommendations")); - recommendationsAction.run(); - recommendationsAction.dispose(); - } - }, { - label: choiceNever, - isSecondary: true, - run: () => { - this.addToImportantRecommendationsIgnore(id); - *//* __GDPR__ - "extensionRecommendations:popup" : { - "userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "extensionId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" } - } - *//* - this.telemetryService.publicLog('extensionRecommendations:popup', { userReaction: 'neverShowAgain', extensionId: name }); - this.notificationService.prompt( - Severity.Info, - localize('ignoreExtensionRecommendations', "Do you want to ignore all extension recommendations?"), - [{ - label: localize('ignoreAll', "Yes, Ignore All"), - run: () => this.setIgnoreRecommendationsConfig(true) - }, { - label: localize('no', "No"), - run: () => this.setIgnoreRecommendationsConfig(false) - }] - ); - } - }], - { - sticky: true, - onCancel: () => { - *//* __GDPR__ - "extensionRecommendations:popup" : { - "userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "extensionId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" } - } - *//* - this.telemetryService.publicLog('extensionRecommendations:popup', { userReaction: 'cancelled', extensionId: name }); - } - } - ); - + this.promptExtensionInstallNotification(extensionId, message); return true; - }*/ + } + }, { + label: choiceNever, + isSecondary: true, + run: () => { + this.addToImportantRecommendationsIgnore(id); + *//* __GDPR__ + "extensionRecommendations:popup" : { + "userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "extensionId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" } + } +*//* + this.telemetryService.publicLog('extensionRecommendations:popup', { userReaction: 'neverShowAgain', extensionId: name }); + this.notificationService.prompt( + Severity.Info, + localize('ignoreExtensionRecommendations', "Do you want to ignore all extension recommendations?"), + [{ + label: localize('ignoreAll', "Yes, Ignore All"), + run: () => this.setIgnoreRecommendationsConfig(true) + }, { + label: localize('no', "No"), + run: () => this.setIgnoreRecommendationsConfig(false) + }] + ); +} +}], +{ +sticky: true, +onCancel: () => { + *//* __GDPR__ + "extensionRecommendations:popup" : { + "userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "extensionId": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" } + } + *//* + this.telemetryService.publicLog('extensionRecommendations:popup', { userReaction: 'cancelled', extensionId: name }); +} +} +); + +return true; +}*/ /*private async promptRecommendedExtensionForFileExtension(fileExtension: string, installed: ILocalExtension[]): Promise { {{SQL CARBON EDIT}} no unused const fileExtensionSuggestionIgnoreList = JSON.parse(this.storageService.get('extensionsAssistant/fileExtensionsSuggestionIgnore', StorageScope.GLOBAL, '[]')); @@ -897,13 +760,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe [{ label: searchMarketplace, run: () => { - *//* __GDPR__ - "fileExtensionSuggestion:popup" : { - "userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "fileExtension": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" } - } - *//* - this.telemetryService.publicLog('fileExtensionSuggestion:popup', { userReaction: 'ok', fileExtension: fileExtension }); + this.telemetryService.publicLog2<{ userReaction: string, fileExtension: string }, FileExtensionSuggestionClassification>('fileExtensionSuggestion:popup', { userReaction: 'ok', fileExtension }); this.viewletService.openViewlet('workbench.view.extensions', true) .then(viewlet => viewlet?.getViewPaneContainer() as IExtensionsViewPaneContainer) .then(viewlet => { @@ -920,29 +777,92 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe JSON.stringify(fileExtensionSuggestionIgnoreList), StorageScope.GLOBAL ); - *//* __GDPR__ - "fileExtensionSuggestion:popup" : { - "userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "fileExtension": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" } - } - *//* - this.telemetryService.publicLog('fileExtensionSuggestion:popup', { userReaction: 'neverShowAgain', fileExtension: fileExtension }); + this.telemetryService.publicLog2<{ userReaction: string, fileExtension: string }, FileExtensionSuggestionClassification>('fileExtensionSuggestion:popup', { userReaction: 'neverShowAgain', fileExtension }); } }], { sticky: true, onCancel: () => { - *//* __GDPR__ - "fileExtensionSuggestion:popup" : { - "userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, - "fileExtension": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" } - } - *//* - this.telemetryService.publicLog('fileExtensionSuggestion:popup', { userReaction: 'cancelled', fileExtension: fileExtension }); + this.telemetryService.publicLog2<{ userReaction: string, fileExtension: string }, FileExtensionSuggestionClassification>('fileExtensionSuggestion:popup', { userReaction: 'cancelled', fileExtension }); + } + } + }, { + label: localize('dontShowAgainExtension', "Don't Show Again for '.{0}' files", fileExtension), + run: () => { + fileExtensionSuggestionIgnoreList.push(fileExtension); + this.storageService.store( + 'extensionsAssistant/fileExtensionsSuggestionIgnore', + JSON.stringify(fileExtensionSuggestionIgnoreList), + StorageScope.GLOBAL + ); + *//* __GDPR__ +"fileExtensionSuggestion:popup" : { +"userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, +"fileExtension": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" } +} +*//* + this.telemetryService.publicLog('fileExtensionSuggestion:popup', { userReaction: 'neverShowAgain', fileExtension: fileExtension }); + } + }], + { + sticky: true, + onCancel: () => { + *//* __GDPR__ + "fileExtensionSuggestion:popup" : { + "userReaction" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }, + "fileExtension": { "classification": "PublicNonPersonalData", "purpose": "FeatureInsight" } + } +*//* + this.telemetryService.publicLog('fileExtensionSuggestion:popup', { userReaction: 'cancelled', fileExtension: fileExtension }); +} +} +); +}*/ + + private promptExtensionInstallNotification(extensionId: string, message: string): void { + this.notificationService.prompt(Severity.Info, message, + [{ + label: localize('install', 'Install'), + run: () => { + this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'install', extensionId }); + this.instantiationService.createInstance(InstallRecommendedExtensionAction, extensionId).run(); + } + }, { + label: localize('showRecommendations', "Show Recommendations"), + run: () => { + this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'show', extensionId }); + + const recommendationsAction = this.instantiationService.createInstance(ShowRecommendedExtensionsAction, ShowRecommendedExtensionsAction.ID, localize('showRecommendations', "Show Recommendations")); + recommendationsAction.run(); + recommendationsAction.dispose(); + } + }, { + label: choiceNever, + isSecondary: true, + run: () => { + this.addToImportantRecommendationsIgnore(extensionId); + this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'neverShowAgain', extensionId }); + this.notificationService.prompt( + Severity.Info, + localize('ignoreExtensionRecommendations', "Do you want to ignore all extension recommendations?"), + [{ + label: localize('ignoreAll', "Yes, Ignore All"), + run: () => this.setIgnoreRecommendationsConfig(true) + }, { + label: localize('no', "No"), + run: () => this.setIgnoreRecommendationsConfig(false) + }] + ); + } + }], + { + sticky: true, + onCancel: () => { + this.telemetryService.publicLog2<{ userReaction: string, extensionId: string }, ExtensionRecommendationsNotificationClassification>('extensionRecommendations:popup', { userReaction: 'cancelled', extensionId }); } } ); - }*/ + } private filterIgnoredOrNotAllowed(recommendationsToSuggest: string[]): string[] { const importantRecommendationsIgnoreList = JSON.parse(this.storageService.get('extensionsAssistant/importantRecommendationsIgnore', StorageScope.GLOBAL, '[]')); @@ -957,17 +877,22 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe }); } - private filterInstalled(recommendationsToSuggest: string[], installed: ILocalExtension[], onAlreadyInstalled?: (id: string) => void): string[] { + /*private filterInstalled(recommendationsToSuggest: string[], installed: ILocalExtension[]): string[] { const installedExtensionsIds = installed.reduce((result, i) => { result.add(i.identifier.id.toLowerCase()); return result; }, new Set()); - return recommendationsToSuggest.filter(id => { + return recommendationsToSuggest.filter(id => !installedExtensionsIds.has(id.toLowerCase())); + }*/ + + private groupByInstalled(recommendationsToSuggest: string[], local: ILocalExtension[]): { installed: string[], uninstalled: string[] } { + const installed: string[] = [], uninstalled: string[] = []; + const installedExtensionsIds = local.reduce((result, i) => { result.add(i.identifier.id.toLowerCase()); return result; }, new Set()); + recommendationsToSuggest.forEach(id => { if (installedExtensionsIds.has(id.toLowerCase())) { - if (onAlreadyInstalled) { - onAlreadyInstalled(id); - } - return false; + installed.push(id); + } else { + uninstalled.push(id); } - return true; }); + return { installed, uninstalled }; } private addToImportantRecommendationsIgnore(id: string) { @@ -992,181 +917,26 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe //#region otherRecommendations - getOtherRecommendations(): Promise { - // {{SQL CARBON EDIT}} - Replace body of this method with our own - let recommendations = Object.keys(this._exeBasedRecommendations).concat(this._recommendations); - shuffle(recommendations, this.sessionSeed); - return Promise.resolve(recommendations.map(extensionId => { + async getOtherRecommendations(): Promise { + const otherRecommendations = Object.keys(this.experimentalRecommendations).concat(Object.keys(this.recommendations)) // {{SQL CARBON EDIT}} add our recommendations + .filter(extensionId => this.isExtensionAllowedToBeRecommended(extensionId)) + .map(extensionId => ({ extensionId, sources: [] })); + await this.fetchProactiveRecommendations(); + const others = distinct([ + ...Object.keys(this.exeBasedRecommendations), + ...this.dynamicWorkspaceRecommendations, + ...otherRecommendations.map(e => e.extensionId), + ]).filter(extensionId => this.isExtensionAllowedToBeRecommended(extensionId)); + shuffle(others, this.sessionSeed); + return others.map(extensionId => { const sources: ExtensionRecommendationSource[] = []; - if (this._exeBasedRecommendations[extensionId]) { + if (this.exeBasedRecommendations[extensionId]) { sources.push('executable'); } - if (this._dynamicWorkspaceRecommendations.indexOf(extensionId) !== -1) { + if (this.dynamicWorkspaceRecommendations.indexOf(extensionId) !== -1) { sources.push('dynamic'); } return ({ extensionId, sources }); - })); - // {{SQL CARBON EDIT}} - End - } - - private fetchProactiveRecommendations(calledDuringStartup?: boolean): Promise { - let fetchPromise = Promise.resolve(undefined); - if (!this.proactiveRecommendationsFetched) { - this.proactiveRecommendationsFetched = true; - - // Executable based recommendations carry out a lot of file stats, delay the resolution so that the startup is not affected - // 10 sec for regular extensions - // 3 secs for important - - const importantExeBasedRecommendations = timeout(calledDuringStartup ? 3000 : 0).then(_ => this.fetchExecutableRecommendations(true)); - importantExeBasedRecommendations.then(_ => this.promptForImportantExeBasedExtension()); - - fetchPromise = timeout(calledDuringStartup ? 10000 : 0).then(_ => Promise.all([this.fetchDynamicWorkspaceRecommendations(), this.fetchExecutableRecommendations(false), importantExeBasedRecommendations])); - - } - return fetchPromise; - } - - /** - * If user has any of the tools listed in this.productService.exeBasedExtensionTips, fetch corresponding recommendations - */ - private async fetchExecutableRecommendations(important: boolean): Promise { - if (isWeb || !this.productService.exeBasedExtensionTips) { - return; - } - - const foundExecutables: Set = new Set(); - const findExecutable = (exeName: string, tip: IExeBasedExtensionTip, path: string) => { - return this.fileService.exists(URI.file(path)).then(exists => { - if (exists && !foundExecutables.has(exeName)) { - foundExecutables.add(exeName); - (tip['recommendations'] || []).forEach(extensionId => { - if (tip.friendlyName) { - if (important) { - this._importantExeBasedRecommendations[extensionId.toLowerCase()] = tip; - } - this._exeBasedRecommendations[extensionId.toLowerCase()] = tip; - } - }); - } - }); - }; - - const promises: Promise[] = []; - // Loop through recommended extensions - forEach(this.productService.exeBasedExtensionTips, entry => { - if (typeof entry.value !== 'object' || !Array.isArray(entry.value['recommendations'])) { - return; - } - if (important !== !!entry.value.important) { - return; - } - const exeName = entry.key; - if (platform === 'win32') { - let windowsPath = entry.value['windowsPath']; - if (!windowsPath || typeof windowsPath !== 'string') { - return; - } - windowsPath = windowsPath.replace('%USERPROFILE%', processEnv['USERPROFILE']!) - .replace('%ProgramFiles(x86)%', processEnv['ProgramFiles(x86)']!) - .replace('%ProgramFiles%', processEnv['ProgramFiles']!) - .replace('%APPDATA%', processEnv['APPDATA']!) - .replace('%WINDIR%', processEnv['WINDIR']!); - promises.push(findExecutable(exeName, entry.value, windowsPath)); - } else { - promises.push(findExecutable(exeName, entry.value, join('/usr/local/bin', exeName))); - promises.push(findExecutable(exeName, entry.value, join(this.environmentService.userHome, exeName))); - } - }); - - await Promise.all(promises); - } - - /** - * Fetch extensions used by others on the same workspace as recommendations from cache - */ - private fetchCachedDynamicWorkspaceRecommendations() { - if (this.contextService.getWorkbenchState() !== WorkbenchState.FOLDER) { - return; - } - - const storageKey = 'extensionsAssistant/dynamicWorkspaceRecommendations'; - let storedRecommendationsJson: { [key: string]: any } = {}; - try { - storedRecommendationsJson = JSON.parse(this.storageService.get(storageKey, StorageScope.WORKSPACE, '{}')); - } catch (e) { - this.storageService.remove(storageKey, StorageScope.WORKSPACE); - } - - if (Array.isArray(storedRecommendationsJson['recommendations']) - && isNumber(storedRecommendationsJson['timestamp']) - && storedRecommendationsJson['timestamp'] > 0 - && (Date.now() - storedRecommendationsJson['timestamp']) / milliSecondsInADay < 14) { - this._dynamicWorkspaceRecommendations = storedRecommendationsJson['recommendations']; - /* __GDPR__ - "dynamicWorkspaceRecommendations" : { - "count" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "cache" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } - } - */ - this.telemetryService.publicLog('dynamicWorkspaceRecommendations', { count: this._dynamicWorkspaceRecommendations.length, cache: 1 }); - } - } - - /** - * Fetch extensions used by others on the same workspace as recommendations from recommendation service - */ - private fetchDynamicWorkspaceRecommendations(): Promise { - if (this.contextService.getWorkbenchState() !== WorkbenchState.FOLDER - || !this.fileService.canHandleResource(this.contextService.getWorkspace().folders[0].uri) - || this._dynamicWorkspaceRecommendations.length - || !this._extensionsRecommendationsUrl) { - return Promise.resolve(undefined); - } - - const storageKey = 'extensionsAssistant/dynamicWorkspaceRecommendations'; - const workspaceUri = this.contextService.getWorkspace().folders[0].uri; - return Promise.all([this.workspaceTagsService.getHashedRemotesFromUri(workspaceUri, false), this.workspaceTagsService.getHashedRemotesFromUri(workspaceUri, true)]).then(([hashedRemotes1, hashedRemotes2]) => { - const hashedRemotes = (hashedRemotes1 || []).concat(hashedRemotes2 || []); - if (!hashedRemotes.length) { - return undefined; - } - - return this.requestService.request({ type: 'GET', url: this._extensionsRecommendationsUrl }, CancellationToken.None).then(context => { - if (context.res.statusCode !== 200) { - return Promise.resolve(undefined); - } - return asJson(context).then((result: { [key: string]: any } | null) => { - if (!result) { - return; - } - const allRecommendations: IDynamicWorkspaceRecommendations[] = Array.isArray(result['workspaceRecommendations']) ? result['workspaceRecommendations'] : []; - if (!allRecommendations.length) { - return; - } - - let foundRemote = false; - for (let i = 0; i < hashedRemotes.length && !foundRemote; i++) { - for (let j = 0; j < allRecommendations.length && !foundRemote; j++) { - if (Array.isArray(allRecommendations[j].remoteSet) && allRecommendations[j].remoteSet.indexOf(hashedRemotes[i]) > -1) { - foundRemote = true; - this._dynamicWorkspaceRecommendations = allRecommendations[j].recommendations.filter(id => this.isExtensionAllowedToBeRecommended(id)) || []; - this.storageService.store(storageKey, JSON.stringify({ - recommendations: this._dynamicWorkspaceRecommendations, - timestamp: Date.now() - }), StorageScope.WORKSPACE); - /* __GDPR__ - "dynamicWorkspaceRecommendations" : { - "count" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, - "cache" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } - } - */ - this.telemetryService.publicLog('dynamicWorkspaceRecommendations', { count: this._dynamicWorkspaceRecommendations.length, cache: 0 }); - } - } - } - }); - }); }); } @@ -1180,7 +950,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe const action = experiment.action; if (action && experiment.state === ExperimentState.Run && action.properties && Array.isArray(action.properties.recommendations) && action.properties.recommendationReason) { action.properties.recommendations.forEach((id: string) => { - this._experimentalRecommendations[id] = action.properties.recommendationReason; + this.experimentalRecommendations[id] = action.properties.recommendationReason; }); } }); @@ -1190,8 +960,123 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe //#endregion + //#region proactive recommendations - exe based and dynamic workspace recommendations + + private proactiveRecommendationsPromise: Promise | undefined = undefined; + private async fetchProactiveRecommendations(): Promise { + if (!this.proactiveRecommendationsPromise) { + this.proactiveRecommendationsPromise = Promise.all([this.fetchDynamicWorkspaceRecommendations(), this.fetchOtherExeBasedRecommendations()]) + .then(() => { + this._register(this.contextService.onDidChangeWorkbenchState(() => this.dynamicWorkspaceRecommendations = [])); + }); + } + return this.proactiveRecommendationsPromise; + } + + private async fetchAndPromptImportantExeBasedRecommendations(): Promise { + const importantExeBasedRecommendations: IStringDictionary = {}; + const importantExectuableBasedTips = await this.extensionTipsService.getImportantExecutableBasedTips(); + importantExectuableBasedTips.forEach(tip => { + this.exeBasedRecommendations[tip.extensionId.toLowerCase()] = tip; + importantExeBasedRecommendations[tip.extensionId.toLowerCase()] = tip; + }); + + const local = await this.extensionManagementService.getInstalled(ExtensionType.User); + const { installed, uninstalled } = this.groupByInstalled(Object.keys(importantExeBasedRecommendations), local); + + /* Log installed and uninstalled exe based recommendations */ + for (const extensionId of installed) { + const tip = importantExeBasedRecommendations[extensionId]; + this.telemetryService.publicLog2<{ exeName: string, extensionId: string }, ExeExtensionRecommendationsClassification>('exeExtensionRecommendations:alreadyInstalled', { extensionId, exeName: basename(tip.windowsPath!) }); + } + for (const extensionId of uninstalled) { + const tip = importantExeBasedRecommendations[extensionId]; + this.telemetryService.publicLog2<{ exeName: string, extensionId: string }, ExeExtensionRecommendationsClassification>('exeExtensionRecommendations:notInstalled', { extensionId, exeName: basename(tip.windowsPath!) }); + } + + this.promptImportantExeBasedRecommendations(uninstalled, importantExeBasedRecommendations); + } + + private promptImportantExeBasedRecommendations(recommendations: string[], importantExeBasedRecommendations: IStringDictionary): void { + if (this.hasToIgnoreRecommendationNotifications()) { + return; + } + recommendations = this.filterIgnoredOrNotAllowed(recommendations); + if (recommendations.length === 0) { + return; + } + + const extensionId = recommendations[0]; + const tip = importantExeBasedRecommendations[extensionId]; + const message = localize('exeRecommended', "The '{0}' extension is recommended as you have {1} installed on your system.", tip.friendlyName!, tip.exeFriendlyName || basename(tip.windowsPath!)); + this.promptExtensionInstallNotification(extensionId, message); + } + + private async fetchOtherExeBasedRecommendations(): Promise { + const otherExectuableBasedTips = await this.extensionTipsService.getOtherExecutableBasedTips(); + otherExectuableBasedTips.forEach(tip => this.exeBasedRecommendations[tip.extensionId.toLowerCase()] = tip); + } + + /** + * Fetch extensions used by others on the same workspace as recommendations + */ + private async fetchDynamicWorkspaceRecommendations(): Promise { + if (this.dynamicWorkspaceRecommendations.length + || this.contextService.getWorkbenchState() !== WorkbenchState.FOLDER + || !this.fileService.canHandleResource(this.contextService.getWorkspace().folders[0].uri) + ) { + return; + } + + const cachedDynamicWorkspaceRecommendations = this.getCachedDynamicWorkspaceRecommendations(); + if (cachedDynamicWorkspaceRecommendations) { + this.dynamicWorkspaceRecommendations = cachedDynamicWorkspaceRecommendations; + this.telemetryService.publicLog2<{ count: number, cache: number }, DynamicWorkspaceRecommendationsClassification>('dynamicWorkspaceRecommendations', { count: this.dynamicWorkspaceRecommendations.length, cache: 1 }); + return; + } + + const workspaceUri = this.contextService.getWorkspace().folders[0].uri; + const [hashedRemotes1, hashedRemotes2] = await Promise.all([this.workspaceTagsService.getHashedRemotesFromUri(workspaceUri, false), this.workspaceTagsService.getHashedRemotesFromUri(workspaceUri, true)]); + const hashedRemotes = (hashedRemotes1 || []).concat(hashedRemotes2 || []); + if (!hashedRemotes.length) { + return; + } + + const workspacesTips = await this.extensionTipsService.getAllWorkspacesTips(); + if (!workspacesTips.length) { + return; + } + + for (const hashedRemote of hashedRemotes) { + const workspaceTip = workspacesTips.filter(workspaceTip => isNonEmptyArray(workspaceTip.remoteSet) && workspaceTip.remoteSet.indexOf(hashedRemote) > -1)[0]; + if (workspaceTip) { + this.dynamicWorkspaceRecommendations = workspaceTip.recommendations.filter(id => this.isExtensionAllowedToBeRecommended(id)) || []; + this.storageService.store(dynamicWorkspaceRecommendationsStorageKey, JSON.stringify({ recommendations: this.dynamicWorkspaceRecommendations, timestamp: Date.now() }), StorageScope.WORKSPACE); + this.telemetryService.publicLog2<{ count: number, cache: number }, DynamicWorkspaceRecommendationsClassification>('dynamicWorkspaceRecommendations', { count: this.dynamicWorkspaceRecommendations.length, cache: 0 }); + return; + } + } + } + + private getCachedDynamicWorkspaceRecommendations(): string[] | undefined { + try { + const storedDynamicWorkspaceRecommendations: IStoredDynamicWorkspaceRecommendations = JSON.parse(this.storageService.get(dynamicWorkspaceRecommendationsStorageKey, StorageScope.WORKSPACE, '{}')); + if (isNonEmptyArray(storedDynamicWorkspaceRecommendations.recommendations) + && isNumber(storedDynamicWorkspaceRecommendations.timestamp) + && storedDynamicWorkspaceRecommendations.timestamp > 0 + && (Date.now() - storedDynamicWorkspaceRecommendations.timestamp) / milliSecondsInADay < 14) { + return storedDynamicWorkspaceRecommendations.recommendations; + } + } catch (e) { + this.storageService.remove(dynamicWorkspaceRecommendationsStorageKey, StorageScope.WORKSPACE); + } + return undefined; + } + + //#endregion + private isExtensionAllowedToBeRecommended(id: string): boolean { - return this._allIgnoredRecommendations.indexOf(id.toLowerCase()) === -1; + return this.allIgnoredRecommendations.indexOf(id.toLowerCase()) === -1; } // {{SQL CARBON EDIT}} promptRecommendedExtensionsByScenario(scenarioType: string): void { @@ -1212,7 +1097,7 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe recommendationMessage = localize('VisualizerExtensionsRecommended', "Azure Data Studio has extension recommendations for data visualization.\nOnce installed, you can select the Visualizer icon to visualize your query results."); } Promise.all([getRecommendationPromise, getLocalExtensionPromise]).then(() => { - if (!recommendations.every(rec => { return firstIndex(localExtensions, local => local.identifier.id.toLocaleLowerCase() === rec.extensionId.toLocaleLowerCase()) !== -1; })) { + if (!recommendations.every(rec => { return localExtensions.findIndex(local => local.identifier.id.toLocaleLowerCase() === rec.extensionId.toLocaleLowerCase()) !== -1; })) { return new Promise(c => { this.notificationService.prompt( Severity.Info, @@ -1282,10 +1167,19 @@ export class ExtensionTipsService extends Disposable implements IExtensionTipsSe if (!scenarioType) { return Promise.reject(new Error(localize('scenarioTypeUndefined', 'The scenario type for extension recommendations must be provided.'))); } - return Promise.resolve((this.productService.recommendedExtensionsByScenario[scenarioType] || []) .filter(extensionId => this.isExtensionAllowedToBeRecommended(extensionId)) .map(extensionId => ({ extensionId, sources: ['application'] }))); } // {{SQL CARBON EDIT}} - End + + private hasToIgnoreRecommendationNotifications(): boolean { + const config = this.configurationService.getValue(ConfigurationKey); + return config.ignoreRecommendations || config.showRecommendationsOnlyOnDemand; + } + + private hasToIgnoreWorkspaceRecommendationNotifications(): boolean { + return this.hasToIgnoreRecommendationNotifications() || this.storageService.getBoolean('extensionsAssistant/workspaceRecommendationsIgnore', StorageScope.WORKSPACE, false); + } + } diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index 0fc68b6f94..c2f0cb82e8 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -9,7 +9,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { SyncActionDescriptor, MenuRegistry, MenuId, registerAction2, Action2 } from 'vs/platform/actions/common/actions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ExtensionsLabel, ExtensionsChannelId, PreferencesLabel, IExtensionManagementService, IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IExtensionManagementServerService, IExtensionTipsService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IExtensionManagementServerService, IExtensionRecommendationsService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } from 'vs/workbench/common/actions'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IOutputChannelRegistry, Extensions as OutputExtensions } from 'vs/workbench/services/output/common/output'; @@ -40,7 +40,6 @@ import { ExtensionDependencyChecker } from 'vs/workbench/contrib/extensions/brow import { CancellationToken } from 'vs/base/common/cancellation'; import { ExtensionType, ExtensionsPolicy } from 'vs/platform/extensions/common/extensions'; // {{SQL CARBON EDIT}} import { RemoteExtensionsInstaller } from 'vs/workbench/contrib/extensions/browser/remoteExtensionsInstaller'; -import { ExtensionTipsService } from 'vs/workbench/contrib/extensions/browser/extensionTipsService'; import { IViewContainersRegistry, ViewContainerLocation, Extensions as ViewContainerExtensions } from 'vs/workbench/common/views'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; @@ -50,10 +49,11 @@ import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configur import { CONTEXT_SYNC_ENABLEMENT } from 'vs/platform/userDataSync/common/userDataSync'; import { IQuickAccessRegistry, Extensions } from 'vs/platform/quickinput/common/quickAccess'; import { InstallExtensionQuickAccessProvider, ManageExtensionsQuickAccessProvider } from 'vs/workbench/contrib/extensions/browser/extensionsQuickAccess'; +import { ExtensionRecommendationsService } from 'vs/workbench/contrib/extensions/browser/extensionRecommendationsService'; // Singletons registerSingleton(IExtensionsWorkbenchService, ExtensionsWorkbenchService); -registerSingleton(IExtensionTipsService, ExtensionTipsService); +registerSingleton(IExtensionRecommendationsService, ExtensionRecommendationsService); Registry.as(OutputExtensions.OutputChannels) .registerChannel({ id: ExtensionsChannelId, label: ExtensionsLabel, log: false }); @@ -286,7 +286,7 @@ CommandsRegistry.registerCommand({ const installed = await extensionManagementService.getInstalled(ExtensionType.User); const [extensionToUninstall] = installed.filter(e => areSameExtensions(e.identifier, { id })); if (!extensionToUninstall) { - throw new Error(localize('notInstalled', "Extension '{0}' is not installed. Make sure you use the full extension ID, including the publisher, e.g.: ms-vscode.csharp.", id)); + throw new Error(localize('notInstalled', "Extension '{0}' is not installed. Make sure you use the full extension ID, including the publisher, e.g.: ms-dotnettools.csharp.", id)); } try { diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index c37c98ffd0..b2fad3cc71 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -16,7 +16,7 @@ import { dispose, Disposable } from 'vs/base/common/lifecycle'; import { IExtension, ExtensionState, IExtensionsWorkbenchService, VIEWLET_ID, IExtensionsViewPaneContainer, AutoUpdateConfigurationKey, IExtensionContainer, EXTENSIONS_CONFIG, TOGGLE_IGNORE_EXTENSION_ACTION_ID } from 'vs/workbench/contrib/extensions/common/extensions'; import { ExtensionsConfigurationInitialContent } from 'vs/workbench/contrib/extensions/common/extensionsFileTemplate'; import { IGalleryExtension, IExtensionGalleryService, INSTALL_ERROR_MALICIOUS, INSTALL_ERROR_INCOMPATIBLE, IGalleryExtensionVersion, ILocalExtension, INSTALL_ERROR_NOT_SUPPORTED } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionTipsService, IExtensionRecommendation, IExtensionsConfigContent, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionRecommendationsService, IExtensionRecommendation, IExtensionsConfigContent, IExtensionManagementServer } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { ExtensionType, ExtensionIdentifier, IExtensionDescription, IExtensionManifest, isLanguagePackExtension, ExtensionsPolicy, ExtensionsPolicyKey } from 'vs/platform/extensions/common/extensions'; // {{SQL CARBON EDIT}} import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; @@ -1823,7 +1823,7 @@ export class IgnoreExtensionRecommendationAction extends Action { constructor( private readonly extension: IExtension, - @IExtensionTipsService private readonly extensionsTipsService: IExtensionTipsService, + @IExtensionRecommendationsService private readonly extensionsTipsService: IExtensionRecommendationsService, ) { super(IgnoreExtensionRecommendationAction.ID, 'Ignore Recommendation'); @@ -1846,7 +1846,7 @@ export class UndoIgnoreExtensionRecommendationAction extends Action { constructor( private readonly extension: IExtension, - @IExtensionTipsService private readonly extensionsTipsService: IExtensionTipsService, + @IExtensionRecommendationsService private readonly extensionsTipsService: IExtensionRecommendationsService, ) { super(UndoIgnoreExtensionRecommendationAction.ID, 'Undo'); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts index a52a512718..bcebc4206a 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts @@ -10,7 +10,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { isPromiseCanceledError, getErrorMessage } from 'vs/base/common/errors'; import { PagedModel, IPagedModel, IPager, DelayedPagedModel } from 'vs/base/common/paging'; import { SortBy, SortOrder, IQueryOptions } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IExtensionManagementServer, IExtensionManagementServerService, IExtensionTipsService, IExtensionRecommendation, EnablementState } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IExtensionManagementServer, IExtensionManagementServerService, IExtensionRecommendationsService, IExtensionRecommendation, EnablementState } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; @@ -100,7 +100,7 @@ export class ExtensionsListView extends ViewPane { @IExtensionService private readonly extensionService: IExtensionService, @IExtensionsWorkbenchService protected extensionsWorkbenchService: IExtensionsWorkbenchService, @IEditorService private readonly editorService: IEditorService, - @IExtensionTipsService protected tipsService: IExtensionTipsService, + @IExtensionRecommendationsService protected tipsService: IExtensionRecommendationsService, @ITelemetryService telemetryService: ITelemetryService, @IConfigurationService configurationService: IConfigurationService, @IWorkspaceContextService protected contextService: IWorkspaceContextService, @@ -956,7 +956,7 @@ export class ServerExtensionsView extends ExtensionsListView { @IInstantiationService instantiationService: IInstantiationService, @IExtensionService extensionService: IExtensionService, @IEditorService editorService: IEditorService, - @IExtensionTipsService tipsService: IExtensionTipsService, + @IExtensionRecommendationsService tipsService: IExtensionRecommendationsService, @ITelemetryService telemetryService: ITelemetryService, @IConfigurationService configurationService: IConfigurationService, @IWorkspaceContextService contextService: IWorkspaceContextService, diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts index 4bea4f0d22..566dd417fb 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts @@ -9,7 +9,7 @@ import { IExtension, IExtensionsWorkbenchService, IExtensionContainer } from 'vs import { append, $, addClass, removeNode } from 'vs/base/browser/dom'; import * as platform from 'vs/base/common/platform'; import { localize } from 'vs/nls'; -import { IExtensionTipsService, IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IExtensionRecommendationsService, IExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { ILabelService } from 'vs/platform/label/common/label'; import { extensionButtonProminentBackground, extensionButtonProminentForeground, ExtensionToolTipAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; import { IThemeService, IColorTheme } from 'vs/platform/theme/common/themeService'; @@ -198,12 +198,12 @@ export class RecommendationWidget extends ExtensionWidget { constructor( private parent: HTMLElement, @IThemeService private readonly themeService: IThemeService, - @IExtensionTipsService private readonly extensionTipsService: IExtensionTipsService + @IExtensionRecommendationsService private readonly extensionRecommendationsService: IExtensionRecommendationsService ) { super(); this.render(); this._register(toDisposable(() => this.clear())); - this._register(this.extensionTipsService.onRecommendationChange(() => this.render())); + this._register(this.extensionRecommendationsService.onRecommendationChange(() => this.render())); } private clear(): void { @@ -221,7 +221,7 @@ export class RecommendationWidget extends ExtensionWidget { if (!this.extension) { return; } - const extRecommendations = this.extensionTipsService.getAllRecommendationsWithReason(); + const extRecommendations = this.extensionRecommendationsService.getAllRecommendationsWithReason(); if (extRecommendations[this.extension.identifier.id.toLowerCase()]) { this.element = append(this.parent, $('div.extension-bookmark')); const recommendation = append(this.element, $('.recommendation')); diff --git a/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css b/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css index 15d110b7be..093f2a4547 100644 --- a/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css +++ b/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css @@ -135,6 +135,11 @@ padding-left: 14px; } +.extension-editor > .header > .details > .subtitle .version { + font-size: 90%; + font-style: italic; +} + .extension-editor > .header > .details > .description { margin-top: 10px; white-space: nowrap; diff --git a/src/vs/workbench/contrib/extensions/common/extensionsUtils.ts b/src/vs/workbench/contrib/extensions/common/extensionsUtils.ts index 769fec7261..64eb19a098 100644 --- a/src/vs/workbench/contrib/extensions/common/extensionsUtils.ts +++ b/src/vs/workbench/contrib/extensions/common/extensionsUtils.ts @@ -10,7 +10,7 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { Disposable } from 'vs/base/common/lifecycle'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IExtensionManagementService, ILocalExtension, IExtensionIdentifier, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionTipsService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionRecommendationsService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -28,7 +28,7 @@ export class KeymapExtensions extends Disposable implements IWorkbenchContributi constructor( @IInstantiationService private readonly instantiationService: IInstantiationService, @IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService, - @IExtensionTipsService private readonly tipsService: IExtensionTipsService, + @IExtensionRecommendationsService private readonly tipsService: IExtensionRecommendationsService, @ILifecycleService lifecycleService: ILifecycleService, @INotificationService private readonly notificationService: INotificationService, @ITelemetryService private readonly telemetryService: ITelemetryService, @@ -122,7 +122,7 @@ export async function getInstalledExtensions(accessor: ServicesAccessor): Promis }); } -export function isKeymapExtension(tipsService: IExtensionTipsService, extension: IExtensionStatus): boolean { +export function isKeymapExtension(tipsService: IExtensionRecommendationsService, extension: IExtensionStatus): boolean { const cats = extension.local.manifest.categories; return cats && cats.indexOf('Keymaps') !== -1 || tipsService.getKeymapRecommendations().some(({ extensionId }) => areSameExtensions({ id: extensionId }, extension.local.identifier)); } diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts index 8af80587e0..e15aeb43e4 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensions.contribution.ts @@ -21,6 +21,7 @@ import { RuntimeExtensionsInput } from 'vs/workbench/contrib/extensions/electron import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { ExtensionsAutoProfiler } from 'vs/workbench/contrib/extensions/electron-browser/extensionsAutoProfiler'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-browser/environmentService'; import { OpenExtensionsFolderAction } from 'vs/workbench/contrib/extensions/electron-browser/extensionsActions'; import { ExtensionsLabel } from 'vs/platform/extensionManagement/common/extensionManagement'; @@ -64,7 +65,7 @@ actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(ShowRuntimeEx class ExtensionsContributions implements IWorkbenchContribution { constructor( - @IWorkbenchEnvironmentService workbenchEnvironmentService: IWorkbenchEnvironmentService + @IWorkbenchEnvironmentService workbenchEnvironmentService: INativeWorkbenchEnvironmentService ) { if (workbenchEnvironmentService.extensionsPath) { const openExtensionsFolderActionDescriptor = SyncActionDescriptor.create(OpenExtensionsFolderAction, OpenExtensionsFolderAction.ID, OpenExtensionsFolderAction.LABEL); diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionsActions.ts index af48e9f3f0..875d79409f 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionsActions.ts @@ -7,7 +7,8 @@ import { localize } from 'vs/nls'; import { Action } from 'vs/base/common/actions'; import { IFileService } from 'vs/platform/files/common/files'; import { URI } from 'vs/base/common/uri'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-browser/environmentService'; import { IElectronService } from 'vs/platform/electron/node/electron'; import { Schemas } from 'vs/base/common/network'; @@ -21,7 +22,7 @@ export class OpenExtensionsFolderAction extends Action { label: string, @IElectronService private readonly electronService: IElectronService, @IFileService private readonly fileService: IFileService, - @IEnvironmentService private readonly environmentService: IEnvironmentService + @IWorkbenchEnvironmentService private readonly environmentService: INativeWorkbenchEnvironmentService ) { super(id, label, undefined, true); } diff --git a/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts b/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts index 139c80db17..99d7ed14e7 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts @@ -48,6 +48,7 @@ import { IOpenerService } from 'vs/platform/opener/common/opener'; import { URI } from 'vs/base/common/uri'; import { editorBackground } from 'vs/platform/theme/common/colorRegistry'; import { domEvent } from 'vs/base/browser/event'; +import { IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; export const IExtensionHostProfileService = createDecorator('extensionHostProfileService'); export const CONTEXT_PROFILE_SESSION_STATE = new RawContextKey('profileSessionState', 'none'); @@ -436,7 +437,8 @@ export class RuntimeExtensionsEditor extends BaseEditor { horizontalScrolling: false, overrideStyles: { listBackground: editorBackground - } + }, + accessibilityProvider: new RuntimeExtensionsEditorAccessibilityProvider() }); this._list.splice(0, this._list.length, this._elements || undefined); @@ -689,3 +691,9 @@ export class SaveExtensionHostProfileAction extends Action { return writeFile(savePath, JSON.stringify(profileInfo ? profileInfo.data : {}, null, '\t')); } } + +class RuntimeExtensionsEditorAccessibilityProvider implements IAccessibilityProvider { + getAriaLabel(element: IRuntimeExtension): string | null { + return element.description.name; + } +} diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsTipsService.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts similarity index 82% rename from src/vs/workbench/contrib/extensions/test/electron-browser/extensionsTipsService.test.ts rename to src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts index 77225fe1ae..c926bf8f58 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsTipsService.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionRecommendationsService.test.ts @@ -12,10 +12,9 @@ import * as uuid from 'vs/base/common/uuid'; import { mkdirp, rimraf, RimRafMode } from 'vs/base/node/pfs'; import { IExtensionGalleryService, IGalleryExtensionAssets, IGalleryExtension, IExtensionManagementService, - DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier + DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier, IExtensionTipsService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IWorkbenchExtensionEnablementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; -import { ExtensionTipsService } from 'vs/workbench/contrib/extensions/browser/extensionTipsService'; import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService'; import { TestInstantiationService } from 'vs/platform/instantiation/test/common/instantiationServiceMock'; import { Emitter } from 'vs/base/common/event'; @@ -54,6 +53,10 @@ import { Schemas } from 'vs/base/common/network'; import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider'; import { IFileService } from 'vs/platform/files/common/files'; import { IProductService } from 'vs/platform/product/common/productService'; +import { ExtensionTipsService } from 'vs/platform/extensionManagement/node/extensionTipsService'; +import { ExtensionRecommendationsService } from 'vs/workbench/contrib/extensions/browser/extensionRecommendationsService'; +import { NoOpWorkspaceTagsService } from 'vs/workbench/contrib/tags/browser/workspaceTagsService'; +import { IWorkspaceTagsService } from 'vs/workbench/contrib/tags/common/workspaceTags'; const mockExtensionGallery: IGalleryExtension[] = [ aGalleryExtension('MockExtension1', { @@ -169,11 +172,11 @@ function aGalleryExtension(name: string, properties: any = {}, galleryExtensionP return galleryExtension; } -suite.skip('ExtensionsTipsService Test', () => { // {{SQL CARBON EDIT}} skip suite +suite.skip('ExtensionRecommendationsService Test', () => { // {{SQL CARBON EDIT}} skip suite let workspaceService: IWorkspaceContextService; let instantiationService: TestInstantiationService; let testConfigurationService: TestConfigurationService; - let testObject: ExtensionTipsService; + let testObject: ExtensionRecommendationsService; let parentResource: string; let installEvent: Emitter, didInstallEvent: Emitter, @@ -203,11 +206,12 @@ suite.skip('ExtensionsTipsService Test', () => { // {{SQL CARBON EDIT}} skip sui instantiationService.stub(IWorkbenchExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); instantiationService.stub(ITelemetryService, NullTelemetryService); instantiationService.stub(IURLService, URLService); + instantiationService.stub(IWorkspaceTagsService, new NoOpWorkspaceTagsService()); instantiationService.set(IProductService, { ...productService, ...{ extensionTips: { - 'ms-vscode.csharp': '{**/*.cs,**/project.json,**/global.json,**/*.csproj,**/*.sln,**/appsettings.json}', + 'ms-dotnettools.csharp': '{**/*.cs,**/project.json,**/global.json,**/*.csproj,**/*.sln,**/appsettings.json}', 'msjsdiag.debugger-for-chrome': '{**/*.ts,**/*.tsx,**/*.js,**/*.jsx,**/*.es6,**/*.mjs,**/*.cjs,**/.babelrc}', 'lukehoban.Go': '**/*.go' }, @@ -226,6 +230,7 @@ suite.skip('ExtensionsTipsService Test', () => { // {{SQL CARBON EDIT}} skip sui experimentService = instantiationService.createInstance(TestExperimentService); instantiationService.stub(IExperimentService, experimentService); + instantiationService.stub(IExtensionTipsService, instantiationService.createInstance(ExtensionTipsService)); onModelAddedEvent = new Emitter(); }); @@ -262,7 +267,7 @@ suite.skip('ExtensionsTipsService Test', () => { // {{SQL CARBON EDIT}} skip sui }); teardown(done => { - (testObject).dispose(); + (testObject).dispose(); if (parentResource) { rimraf(parentResource, RimRafMode.MOVE).then(done, done); } else { @@ -296,7 +301,7 @@ suite.skip('ExtensionsTipsService Test', () => { // {{SQL CARBON EDIT}} skip sui function testNoPromptForValidRecommendations(recommendations: string[]) { return setUpFolderWorkspace('myFolder', recommendations).then(() => { - testObject = instantiationService.createInstance(ExtensionTipsService); + testObject = instantiationService.createInstance(ExtensionRecommendationsService); return testObject.loadWorkspaceConfigPromise.then(() => { assert.equal(Object.keys(testObject.getAllRecommendationsWithReason()).length, recommendations.length); assert.ok(!prompted); @@ -306,7 +311,7 @@ suite.skip('ExtensionsTipsService Test', () => { // {{SQL CARBON EDIT}} skip sui function testNoPromptOrRecommendationsForValidRecommendations(recommendations: string[]) { return setUpFolderWorkspace('myFolder', mockTestData.validRecommendedExtensions).then(() => { - testObject = instantiationService.createInstance(ExtensionTipsService); + testObject = instantiationService.createInstance(ExtensionRecommendationsService); assert.ok(!prompted); return testObject.getWorkspaceRecommendations().then(() => { @@ -316,7 +321,7 @@ suite.skip('ExtensionsTipsService Test', () => { // {{SQL CARBON EDIT}} skip sui }); } - test('ExtensionTipsService: No Prompt for valid workspace recommendations when galleryService is absent', () => { + test('ExtensionRecommendationsService: No Prompt for valid workspace recommendations when galleryService is absent', () => { const galleryQuerySpy = sinon.spy(); instantiationService.stub(IExtensionGalleryService, { query: galleryQuerySpy, isEnabled: () => false }); @@ -324,18 +329,18 @@ suite.skip('ExtensionsTipsService Test', () => { // {{SQL CARBON EDIT}} skip sui .then(() => assert.ok(galleryQuerySpy.notCalled)); }); - test('ExtensionTipsService: No Prompt for valid workspace recommendations during extension development', () => { + test('ExtensionRecommendationsService: No Prompt for valid workspace recommendations during extension development', () => { instantiationService.stub(IEnvironmentService, { extensionDevelopmentLocationURI: [URI.file('/folder/file')] }); return testNoPromptOrRecommendationsForValidRecommendations(mockTestData.validRecommendedExtensions); }); - test('ExtensionTipsService: No workspace recommendations or prompts when extensions.json has empty array', () => { + test('ExtensionRecommendationsService: No workspace recommendations or prompts when extensions.json has empty array', () => { return testNoPromptForValidRecommendations([]); }); - test('ExtensionTipsService: Prompt for valid workspace recommendations', () => { + test('ExtensionRecommendationsService: Prompt for valid workspace recommendations', () => { return setUpFolderWorkspace('myFolder', mockTestData.recommendedExtensions).then(() => { - testObject = instantiationService.createInstance(ExtensionTipsService); + testObject = instantiationService.createInstance(ExtensionRecommendationsService); return testObject.loadWorkspaceConfigPromise.then(() => { const recommendations = Object.keys(testObject.getAllRecommendationsWithReason()); @@ -349,41 +354,40 @@ suite.skip('ExtensionsTipsService Test', () => { // {{SQL CARBON EDIT}} skip sui }); }); - test('ExtensionTipsService: No Prompt for valid workspace recommendations if they are already installed', () => { + test('ExtensionRecommendationsService: No Prompt for valid workspace recommendations if they are already installed', () => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', mockExtensionLocal); return testNoPromptForValidRecommendations(mockTestData.validRecommendedExtensions); }); - test('ExtensionTipsService: No Prompt for valid workspace recommendations with casing mismatch if they are already installed', () => { + test('ExtensionRecommendationsService: No Prompt for valid workspace recommendations with casing mismatch if they are already installed', () => { instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', mockExtensionLocal); return testNoPromptForValidRecommendations(mockTestData.validRecommendedExtensions.map(x => x.toUpperCase())); }); - test('ExtensionTipsService: No Prompt for valid workspace recommendations if ignoreRecommendations is set', () => { + test('ExtensionRecommendationsService: No Prompt for valid workspace recommendations if ignoreRecommendations is set', () => { testConfigurationService.setUserConfiguration(ConfigurationKey, { ignoreRecommendations: true }); return testNoPromptForValidRecommendations(mockTestData.validRecommendedExtensions); }); - test('ExtensionTipsService: No Prompt for valid workspace recommendations if showRecommendationsOnlyOnDemand is set', () => { + test('ExtensionRecommendationsService: No Prompt for valid workspace recommendations if showRecommendationsOnlyOnDemand is set', () => { testConfigurationService.setUserConfiguration(ConfigurationKey, { showRecommendationsOnlyOnDemand: true }); return setUpFolderWorkspace('myFolder', mockTestData.validRecommendedExtensions).then(() => { - testObject = instantiationService.createInstance(ExtensionTipsService); + testObject = instantiationService.createInstance(ExtensionRecommendationsService); return testObject.loadWorkspaceConfigPromise.then(() => { - assert.equal(Object.keys(testObject.getAllRecommendationsWithReason()).length, 0); assert.ok(!prompted); }); }); }); - test('ExtensionTipsService: No Prompt for valid workspace recommendations if ignoreRecommendations is set for current workspace', () => { + test('ExtensionRecommendationsService: No Prompt for valid workspace recommendations if ignoreRecommendations is set for current workspace', () => { instantiationService.stub(IStorageService, >{ get: (a: string, b: StorageScope, c?: string) => c, getBoolean: (a: string, b: StorageScope, c?: boolean) => a === 'extensionsAssistant/workspaceRecommendationsIgnore' || c }); return testNoPromptForValidRecommendations(mockTestData.validRecommendedExtensions); }); - test('ExtensionTipsService: No Recommendations of globally ignored recommendations', () => { + test('ExtensionRecommendationsService: No Recommendations of globally ignored recommendations', () => { const storageGetterStub = (a: string, _: StorageScope, c?: string) => { - const storedRecommendations = '["ms-vscode.csharp", "ms-python.python", "ms-vscode.vscode-typescript-tslint-plugin"]'; - const ignoredRecommendations = '["ms-vscode.csharp", "mockpublisher2.mockextension2"]'; // ignore a stored recommendation and a workspace recommendation. + const storedRecommendations = '["ms-dotnettools.csharp", "ms-python.python", "ms-vscode.vscode-typescript-tslint-plugin"]'; + const ignoredRecommendations = '["ms-dotnettools.csharp", "mockpublisher2.mockextension2"]'; // ignore a stored recommendation and a workspace recommendation. if (a === 'extensionsAssistant/recommendations') { return storedRecommendations; } if (a === 'extensionsAssistant/ignored_recommendations') { return ignoredRecommendations; } return c; @@ -395,10 +399,10 @@ suite.skip('ExtensionsTipsService Test', () => { // {{SQL CARBON EDIT}} skip sui }); return setUpFolderWorkspace('myFolder', mockTestData.validRecommendedExtensions).then(() => { - testObject = instantiationService.createInstance(ExtensionTipsService); + testObject = instantiationService.createInstance(ExtensionRecommendationsService); return testObject.loadWorkspaceConfigPromise.then(() => { const recommendations = testObject.getAllRecommendationsWithReason(); - assert.ok(!recommendations['ms-vscode.csharp']); // stored recommendation that has been globally ignored + assert.ok(!recommendations['ms-dotnettools.csharp']); // stored recommendation that has been globally ignored assert.ok(recommendations['ms-python.python']); // stored recommendation assert.ok(recommendations['mockpublisher1.mockextension1']); // workspace recommendation assert.ok(!recommendations['mockpublisher2.mockextension2']); // workspace recommendation that has been globally ignored @@ -406,19 +410,19 @@ suite.skip('ExtensionsTipsService Test', () => { // {{SQL CARBON EDIT}} skip sui }); }); - test('ExtensionTipsService: No Recommendations of workspace ignored recommendations', () => { - const ignoredRecommendations = ['ms-vscode.csharp', 'mockpublisher2.mockextension2']; // ignore a stored recommendation and a workspace recommendation. - const storedRecommendations = '["ms-vscode.csharp", "ms-python.python"]'; + test('ExtensionRecommendationsService: No Recommendations of workspace ignored recommendations', () => { + const ignoredRecommendations = ['ms-dotnettools.csharp', 'mockpublisher2.mockextension2']; // ignore a stored recommendation and a workspace recommendation. + const storedRecommendations = '["ms-dotnettools.csharp", "ms-python.python"]'; instantiationService.stub(IStorageService, >{ get: (a: string, b: StorageScope, c?: string) => a === 'extensionsAssistant/recommendations' ? storedRecommendations : c, getBoolean: (a: string, _: StorageScope, c?: boolean) => a === 'extensionsAssistant/workspaceRecommendationsIgnore' || c }); return setUpFolderWorkspace('myFolder', mockTestData.validRecommendedExtensions, ignoredRecommendations).then(() => { - testObject = instantiationService.createInstance(ExtensionTipsService); + testObject = instantiationService.createInstance(ExtensionRecommendationsService); return testObject.loadWorkspaceConfigPromise.then(() => { const recommendations = testObject.getAllRecommendationsWithReason(); - assert.ok(!recommendations['ms-vscode.csharp']); // stored recommendation that has been workspace ignored + assert.ok(!recommendations['ms-dotnettools.csharp']); // stored recommendation that has been workspace ignored assert.ok(recommendations['ms-python.python']); // stored recommendation assert.ok(recommendations['mockpublisher1.mockextension1']); // workspace recommendation assert.ok(!recommendations['mockpublisher2.mockextension2']); // workspace recommendation that has been workspace ignored @@ -426,37 +430,37 @@ suite.skip('ExtensionsTipsService Test', () => { // {{SQL CARBON EDIT}} skip sui }); }); - test('ExtensionTipsService: Able to retrieve collection of all ignored recommendations', () => { + test('ExtensionRecommendationsService: Able to retrieve collection of all ignored recommendations', () => { const storageGetterStub = (a: string, _: StorageScope, c?: string) => { - const storedRecommendations = '["ms-vscode.csharp", "ms-python.python"]'; + const storedRecommendations = '["ms-dotnettools.csharp", "ms-python.python"]'; const globallyIgnoredRecommendations = '["mockpublisher2.mockextension2"]'; // ignore a workspace recommendation. if (a === 'extensionsAssistant/recommendations') { return storedRecommendations; } if (a === 'extensionsAssistant/ignored_recommendations') { return globallyIgnoredRecommendations; } return c; }; - const workspaceIgnoredRecommendations = ['ms-vscode.csharp']; // ignore a stored recommendation and a workspace recommendation. + const workspaceIgnoredRecommendations = ['ms-dotnettools.csharp']; // ignore a stored recommendation and a workspace recommendation. instantiationService.stub(IStorageService, >{ get: storageGetterStub, getBoolean: (a: string, _: StorageScope, c?: boolean) => a === 'extensionsAssistant/workspaceRecommendationsIgnore' || c }); return setUpFolderWorkspace('myFolder', mockTestData.validRecommendedExtensions, workspaceIgnoredRecommendations).then(() => { - testObject = instantiationService.createInstance(ExtensionTipsService); + testObject = instantiationService.createInstance(ExtensionRecommendationsService); return testObject.loadWorkspaceConfigPromise.then(() => { const recommendations = testObject.getAllRecommendationsWithReason(); assert.ok(recommendations['ms-python.python']); assert.ok(!recommendations['mockpublisher2.mockextension2']); - assert.ok(!recommendations['ms-vscode.csharp']); + assert.ok(!recommendations['ms-dotnettools.csharp']); }); }); }); - test('ExtensionTipsService: Able to dynamically ignore/unignore global recommendations', () => { + test('ExtensionRecommendationsService: Able to dynamically ignore/unignore global recommendations', () => { const storageGetterStub = (a: string, _: StorageScope, c?: string) => { - const storedRecommendations = '["ms-vscode.csharp", "ms-python.python"]'; + const storedRecommendations = '["ms-dotnettools.csharp", "ms-python.python"]'; const globallyIgnoredRecommendations = '["mockpublisher2.mockextension2"]'; // ignore a workspace recommendation. if (a === 'extensionsAssistant/recommendations') { return storedRecommendations; } if (a === 'extensionsAssistant/ignored_recommendations') { return globallyIgnoredRecommendations; } @@ -470,7 +474,7 @@ suite.skip('ExtensionsTipsService Test', () => { // {{SQL CARBON EDIT}} skip sui }); return setUpFolderWorkspace('myFolder', mockTestData.validRecommendedExtensions).then(() => { - testObject = instantiationService.createInstance(ExtensionTipsService); + testObject = instantiationService.createInstance(ExtensionRecommendationsService); return testObject.loadWorkspaceConfigPromise.then(() => { const recommendations = testObject.getAllRecommendationsWithReason(); assert.ok(recommendations['ms-python.python']); @@ -510,7 +514,7 @@ suite.skip('ExtensionsTipsService Test', () => { // {{SQL CARBON EDIT}} skip sui }); await setUpFolderWorkspace('myFolder', []); - testObject = instantiationService.createInstance(ExtensionTipsService); + testObject = instantiationService.createInstance(ExtensionRecommendationsService); testObject.onRecommendationChange(changeHandlerTarget); testObject.toggleIgnoredRecommendation(ignoredExtensionId, true); await testObject.loadWorkspaceConfigPromise; @@ -520,35 +524,35 @@ suite.skip('ExtensionsTipsService Test', () => { // {{SQL CARBON EDIT}} skip sui assert.ok(storageSetterTarget.calledWithExactly('extensionsAssistant/ignored_recommendations', `["ms-vscode.vscode","${ignoredExtensionId.toLowerCase()}"]`, StorageScope.GLOBAL)); }); - test('ExtensionTipsService: Get file based recommendations from storage (old format)', () => { - const storedRecommendations = '["ms-vscode.csharp", "ms-python.python", "ms-vscode.vscode-typescript-tslint-plugin"]'; + test('ExtensionRecommendationsService: Get file based recommendations from storage (old format)', () => { + const storedRecommendations = '["ms-dotnettools.csharp", "ms-python.python", "ms-vscode.vscode-typescript-tslint-plugin"]'; instantiationService.stub(IStorageService, >{ get: (a: string, b: StorageScope, c?: string) => a === 'extensionsAssistant/recommendations' ? storedRecommendations : c, getBoolean: (a: string, b: StorageScope, c: boolean) => c }); return setUpFolderWorkspace('myFolder', []).then(() => { - testObject = instantiationService.createInstance(ExtensionTipsService); + testObject = instantiationService.createInstance(ExtensionRecommendationsService); return testObject.loadWorkspaceConfigPromise.then(() => { const recommendations = testObject.getFileBasedRecommendations(); assert.equal(recommendations.length, 2); - assert.ok(recommendations.some(({ extensionId }) => extensionId === 'ms-vscode.csharp')); // stored recommendation that exists in product.extensionTips + assert.ok(recommendations.some(({ extensionId }) => extensionId === 'ms-dotnettools.csharp')); // stored recommendation that exists in product.extensionTips assert.ok(recommendations.some(({ extensionId }) => extensionId === 'ms-python.python')); // stored recommendation that exists in product.extensionImportantTips assert.ok(recommendations.every(({ extensionId }) => extensionId !== 'ms-vscode.vscode-typescript-tslint-plugin')); // stored recommendation that is no longer in neither product.extensionTips nor product.extensionImportantTips }); }); }); - test('ExtensionTipsService: Get file based recommendations from storage (new format)', () => { + test('ExtensionRecommendationsService: Get file based recommendations from storage (new format)', () => { const milliSecondsInADay = 1000 * 60 * 60 * 24; const now = Date.now(); const tenDaysOld = 10 * milliSecondsInADay; - const storedRecommendations = `{"ms-vscode.csharp": ${now}, "ms-python.python": ${now}, "ms-vscode.vscode-typescript-tslint-plugin": ${now}, "lukehoban.Go": ${tenDaysOld}}`; + const storedRecommendations = `{"ms-dotnettools.csharp": ${now}, "ms-python.python": ${now}, "ms-vscode.vscode-typescript-tslint-plugin": ${now}, "lukehoban.Go": ${tenDaysOld}}`; instantiationService.stub(IStorageService, >{ get: (a: string, b: StorageScope, c?: string) => a === 'extensionsAssistant/recommendations' ? storedRecommendations : c, getBoolean: (a: string, b: StorageScope, c: boolean) => c }); return setUpFolderWorkspace('myFolder', []).then(() => { - testObject = instantiationService.createInstance(ExtensionTipsService); + testObject = instantiationService.createInstance(ExtensionRecommendationsService); return testObject.loadWorkspaceConfigPromise.then(() => { const recommendations = testObject.getFileBasedRecommendations(); assert.equal(recommendations.length, 2); - assert.ok(recommendations.some(({ extensionId }) => extensionId === 'ms-vscode.csharp')); // stored recommendation that exists in product.extensionTips + assert.ok(recommendations.some(({ extensionId }) => extensionId === 'ms-dotnettools.csharp')); // stored recommendation that exists in product.extensionTips assert.ok(recommendations.some(({ extensionId }) => extensionId === 'ms-python.python')); // stored recommendation that exists in product.extensionImportantTips assert.ok(recommendations.every(({ extensionId }) => extensionId !== 'ms-vscode.vscode-typescript-tslint-plugin')); // stored recommendation that is no longer in neither product.extensionTips nor product.extensionImportantTips assert.ok(recommendations.every(({ extensionId }) => extensionId !== 'lukehoban.Go')); //stored recommendation that is older than a week diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts index e60e696d12..639edc7db6 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsActions.test.ts @@ -13,10 +13,10 @@ import { IExtensionManagementService, IExtensionGalleryService, ILocalExtension, IGalleryExtension, DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer, IExtensionTipsService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer, IExtensionRecommendationsService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; -import { ExtensionTipsService } from 'vs/workbench/contrib/extensions/browser/extensionTipsService'; +import { ExtensionRecommendationsService } from 'vs/workbench/contrib/extensions/browser/extensionRecommendationsService'; import { TestExtensionEnablementService } from 'vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test'; import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService'; import { IURLService } from 'vs/platform/url/common/url'; @@ -94,7 +94,7 @@ suite('ExtensionsActions Test', () => { instantiationService.stub(IWorkbenchExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); instantiationService.stub(ILabelService, { onDidChangeFormatters: new Emitter().event }); - instantiationService.set(IExtensionTipsService, instantiationService.createInstance(ExtensionTipsService)); + instantiationService.set(IExtensionRecommendationsService, instantiationService.createInstance(ExtensionRecommendationsService)); instantiationService.stub(IURLService, URLService); instantiationService.stubPromise(IExtensionManagementService, 'getInstalled', []); diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts index 6c3d678e22..d806c812b0 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsViews.test.ts @@ -14,10 +14,10 @@ import { IExtensionManagementService, IExtensionGalleryService, ILocalExtension, IGalleryExtension, IQueryOptions, DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IExtensionIdentifier, SortBy } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer, IExtensionTipsService, ExtensionRecommendationReason } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionManagementServer, IExtensionRecommendationsService, ExtensionRecommendationReason } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; -import { ExtensionTipsService } from 'vs/workbench/contrib/extensions/browser/extensionTipsService'; +import { ExtensionRecommendationsService } from 'vs/workbench/contrib/extensions/browser/extensionRecommendationsService'; import { TestExtensionEnablementService } from 'vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test'; import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService'; import { IURLService } from 'vs/platform/url/common/url'; @@ -110,16 +110,16 @@ suite('ExtensionsListView Tests', () => { instantiationService.stub(IWorkbenchExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); - instantiationService.stub(IExtensionTipsService, ExtensionTipsService); + instantiationService.stub(IExtensionRecommendationsService, ExtensionRecommendationsService); instantiationService.stub(IURLService, URLService); - instantiationService.stubPromise(IExtensionTipsService, 'getWorkspaceRecommendations', [ + instantiationService.stubPromise(IExtensionRecommendationsService, 'getWorkspaceRecommendations', [ { extensionId: workspaceRecommendationA.identifier.id }, { extensionId: workspaceRecommendationB.identifier.id }]); - instantiationService.stub(IExtensionTipsService, 'getFileBasedRecommendations', [ + instantiationService.stub(IExtensionRecommendationsService, 'getFileBasedRecommendations', [ { extensionId: fileBasedRecommendationA.identifier.id }, { extensionId: fileBasedRecommendationB.identifier.id }]); - instantiationService.stubPromise(IExtensionTipsService, 'getOtherRecommendations', [ + instantiationService.stubPromise(IExtensionRecommendationsService, 'getOtherRecommendations', [ { extensionId: otherRecommendationA.identifier.id } ]); const reasons: { [key: string]: any } = {}; @@ -129,7 +129,7 @@ suite('ExtensionsListView Tests', () => { reasons[fileBasedRecommendationB.identifier.id] = { reasonId: ExtensionRecommendationReason.File }; reasons[otherRecommendationA.identifier.id] = { reasonId: ExtensionRecommendationReason.Executable }; - instantiationService.stub(IExtensionTipsService, 'getAllRecommendationsWithReason', reasons); + instantiationService.stub(IExtensionRecommendationsService, 'getAllRecommendationsWithReason', reasons); }); diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts index 9981a0234b..c8b47f34c5 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsWorkbenchService.test.ts @@ -14,10 +14,10 @@ import { IExtensionManagementService, IExtensionGalleryService, ILocalExtension, IGalleryExtension, DidInstallExtensionEvent, DidUninstallExtensionEvent, InstallExtensionEvent, IGalleryExtensionAssets, IExtensionIdentifier, InstallOperation } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionTipsService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionManagementServerService, IExtensionRecommendationsService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { getGalleryExtensionId } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { ExtensionManagementService } from 'vs/platform/extensionManagement/node/extensionManagementService'; -import { ExtensionTipsService } from 'vs/workbench/contrib/extensions/browser/extensionTipsService'; +import { ExtensionRecommendationsService } from 'vs/workbench/contrib/extensions/browser/extensionRecommendationsService'; import { TestExtensionEnablementService } from 'vs/workbench/services/extensionManagement/test/browser/extensionEnablementService.test'; import { ExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionGalleryService'; import { IURLService } from 'vs/platform/url/common/url'; @@ -91,7 +91,7 @@ suite('ExtensionsWorkbenchServiceTest', () => { instantiationService.stub(IWorkbenchExtensionEnablementService, new TestExtensionEnablementService(instantiationService)); - instantiationService.set(IExtensionTipsService, instantiationService.createInstance(ExtensionTipsService)); + instantiationService.set(IExtensionRecommendationsService, instantiationService.createInstance(ExtensionRecommendationsService)); instantiationService.stub(INotificationService, { prompt: () => null! }); }); diff --git a/src/vs/workbench/contrib/externalTerminal/node/externalTerminalService.test.ts b/src/vs/workbench/contrib/externalTerminal/node/externalTerminalService.test.ts index d827d813a9..241c9ae283 100644 --- a/src/vs/workbench/contrib/externalTerminal/node/externalTerminalService.test.ts +++ b/src/vs/workbench/contrib/externalTerminal/node/externalTerminalService.test.ts @@ -125,6 +125,28 @@ suite('ExternalTerminalService', () => { ); }); + test(`WinTerminalService - windows terminal should open workspace directory`, done => { + let testShell = 'wt'; + let testCwd = 'c:/foo'; + let mockSpawner = { + spawn: (command: any, args: any, opts: any) => { + // assert + equal(opts.cwd, 'C:/foo'); + done(); + return { on: (evt: any) => evt }; + } + }; + let testService = new WindowsExternalTerminalService(mockConfig); + (testService).spawnTerminal( + mockSpawner, + mockConfig, + testShell, + testCwd, + mockOnExit, + mockOnError + ); + }); + test(`MacTerminalService - uses terminal from configuration`, done => { let testCwd = 'path/to/workspace'; let mockSpawner = { diff --git a/src/vs/workbench/contrib/externalTerminal/node/externalTerminalService.ts b/src/vs/workbench/contrib/externalTerminal/node/externalTerminalService.ts index 7239d3e2d0..8bb1229ced 100644 --- a/src/vs/workbench/contrib/externalTerminal/node/externalTerminalService.ts +++ b/src/vs/workbench/contrib/externalTerminal/node/externalTerminalService.ts @@ -97,6 +97,10 @@ export class WindowsExternalTerminalService implements IExternalTerminalService cmdArgs.push('""'); } cmdArgs.push(exec); + // Add starting directory parameter for Windows Terminal (see #90734) + if (basename === 'wt' || basename === 'wt.exe') { + cmdArgs.push('-d .'); + } return new Promise((c, e) => { const env = cwd ? { cwd: cwd } : undefined; diff --git a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts index feecfe0134..d1313c404f 100644 --- a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts +++ b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts @@ -47,6 +47,7 @@ import { AutoSaveMode, IFilesConfigurationService } from 'vs/workbench/services/ import { IViewDescriptorService } from 'vs/workbench/common/views'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { Orientation } from 'vs/base/browser/ui/splitview/splitview'; +import { IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; const $ = dom.$; @@ -224,7 +225,8 @@ export class OpenEditorsView extends ViewPane { dnd: new OpenEditorsDragAndDrop(this.instantiationService, this.editorGroupService), overrideStyles: { listBackground: this.getBackgroundColor() - } + }, + accessibilityProvider: new OpenEditorsAccessibilityProvider() }); this._register(this.list); this._register(this.listLabels); @@ -688,3 +690,13 @@ class OpenEditorsDragAndDrop implements IListDragAndDrop { + getAriaLabel(element: OpenEditor | IEditorGroup): string | null { + if (element instanceof OpenEditor) { + return `${element.editor.getName()} ${element.editor.getDescription()}`; + } + + return element.ariaLabel; + } +} diff --git a/src/vs/workbench/contrib/files/electron-browser/files.contribution.ts b/src/vs/workbench/contrib/files/electron-browser/files.contribution.ts index 21d6deb5f0..118202aeef 100644 --- a/src/vs/workbench/contrib/files/electron-browser/files.contribution.ts +++ b/src/vs/workbench/contrib/files/electron-browser/files.contribution.ts @@ -3,7 +3,10 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as os from 'os'; +import * as fs from 'fs'; import * as nls from 'vs/nls'; +import { join } from 'vs/base/common/path'; import { Registry } from 'vs/platform/registry/common/platform'; import { EditorInput } from 'vs/workbench/common/editor'; import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput'; @@ -11,9 +14,6 @@ import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { IEditorRegistry, EditorDescriptor, Extensions as EditorExtensions } from 'vs/workbench/browser/editor'; import { NativeTextFileEditor } from 'vs/workbench/contrib/files/electron-browser/textFileEditor'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; -import * as os from 'os'; -import * as fs from 'fs'; -import * as path from 'path'; // Register file editor Registry.as(EditorExtensions.Editors).registerEditor( @@ -29,5 +29,5 @@ Registry.as(EditorExtensions.Editors).registerEditor( // Register mkdtemp command CommandsRegistry.registerCommand('mkdtemp', function () { - return fs.promises.mkdtemp(path.join(os.tmpdir(), 'vscodetmp-')); + return fs.promises.mkdtemp(join(os.tmpdir(), 'vscodetmp-')); }); diff --git a/src/vs/workbench/contrib/issue/browser/issueService.ts b/src/vs/workbench/contrib/issue/browser/issueService.ts index 35edf62fce..a8b0e402ef 100644 --- a/src/vs/workbench/contrib/issue/browser/issueService.ts +++ b/src/vs/workbench/contrib/issue/browser/issueService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from 'vs/base/common/uri'; -import { normalizeGitHubUrl } from 'vs/code/common/issue/issueReporterUtil'; +import { normalizeGitHubUrl } from 'vs/platform/issue/common/issueReporterUtil'; import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { ExtensionType } from 'vs/platform/extensions/common/extensions'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; diff --git a/src/vs/workbench/contrib/markers/browser/markersView.ts b/src/vs/workbench/contrib/markers/browser/markersView.ts index 1c49de445b..c31c496e56 100644 --- a/src/vs/workbench/contrib/markers/browser/markersView.ts +++ b/src/vs/workbench/contrib/markers/browser/markersView.ts @@ -23,7 +23,7 @@ import { IMarkersWorkbenchService } from 'vs/workbench/contrib/markers/browser/m import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { localize } from 'vs/nls'; import { IContextKey, IContextKeyService, ContextKeyEqualsExpr, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { Iterator } from 'vs/base/common/iterator'; +import { Iterable } from 'vs/base/common/iterator'; import { ITreeElement, ITreeNode, ITreeContextMenuEvent, ITreeRenderer } from 'vs/base/browser/ui/tree/tree'; import { Relay, Event, Emitter } from 'vs/base/common/event'; import { WorkbenchObjectTree, ResourceNavigator, IListService, IWorkbenchObjectTreeOptions } from 'vs/platform/list/browser/listService'; @@ -50,16 +50,13 @@ import { ViewPane, IViewPaneOptions } from 'vs/workbench/browser/parts/views/vie import { IViewDescriptorService } from 'vs/workbench/common/views'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -function createResourceMarkersIterator(resourceMarkers: ResourceMarkers): Iterator> { - const markersIt = Iterator.fromArray(resourceMarkers.markers); - - return Iterator.map(markersIt, m => { - const relatedInformationIt = Iterator.from(m.relatedInformation); - const children = Iterator.map(relatedInformationIt, r => ({ element: r })); +function createResourceMarkersIterator(resourceMarkers: ResourceMarkers): Iterable> { + return Iterable.map(resourceMarkers.markers, m => { + const relatedInformationIt = Iterable.from(m.relatedInformation); + const children = Iterable.map(relatedInformationIt, r => ({ element: r })); return { element: m, children }; }); - } export class MarkersView extends ViewPane implements IMarkerFilterController { @@ -338,7 +335,7 @@ export class MarkersView extends ViewPane implements IMarkerFilterController { } else { resourceMarkers = this.markersWorkbenchService.markersModel.resourceMarkers; } - this.tree.setChildren(null, Iterator.map(Iterator.fromArray(resourceMarkers), m => ({ element: m, children: createResourceMarkersIterator(m) }))); + this.tree.setChildren(null, Iterable.map(resourceMarkers, m => ({ element: m, children: createResourceMarkersIterator(m) }))); } private updateFilter() { diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/notebookActions.ts b/src/vs/workbench/contrib/notebook/browser/contrib/notebookActions.ts index b2f531c1ed..1017611d55 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/notebookActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/notebookActions.ts @@ -32,13 +32,14 @@ const COPY_CELL_UP_COMMAND_ID = 'workbench.notebook.cell.copyUp'; const COPY_CELL_DOWN_COMMAND_ID = 'workbench.notebook.cell.copyDown'; const EXECUTE_CELL_COMMAND_ID = 'workbench.notebook.cell.execute'; -const EXECUTE_ACTIVE_CELL_COMMAND_ID = 'workbench.notebook.cell.executeActive'; const CANCEL_CELL_COMMAND_ID = 'workbench.notebook.cell.cancelExecution'; const EXECUTE_NOTEBOOK_COMMAND_ID = 'workbench.notebook.executeNotebook'; const CANCEL_NOTEBOOK_COMMAND_ID = 'workbench.notebook.cancelExecution'; const NOTEBOOK_ACTIONS_CATEGORY = localize('notebookActions.category', "Notebook"); +const EDITOR_WIDGET_ACTION_WEIGHT = KeybindingWeight.EditorContrib; // smaller than Suggest Widget, etc + const enum CellToolbarOrder { MoveCellUp, MoveCellDown, @@ -60,7 +61,7 @@ registerAction2(class extends Action2 { win: { primary: KeyMod.WinCtrl | KeyMod.Alt | KeyCode.Enter }, - weight: KeybindingWeight.WorkbenchContrib + weight: EDITOR_WIDGET_ACTION_WEIGHT }, icon: { id: 'codicon/play' }, f1: true @@ -68,7 +69,7 @@ registerAction2(class extends Action2 { } async run(accessor: ServicesAccessor, context?: INotebookCellActionContext): Promise { - if (!context) { + if (!isCellActionContext(context)) { context = getActiveCellContext(accessor); if (!context) { return; @@ -83,13 +84,15 @@ registerAction2(class extends Action2 { constructor() { super({ id: CANCEL_CELL_COMMAND_ID, - title: localize('notebookActions.cancel', "Stop Execution"), - icon: { id: 'codicon/primitive-square' } + title: localize('notebookActions.cancel', "Stop Cell Execution"), + category: NOTEBOOK_ACTIONS_CATEGORY, + icon: { id: 'codicon/primitive-square' }, + f1: true }); } async run(accessor: ServicesAccessor, context?: INotebookCellActionContext): Promise { - if (!context) { + if (!isCellActionContext(context)) { context = getActiveCellContext(accessor); if (!context) { return; @@ -145,7 +148,7 @@ registerAction2(class extends Action2 { keybinding: { when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, InputFocusedContext), primary: KeyMod.Shift | KeyCode.Enter, - weight: KeybindingWeight.WorkbenchContrib + weight: EDITOR_WIDGET_ACTION_WEIGHT } }); } @@ -185,7 +188,7 @@ registerAction2(class extends Action2 { keybinding: { when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, InputFocusedContext), primary: KeyMod.Alt | KeyCode.Enter, - weight: KeybindingWeight.WorkbenchContrib + weight: EDITOR_WIDGET_ACTION_WEIGHT } }); } @@ -210,7 +213,9 @@ registerAction2(class extends Action2 { constructor() { super({ id: EXECUTE_NOTEBOOK_COMMAND_ID, - title: localize('notebookActions.executeNotebook', "Execute Notebook") + title: localize('notebookActions.executeNotebook', "Execute Notebook"), + category: NOTEBOOK_ACTIONS_CATEGORY, + f1: true }); } @@ -229,7 +234,9 @@ registerAction2(class extends Action2 { constructor() { super({ id: CANCEL_NOTEBOOK_COMMAND_ID, - title: localize('notebookActions.cancelNotebook', "Cancel Notebook Execution") + title: localize('notebookActions.cancelNotebook', "Cancel Notebook Execution"), + category: NOTEBOOK_ACTIONS_CATEGORY, + f1: true }); } @@ -244,29 +251,6 @@ registerAction2(class extends Action2 { } }); -registerAction2(class extends Action2 { - constructor() { - super({ - id: EXECUTE_ACTIVE_CELL_COMMAND_ID, - title: localize('notebookActions.executeNotebookCell', "Execute Notebook Active Cell") - }); - } - - async run(accessor: ServicesAccessor): Promise { - let editorService = accessor.get(IEditorService); - let editor = getActiveNotebookEditor(editorService); - - if (!editor) { - return; - } - - let activeCell = editor.getActiveCell(); - if (activeCell) { - return editor.executeNotebookCell(activeCell); - } - } -}); - registerAction2(class extends Action2 { constructor() { super({ @@ -275,7 +259,7 @@ registerAction2(class extends Action2 { keybinding: { when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, InputFocusedContext), primary: KeyCode.Escape, - weight: KeybindingWeight.EditorContrib - 5 + weight: EDITOR_WIDGET_ACTION_WEIGHT - 5 } }); } @@ -366,7 +350,7 @@ MenuRegistry.appendMenuItem(MenuId.EditorTitle, { MenuRegistry.appendMenuItem(MenuId.EditorTitle, { command: { - id: EXECUTE_ACTIVE_CELL_COMMAND_ID, + id: EXECUTE_CELL_COMMAND_ID, title: localize('notebookActions.menu.execute', "Execute Notebook Cell"), icon: { id: 'codicon/run' } }, @@ -384,7 +368,9 @@ registerAction2(class extends Action2 { when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)), primary: KeyCode.KEY_Y, weight: KeybindingWeight.WorkbenchContrib - } + }, + category: NOTEBOOK_ACTIONS_CATEGORY, + f1: true }); } @@ -402,7 +388,9 @@ registerAction2(class extends Action2 { when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)), primary: KeyCode.KEY_M, weight: KeybindingWeight.WorkbenchContrib - } + }, + category: NOTEBOOK_ACTIONS_CATEGORY, + f1: true }); } @@ -479,6 +467,10 @@ export interface INotebookCellActionContext { notebookEditor: INotebookEditor; } +function isCellActionContext(context: any): context is INotebookCellActionContext { + return context && !!context.cell && !!context.notebookEditor; +} + function getActiveCellContext(accessor: ServicesAccessor): INotebookCellActionContext | undefined { const editorService = accessor.get(IEditorService); @@ -508,7 +500,7 @@ abstract class InsertCellCommand extends Action2 { } async run(accessor: ServicesAccessor, context?: INotebookCellActionContext): Promise { - if (!context) { + if (!isCellActionContext(context)) { context = getActiveCellContext(accessor); if (!context) { return; @@ -639,7 +631,7 @@ registerAction2(class extends Action2 { } run(accessor: ServicesAccessor, context?: INotebookCellActionContext) { - if (!context) { + if (!isCellActionContext(context)) { context = getActiveCellContext(accessor); if (!context) { return; @@ -669,7 +661,7 @@ registerAction2(class extends Action2 { } run(accessor: ServicesAccessor, context?: INotebookCellActionContext) { - if (!context) { + if (!isCellActionContext(context)) { context = getActiveCellContext(accessor); if (!context) { return; @@ -693,13 +685,21 @@ registerAction2(class extends Action2 { order: CellToolbarOrder.DeleteCell, when: ContextKeyExpr.equals(NOTEBOOK_EDITABLE_CONTEXT_KEY, true) }, + keybinding: { + primary: KeyCode.Delete, + mac: { + primary: KeyMod.CtrlCmd | KeyCode.Backspace + }, + when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.not(InputFocusedContextKey)), + weight: KeybindingWeight.WorkbenchContrib + }, icon: { id: 'codicon/trash' }, f1: true }); } run(accessor: ServicesAccessor, context?: INotebookCellActionContext) { - if (!context) { + if (!isCellActionContext(context)) { context = getActiveCellContext(accessor); if (!context) { return; @@ -745,7 +745,7 @@ registerAction2(class extends Action2 { } async run(accessor: ServicesAccessor, context?: INotebookCellActionContext) { - if (!context) { + if (!isCellActionContext(context)) { context = getActiveCellContext(accessor); if (!context) { return; @@ -779,7 +779,7 @@ registerAction2(class extends Action2 { } async run(accessor: ServicesAccessor, context?: INotebookCellActionContext) { - if (!context) { + if (!isCellActionContext(context)) { context = getActiveCellContext(accessor); if (!context) { return; @@ -795,12 +795,14 @@ registerAction2(class extends Action2 { super( { id: COPY_CELL_UP_COMMAND_ID, - title: localize('notebookActions.copyCellUp', "Copy Cell Up") + title: localize('notebookActions.copyCellUp', "Copy Cell Up"), + category: NOTEBOOK_ACTIONS_CATEGORY, + f1: true }); } async run(accessor: ServicesAccessor, context?: INotebookCellActionContext) { - if (!context) { + if (!isCellActionContext(context)) { context = getActiveCellContext(accessor); if (!context) { return; @@ -816,12 +818,14 @@ registerAction2(class extends Action2 { super( { id: COPY_CELL_DOWN_COMMAND_ID, - title: localize('notebookActions.copyCellDown', "Copy Cell Down") + title: localize('notebookActions.copyCellDown', "Copy Cell Down"), + category: NOTEBOOK_ACTIONS_CATEGORY, + f1: true }); } async run(accessor: ServicesAccessor, context?: INotebookCellActionContext) { - if (!context) { + if (!isCellActionContext(context)) { context = getActiveCellContext(accessor); if (!context) { return; @@ -840,13 +844,13 @@ registerAction2(class extends Action2 { keybinding: { when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.has(InputFocusedContextKey), EditorContextKeys.editorTextFocus, NOTEBOOK_EDITOR_CURSOR_BOUNDARY.notEqualsTo('top'), NOTEBOOK_EDITOR_CURSOR_BOUNDARY.notEqualsTo('none')), primary: KeyCode.DownArrow, - weight: KeybindingWeight.EditorContrib // smaller than Suggest Widget, etc + weight: EDITOR_WIDGET_ACTION_WEIGHT } }); } async run(accessor: ServicesAccessor, context?: INotebookCellActionContext): Promise { - if (!context) { + if (!isCellActionContext(context)) { context = getActiveCellContext(accessor); if (!context) { return; @@ -879,13 +883,13 @@ registerAction2(class extends Action2 { keybinding: { when: ContextKeyExpr.and(NOTEBOOK_EDITOR_FOCUSED, ContextKeyExpr.has(InputFocusedContextKey), EditorContextKeys.editorTextFocus, NOTEBOOK_EDITOR_CURSOR_BOUNDARY.notEqualsTo('bottom'), NOTEBOOK_EDITOR_CURSOR_BOUNDARY.notEqualsTo('none')), primary: KeyCode.UpArrow, - weight: KeybindingWeight.EditorContrib // smaller than Suggest Widget, etc + weight: EDITOR_WIDGET_ACTION_WEIGHT }, }); } async run(accessor: ServicesAccessor, context?: INotebookCellActionContext): Promise { - if (!context) { + if (!isCellActionContext(context)) { context = getActiveCellContext(accessor); if (!context) { return; diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.css b/src/vs/workbench/contrib/notebook/browser/media/notebook.css similarity index 98% rename from src/vs/workbench/contrib/notebook/browser/notebook.css rename to src/vs/workbench/contrib/notebook/browser/media/notebook.css index 20899957f1..7fc79d3d12 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.css +++ b/src/vs/workbench/contrib/notebook/browser/media/notebook.css @@ -215,6 +215,7 @@ cursor: auto; } +.monaco-workbench .part.editor > .content .notebook-editor > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container:focus-within, .monaco-workbench .part.editor > .content .notebook-editor > .cell-list-container > .monaco-list > .monaco-scrollable-element > .monaco-list-rows > .monaco-list-row .cell-bottom-toolbar-container:hover { opacity: 1; } @@ -372,7 +373,7 @@ /** Theming */ -.monaco-workbench .part.editor > .content .notebook-editor .cell.markdown pre { +/* .monaco-workbench .part.editor > .content .notebook-editor .cell.markdown pre { background-color: rgba(220, 220, 220, 0.4); } @@ -406,4 +407,4 @@ .vs-dark .monaco-workbench .part.editor > .content .notebook-editor .cell.markdown hr, .vs-dark .monaco-workbench .part.editor > .content .notebook-editor .cell.markdown table > tbody > tr > td { border-color: rgba(255, 255, 255, 0.18); -} +} */ diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts index 242a99baa3..b207ade62f 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts @@ -3,6 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import 'vs/css!./media/notebook'; import { getZoomLevel } from 'vs/base/browser/browser'; import * as DOM from 'vs/base/browser/dom'; import { IMouseWheelEvent, StandardMouseEvent } from 'vs/base/browser/mouseEvent'; @@ -10,7 +11,6 @@ import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cance import { Color, RGBA } from 'vs/base/common/color'; import { Emitter, Event } from 'vs/base/common/event'; import { DisposableStore, MutableDisposable } from 'vs/base/common/lifecycle'; -import 'vs/css!./notebook'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; import { BareFontInfo } from 'vs/editor/common/config/fontInfo'; diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorInput.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorInput.ts index 6c2d9c2769..b6508bd0fa 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorInput.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorInput.ts @@ -126,7 +126,9 @@ export class NotebookEditorInput extends EditorInput { async resolve(): Promise { if (!this.promise) { - await this.notebookService.canResolve(this.viewType!); + if (!await this.notebookService.canResolve(this.viewType!)) { + throw new Error(`Cannot open notebook of type '${this.viewType}'`); + } this.promise = this.notebookService.resolveNotebook(this.viewType!, this.resource).then(notebook => { this.textModel = new NotebookEditorModel(notebook!); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookService.ts b/src/vs/workbench/contrib/notebook/browser/notebookService.ts index ef4d4e32d0..ad11416dd6 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookService.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookService.ts @@ -34,7 +34,7 @@ export interface IMainNotebookController { export interface INotebookService { _serviceBrand: undefined; - canResolve(viewType: string): Promise; + canResolve(viewType: string): Promise; onDidChangeActiveEditor: Event<{ viewType: string, uri: URI }>; registerNotebookController(viewType: string, extensionData: NotebookExtensionDescription, controller: IMainNotebookController): void; unregisterNotebookProvider(viewType: string): void; @@ -127,7 +127,6 @@ export class NotebookService extends Disposable implements INotebookService { private readonly _models: { [modelId: string]: ModelData; }; private _onDidChangeActiveEditor = new Emitter<{ viewType: string, uri: URI }>(); onDidChangeActiveEditor: Event<{ viewType: string, uri: URI }> = this._onDidChangeActiveEditor.event; - private _resolvePool = new Map void)[]>(); constructor( @IExtensionService private readonly extensionService: IExtensionService @@ -167,33 +166,16 @@ export class NotebookService extends Disposable implements INotebookService { }); } - async canResolve(viewType: string): Promise { - if (this._notebookProviders.has(viewType)) { - return; + async canResolve(viewType: string): Promise { + if (!this._notebookProviders.has(viewType)) { + // this awaits full activation of all matching extensions + await this.extensionService.activateByEvent(`onNotebookEditor:${viewType}`); } - - this.extensionService.activateByEvent(`onNotebookEditor:${viewType}`); - - let resolve: () => void; - const promise = new Promise(r => { resolve = r; }); - if (!this._resolvePool.has(viewType)) { - this._resolvePool.set(viewType, []); - } - - let resolves = this._resolvePool.get(viewType)!; - resolves.push(resolve!); - this._resolvePool.set(viewType, resolves); - return promise; + return this._notebookProviders.has(viewType); } registerNotebookController(viewType: string, extensionData: NotebookExtensionDescription, controller: IMainNotebookController) { this._notebookProviders.set(viewType, { extensionData, controller }); - - let resolves = this._resolvePool.get(viewType); - if (resolves) { - resolves.forEach(resolve => resolve()); - this._resolvePool.delete(viewType); - } } unregisterNotebookProvider(viewType: string): void { diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts index ccca6a0d04..5b398de647 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts @@ -269,9 +269,7 @@ export class NotebookCellList extends WorkbenchList implements ID this.view.setScrollTop(lineOffsetInView - this.view.renderHeight / 2); if (revealType === CellRevealType.Range) { - setTimeout(() => { - element.revealRangeInCenter(range); - }, 240); + element.revealRangeInCenter(range); } }; diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index e672c2cf65..649a0da740 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -3,6 +3,8 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +// eslint-disable-next-line code-import-patterns +import 'vs/css!vs/workbench/contrib/notebook/browser/media/notebook'; import { getZoomLevel } from 'vs/base/browser/browser'; import * as DOM from 'vs/base/browser/dom'; import { IListRenderer, IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; @@ -12,7 +14,6 @@ import { IAction, ActionRunner } from 'vs/base/common/actions'; import { escape } from 'vs/base/common/strings'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { deepClone } from 'vs/base/common/objects'; -import 'vs/css!vs/workbench/contrib/notebook/browser/notebook'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; import { IEditorOptions } from 'vs/editor/common/config/editorOptions'; @@ -147,6 +148,7 @@ abstract class AbstractCellRenderer { DOM.append(container, $('.seperator')); const addCodeCell = DOM.append(container, $('span.button')); addCodeCell.innerHTML = renderCodicons(escape(`$(add) Code `)); + addCodeCell.tabIndex = 0; const insertCellBelow = this.instantiationService.createInstance(InsertCodeCellAction); disposables.add(DOM.addDisposableListener(addCodeCell, DOM.EventType.CLICK, () => { @@ -156,6 +158,7 @@ abstract class AbstractCellRenderer { DOM.append(container, $('.seperator-short')); const addMarkdownCell = DOM.append(container, $('span.button')); addMarkdownCell.innerHTML = renderCodicons(escape('$(add) Markdown ')); + addMarkdownCell.tabIndex = 0; const insertMarkdownBelow = this.instantiationService.createInstance(InsertMarkdownCellAction); disposables.add(DOM.addDisposableListener(addMarkdownCell, DOM.EventType.CLICK, () => { this.actionRunner.run(insertMarkdownBelow, context); @@ -374,7 +377,6 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende const runToolbar = this.createToolbar(runButtonContainer); disposables.add(runToolbar); - const bottomCellContainer = DOM.append(container, $('.cell-bottom-toolbar-container')); const executionOrderLabel = DOM.append(runButtonContainer, $('div.execution-count-label')); @@ -397,6 +399,8 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende progressBar.hide(); disposables.add(progressBar); + const bottomCellContainer = DOM.append(container, $('.cell-bottom-toolbar-container')); + return { container, cellContainer, diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts index 620d1c769d..f612dfde51 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/codeCell.ts @@ -16,6 +16,8 @@ import { INotebookService } from 'vs/workbench/contrib/notebook/browser/notebook import { getResizesObserver } from 'vs/workbench/contrib/notebook/browser/view/renderers/sizeObserver'; import { CodeCellViewModel } from 'vs/workbench/contrib/notebook/browser/viewModel/codeCellViewModel'; import { CellOutputKind, IOutput, IRenderOutput, ITransformedDisplayOutputDto } from 'vs/workbench/contrib/notebook/common/notebookCommon'; +import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; +import { KeyCode } from 'vs/base/common/keyCodes'; interface IMimeTypeRenderer extends IQuickPickItem { index: number; @@ -211,7 +213,7 @@ export class CodeCell extends Disposable { if (viewCell.outputs.length > 0) { let layoutCache = false; - if (this.viewCell.layoutInfo.totalHeight !== 0) { + if (this.viewCell.layoutInfo.totalHeight !== 0 && this.viewCell.layoutInfo.totalHeight > totalHeight) { layoutCache = true; this.relayoutCell(); } @@ -255,12 +257,26 @@ export class CodeCell extends Disposable { outputItemDiv.style.position = 'relative'; const mimeTypePicker = DOM.$('.multi-mimetype-output'); DOM.addClasses(mimeTypePicker, 'codicon', 'codicon-code'); + mimeTypePicker.tabIndex = 0; + mimeTypePicker.title = nls.localize('mimeTypePicker', "Choose a different output mimetype"); outputItemDiv.appendChild(mimeTypePicker); this.outputResizeListeners.get(currOutput)!.add(DOM.addStandardDisposableListener(mimeTypePicker, 'mousedown', async e => { - e.preventDefault(); - e.stopPropagation(); - await this.pickActiveMimeTypeRenderer(transformedDisplayOutput); + if (e.leftButton) { + e.preventDefault(); + e.stopPropagation(); + await this.pickActiveMimeTypeRenderer(transformedDisplayOutput); + } })); + + this.outputResizeListeners.get(currOutput)!.add((DOM.addDisposableListener(mimeTypePicker, DOM.EventType.KEY_DOWN, async e => { + const event = new StandardKeyboardEvent(e); + if ((event.equals(KeyCode.Enter) || event.equals(KeyCode.Space))) { + e.preventDefault(); + e.stopPropagation(); + await this.pickActiveMimeTypeRenderer(transformedDisplayOutput); + } + }))); + } let pickedMimeTypeRenderer = currOutput.orderedMimeTypes[currOutput.pickedMimeTypeIndex]; @@ -341,7 +357,7 @@ export class CodeCell extends Disposable { generateRendererInfo(renderId: number | undefined): string { if (renderId === undefined || renderId === -1) { - return 'builtin'; + return nls.localize('builtinRenderInfo', "built-in"); } let renderInfo = this.notebookService.getRendererInfo(renderId); @@ -350,7 +366,7 @@ export class CodeCell extends Disposable { return renderInfo.id.value; } - return 'builtin'; + return nls.localize('builtinRenderInfo', "built-in"); } async pickActiveMimeTypeRenderer(output: ITransformedDisplayOutputDto) { diff --git a/src/vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel.ts b/src/vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel.ts index 8e19cdd982..f0c15308a7 100644 --- a/src/vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/viewModel/markdownCellViewModel.ts @@ -66,10 +66,10 @@ export class MarkdownCellViewModel extends BaseCellViewModel implements ICellVie layoutChange(state: MarkdownCellLayoutChangeEvent) { // recompute - const editorWidth = state.outerWidth !== undefined ? state.outerWidth - CELL_MARGIN * 2 - CELL_RUN_GUTTER : 0; + const editorWidth = state.outerWidth !== undefined ? state.outerWidth - CELL_MARGIN * 2 - CELL_RUN_GUTTER : this._layoutInfo.editorWidth; this._layoutInfo = { - fontInfo: state.font || null, + fontInfo: state.font || this._layoutInfo.fontInfo, editorWidth, bottomToolbarOffset: BOTTOM_CELL_TOOLBAR_HEIGHT, totalHeight: state.totalHeight === undefined ? this._layoutInfo.totalHeight : state.totalHeight diff --git a/src/vs/workbench/contrib/outline/browser/outline.contribution.ts b/src/vs/workbench/contrib/outline/browser/outline.contribution.ts index c7d3694c4b..80dd501ef2 100644 --- a/src/vs/workbench/contrib/outline/browser/outline.contribution.ts +++ b/src/vs/workbench/contrib/outline/browser/outline.contribution.ts @@ -12,13 +12,12 @@ import { OutlineConfigKeys, OutlineViewId } from 'vs/editor/contrib/documentSymb import { VIEW_CONTAINER } from 'vs/workbench/contrib/files/browser/explorerViewlet'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; -// import './outlineNavigation'; - export const PANEL_ID = 'panel.view.outline'; const _outlineDesc = { id: OutlineViewId, name: localize('name', "Outline"), + containerIcon: 'codicon-symbol-class', ctorDescriptor: new SyncDescriptor(OutlinePane), canToggleVisibility: true, canMoveView: true, diff --git a/src/vs/workbench/contrib/outline/browser/outlineNavigation.ts b/src/vs/workbench/contrib/outline/browser/outlineNavigation.ts deleted file mode 100644 index 2641fcc3a2..0000000000 --- a/src/vs/workbench/contrib/outline/browser/outlineNavigation.ts +++ /dev/null @@ -1,202 +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 { Range } from 'vs/editor/common/core/range'; -import { IPosition, Position } from 'vs/editor/common/core/position'; -import { OutlineElement, OutlineModel, TreeElement } from 'vs/editor/contrib/documentSymbols/outlineModel'; -import { localize } from 'vs/nls'; -import { IEditorContribution, ScrollType } from 'vs/editor/common/editorCommon'; -import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; -import { CancellationTokenSource } from 'vs/base/common/cancellation'; -import { EditorStateCancellationTokenSource, CodeEditorStateFlag } from 'vs/editor/browser/core/editorState'; -import { EditorAction, registerEditorAction, registerEditorContribution } from 'vs/editor/browser/editorExtensions'; -import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; -import { forEach } from 'vs/base/common/collections'; -import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; -import { OutlineFilter } from 'vs/editor/contrib/documentSymbols/outlineTree'; -import { binarySearch } from 'vs/base/common/arrays'; -import { ITextResourceConfigurationService } from 'vs/editor/common/services/textResourceConfigurationService'; - -class FlatOutline { - - readonly elements: OutlineElement[] = []; - readonly _elementPositions: IPosition[]; - - constructor(model: OutlineModel, filter: OutlineFilter) { - - const walk = (element: TreeElement) => { - if (element instanceof OutlineElement && !filter.filter(element)) { - return; - } - if (element instanceof OutlineElement) { - this.elements.push(element); - } - forEach(element.children, entry => walk(entry.value)); - }; - - walk(model); - this.elements.sort(FlatOutline._compare); - this._elementPositions = this.elements.map(element => ({ - lineNumber: element.symbol.range.startLineNumber, - column: element.symbol.range.startColumn - })); - } - - private static _compare(a: TreeElement, b: TreeElement): number { - return (a instanceof OutlineElement && b instanceof OutlineElement) - ? Range.compareRangesUsingStarts(a.symbol.range, b.symbol.range) - : 0; - } - - find(position: IPosition, preferAfter: boolean): number { - const idx = binarySearch(this._elementPositions, position, Position.compare); - if (idx >= 0) { - return idx; - } else if (preferAfter) { - return ~idx; - } else { - return ~idx - 1; - } - } -} - -export class OutlineNavigation implements IEditorContribution { - - public static readonly ID = 'editor.contrib.OutlineNavigation'; - - public static get(editor: ICodeEditor): OutlineNavigation { - return editor.getContribution(OutlineNavigation.ID); - } - - private readonly _editor: ICodeEditor; - - private _cts?: CancellationTokenSource; - - constructor( - editor: ICodeEditor, - @ITextResourceConfigurationService private readonly _textResourceConfigService: ITextResourceConfigurationService, - ) { - this._editor = editor; - } - - dispose(): void { - if (this._cts) { - this._cts.dispose(true); - } - } - - async goto(up: boolean) { - - if (this._cts) { - this._cts.dispose(true); - } - - if (!this._editor.hasModel()) { - return; - } - - const textModel = this._editor.getModel(); - const position = this._editor.getPosition(); - - this._cts = new EditorStateCancellationTokenSource(this._editor, CodeEditorStateFlag.Position | CodeEditorStateFlag.Value | CodeEditorStateFlag.Scroll); - - const filter = new OutlineFilter('outline', this._textResourceConfigService); - const outlineModel = await OutlineModel.create(textModel, this._cts.token); - - if (this._cts.token.isCancellationRequested) { - return; - } - - const symbols = new FlatOutline(outlineModel, filter); - const idx = symbols.find(position, !up); - const element = symbols.elements[idx]; - - if (element) { - if (Range.containsPosition(element.symbol.selectionRange, position)) { - // at the "name" of a symbol -> move - const nextElement = symbols.elements[idx + (up ? -1 : +1)]; - this._revealElement(nextElement); - - } else { - // enclosing, lastBefore, or firstAfter element - this._revealElement(element); - } - } - } - - private _revealElement(element: OutlineElement | undefined): void { - if (!element) { - return; - } - const pos = Range.lift(element.symbol.selectionRange).getStartPosition(); - this._editor.setPosition(pos); - this._editor.revealPosition(pos, ScrollType.Smooth); - - const modelNow = this._editor.getModel(); - const ids = this._editor.deltaDecorations([], [{ - range: element.symbol.selectionRange, - options: { - className: 'symbolHighlight', - } - }]); - setTimeout(() => { - if (modelNow === this._editor.getModel()) { - this._editor.deltaDecorations(ids, []); - } - }, 350); - } -} - -registerEditorContribution(OutlineNavigation.ID, OutlineNavigation); - -registerEditorAction(class extends EditorAction { - - constructor() { - super({ - id: 'editor.action.gotoNextSymbol', - label: localize('label.next', "Go to Next Symbol"), - alias: 'Go to Next Symbol', - precondition: EditorContextKeys.hasDocumentSymbolProvider, - kbOpts: { - weight: KeybindingWeight.EditorContrib, - kbExpr: EditorContextKeys.focus, - primary: undefined, - mac: { - primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.DownArrow, - }, - } - }); - } - - run(_accessor: ServicesAccessor, editor: ICodeEditor): void | Promise { - OutlineNavigation.get(editor).goto(false); - } -}); - -registerEditorAction(class extends EditorAction { - - constructor() { - super({ - id: 'editor.action.gotoPrevSymbol', - label: localize('label.prev', "Go to Previous Symbol"), - alias: 'Go to Previous Symbol', - precondition: EditorContextKeys.hasDocumentSymbolProvider, - kbOpts: { - weight: KeybindingWeight.EditorContrib, - kbExpr: EditorContextKeys.focus, - primary: undefined, - mac: { - primary: KeyMod.WinCtrl | KeyMod.Shift | KeyCode.UpArrow, - }, - } - }); - } - - run(_accessor: ServicesAccessor, editor: ICodeEditor): void | Promise { - OutlineNavigation.get(editor).goto(true); - } -}); diff --git a/src/vs/workbench/contrib/output/browser/media/output.css b/src/vs/workbench/contrib/output/browser/media/output.css index 3c95882b6c..c28e5070a0 100644 --- a/src/vs/workbench/contrib/output/browser/media/output.css +++ b/src/vs/workbench/contrib/output/browser/media/output.css @@ -3,7 +3,19 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.monaco-workbench .part > .title > .title-actions .switch-output { + +.monaco-workbench .part.sidebar > .title > .title-actions .switch-output, +.monaco-pane-view .pane > .pane-header .monaco-action-bar .switch-output { + border-width: 1px; + border-style: solid; +} + +.part.panel > .title > .title-actions .switch-output > .monaco-select-box { + border-width: 1px; + border-style: solid; +} + +.monaco-workbench .part.sidebar > .title > .title-actions .switch-output { display: flex; align-items: center; font-size: 11px; @@ -13,11 +25,11 @@ margin-top: 7px; } -.monaco-workbench.mac .part > .title > .title-actions .switch-output { +.monaco-workbench.mac .part.sidebar > .title > .title-actions .switch-output { border-radius: 4px; } -.monaco-workbench .part > .title > .title-actions .switch-output > .monaco-select-box { +.monaco-workbench .part.sidebar > .title > .title-actions .switch-output > .monaco-select-box { border: none !important; display: block !important; background-color: unset !important; @@ -27,6 +39,6 @@ border: none !important; } -.monaco-workbench .part > .title > .title-actions .switch-output > .monaco-select-box { +.monaco-workbench .part.sidebar > .title > .title-actions .switch-output > .monaco-select-box { padding: 0 22px 0 6px; } diff --git a/src/vs/workbench/contrib/output/browser/outputView.ts b/src/vs/workbench/contrib/output/browser/outputView.ts index fbfadaa7a6..3161c0c0de 100644 --- a/src/vs/workbench/contrib/output/browser/outputView.ts +++ b/src/vs/workbench/contrib/output/browser/outputView.ts @@ -293,7 +293,7 @@ class SwitchOutputActionViewItem extends SelectActionViewItem { super.render(container); addClass(container, 'switch-output'); this._register(attachStylerCallback(this.themeService, { selectBorder }, colors => { - container.style.border = colors.selectBorder ? `1px solid ${colors.selectBorder}` : ''; + container.style.borderColor = colors.selectBorder ? `${colors.selectBorder}` : ''; })); } diff --git a/src/vs/workbench/contrib/performance/electron-browser/startupProfiler.ts b/src/vs/workbench/contrib/performance/electron-browser/startupProfiler.ts index 199997ee7c..9c5791a251 100644 --- a/src/vs/workbench/contrib/performance/electron-browser/startupProfiler.ts +++ b/src/vs/workbench/contrib/performance/electron-browser/startupProfiler.ts @@ -8,7 +8,8 @@ import { exists, readdir, readFile, rimraf } from 'vs/base/node/pfs'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { localize } from 'vs/nls'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-browser/environmentService'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import product from 'vs/platform/product/common/product'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; @@ -24,7 +25,7 @@ export class StartupProfiler implements IWorkbenchContribution { constructor( @IDialogService private readonly _dialogService: IDialogService, - @IEnvironmentService private readonly _environmentService: IEnvironmentService, + @IWorkbenchEnvironmentService private readonly _environmentService: INativeWorkbenchEnvironmentService, @ITextModelService private readonly _textModelResolverService: ITextModelService, @IClipboardService private readonly _clipboardService: IClipboardService, @ILifecycleService lifecycleService: ILifecycleService, diff --git a/src/vs/workbench/contrib/performance/electron-browser/startupTimings.ts b/src/vs/workbench/contrib/performance/electron-browser/startupTimings.ts index e4550c3077..7cbbfc6ecc 100644 --- a/src/vs/workbench/contrib/performance/electron-browser/startupTimings.ts +++ b/src/vs/workbench/contrib/performance/electron-browser/startupTimings.ts @@ -8,7 +8,8 @@ import { timeout } from 'vs/base/common/async'; import { promisify } from 'util'; import { onUnexpectedError } from 'vs/base/common/errors'; import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-browser/environmentService'; import { ILifecycleService, StartupKind } from 'vs/platform/lifecycle/common/lifecycle'; import product from 'vs/platform/product/common/product'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -33,7 +34,7 @@ export class StartupTimings implements IWorkbenchContribution { @ITelemetryService private readonly _telemetryService: ITelemetryService, @ILifecycleService private readonly _lifecycleService: ILifecycleService, @IUpdateService private readonly _updateService: IUpdateService, - @IEnvironmentService private readonly _envService: IEnvironmentService + @IWorkbenchEnvironmentService private readonly _envService: INativeWorkbenchEnvironmentService ) { // this._report().catch(onUnexpectedError); diff --git a/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts b/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts index 1fc7e907d1..e7c0203775 100644 --- a/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts +++ b/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts @@ -46,6 +46,7 @@ import { IStorageService } from 'vs/platform/storage/common/storage'; import { InputBox, MessageType } from 'vs/base/browser/ui/inputbox/inputBox'; import { Emitter, Event } from 'vs/base/common/event'; import { MenuRegistry, MenuId, isIMenuItem } from 'vs/platform/actions/common/actions'; +import { IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; const $ = DOM.$; @@ -458,6 +459,7 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditorP ariaLabel: localize('keybindingsLabel', "Keybindings"), setRowLineHeight: false, horizontalScrolling: false, + accessibilityProvider: new AccessibilityProvider(), overrideStyles: { listBackground: editorBackground } @@ -826,7 +828,6 @@ class KeybindingItemRenderer implements IListRenderer this.keybindingsEditor.layoutColumns(elements)); - parent.setAttribute('aria-labelledby', elements.map(e => e.getAttribute('id')).join(' ')); return { parent, @@ -933,7 +934,6 @@ class CommandColumn extends Column { const commandIdMatched = !!(keybindingItem.commandLabel && keybindingItemEntry.commandIdMatches); const commandDefaultLabelMatched = !!keybindingItemEntry.commandDefaultLabelMatches; DOM.toggleClass(this.commandColumn, 'vertical-align-column', commandIdMatched || commandDefaultLabelMatched); - this.commandColumn.setAttribute('aria-label', this.getAriaLabel(keybindingItemEntry)); let commandLabel: HighlightedLabel | undefined; if (keybindingItem.commandLabel) { commandLabel = new HighlightedLabel(this.commandColumn, false); @@ -951,10 +951,6 @@ class CommandColumn extends Column { commandLabel.element.title = keybindingItem.commandLabel ? localize('title', "{0} ({1})", keybindingItem.commandLabel, keybindingItem.command) : keybindingItem.command; } } - - private getAriaLabel(keybindingItemEntry: IKeybindingItemEntry): string { - return keybindingItemEntry.keybindingItem.commandLabel ? keybindingItemEntry.keybindingItem.commandLabel : keybindingItemEntry.keybindingItem.command; - } } class KeybindingColumn extends Column { @@ -974,15 +970,10 @@ class KeybindingColumn extends Column { render(keybindingItemEntry: IKeybindingItemEntry): void { DOM.clearNode(this.keybindingLabel); - this.keybindingLabel.setAttribute('aria-label', this.getAriaLabel(keybindingItemEntry)); if (keybindingItemEntry.keybindingItem.keybinding) { new KeybindingLabel(this.keybindingLabel, OS).set(keybindingItemEntry.keybindingItem.keybinding, keybindingItemEntry.keybindingMatches); } } - - private getAriaLabel(keybindingItemEntry: IKeybindingItemEntry): string { - return keybindingItemEntry.keybindingItem.keybinding ? localize('keybindingAriaLabel', "Keybinding is {0}.", keybindingItemEntry.keybindingItem.keybinding.getAriaLabel()) : localize('noKeybinding', "No Keybinding assigned."); - } } class SourceColumn extends Column { @@ -1000,13 +991,8 @@ class SourceColumn extends Column { render(keybindingItemEntry: IKeybindingItemEntry): void { DOM.clearNode(this.sourceColumn); - this.sourceColumn.setAttribute('aria-label', this.getAriaLabel(keybindingItemEntry)); new HighlightedLabel(this.sourceColumn, false).set(keybindingItemEntry.keybindingItem.source, keybindingItemEntry.sourceMatches); } - - private getAriaLabel(keybindingItemEntry: IKeybindingItemEntry): string { - return localize('sourceAriaLabel', "Source is {0}.", keybindingItemEntry.keybindingItem.source); - } } class WhenColumn extends Column { @@ -1096,7 +1082,6 @@ class WhenColumn extends Column { } }, this, this.renderDisposables); this.whenInput.value = keybindingItemEntry.keybindingItem.when || ''; - this.whenLabel.setAttribute('aria-label', this.getAriaLabel(keybindingItemEntry)); DOM.toggleClass(this.whenLabel, 'code', !!keybindingItemEntry.keybindingItem.when); DOM.toggleClass(this.whenLabel, 'empty', !keybindingItemEntry.keybindingItem.when); if (keybindingItemEntry.keybindingItem.when) { @@ -1117,10 +1102,18 @@ class WhenColumn extends Column { this.keybindingsEditor.selectKeybinding(keybindingItemEntry); }, this, this.renderDisposables); } +} - private getAriaLabel(keybindingItemEntry: IKeybindingItemEntry): string { - return keybindingItemEntry.keybindingItem.when ? localize('whenAriaLabel', "When is {0}.", keybindingItemEntry.keybindingItem.when) : localize('noWhen', "No when context."); +class AccessibilityProvider implements IAccessibilityProvider { + + getAriaLabel(keybindingItemEntry: IKeybindingItemEntry): string { + let ariaLabel = localize('commandAriaLabel', "Command is {0}.", keybindingItemEntry.keybindingItem.commandLabel ? keybindingItemEntry.keybindingItem.commandLabel : keybindingItemEntry.keybindingItem.command); + ariaLabel += keybindingItemEntry.keybindingItem.keybinding ? localize('keybindingAriaLabel', "Keybinding is {0}.", keybindingItemEntry.keybindingItem.keybinding.getAriaLabel()) : localize('noKeybinding', "No Keybinding assigned."); + ariaLabel += localize('sourceAriaLabel', "Source is {0}.", keybindingItemEntry.keybindingItem.source); + ariaLabel += keybindingItemEntry.keybindingItem.when ? localize('whenAriaLabel', "When is {0}.", keybindingItemEntry.keybindingItem.when) : localize('noWhen', "No when context."); + return ariaLabel; } + } registerThemingParticipant((theme: IColorTheme, collector: ICssStyleCollector) => { diff --git a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts index 99fe4ffed1..c012534629 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferences.contribution.ts @@ -3,10 +3,10 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import 'vs/css!./media/preferences'; import { KeyChord, KeyCode, KeyMod } from 'vs/base/common/keyCodes'; import { Disposable } from 'vs/base/common/lifecycle'; import { URI } from 'vs/base/common/uri'; -import 'vs/css!../browser/media/preferences'; import { Context as SuggestContext } from 'vs/editor/contrib/suggest/suggest'; import * as nls from 'vs/nls'; import { Action2, MenuId, MenuRegistry, registerAction2 } from 'vs/platform/actions/common/actions'; diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts b/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts index 1f543b31ec..7e8b48ca8e 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesEditor.ts @@ -12,7 +12,7 @@ import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cance import { IStringDictionary } from 'vs/base/common/collections'; import { getErrorMessage, isPromiseCanceledError, onUnexpectedError } from 'vs/base/common/errors'; import { Emitter, Event } from 'vs/base/common/event'; -import { ArrayNavigator } from 'vs/base/common/iterator'; +import { ArrayNavigator } from 'vs/base/common/navigator'; import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle'; import * as strings from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; diff --git a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts index 7cadab174d..6e6e60b68f 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsEditor2.ts @@ -14,7 +14,7 @@ import { Delayer, ThrottledDelayer, timeout, IntervalTimer } from 'vs/base/commo import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import * as collections from 'vs/base/common/collections'; import { getErrorMessage, isPromiseCanceledError } from 'vs/base/common/errors'; -import { Iterator } from 'vs/base/common/iterator'; +import { Iterable } from 'vs/base/common/iterator'; import { KeyCode } from 'vs/base/common/keyCodes'; import { Disposable } from 'vs/base/common/lifecycle'; import * as platform from 'vs/base/common/platform'; @@ -58,10 +58,8 @@ import { SettingsEditor2Input } from 'vs/workbench/services/preferences/common/p import { Settings2EditorModel } from 'vs/workbench/services/preferences/common/preferencesModels'; import { fromNow } from 'vs/base/common/date'; -function createGroupIterator(group: SettingsTreeGroupElement): Iterator> { - const groupsIt = Iterator.fromArray(group.children); - - return Iterator.map(groupsIt, g => { +function createGroupIterator(group: SettingsTreeGroupElement): Iterable> { + return Iterable.map(group.children, g => { return { element: g, children: g instanceof SettingsTreeGroupElement ? diff --git a/src/vs/workbench/contrib/preferences/browser/tocTree.ts b/src/vs/workbench/contrib/preferences/browser/tocTree.ts index 2b9feaf98c..7928d8c950 100644 --- a/src/vs/workbench/contrib/preferences/browser/tocTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/tocTree.ts @@ -8,7 +8,7 @@ import { IListVirtualDelegate } from 'vs/base/browser/ui/list/list'; import { DefaultStyleController, IAccessibilityProvider } from 'vs/base/browser/ui/list/listWidget'; import { IObjectTreeOptions, ObjectTree } from 'vs/base/browser/ui/tree/objectTree'; import { ITreeElement, ITreeNode, ITreeRenderer } from 'vs/base/browser/ui/tree/tree'; -import { Iterator } from 'vs/base/common/iterator'; +import { Iterable } from 'vs/base/common/iterator'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { editorBackground, transparent, foreground } from 'vs/platform/theme/common/colorRegistry'; import { attachStyler } from 'vs/platform/theme/common/styler'; @@ -138,11 +138,10 @@ class TOCTreeDelegate implements IListVirtualDelegate { } } -export function createTOCIterator(model: TOCTreeModel | SettingsTreeGroupElement, tree: TOCTree): Iterator> { +export function createTOCIterator(model: TOCTreeModel | SettingsTreeGroupElement, tree: TOCTree): Iterable> { const groupChildren = model.children.filter(c => c instanceof SettingsTreeGroupElement); - const groupsIt = Iterator.fromArray(groupChildren); - return Iterator.map(groupsIt, g => { + return Iterable.map(groupChildren, g => { const hasGroupChildren = g.children.some(c => c instanceof SettingsTreeGroupElement); return { diff --git a/src/vs/workbench/contrib/scm/browser/repositoryPane.ts b/src/vs/workbench/contrib/scm/browser/repositoryPane.ts index f6296d7593..39d466fb15 100644 --- a/src/vs/workbench/contrib/scm/browser/repositoryPane.ts +++ b/src/vs/workbench/contrib/scm/browser/repositoryPane.ts @@ -28,14 +28,14 @@ import { IThemeService, LIGHT, registerThemingParticipant, IFileIconTheme } from import { isSCMResource, isSCMResourceGroup, connectPrimaryMenuToInlineActionBar } from './util'; import { attachBadgeStyler } from 'vs/platform/theme/common/styler'; import { WorkbenchCompressibleObjectTree } from 'vs/platform/list/browser/listService'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { disposableTimeout, ThrottledDelayer } from 'vs/base/common/async'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { ITreeNode, ITreeFilter, ITreeSorter, ITreeContextMenuEvent } from 'vs/base/browser/ui/tree/tree'; import { ResourceTree, IResourceNode } from 'vs/base/common/resourceTree'; import { ISequence, ISplice } from 'vs/base/common/sequence'; import { ICompressibleTreeRenderer, ICompressibleKeyboardNavigationLabelProvider } from 'vs/base/browser/ui/tree/objectTree'; -import { Iterator } from 'vs/base/common/iterator'; +import { Iterable } from 'vs/base/common/iterator'; import { ICompressedTreeNode, ICompressedTreeElement } from 'vs/base/browser/ui/tree/compressedObjectTreeModel'; import { URI } from 'vs/base/common/uri'; import { FileKind } from 'vs/platform/files/common/files'; @@ -397,8 +397,8 @@ interface IGroupItem { function groupItemAsTreeElement(item: IGroupItem, mode: ViewModelMode): ICompressedTreeElement { const children = mode === ViewModelMode.List - ? Iterator.map(Iterator.fromArray(item.resources), element => ({ element, incompressible: true })) - : Iterator.map(item.tree.root.children, node => asTreeElement(node, true)); + ? Iterable.map(item.resources, element => ({ element, incompressible: true })) + : Iterable.map(item.tree.root.children, node => asTreeElement(node, true)); return { element: item.group, children, incompressible: true, collapsible: true }; } @@ -406,7 +406,7 @@ function groupItemAsTreeElement(item: IGroupItem, mode: ViewModelMode): ICompres function asTreeElement(node: IResourceNode, forceIncompressible: boolean): ICompressedTreeElement { return { element: (node.childrenCount === 0 && node.element) ? node.element : node, - children: Iterator.map(node.children, node => asTreeElement(node, false)), + children: Iterable.map(node.children, node => asTreeElement(node, false)), incompressible: !!node.element || forceIncompressible }; } @@ -733,7 +733,6 @@ export class RepositoryPane extends ViewPane { wrappingStrategy: 'advanced', wrappingIndent: 'none', padding: { top: 3, bottom: 3 }, - suggest: { showWords: false }, quickSuggestions: false }; @@ -772,6 +771,7 @@ export class RepositoryPane extends ViewPane { query }); + this.configurationService.updateValue('editor.wordBasedSuggestions', false, { resource: uri }, ConfigurationTarget.MEMORY); this.inputModel = this.modelService.getModel(uri) || this.modelService.createModel('', null, uri); this.inputEditor.setModel(this.inputModel); diff --git a/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts b/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts index 3c5788ff66..3ab20e96d1 100644 --- a/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts +++ b/src/vs/workbench/contrib/search/browser/anythingQuickAccess.ts @@ -134,6 +134,16 @@ export class AnythingQuickAccessProvider extends PickerQuickAccessProvider { + if (this.editorViewState) { + await this.editorService.openEditor( + this.editorViewState.editor, + { viewState: this.editorViewState.state, preserveFocus: true /* import to not close the picker as a result */ }, + this.editorViewState.group + ); + } + } }(this, this.editorService); get defaultFilterValue(): DefaultQuickAccessFilterValue | undefined { @@ -206,15 +216,7 @@ export class AnythingQuickAccessProvider extends PickerQuickAccessProvider { - if (this.pickState.editorViewState) { - this.editorService.openEditor( - this.pickState.editorViewState.editor, - { viewState: this.pickState.editorViewState.state, preserveFocus: true /* import to not close the picker as a result */ }, - this.pickState.editorViewState.group - ); - } - })); + disposables.add(once(token.onCancellationRequested)(() => this.pickState.restoreEditorViewState())); // Start picker disposables.add(super.provide(picker, token)); @@ -912,6 +914,12 @@ export class AnythingQuickAccessProvider extends PickerQuickAccessProvider> { + private createResultIterator(collapseResults: ISearchConfigurationProperties['collapseResults']): Iterable> { const folderMatches = this.searchResult.folderMatches() .filter(fm => !fm.isEmpty()) .sort(searchMatchComparer); @@ -542,20 +542,17 @@ export class SearchView extends ViewPane { return this.createFolderIterator(folderMatches[0], collapseResults); } - const foldersIt = Iterator.fromArray(folderMatches); - return Iterator.map(foldersIt, folderMatch => { + return Iterable.map(folderMatches, folderMatch => { const children = this.createFolderIterator(folderMatch, collapseResults); return >{ element: folderMatch, children }; }); } - private createFolderIterator(folderMatch: FolderMatch, collapseResults: ISearchConfigurationProperties['collapseResults']): Iterator> { + private createFolderIterator(folderMatch: FolderMatch, collapseResults: ISearchConfigurationProperties['collapseResults']): Iterable> { const sortOrder = this.searchConfig.sortOrder; - const filesIt = Iterator.fromArray( - folderMatch.matches() - .sort((a, b) => searchMatchComparer(a, b, sortOrder))); + const matches = folderMatch.matches().sort((a, b) => searchMatchComparer(a, b, sortOrder)); - return Iterator.map(filesIt, fileMatch => { + return Iterable.map(matches, fileMatch => { const children = this.createFileIterator(fileMatch); let nodeExists = true; @@ -568,14 +565,12 @@ export class SearchView extends ViewPane { }); } - private createFileIterator(fileMatch: FileMatch): Iterator> { - const matchesIt = Iterator.from( - fileMatch.matches() - .sort(searchMatchComparer)); - return Iterator.map(matchesIt, r => (>{ element: r })); + private createFileIterator(fileMatch: FileMatch): Iterable> { + const matches = fileMatch.matches().sort(searchMatchComparer); + return Iterable.map(matches, r => (>{ element: r })); } - private createIterator(match: FolderMatch | FileMatch | SearchResult, collapseResults: ISearchConfigurationProperties['collapseResults']): Iterator> { + private createIterator(match: FolderMatch | FileMatch | SearchResult, collapseResults: ISearchConfigurationProperties['collapseResults']): Iterable> { return match instanceof SearchResult ? this.createResultIterator(collapseResults) : match instanceof FolderMatch ? this.createFolderIterator(match, collapseResults) : this.createFileIterator(match); diff --git a/src/vs/workbench/contrib/search/common/queryBuilder.ts b/src/vs/workbench/contrib/search/common/queryBuilder.ts index d7fd973d17..a4359b286d 100644 --- a/src/vs/workbench/contrib/search/common/queryBuilder.ts +++ b/src/vs/workbench/contrib/search/common/queryBuilder.ts @@ -236,8 +236,15 @@ export class QueryBuilder { return path.isAbsolute(segment) || /^\.\.?([\/\\]|$)/.test(segment); }; + const userHome = this.environmentService.userHome; const segments = splitGlobPattern(pattern) - .map(segment => untildify(segment, this.environmentService.userHome)); + .map(segment => { + if (userHome) { + return untildify(segment, userHome.fsPath); + } + + return segment; + }); const groups = collections.groupBy(segments, segment => isSearchPath(segment) ? 'searchPaths' : 'exprSegments'); diff --git a/src/vs/workbench/contrib/search/common/searchModel.ts b/src/vs/workbench/contrib/search/common/searchModel.ts index f6bd49b10f..1e781c0b61 100644 --- a/src/vs/workbench/contrib/search/common/searchModel.ts +++ b/src/vs/workbench/contrib/search/common/searchModel.ts @@ -696,7 +696,7 @@ export class SearchResult extends Disposable { private _folderMatches: FolderMatchWithResource[] = []; private _otherFilesMatch: FolderMatch | null = null; - private _folderMatchesMap: TernarySearchTree = TernarySearchTree.forPaths(); + private _folderMatchesMap: TernarySearchTree = TernarySearchTree.forPaths(); private _showHighlights: boolean = false; private _query: ITextQuery | null = null; diff --git a/src/vs/workbench/contrib/search/test/electron-browser/queryBuilder.test.ts b/src/vs/workbench/contrib/search/test/electron-browser/queryBuilder.test.ts index 8d78b8f5da..dfaa049b3d 100644 --- a/src/vs/workbench/contrib/search/test/electron-browser/queryBuilder.test.ts +++ b/src/vs/workbench/contrib/search/test/electron-browser/queryBuilder.test.ts @@ -62,13 +62,13 @@ suite('QueryBuilder', () => { [ '~/foo/bar', { - searchPaths: [{ searchPath: getUri(userHome, '/foo/bar') }] + searchPaths: [{ searchPath: getUri(userHome.fsPath, '/foo/bar') }] } ], [ '~/foo/bar, a', { - searchPaths: [{ searchPath: getUri(userHome, '/foo/bar') }], + searchPaths: [{ searchPath: getUri(userHome.fsPath, '/foo/bar') }], pattern: patternsToIExpression(...globalGlob('a')) } ], diff --git a/src/vs/workbench/contrib/tags/electron-browser/workspaceTagsService.ts b/src/vs/workbench/contrib/tags/electron-browser/workspaceTagsService.ts index f08d92976f..bccf1fe93d 100644 --- a/src/vs/workbench/contrib/tags/electron-browser/workspaceTagsService.ts +++ b/src/vs/workbench/contrib/tags/electron-browser/workspaceTagsService.ts @@ -6,8 +6,7 @@ import * as crypto from 'crypto'; import { IFileService, IResolveFileResult, IFileStat } from 'vs/platform/files/common/files'; import { IWorkspaceContextService, WorkbenchState, IWorkspace } from 'vs/platform/workspace/common/workspace'; -import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; +import { IWorkbenchEnvironmentService, IEnvironmentConfiguration } from 'vs/workbench/services/environment/common/environmentService'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { INotificationService, NeverShowAgainScope, INeverShowAgainOptions } from 'vs/platform/notification/common/notification'; import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; @@ -39,6 +38,7 @@ const ModulesToLookFor = [ '@ionic', 'vue', 'tns-core-modules', + 'electron', // Other interesting packages 'aws-sdk', 'aws-amplify', @@ -199,6 +199,7 @@ export class WorkspaceTagsService implements IWorkspaceTagsService { "workspace.npm.lerna" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.npm.just-task" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.npm.beachball" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, + "workspace.npm.electron" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.bower" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.yeoman.code.ext" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, "workspace.cordova.high" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true }, @@ -246,7 +247,7 @@ export class WorkspaceTagsService implements IWorkspaceTagsService { "workspace.py.botframework-connector" : { "classification": "SystemMetaData", "purpose": "FeatureInsight", "isMeasurement": true } } */ - private resolveWorkspaceTags(configuration: IWindowConfiguration, participant?: (rootFiles: string[]) => void): Promise { + private resolveWorkspaceTags(configuration: IEnvironmentConfiguration, participant?: (rootFiles: string[]) => void): Promise { const tags: Tags = Object.create(null); const state = this.contextService.getWorkbenchState(); @@ -477,12 +478,12 @@ export class WorkspaceTagsService implements IWorkspaceTagsService { } } - private findFolders(configuration: IWindowConfiguration): URI[] | undefined { + private findFolders(configuration: IEnvironmentConfiguration): URI[] | undefined { const folder = this.findFolder(configuration); return folder && [folder]; } - private findFolder({ filesToOpenOrCreate, filesToDiff }: IWindowConfiguration): URI | undefined { + private findFolder({ filesToOpenOrCreate, filesToDiff }: IEnvironmentConfiguration): URI | undefined { if (filesToOpenOrCreate && filesToOpenOrCreate.length) { return this.parentURI(filesToOpenOrCreate[0].fileUri); } else if (filesToDiff && filesToDiff.length) { diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index 19c6efbdac..be4348bb72 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -761,10 +761,22 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } } - private setRecentlyUsedTask(task: Task): void { - const key = task.getRecentlyUsedKey(); + private async setRecentlyUsedTask(task: Task): Promise { + let key = task.getRecentlyUsedKey(); if (!InMemoryTask.is(task) && key) { - this.getRecentlyUsedTasks().set(key, JSON.stringify(this.createCustomizableTask(task))); + const customizations = this.createCustomizableTask(task); + if (ContributedTask.is(task) && customizations) { + let custom: CustomTask[] = []; + let customized: IStringDictionary = Object.create(null); + await this.computeTasksForSingleConfig(task._source.workspaceFolder ?? this.workspaceFolders[0], { + version: '2.0.0', + tasks: [customizations] + }, TaskRunSource.System, custom, customized, TaskConfig.TaskConfigSource.TasksJson, true); + for (const configuration in customized) { + key = customized[configuration].getRecentlyUsedKey()!; + } + } + this.getRecentlyUsedTasks().set(key, JSON.stringify(customizations)); this.saveRecentlyUsedTasks(); } } @@ -841,8 +853,9 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } else { resolve(undefined); } + } else { + resolve(this.executeTask(task, resolver)); } - resolve(this.executeTask(task, resolver)); }).then((value) => { if (runSource === TaskRunSource.User) { this.getWorkspaceTasks().then(workspaceTasks => { @@ -1397,7 +1410,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer }); } - private handleExecuteResult(executeResult: ITaskExecuteResult): Promise { + private async handleExecuteResult(executeResult: ITaskExecuteResult): Promise { if (executeResult.task.taskLoadMessages && executeResult.task.taskLoadMessages.length > 0) { executeResult.task.taskLoadMessages.forEach(loadMessage => { this._outputChannel.append(loadMessage + '\n'); @@ -1405,7 +1418,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer this.showOutput(); } - this.setRecentlyUsedTask(executeResult.task); + await this.setRecentlyUsedTask(executeResult.task); if (executeResult.kind === TaskExecuteKind.Active) { let active = executeResult.active; if (active && active.same) { @@ -1643,7 +1656,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer await Promise.all(customTasksPromises); if (needsRecentTasksMigration) { // At this point we have all the tasks and can migrate the recently used tasks. - this.migrateRecentTasks(result.all()); + await this.migrateRecentTasks(result.all()); } return result; }, () => { @@ -2243,7 +2256,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return (this.getRecentlyUsedTasksV1().size > 0) && (this.getRecentlyUsedTasks().size === 0); } - public migrateRecentTasks(tasks: Task[]) { + public async migrateRecentTasks(tasks: Task[]) { if (!this.needsRecentTasksMigration()) { return; } @@ -2255,12 +2268,13 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer taskMap[key] = task; } }); - recentlyUsedTasks.keys().reverse().forEach(key => { + const reversed = recentlyUsedTasks.keys().reverse(); + for (const key in reversed) { let task = taskMap[key]; if (task) { - this.setRecentlyUsedTask(task); + await this.setRecentlyUsedTask(task); } - }); + } this.storageService.remove(AbstractTaskService.RecentlyUsedTasks_Key, StorageScope.WORKSPACE); } diff --git a/src/vs/workbench/contrib/tasks/common/taskService.ts b/src/vs/workbench/contrib/tasks/common/taskService.ts index eecd26877b..82337a9d63 100644 --- a/src/vs/workbench/contrib/tasks/common/taskService.ts +++ b/src/vs/workbench/contrib/tasks/common/taskService.ts @@ -79,7 +79,7 @@ export interface ITaskService { tryResolveTask(configuringTask: ConfiguringTask): Promise; getTasksForGroup(group: string): Promise; getRecentlyUsedTasks(): LinkedMap; - migrateRecentTasks(tasks: Task[]): void; + migrateRecentTasks(tasks: Task[]): Promise; createSorter(): TaskSorter; getTaskDescription(task: Task | ConfiguringTask): string | undefined; diff --git a/src/vs/workbench/contrib/terminal/browser/media/terminal.css b/src/vs/workbench/contrib/terminal/browser/media/terminal.css index 29ff609554..9c80dfe556 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/terminal.css +++ b/src/vs/workbench/contrib/terminal/browser/media/terminal.css @@ -161,7 +161,18 @@ transform: rotate(-90deg); } -.monaco-workbench .part > .title > .title-actions .switch-terminal { +.monaco-workbench .part.sidebar > .title > .title-actions .switch-terminal, +.monaco-pane-view .pane > .pane-header .monaco-action-bar .switch-terminal { + border-width: 1px; + border-style: solid; +} + +.part.panel > .title > .title-actions .switch-terminal > .monaco-select-box { + border-width: 1px; + border-style: solid; +} + +.monaco-workbench .part.sidebar > .title > .title-actions .switch-terminal { display: flex; align-items: center; font-size: 11px; @@ -171,11 +182,11 @@ margin-top: 7px; } -.monaco-workbench.mac .part > .title > .title-actions .switch-terminal { +.monaco-workbench.mac .part.sidebar > .title > .title-actions .switch-terminal { border-radius: 4px; } -.monaco-workbench .part > .title > .title-actions .switch-terminal > .monaco-select-box { +.monaco-workbench .part.sidebar > .title > .title-actions .switch-terminal > .monaco-select-box { border: none !important; display: block !important; background-color: unset !important; @@ -185,7 +196,7 @@ border: none !important; } -.monaco-workbench .part > .title > .title-actions .switch-terminal > .monaco-select-box { +.monaco-workbench .part.sidebar > .title > .title-actions .switch-terminal > .monaco-select-box { padding: 0 22px 0 6px; } diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index bf84bae267..44e0b5812f 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -12,16 +12,16 @@ import 'vs/css!./media/xterm'; import * as nls from 'vs/nls'; import { SyncActionDescriptor, registerAction2 } from 'vs/platform/actions/common/actions'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { ContextKeyExpr, ContextKeyExpression } from 'vs/platform/contextkey/common/contextkey'; +import { KeybindingWeight, KeybindingsRegistry, IKeybindings } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { Registry } from 'vs/platform/registry/common/platform'; import * as panel from 'vs/workbench/browser/panel'; import { getQuickNavigateHandler } from 'vs/workbench/browser/quickaccess'; import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; import { Extensions as ViewContainerExtensions, IViewContainersRegistry, ViewContainerLocation, IViewsRegistry } from 'vs/workbench/common/views'; -import { ClearSelectionTerminalAction, ClearTerminalAction, CopyTerminalSelectionAction, CreateNewInActiveWorkspaceTerminalAction, CreateNewTerminalAction, DeleteToLineStartTerminalAction, DeleteWordLeftTerminalAction, DeleteWordRightTerminalAction, FindNext, FindPrevious, FocusActiveTerminalAction, FocusNextPaneTerminalAction, FocusNextTerminalAction, FocusPreviousPaneTerminalAction, FocusPreviousTerminalAction, FocusTerminalFindWidgetAction, HideTerminalFindWidgetAction, KillTerminalAction, MoveToLineEndTerminalAction, MoveToLineStartTerminalAction, QuickAccessTerminalAction, RenameTerminalAction, ResizePaneDownTerminalAction, ResizePaneLeftTerminalAction, ResizePaneRightTerminalAction, ResizePaneUpTerminalAction, RunActiveFileInTerminalAction, RunSelectedTextInTerminalAction, ScrollDownPageTerminalAction, ScrollDownTerminalAction, ScrollToBottomTerminalAction, ScrollToNextCommandAction, ScrollToPreviousCommandAction, ScrollToTopTerminalAction, ScrollUpPageTerminalAction, ScrollUpTerminalAction, SelectAllTerminalAction, SelectDefaultShellWindowsTerminalAction, SelectToNextCommandAction, SelectToNextLineAction, SelectToPreviousCommandAction, SelectToPreviousLineAction, SplitInActiveWorkspaceTerminalAction, SplitTerminalAction, TerminalPasteAction, ToggleCaseSensitiveCommand, ToggleEscapeSequenceLoggingAction, ToggleRegexCommand, ToggleTerminalAction, ToggleWholeWordCommand, NavigationModeFocusPreviousTerminalAction, NavigationModeFocusNextTerminalAction, NavigationModeExitTerminalAction, ManageWorkspaceShellPermissionsTerminalCommand, CreateNewWithCwdTerminalAction, RenameWithArgTerminalAction, SendSequenceTerminalAction } from 'vs/workbench/contrib/terminal/browser/terminalActions'; +import { ClearSelectionTerminalAction, ClearTerminalAction, CopyTerminalSelectionAction, CreateNewInActiveWorkspaceTerminalAction, CreateNewTerminalAction, FindNext, FindPrevious, FocusActiveTerminalAction, FocusNextPaneTerminalAction, FocusNextTerminalAction, FocusPreviousPaneTerminalAction, FocusPreviousTerminalAction, FocusTerminalFindWidgetAction, HideTerminalFindWidgetAction, KillTerminalAction, QuickAccessTerminalAction, RenameTerminalAction, ResizePaneDownTerminalAction, ResizePaneLeftTerminalAction, ResizePaneRightTerminalAction, ResizePaneUpTerminalAction, RunActiveFileInTerminalAction, RunSelectedTextInTerminalAction, ScrollDownPageTerminalAction, ScrollDownTerminalAction, ScrollToBottomTerminalAction, ScrollToNextCommandAction, ScrollToPreviousCommandAction, ScrollToTopTerminalAction, ScrollUpPageTerminalAction, ScrollUpTerminalAction, SelectAllTerminalAction, SelectDefaultShellWindowsTerminalAction, SelectToNextCommandAction, SelectToNextLineAction, SelectToPreviousCommandAction, SelectToPreviousLineAction, SplitInActiveWorkspaceTerminalAction, SplitTerminalAction, TerminalPasteAction, ToggleCaseSensitiveCommand, ToggleEscapeSequenceLoggingAction, ToggleRegexCommand, ToggleTerminalAction, ToggleWholeWordCommand, NavigationModeFocusPreviousTerminalAction, NavigationModeFocusNextTerminalAction, NavigationModeExitTerminalAction, ManageWorkspaceShellPermissionsTerminalCommand, CreateNewWithCwdTerminalAction, RenameWithArgTerminalAction, SendSequenceTerminalAction, terminalSendSequenceCommand } from 'vs/workbench/contrib/terminal/browser/terminalActions'; import { TerminalViewPane } from 'vs/workbench/contrib/terminal/browser/terminalView'; -import { KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_NOT_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, TERMINAL_VIEW_ID, DEFAULT_LETTER_SPACING, DEFAULT_LINE_HEIGHT, TerminalCursorStyle, TERMINAL_ACTION_CATEGORY, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminal'; +import { KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_NOT_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE_KEY, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, TERMINAL_VIEW_ID, DEFAULT_LETTER_SPACING, DEFAULT_LINE_HEIGHT, TerminalCursorStyle, TERMINAL_ACTION_CATEGORY, KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS, TERMINAL_COMMAND_ID } from 'vs/workbench/contrib/terminal/common/terminal'; import { registerColors } from 'vs/workbench/contrib/terminal/common/terminalColorRegistry'; import { setupTerminalCommands } from 'vs/workbench/contrib/terminal/browser/terminalCommands'; import { setupTerminalMenu } from 'vs/workbench/contrib/terminal/common/terminalMenu'; @@ -32,7 +32,7 @@ import { TerminalService } from 'vs/workbench/contrib/terminal/browser/terminalS import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { registerShellConfiguration } from 'vs/workbench/contrib/terminal/common/terminalShellConfig'; import { CONTEXT_ACCESSIBILITY_MODE_ENABLED } from 'vs/platform/accessibility/common/accessibility'; -import { ITerminalService } from 'vs/workbench/contrib/terminal/browser/terminal'; +import { ITerminalService, WindowsShellType } from 'vs/workbench/contrib/terminal/browser/terminal'; import { BrowserFeatures } from 'vs/base/browser/canIUse'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer'; @@ -333,6 +333,11 @@ configurationRegistry.registerConfiguration({ type: 'boolean', default: true }, + 'terminal.integrated.wordSeparators': { + description: nls.localize('terminal.integrated.wordSeparators', "A string containing all characters to be considered word separators by the double click to select word feature."), + type: 'string', + default: ' ()[]{}\',"`' + }, 'terminal.integrated.experimentalUseTitleEvent': { description: nls.localize('terminal.integrated.experimentalUseTitleEvent', "An experimental setting that will use the terminal title event for the dropdown title. This setting will only apply to new terminals."), type: 'boolean', @@ -447,26 +452,6 @@ actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(HideTerminalF primary: KeyCode.Escape, secondary: [KeyMod.Shift | KeyCode.Escape] }, ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE)), 'Terminal: Hide Find Widget', category); -actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(DeleteWordLeftTerminalAction, DeleteWordLeftTerminalAction.ID, DeleteWordLeftTerminalAction.LABEL, { - primary: KeyMod.CtrlCmd | KeyCode.Backspace, - mac: { primary: KeyMod.Alt | KeyCode.Backspace } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Delete Word Left', category); -actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(DeleteWordRightTerminalAction, DeleteWordRightTerminalAction.ID, DeleteWordRightTerminalAction.LABEL, { - primary: KeyMod.CtrlCmd | KeyCode.Delete, - mac: { primary: KeyMod.Alt | KeyCode.Delete } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Delete Word Right', category); -actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(DeleteToLineStartTerminalAction, DeleteToLineStartTerminalAction.ID, DeleteToLineStartTerminalAction.LABEL, { - primary: 0, - mac: { primary: KeyMod.CtrlCmd | KeyCode.Backspace } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Delete To Line Start', category); -actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(MoveToLineStartTerminalAction, MoveToLineStartTerminalAction.ID, MoveToLineStartTerminalAction.LABEL, { - primary: 0, - mac: { primary: KeyMod.CtrlCmd | KeyCode.LeftArrow } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Move To Line Start', category); -actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(MoveToLineEndTerminalAction, MoveToLineEndTerminalAction.ID, MoveToLineEndTerminalAction.LABEL, { - primary: 0, - mac: { primary: KeyMod.CtrlCmd | KeyCode.RightArrow } -}, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Move To Line End', category); actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(SplitTerminalAction, SplitTerminalAction.ID, SplitTerminalAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_5, mac: { @@ -576,14 +561,60 @@ if (BrowserFeatures.clipboard.writeText) { linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_C } }, ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_TEXT_SELECTED, KEYBINDING_CONTEXT_TERMINAL_FOCUS)), 'Terminal: Copy Selection', category); } + +function registerSendSequenceKeybinding(text: string, rule: { when?: ContextKeyExpression } & IKeybindings): void { + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: SendSequenceTerminalAction.ID, + weight: KeybindingWeight.WorkbenchContrib, + when: rule.when || KEYBINDING_CONTEXT_TERMINAL_FOCUS, + primary: rule.primary, + mac: rule.mac, + linux: rule.linux, + win: rule.win, + handler: terminalSendSequenceCommand, + args: { text } + }); +} + if (BrowserFeatures.clipboard.readText) { actionRegistry.registerWorkbenchAction(SyncActionDescriptor.create(TerminalPasteAction, TerminalPasteAction.ID, TerminalPasteAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_V, win: { primary: KeyMod.CtrlCmd | KeyCode.KEY_V, secondary: [KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_V] }, linux: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_V } }, KEYBINDING_CONTEXT_TERMINAL_FOCUS), 'Terminal: Paste into Active Terminal', category); + // An extra Windows-only ctrl+v keybinding is used for pwsh that sends ctrl+v directly to the + // shell, this gets handled by PSReadLine which properly handles multi-line pastes + if (platform.isWindows) { + registerSendSequenceKeybinding(String.fromCharCode('V'.charCodeAt(0) - 64), { // ctrl+v + when: ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_FOCUS, ContextKeyExpr.equals(KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE_KEY, WindowsShellType.PowerShell)), + primary: KeyMod.CtrlCmd | KeyCode.KEY_V + }); + } } +// Delete word left: ctrl+w +registerSendSequenceKeybinding(String.fromCharCode('W'.charCodeAt(0) - 64), { + primary: KeyMod.CtrlCmd | KeyCode.Backspace, + mac: { primary: KeyMod.Alt | KeyCode.Backspace } +}); +// Delete word right: alt+d +registerSendSequenceKeybinding('\x1bd', { + primary: KeyMod.CtrlCmd | KeyCode.Delete, + mac: { primary: KeyMod.Alt | KeyCode.Delete } +}); +// Delete to line start: ctrl+u +registerSendSequenceKeybinding('\u0015', { + mac: { primary: KeyMod.CtrlCmd | KeyCode.Backspace } +}); +// Move to line start: ctrl+A +registerSendSequenceKeybinding(String.fromCharCode('A'.charCodeAt(0) - 64), { + mac: { primary: KeyMod.CtrlCmd | KeyCode.LeftArrow } +}); +// Move to line end: ctrl+E +registerSendSequenceKeybinding(String.fromCharCode('E'.charCodeAt(0) - 64), { + mac: { primary: KeyMod.CtrlCmd | KeyCode.RightArrow } +}); + registerAction2(class extends SendSequenceTerminalAction { constructor() { super({ diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index 9e5076cc57..93106405e1 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -139,7 +139,7 @@ export interface ITerminalService { */ addLinkHandler(key: string, callback: TerminalLinkHandlerCallback): IDisposable; - selectDefaultWindowsShell(): Promise; + selectDefaultShell(): Promise; setContainers(panelContainer: HTMLElement, terminalContainer: HTMLElement): void; manageWorkspaceShellPermissions(): void; @@ -180,10 +180,10 @@ export interface ISearchOptions { } export enum WindowsShellType { - CommandPrompt, - PowerShell, - Wsl, - GitBash + CommandPrompt = 'cmd', + PowerShell = 'pwsh', + Wsl = 'wsl', + GitBash = 'gitbash' } export type TerminalShellType = WindowsShellType | undefined; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index 89c38a6f5b..4334f2ff61 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -165,115 +165,31 @@ export class SelectAllTerminalAction extends Action { } } -export abstract class BaseSendTextTerminalAction extends Action { - constructor( - id: string, - label: string, - private _text: string, - @ITerminalService private readonly _terminalService: ITerminalService - ) { - super(id, label); - } - - public run(event?: any): Promise { - const terminalInstance = this._terminalService.getActiveInstance(); - if (terminalInstance) { - terminalInstance.sendText(this._text, false); - } - return Promise.resolve(undefined); - } -} - -export class DeleteWordLeftTerminalAction extends BaseSendTextTerminalAction { - public static readonly ID = TERMINAL_COMMAND_ID.DELETE_WORD_LEFT; - public static readonly LABEL = nls.localize('workbench.action.terminal.deleteWordLeft', "Delete Word Left"); - - constructor( - id: string, - label: string, - @ITerminalService terminalService: ITerminalService - ) { - // Send ctrl+W - super(id, label, String.fromCharCode('W'.charCodeAt(0) - 64), terminalService); - } -} - -export class DeleteWordRightTerminalAction extends BaseSendTextTerminalAction { - public static readonly ID = TERMINAL_COMMAND_ID.DELETE_WORD_RIGHT; - public static readonly LABEL = nls.localize('workbench.action.terminal.deleteWordRight', "Delete Word Right"); - - constructor( - id: string, - label: string, - @ITerminalService terminalService: ITerminalService - ) { - // Send alt+d - super(id, label, '\x1bd', terminalService); - } -} - -export class DeleteToLineStartTerminalAction extends BaseSendTextTerminalAction { - public static readonly ID = TERMINAL_COMMAND_ID.DELETE_TO_LINE_START; - public static readonly LABEL = nls.localize('workbench.action.terminal.deleteToLineStart', "Delete To Line Start"); - - constructor( - id: string, - label: string, - @ITerminalService terminalService: ITerminalService - ) { - // Send ctrl+u - super(id, label, '\u0015', terminalService); - } -} - -export class MoveToLineStartTerminalAction extends BaseSendTextTerminalAction { - public static readonly ID = TERMINAL_COMMAND_ID.MOVE_TO_LINE_START; - public static readonly LABEL = nls.localize('workbench.action.terminal.moveToLineStart', "Move To Line Start"); - - constructor( - id: string, - label: string, - @ITerminalService terminalService: ITerminalService - ) { - // Send ctrl+A - super(id, label, String.fromCharCode('A'.charCodeAt(0) - 64), terminalService); - } -} - -export class MoveToLineEndTerminalAction extends BaseSendTextTerminalAction { - public static readonly ID = TERMINAL_COMMAND_ID.MOVE_TO_LINE_END; - public static readonly LABEL = nls.localize('workbench.action.terminal.moveToLineEnd', "Move To Line End"); - - constructor( - id: string, - label: string, - @ITerminalService terminalService: ITerminalService - ) { - // Send ctrl+E - super(id, label, String.fromCharCode('E'.charCodeAt(0) - 64), terminalService); - } -} export class SendSequenceTerminalAction extends Action2 { public static readonly ID = TERMINAL_COMMAND_ID.SEND_SEQUENCE; public static readonly LABEL = nls.localize('workbench.action.terminal.sendSequence', "Send Custom Sequence To Terminal"); public run(accessor: ServicesAccessor, args: any): void { - const terminalInstance = accessor.get(ITerminalService).getActiveInstance(); - if (!terminalInstance) { - return; - } - - const configurationResolverService = accessor.get(IConfigurationResolverService); - const workspaceContextService = accessor.get(IWorkspaceContextService); - const historyService = accessor.get(IHistoryService); - const activeWorkspaceRootUri = historyService.getLastActiveWorkspaceRoot(Schemas.file); - const lastActiveWorkspaceRoot = activeWorkspaceRootUri ? withNullAsUndefined(workspaceContextService.getWorkspaceFolder(activeWorkspaceRootUri)) : undefined; - const resolvedText = configurationResolverService.resolve(lastActiveWorkspaceRoot, args.text); - terminalInstance.sendText(resolvedText, false); + terminalSendSequenceCommand(accessor, args); } } +export const terminalSendSequenceCommand = (accessor: ServicesAccessor, args: any) => { + const terminalInstance = accessor.get(ITerminalService).getActiveInstance(); + if (!terminalInstance) { + return; + } + + const configurationResolverService = accessor.get(IConfigurationResolverService); + const workspaceContextService = accessor.get(IWorkspaceContextService); + const historyService = accessor.get(IHistoryService); + const activeWorkspaceRootUri = historyService.getLastActiveWorkspaceRoot(Schemas.file); + const lastActiveWorkspaceRoot = activeWorkspaceRootUri ? withNullAsUndefined(workspaceContextService.getWorkspaceFolder(activeWorkspaceRootUri)) : undefined; + const resolvedText = configurationResolverService.resolve(lastActiveWorkspaceRoot, args.text); + terminalInstance.sendText(resolvedText, false); +}; + export class CreateNewWithCwdTerminalAction extends Action2 { public static readonly ID = TERMINAL_COMMAND_ID.NEW_WITH_CWD; public static readonly LABEL = nls.localize('workbench.action.terminal.newWithCwd', "Create New Integrated Terminal Starting in a Custom Working Directory"); @@ -609,7 +525,7 @@ export class SelectDefaultShellWindowsTerminalAction extends Action { } public run(event?: any): Promise { - return this._terminalService.selectDefaultWindowsShell(); + return this._terminalService.selectDefaultShell(); } } @@ -679,6 +595,7 @@ export class RunActiveFileInTerminalAction extends Action { return Promise.resolve(undefined); } + // TODO: Convert this to ctrl+c, ctrl+v for pwsh? const path = await this.terminalService.preparePathForTerminalAsync(uri.fsPath, instance.shellLaunchConfig.executable, instance.title, instance.shellType); instance.sendText(path, true); return this.terminalService.showPanel(); @@ -707,7 +624,7 @@ export class SwitchTerminalAction extends Action { } if (item === SelectDefaultShellWindowsTerminalAction.LABEL) { this.terminalService.refreshActiveTab(); - return this.terminalService.selectDefaultWindowsShell(); + return this.terminalService.selectDefaultShell(); } const selectedTabIndex = parseInt(item.split(':')[0], 10) - 1; this.terminalService.setActiveTabByIndex(selectedTabIndex); @@ -725,7 +642,7 @@ export class SwitchTerminalActionViewItem extends SelectActionViewItem { @IThemeService private readonly themeService: IThemeService, @IContextViewService contextViewService: IContextViewService ) { - super(null, action, terminalService.getTabLabels().map(label => { text: label }), terminalService.activeTabIndex, contextViewService, { ariaLabel: nls.localize('terminals', 'Open Terminals.') }); + super(null, action, getTerminalSelectOpenItems(terminalService), terminalService.activeTabIndex, contextViewService, { ariaLabel: nls.localize('terminals', 'Open Terminals.') }); this._register(terminalService.onInstancesChanged(this._updateItems, this)); this._register(terminalService.onActiveTabChanged(this._updateItems, this)); @@ -738,18 +655,22 @@ export class SwitchTerminalActionViewItem extends SelectActionViewItem { super.render(container); addClass(container, 'switch-terminal'); this._register(attachStylerCallback(this.themeService, { selectBorder }, colors => { - container.style.border = colors.selectBorder ? `1px solid ${colors.selectBorder}` : ''; + container.style.borderColor = colors.selectBorder ? `${colors.selectBorder}` : ''; })); } private _updateItems(): void { - const items = this.terminalService.getTabLabels().map(label => { text: label }); - items.push({ text: SwitchTerminalActionViewItem.SEPARATOR, isDisabled: true }); - items.push({ text: SelectDefaultShellWindowsTerminalAction.LABEL }); - this.setOptions(items, this.terminalService.activeTabIndex); + this.setOptions(getTerminalSelectOpenItems(this.terminalService), this.terminalService.activeTabIndex); } } +function getTerminalSelectOpenItems(terminalService: ITerminalService): ISelectOptionItem[] { + const items = terminalService.getTabLabels().map(label => { text: label }); + items.push({ text: SwitchTerminalActionViewItem.SEPARATOR, isDisabled: true }); + items.push({ text: SelectDefaultShellWindowsTerminalAction.LABEL }); + return items; +} + export class ScrollDownTerminalAction extends Action { public static readonly ID = TERMINAL_COMMAND_ID.SCROLL_DOWN_LINE; @@ -957,6 +878,7 @@ export class ClearTerminalAction extends Action { const terminalInstance = this.terminalService.getActiveInstance(); if (terminalInstance) { terminalInstance.clear(); + terminalInstance.focus(); } return Promise.resolve(undefined); } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts b/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts index 2641ae0263..679032f0b3 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts @@ -124,9 +124,23 @@ export class TerminalConfigHelper implements IBrowserTerminalConfigHelper { fontSize, letterSpacing, lineHeight, - charWidth: rect && rect.width ? rect.width : 0, - charHeight: rect && rect.height ? Math.ceil(rect.height) : 0 + charWidth: 0, + charHeight: 0 }; + + if (rect && rect.width && rect.height) { + this._lastFontMeasurement.charHeight = Math.ceil(rect.height); + // Char width is calculated differently for DOM and the other renderer types. Refer to + // how each renderer updates their dimensions in xterm.js + if (this.config.rendererType === 'dom') { + this._lastFontMeasurement.charWidth = rect.width; + } else { + const scaledCharWidth = rect.width * window.devicePixelRatio; + const scaledCellWidth = scaledCharWidth + Math.round(letterSpacing); + this._lastFontMeasurement.charWidth = Math.round(scaledCellWidth / window.devicePixelRatio); + } + } + return this._lastFontMeasurement; } @@ -167,14 +181,14 @@ export class TerminalConfigHelper implements IBrowserTerminalConfigHelper { // Get the character dimensions from xterm if it's available if (xtermCore) { - if (xtermCore._charSizeService && xtermCore._charSizeService.width && xtermCore._charSizeService.height) { + if (xtermCore._renderService && xtermCore._renderService.dimensions?.actualCellWidth && xtermCore._renderService.dimensions?.actualCellHeight) { return { fontFamily, fontSize, letterSpacing, lineHeight, - charHeight: xtermCore._charSizeService.height, - charWidth: xtermCore._charSizeService.width + charHeight: xtermCore._renderService.dimensions.actualCellHeight / lineHeight, + charWidth: xtermCore._renderService.dimensions.actualCellWidth }; } } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts index b7484dd4a6..1f505167c9 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstance.ts @@ -277,6 +277,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { public constructor( private readonly _terminalFocusContextKey: IContextKey, + private readonly _terminalShellTypeContextKey: IContextKey, private readonly _configHelper: TerminalConfigHelper, private _container: HTMLElement | undefined, private _shellLaunchConfig: IShellLaunchConfig, @@ -488,7 +489,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { fastScrollSensitivity: editorOptions.fastScrollSensitivity, scrollSensitivity: editorOptions.mouseWheelScrollSensitivity, rendererType: config.rendererType === 'auto' || config.rendererType === 'experimentalWebgl' ? 'canvas' : config.rendererType, - wordSeparator: ' ()[]{}\',"`' + wordSeparator: config.wordSeparators }); this._xterm = xterm; this._xtermCore = (xterm as any)._core as XTermCore; @@ -671,13 +672,18 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { while (!dom.hasClass(currentElement, 'part')) { currentElement = currentElement.parentElement!; } - const hidePanelElement = currentElement.querySelector('.hide-panel-action'); - hidePanelElement.focus(); + const hidePanelElement = currentElement.querySelector('.hide-panel-action'); + hidePanelElement?.focus(); })); xtermHelper.insertBefore(focusTrap, xterm.textarea); this._register(dom.addDisposableListener(xterm.textarea, 'focus', () => { this._terminalFocusContextKey.set(true); + if (this.shellType) { + this._terminalShellTypeContextKey.set(this.shellType.toString()); + } else { + this._terminalShellTypeContextKey.reset(); + } this._onFocused.fire(this); })); this._register(dom.addDisposableListener(xterm.textarea, 'blur', () => { @@ -686,6 +692,11 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { })); this._register(dom.addDisposableListener(xterm.element, 'focus', () => { this._terminalFocusContextKey.set(true); + if (this.shellType) { + this._terminalShellTypeContextKey.set(this.shellType.toString()); + } else { + this._terminalShellTypeContextKey.reset(); + } })); this._register(dom.addDisposableListener(xterm.element, 'blur', () => { this._terminalFocusContextKey.reset(); @@ -1248,6 +1259,7 @@ export class TerminalInstance extends Disposable implements ITerminalInstance { this._safeSetOption('macOptionIsMeta', config.macOptionIsMeta); this._safeSetOption('macOptionClickForcesSelection', config.macOptionClickForcesSelection); this._safeSetOption('rightClickSelectsWord', config.rightClickBehavior === 'selectWord'); + this._safeSetOption('wordSeparator', config.wordSeparators); if (config.rendererType !== 'experimentalWebgl') { // Never set webgl as it's an addon not a rendererType this._safeSetOption('rendererType', config.rendererType === 'auto' ? 'canvas' : config.rendererType); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts index 6fcb662fdd..a2ebfc7e88 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts @@ -24,6 +24,7 @@ import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteA import { Disposable } from 'vs/base/common/lifecycle'; import { withNullAsUndefined } from 'vs/base/common/types'; import { IEnvironmentVariableService, IMergedEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariable'; +import { IRemotePathService } from 'vs/workbench/services/path/common/remotePathService'; /** The amount of time to consider terminal errors to be related to the launch */ const LAUNCHING_DURATION = 500; @@ -90,6 +91,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce @IProductService private readonly _productService: IProductService, @ITerminalInstanceService private readonly _terminalInstanceService: ITerminalInstanceService, @IRemoteAgentService private readonly _remoteAgentService: IRemoteAgentService, + @IRemotePathService private readonly _remotePathService: IRemotePathService, @IEnvironmentVariableService private readonly _environmentVariableService: IEnvironmentVariableService ) { super(); @@ -133,23 +135,22 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce const hasRemoteAuthority = !!this.remoteAuthority; let launchRemotely = hasRemoteAuthority || forceExtHostProcess; - this.userHome = this._environmentService.userHome; + const userHomeUri = await this._remotePathService.userHome; this.os = platform.OS; if (launchRemotely) { + this.userHome = userHomeUri.path; if (hasRemoteAuthority) { - this._remoteAgentService.getEnvironment().then(env => { - if (!env) { - return; - } - this.userHome = env.userHome.path; - this.os = env.os; - }); + const remoteEnv = await this._remoteAgentService.getEnvironment(); + if (remoteEnv) { + this.os = remoteEnv.os; + } } const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(); this._process = this._instantiationService.createInstance(TerminalProcessExtHostProxy, this._terminalId, shellLaunchConfig, activeWorkspaceRootUri, cols, rows, this._configHelper); } else { - this._process = await this._launchProcess(shellLaunchConfig, cols, rows, isScreenReaderModeEnabled); + this.userHome = userHomeUri.fsPath; + this._process = await this._launchProcess(shellLaunchConfig, cols, rows, this.userHome, isScreenReaderModeEnabled); } } this.processState = ProcessState.LAUNCHING; @@ -194,6 +195,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce shellLaunchConfig: IShellLaunchConfig, cols: number, rows: number, + userHome: string, isScreenReaderModeEnabled: boolean ): Promise { const activeWorkspaceRootUri = this._historyService.getLastActiveWorkspaceRoot(Schemas.file); @@ -220,7 +222,7 @@ export class TerminalProcessManager extends Disposable implements ITerminalProce const initialCwd = terminalEnvironment.getCwd( shellLaunchConfig, - this._environmentService.userHome, + userHome, lastActiveWorkspace, this._configurationResolverService, activeWorkspaceRootUri, diff --git a/src/vs/workbench/contrib/terminal/browser/terminalService.ts b/src/vs/workbench/contrib/terminal/browser/terminalService.ts index 5397c73610..6611829998 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalService.ts @@ -4,9 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import { TERMINAL_VIEW_ID, IShellLaunchConfig, ITerminalConfigHelper, ITerminalNativeService, ISpawnExtHostProcessRequest, IStartExtensionTerminalRequest, IAvailableShellsRequest, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_IS_OPEN, ITerminalProcessExtHostProxy, IShellDefinition, LinuxDistro } from 'vs/workbench/contrib/terminal/common/terminal'; +import { TERMINAL_VIEW_ID, IShellLaunchConfig, ITerminalConfigHelper, ITerminalNativeService, ISpawnExtHostProcessRequest, IStartExtensionTerminalRequest, IAvailableShellsRequest, KEYBINDING_CONTEXT_TERMINAL_FOCUS, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE, KEYBINDING_CONTEXT_TERMINAL_IS_OPEN, ITerminalProcessExtHostProxy, IShellDefinition, LinuxDistro, KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE } from 'vs/workbench/contrib/terminal/common/terminal'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { TerminalViewPane } from 'vs/workbench/contrib/terminal/browser/terminalView'; @@ -26,10 +25,12 @@ import { FindReplaceState } from 'vs/editor/contrib/find/findState'; import { escapeNonWindowsPath } from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; import { isWindows, isMacintosh, OperatingSystem } from 'vs/base/common/platform'; import { basename } from 'vs/base/common/path'; -import { IOpenFileRequest } from 'vs/platform/windows/common/windows'; +// TODO@daniel code layering +// eslint-disable-next-line code-layering, code-import-patterns +import { INativeOpenFileRequest } from 'vs/platform/windows/node/window'; import { find } from 'vs/base/common/arrays'; import { timeout } from 'vs/base/common/async'; -import { IViewsService } from 'vs/workbench/common/views'; +import { IViewsService, ViewContainerLocation, IViewDescriptorService } from 'vs/workbench/common/views'; import { IDisposable } from 'vs/base/common/lifecycle'; interface IExtHostReadyEntry { @@ -40,12 +41,13 @@ interface IExtHostReadyEntry { export class TerminalService implements ITerminalService { public _serviceBrand: undefined; - protected _isShuttingDown: boolean; - protected _terminalFocusContextKey: IContextKey; - protected _findWidgetVisible: IContextKey; - protected _terminalTabs: ITerminalTab[] = []; - protected _backgroundedTerminalInstances: ITerminalInstance[] = []; - protected get _terminalInstances(): ITerminalInstance[] { + private _isShuttingDown: boolean; + private _terminalFocusContextKey: IContextKey; + private _terminalShellTypeContextKey: IContextKey; + private _findWidgetVisible: IContextKey; + private _terminalTabs: ITerminalTab[] = []; + private _backgroundedTerminalInstances: ITerminalInstance[] = []; + private get _terminalInstances(): ITerminalInstance[] { return this._terminalTabs.reduce((p, c) => p.concat(c.terminalInstances), []); } private _findState: FindReplaceState; @@ -62,38 +64,37 @@ export class TerminalService implements ITerminalService { public get configHelper(): ITerminalConfigHelper { return this._configHelper; } - protected readonly _onActiveTabChanged = new Emitter(); + private readonly _onActiveTabChanged = new Emitter(); public get onActiveTabChanged(): Event { return this._onActiveTabChanged.event; } - protected readonly _onInstanceCreated = new Emitter(); + private readonly _onInstanceCreated = new Emitter(); public get onInstanceCreated(): Event { return this._onInstanceCreated.event; } - protected readonly _onInstanceDisposed = new Emitter(); + private readonly _onInstanceDisposed = new Emitter(); public get onInstanceDisposed(): Event { return this._onInstanceDisposed.event; } - protected readonly _onInstanceProcessIdReady = new Emitter(); + private readonly _onInstanceProcessIdReady = new Emitter(); public get onInstanceProcessIdReady(): Event { return this._onInstanceProcessIdReady.event; } - protected readonly _onInstanceRequestSpawnExtHostProcess = new Emitter(); + private readonly _onInstanceRequestSpawnExtHostProcess = new Emitter(); public get onInstanceRequestSpawnExtHostProcess(): Event { return this._onInstanceRequestSpawnExtHostProcess.event; } - protected readonly _onInstanceRequestStartExtensionTerminal = new Emitter(); + private readonly _onInstanceRequestStartExtensionTerminal = new Emitter(); public get onInstanceRequestStartExtensionTerminal(): Event { return this._onInstanceRequestStartExtensionTerminal.event; } - protected readonly _onInstanceDimensionsChanged = new Emitter(); + private readonly _onInstanceDimensionsChanged = new Emitter(); public get onInstanceDimensionsChanged(): Event { return this._onInstanceDimensionsChanged.event; } - protected readonly _onInstanceMaximumDimensionsChanged = new Emitter(); + private readonly _onInstanceMaximumDimensionsChanged = new Emitter(); public get onInstanceMaximumDimensionsChanged(): Event { return this._onInstanceMaximumDimensionsChanged.event; } - protected readonly _onInstancesChanged = new Emitter(); + private readonly _onInstancesChanged = new Emitter(); public get onInstancesChanged(): Event { return this._onInstancesChanged.event; } - protected readonly _onInstanceTitleChanged = new Emitter(); + private readonly _onInstanceTitleChanged = new Emitter(); public get onInstanceTitleChanged(): Event { return this._onInstanceTitleChanged.event; } - protected readonly _onActiveInstanceChanged = new Emitter(); + private readonly _onActiveInstanceChanged = new Emitter(); public get onActiveInstanceChanged(): Event { return this._onActiveInstanceChanged.event; } - protected readonly _onTabDisposed = new Emitter(); + private readonly _onTabDisposed = new Emitter(); public get onTabDisposed(): Event { return this._onTabDisposed.event; } - protected readonly _onRequestAvailableShells = new Emitter(); + private readonly _onRequestAvailableShells = new Emitter(); public get onRequestAvailableShells(): Event { return this._onRequestAvailableShells.event; } private readonly _terminalNativeService: ITerminalNativeService | undefined; constructor( @IContextKeyService private _contextKeyService: IContextKeyService, - @IPanelService private _panelService: IPanelService, @IWorkbenchLayoutService private _layoutService: IWorkbenchLayoutService, @ILifecycleService lifecycleService: ILifecycleService, @IDialogService private _dialogService: IDialogService, @@ -103,6 +104,7 @@ export class TerminalService implements ITerminalService { @IQuickInputService private _quickInputService: IQuickInputService, @IConfigurationService private _configurationService: IConfigurationService, @IViewsService private _viewsService: IViewsService, + @IViewDescriptorService private readonly _viewDescriptorService: IViewDescriptorService, @optional(ITerminalNativeService) terminalNativeService: ITerminalNativeService ) { // @optional could give undefined and properly typing it breaks service registration @@ -118,6 +120,7 @@ export class TerminalService implements ITerminalService { this._terminalNativeService.onOsResume(() => this._onOsResume()); } this._terminalFocusContextKey = KEYBINDING_CONTEXT_TERMINAL_FOCUS.bindTo(this._contextKeyService); + this._terminalShellTypeContextKey = KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE.bindTo(this._contextKeyService); this._findWidgetVisible = KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_VISIBLE.bindTo(this._contextKeyService); this._configHelper = this._instantiationService.createInstance(TerminalConfigHelper, this._terminalNativeService?.linuxDistro || LinuxDistro.Unknown); this.onTabDisposed(tab => this._removeTab(tab)); @@ -207,7 +210,7 @@ export class TerminalService implements ITerminalService { this.terminalInstances.forEach(instance => instance.dispose(true)); } - private async _onOpenFileRequest(request: IOpenFileRequest): Promise { + private async _onOpenFileRequest(request: INativeOpenFileRequest): Promise { // if the request to open files is coming in from the integrated terminal (identified though // the termProgram variable) and we are instructed to wait for editors close, wait for the // marker file to get deleted and then focus back to the integrated terminal. @@ -582,8 +585,8 @@ export class TerminalService implements ITerminalService { }); } - public async selectDefaultWindowsShell(): Promise { - const shells = await this._detectWindowsShells(); + public async selectDefaultShell(): Promise { + const shells = await this._detectShells(); const options: IPickOptions = { placeHolder: nls.localize('terminal.integrated.chooseWindowsShell', "Select your preferred terminal shell, you can change this later in your settings") }; @@ -605,13 +608,13 @@ export class TerminalService implements ITerminalService { await this._configurationService.updateValue(`terminal.integrated.shell.${platformKey}`, shell, ConfigurationTarget.USER); } - private _detectWindowsShells(): Promise { + private _detectShells(): Promise { return new Promise(r => this._onRequestAvailableShells.fire({ callback: r })); } public createInstance(container: HTMLElement | undefined, shellLaunchConfig: IShellLaunchConfig): ITerminalInstance { - const instance = this._instantiationService.createInstance(TerminalInstance, this._terminalFocusContextKey, this._configHelper, container, shellLaunchConfig); + const instance = this._instantiationService.createInstance(TerminalInstance, this._terminalFocusContextKey, this._terminalShellTypeContextKey, this._configHelper, container, shellLaunchConfig); this._onInstanceCreated.fire(instance); return instance; } @@ -623,11 +626,7 @@ export class TerminalService implements ITerminalService { this._initInstanceListeners(instance); return instance; } - const terminalTab = this._instantiationService.createInstance(TerminalTab, - this._terminalFocusContextKey, - this.configHelper, - this._terminalContainer, - shell); + const terminalTab = this._instantiationService.createInstance(TerminalTab, this._terminalContainer, shell); this._terminalTabs.push(terminalTab); const instance = terminalTab.terminalInstances[0]; terminalTab.addDisposable(terminalTab.onDisposed(this._onTabDisposed.fire, this._onTabDisposed)); @@ -644,11 +643,7 @@ export class TerminalService implements ITerminalService { protected _showBackgroundTerminal(instance: ITerminalInstance): void { this._backgroundedTerminalInstances.splice(this._backgroundedTerminalInstances.indexOf(instance), 1); instance.shellLaunchConfig.hideFromUser = false; - const terminalTab = this._instantiationService.createInstance(TerminalTab, - this._terminalFocusContextKey, - this.configHelper, - this._terminalContainer, - instance); + const terminalTab = this._instantiationService.createInstance(TerminalTab, this._terminalContainer, instance); this._terminalTabs.push(terminalTab); terminalTab.addDisposable(terminalTab.onDisposed(this._onTabDisposed.fire, this._onTabDisposed)); terminalTab.addDisposable(terminalTab.onInstancesChanged(this._onInstancesChanged.fire, this._onInstancesChanged)); @@ -698,9 +693,13 @@ export class TerminalService implements ITerminalService { } public hidePanel(): void { - const panel = this._panelService.getActivePanel(); - if (panel && panel.getId() === TERMINAL_VIEW_ID) { - this._layoutService.setPanelHidden(true); + // Hide the panel if the terminal is in the panel and it has no sibling views + const location = this._viewDescriptorService.getViewLocation(TERMINAL_VIEW_ID); + if (location === ViewContainerLocation.Panel) { + const panel = this._viewDescriptorService.getViewContainer(TERMINAL_VIEW_ID); + if (panel && this._viewDescriptorService.getViewDescriptors(panel).activeViewDescriptors.length === 1) { + this._layoutService.setPanelHidden(true); + } } } } diff --git a/src/vs/workbench/contrib/terminal/browser/terminalTab.ts b/src/vs/workbench/contrib/terminal/browser/terminalTab.ts index c3f11c7455..9c18b95943 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalTab.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalTab.ts @@ -4,8 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as aria from 'vs/base/browser/ui/aria/aria'; import * as nls from 'vs/nls'; -import { IShellLaunchConfig, ITerminalConfigHelper, TERMINAL_VIEW_ID } from 'vs/workbench/contrib/terminal/common/terminal'; -import { IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IShellLaunchConfig, TERMINAL_VIEW_ID } from 'vs/workbench/contrib/terminal/common/terminal'; import { Event, Emitter } from 'vs/base/common/event'; import { IDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { SplitView, Orientation, IView, Sizing } from 'vs/base/browser/ui/splitview/splitview'; @@ -228,8 +227,6 @@ export class TerminalTab extends Disposable implements ITerminalTab { public readonly onInstancesChanged: Event = this._onInstancesChanged.event; constructor( - terminalFocusContextKey: IContextKey, - configHelper: ITerminalConfigHelper, private _container: HTMLElement | undefined, shellLaunchConfigOrInstance: IShellLaunchConfig | ITerminalInstance, @ITerminalService private readonly _terminalService: ITerminalService, diff --git a/src/vs/workbench/contrib/terminal/browser/terminalView.ts b/src/vs/workbench/contrib/terminal/browser/terminalView.ts index adeba316bf..4263a58449 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalView.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalView.ts @@ -232,6 +232,13 @@ export class TerminalViewPane extends ViewPane { if (!terminal) { return; } + + // copyPaste: Shift+right click should open context menu + if (rightClickBehavior === 'copyPaste' && event.shiftKey) { + this._openContextMenu(event); + return; + } + if (rightClickBehavior === 'copyPaste' && terminal.hasSelection()) { await terminal.copySelection(); terminal.clearSelection(); @@ -253,13 +260,7 @@ export class TerminalViewPane extends ViewPane { })); this._register(dom.addDisposableListener(parentDomElement, 'contextmenu', (event: MouseEvent) => { if (!this._cancelContextMenu) { - const standardEvent = new StandardMouseEvent(event); - const anchor: { x: number, y: number } = { x: standardEvent.posx, y: standardEvent.posy }; - this._contextMenuService.showContextMenu({ - getAnchor: () => anchor, - getActions: () => this._getContextMenuActions(), - getActionsContext: () => this._parentDomElement - }); + this._openContextMenu(event); } event.preventDefault(); event.stopImmediatePropagation(); @@ -306,6 +307,16 @@ export class TerminalViewPane extends ViewPane { })); } + private _openContextMenu(event: MouseEvent): void { + const standardEvent = new StandardMouseEvent(event); + const anchor: { x: number, y: number } = { x: standardEvent.posx, y: standardEvent.posy }; + this._contextMenuService.showContextMenu({ + getAnchor: () => anchor, + getActions: () => this._getContextMenuActions(), + getActionsContext: () => this._parentDomElement + }); + } + private _updateTheme(theme?: IColorTheme): void { if (!theme) { theme = this.themeService.getColorTheme(); diff --git a/src/vs/workbench/contrib/terminal/browser/xterm-private.d.ts b/src/vs/workbench/contrib/terminal/browser/xterm-private.d.ts index a0cf456dfe..5af7a57fe0 100644 --- a/src/vs/workbench/contrib/terminal/browser/xterm-private.d.ts +++ b/src/vs/workbench/contrib/terminal/browser/xterm-private.d.ts @@ -17,6 +17,10 @@ export interface XTermCore { }; _renderService: { + dimensions: { + actualCellWidth: number; + actualCellHeight: number; + }, _renderer: { _renderLayers: any[]; }; diff --git a/src/vs/workbench/contrib/terminal/common/environmentVariableCollection.ts b/src/vs/workbench/contrib/terminal/common/environmentVariableCollection.ts index 5ebde1c0f2..d261e97104 100644 --- a/src/vs/workbench/contrib/terminal/common/environmentVariableCollection.ts +++ b/src/vs/workbench/contrib/terminal/common/environmentVariableCollection.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { IEnvironmentVariableCollection, EnvironmentVariableMutatorType, IMergedEnvironmentVariableCollection, IMergedEnvironmentVariableCollectionDiff, IExtensionOwnedEnvironmentVariableMutator } from 'vs/workbench/contrib/terminal/common/environmentVariable'; -import { IProcessEnvironment } from 'vs/base/common/platform'; +import { IProcessEnvironment, isWindows } from 'vs/base/common/platform'; export class MergedEnvironmentVariableCollection implements IMergedEnvironmentVariableCollection { readonly map: Map = new Map(); @@ -42,17 +42,23 @@ export class MergedEnvironmentVariableCollection implements IMergedEnvironmentVa } applyToProcessEnvironment(env: IProcessEnvironment): void { + let lowerToActualVariableNames: { [lowerKey: string]: string | undefined } | undefined; + if (isWindows) { + lowerToActualVariableNames = {}; + Object.keys(env).forEach(e => lowerToActualVariableNames![e.toLowerCase()] = e); + } this.map.forEach((mutators, variable) => { + const actualVariable = isWindows ? lowerToActualVariableNames![variable.toLowerCase()] || variable : variable; mutators.forEach(mutator => { switch (mutator.type) { case EnvironmentVariableMutatorType.Append: - env[variable] = (env[variable] || '') + mutator.value; + env[actualVariable] = (env[actualVariable] || '') + mutator.value; break; case EnvironmentVariableMutatorType.Prepend: - env[variable] = mutator.value + (env[variable] || ''); + env[actualVariable] = mutator.value + (env[actualVariable] || ''); break; case EnvironmentVariableMutatorType.Replace: - env[variable] = mutator.value; + env[actualVariable] = mutator.value; break; } }); diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index a44a4b97fa..9ba217172e 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -16,10 +16,17 @@ export const TERMINAL_VIEW_ID = 'workbench.panel.terminal'; /** A context key that is set when there is at least one opened integrated terminal. */ export const KEYBINDING_CONTEXT_TERMINAL_IS_OPEN = new RawContextKey('terminalIsOpen', false); + /** A context key that is set when the integrated terminal has focus. */ export const KEYBINDING_CONTEXT_TERMINAL_FOCUS = new RawContextKey('terminalFocus', false); + +export const KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE_KEY = 'terminalShellType'; +/** A context key that is set to the detected shell for the most recently active terminal, this is set to the last known value when no terminals exist. */ +export const KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE = new RawContextKey(KEYBINDING_CONTEXT_TERMINAL_SHELL_TYPE_KEY, undefined); + /** A context key that is set when the integrated terminal does not have focus. */ export const KEYBINDING_CONTEXT_TERMINAL_NOT_FOCUSED = KEYBINDING_CONTEXT_TERMINAL_FOCUS.toNegated(); + /** A context key that is set when the user is navigating the accessibility tree */ export const KEYBINDING_CONTEXT_TERMINAL_A11Y_TREE_FOCUS = new RawContextKey('terminalA11yTreeFocus', false); @@ -120,6 +127,7 @@ export interface ITerminalConfiguration { showExitAlert: boolean; splitCwd: 'workspaceRoot' | 'initial' | 'inherited'; windowsEnableConpty: boolean; + wordSeparators: string; experimentalUseTitleEvent: boolean; enableFileLinks: boolean; unicodeVersion: '6' | '11'; diff --git a/src/vs/workbench/contrib/terminal/node/terminal.ts b/src/vs/workbench/contrib/terminal/node/terminal.ts index 558655ad13..dafcde02b1 100644 --- a/src/vs/workbench/contrib/terminal/node/terminal.ts +++ b/src/vs/workbench/contrib/terminal/node/terminal.ts @@ -109,8 +109,8 @@ async function detectAvailableWindowsShells(): Promise { const expectedLocations: { [key: string]: string[] } = { 'Command Prompt': [`${system32Path}\\cmd.exe`], - PowerShell: [`${system32Path}\\WindowsPowerShell\\v1.0\\powershell.exe`], - 'PowerShell Core': [await getShellPathFromRegistry('pwsh')], + 'Windows PowerShell': [`${system32Path}\\WindowsPowerShell\\v1.0\\powershell.exe`], + 'PowerShell': [await getShellPathFromRegistry('pwsh')], 'WSL Bash': [`${system32Path}\\${useWSLexe ? 'wsl.exe' : 'bash.exe'}`], 'Git Bash': [ `${process.env['ProgramW6432']}\\Git\\bin\\bash.exe`, diff --git a/src/vs/workbench/contrib/terminal/test/common/environmentVariableCollection.test.ts b/src/vs/workbench/contrib/terminal/test/common/environmentVariableCollection.test.ts index f6649d3137..7d04f045b3 100644 --- a/src/vs/workbench/contrib/terminal/test/common/environmentVariableCollection.test.ts +++ b/src/vs/workbench/contrib/terminal/test/common/environmentVariableCollection.test.ts @@ -5,7 +5,7 @@ import { deepStrictEqual, strictEqual } from 'assert'; import { EnvironmentVariableMutatorType } from 'vs/workbench/contrib/terminal/common/environmentVariable'; -import { IProcessEnvironment } from 'vs/base/common/platform'; +import { IProcessEnvironment, isWindows } from 'vs/base/common/platform'; import { MergedEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariableCollection'; import { deserializeEnvironmentVariableCollection } from 'vs/workbench/contrib/terminal/common/environmentVariableShared'; @@ -119,6 +119,40 @@ suite('EnvironmentVariable - MergedEnvironmentVariableCollection', () => { C: 'c' }); }); + + test('should apply to variable case insensitively on Windows only', () => { + const merged = new MergedEnvironmentVariableCollection(new Map([ + ['ext', { + map: deserializeEnvironmentVariableCollection([ + ['a', { value: 'a', type: EnvironmentVariableMutatorType.Replace }], + ['b', { value: 'b', type: EnvironmentVariableMutatorType.Append }], + ['c', { value: 'c', type: EnvironmentVariableMutatorType.Prepend }] + ]) + }] + ])); + const env: IProcessEnvironment = { + A: 'A', + B: 'B', + C: 'C' + }; + merged.applyToProcessEnvironment(env); + if (isWindows) { + deepStrictEqual(env, { + A: 'a', + B: 'Bb', + C: 'cC' + }); + } else { + deepStrictEqual(env, { + a: 'a', + A: 'A', + b: 'b', + B: 'B', + c: 'c', + C: 'C' + }); + } + }); }); suite('diff', () => { diff --git a/src/vs/workbench/contrib/timeline/browser/media/timelinePane.css b/src/vs/workbench/contrib/timeline/browser/media/timelinePane.css index 87c41770e8..b7c3f473e6 100644 --- a/src/vs/workbench/contrib/timeline/browser/media/timelinePane.css +++ b/src/vs/workbench/contrib/timeline/browser/media/timelinePane.css @@ -8,10 +8,14 @@ } .monaco-workbench .timeline-view.pane-header .description { + display: block; + font-weight: normal; margin-left: 10px; opacity: 0.6; + overflow: hidden; + text-overflow: ellipsis; text-transform: none; - font-weight: normal; + white-space: nowrap; } .monaco-workbench .timeline-view.pane-header:not(.expanded) .description { diff --git a/src/vs/workbench/contrib/timeline/browser/timeline.contribution.ts b/src/vs/workbench/contrib/timeline/browser/timeline.contribution.ts index 0c70787ab5..f7817eae3f 100644 --- a/src/vs/workbench/contrib/timeline/browser/timeline.contribution.ts +++ b/src/vs/workbench/contrib/timeline/browser/timeline.contribution.ts @@ -22,6 +22,7 @@ import { ResourceContextKey } from 'vs/workbench/common/resources'; export class TimelinePaneDescriptor implements IViewDescriptor { readonly id = TimelinePaneId; readonly name = TimelinePane.TITLE; + readonly containerIcon = 'codicon-history'; readonly ctorDescriptor = new SyncDescriptor(TimelinePane); readonly order = 2; readonly weight = 30; diff --git a/src/vs/workbench/contrib/timeline/browser/timelinePane.ts b/src/vs/workbench/contrib/timeline/browser/timelinePane.ts index f3c53e175c..b4044e6913 100644 --- a/src/vs/workbench/contrib/timeline/browser/timelinePane.ts +++ b/src/vs/workbench/contrib/timeline/browser/timelinePane.ts @@ -42,34 +42,10 @@ import { Schemas } from 'vs/base/common/network'; const PageSize = 20; -interface CommandItem { - handle: 'vscode-command:loadMore'; - timestamp: number; - label: string; - themeIcon?: { id: string }; - description?: string; - detail?: string; - contextValue?: string; +type TreeElement = TimelineItem | LoadMoreCommand; - // Make things easier for duck typing - id: undefined; - icon: undefined; - iconDark: undefined; - source: undefined; - relativeTime: undefined; - hideRelativeTime: undefined; -} - -type TreeElement = TimelineItem | CommandItem; - -// function isCommandItem(item: TreeElement | undefined): item is CommandItem { -// return item?.handle.startsWith('vscode-command:') ?? false; -// } - -function isLoadMoreCommandItem(item: TreeElement | undefined): item is CommandItem & { - handle: 'vscode-command:loadMore'; -} { - return item?.handle === 'vscode-command:loadMore'; +function isLoadMoreCommand(item: TreeElement | undefined): item is LoadMoreCommand { + return item instanceof LoadMoreCommand; } function isTimelineItem(item: TreeElement | undefined): item is TimelineItem { @@ -193,6 +169,44 @@ class TimelineAggregate { } } +class LoadMoreCommand { + readonly handle = 'vscode-command:loadMore'; + readonly timestamp = 0; + readonly description = undefined; + readonly detail = undefined; + readonly contextValue = undefined; + // Make things easier for duck typing + readonly id = undefined; + readonly icon = undefined; + readonly iconDark = undefined; + readonly source = undefined; + readonly relativeTime = undefined; + readonly hideRelativeTime = undefined; + + constructor(loading: boolean) { + this._loading = loading; + } + private _loading: boolean = false; + get loading(): boolean { + return this._loading; + } + set loading(value: boolean) { + this._loading = value; + } + + get ariaLabel() { + return this.label; + } + + get label() { + return this.loading ? localize('timeline.loadingMore', "Loading...") : localize('timeline.loadMore', "Load more"); + } + + get themeIcon(): { id: string; } | undefined { + return undefined; //this.loading ? { id: 'sync~spin' } : undefined; + } +} + export const TimelineFollowActiveEditorContext = new RawContextKey('timelineFollowActiveEditor', true); export class TimelinePane extends ViewPane { @@ -359,9 +373,9 @@ export class TimelinePane extends ViewPane { } if (this.isBodyVisible()) { - this.updateTimeline(timeline, e.reset ?? false); + this.updateTimeline(timeline, e.reset); } else { - timeline.invalidate(e.reset ?? false); + timeline.invalidate(e.reset); } } } @@ -497,6 +511,7 @@ export class TimelinePane extends ViewPane { // don't bother querying for more if ( !reset && + options?.cursor !== undefined && timeline !== undefined && (!timeline?.more || timeline.items.length > timeline.lastRenderedIndex + PageSize) ) { @@ -595,8 +610,12 @@ export class TimelinePane extends ViewPane { } else { this.refresh(); } - } else if (this.pendingRequests.size === 0 && this._pendingRefresh) { - this.refresh(); + } else if (this.pendingRequests.size === 0) { + if (this._pendingRefresh) { + this.refresh(); + } else { + this.tree.rerender(); + } } } @@ -713,11 +732,11 @@ export class TimelinePane extends ViewPane { if (more) { yield { - element: { - handle: 'vscode-command:loadMore', - label: localize('timeline.loadMore', 'Load more'), - timestamp: 0 - } as CommandItem + element: new LoadMoreCommand(this.pendingRequests.size !== 0) + }; + } else if (this.pendingRequests.size !== 0) { + yield { + element: new LoadMoreCommand(true) }; } } @@ -732,13 +751,13 @@ export class TimelinePane extends ViewPane { if (this.uri === undefined) { this.titleDescription = undefined; - this.message = localize('timeline.editorCannotProvideTimeline', 'The active editor cannot provide timeline information.'); + this.message = localize('timeline.editorCannotProvideTimeline', "The active editor cannot provide timeline information."); } else if (this._isEmpty) { if (this.pendingRequests.size !== 0) { this.setLoadingUriMessage(); } else { this.titleDescription = basename(this.uri.fsPath); - this.message = localize('timeline.noTimelineInfo', 'No timeline information was provided.'); + this.message = localize('timeline.noTimelineInfo', "No timeline information was provided."); } } else { this.titleDescription = basename(this.uri.fsPath); @@ -810,7 +829,7 @@ export class TimelinePane extends ViewPane { this.$message = DOM.append(this.$container, DOM.$('.message')); DOM.addClass(this.$message, 'timeline-subtle'); - this.message = localize('timeline.editorCannotProvideTimeline', 'The active editor cannot provide timeline information.'); + this.message = localize('timeline.editorCannotProvideTimeline', "The active editor cannot provide timeline information."); this.$tree = document.createElement('div'); DOM.addClasses(this.$tree, 'customview-tree', 'file-icon-themable-tree', 'hide-arrows'); @@ -821,6 +840,15 @@ export class TimelinePane extends ViewPane { this.tree = >this.instantiationService.createInstance(WorkbenchObjectTree, 'TimelinePane', this.$tree, new TimelineListVirtualDelegate(), [this.treeRenderer], { identityProvider: new TimelineIdentityProvider(), + accessibilityProvider: { + getAriaLabel(element: TreeElement): string { + if (isLoadMoreCommand(element)) { + return element.ariaLabel; + } + return element.ariaLabel ?? localize('timeline.aria.item', "{0}: {1}", element.relativeTime ?? '', element.label); + } + }, + ariaLabel: this.title, keyboardNavigationLabelProvider: new TimelineKeyboardNavigationLabelProvider(), overrideStyles: { listBackground: this.getBackgroundColor(), @@ -850,7 +878,10 @@ export class TimelinePane extends ViewPane { this.commandService.executeCommand(item.command.id, ...(item.command.arguments || [])); } } - else if (isLoadMoreCommandItem(item)) { + else if (isLoadMoreCommand(item)) { + item.loading = true; + this.tree.rerender(item); + if (this.pendingRequests.size !== 0) { return; } @@ -878,7 +909,7 @@ export class TimelinePane extends ViewPane { setLoadingUriMessage() { const file = this.uri && basename(this.uri.fsPath); this.titleDescription = file ?? ''; - this.message = file ? localize('timeline.loading', 'Loading timeline for {0}...', file) : ''; + this.message = file ? localize('timeline.loading', "Loading timeline for {0}...", file) : ''; } private onContextMenu(commands: TimelinePaneCommands, treeEvent: ITreeContextMenuEvent): void { @@ -952,6 +983,8 @@ export class TimelineElementTemplate implements IDisposable { } reset() { + this.icon.className = ''; + this.icon.style.backgroundImage = ''; this.actionBar.clear(); } } diff --git a/src/vs/workbench/contrib/timeline/common/timeline.ts b/src/vs/workbench/contrib/timeline/common/timeline.ts index 97d93ac2a4..612631a0c8 100644 --- a/src/vs/workbench/contrib/timeline/common/timeline.ts +++ b/src/vs/workbench/contrib/timeline/common/timeline.ts @@ -24,6 +24,7 @@ export interface TimelineItem { id?: string; timestamp: number; label: string; + ariaLabel?: string; icon?: URI, iconDark?: URI, themeIcon?: { id: string }, @@ -38,8 +39,8 @@ export interface TimelineItem { export interface TimelineChangeEvent { id: string; - uri?: URI; - reset?: boolean + uri: URI | undefined; + reset: boolean } export interface TimelineOptions { diff --git a/src/vs/workbench/contrib/update/browser/releaseNotesEditor.ts b/src/vs/workbench/contrib/update/browser/releaseNotesEditor.ts index 6b9fa6556e..6c2ce33079 100644 --- a/src/vs/workbench/contrib/update/browser/releaseNotesEditor.ts +++ b/src/vs/workbench/contrib/update/browser/releaseNotesEditor.ts @@ -164,8 +164,8 @@ export class ReleaseNotesManager { .then(undefined, onUnexpectedError); } - private async addGAParameters(uri: URI, origin: string, experiment = '1'): Promise { - if (this._environmentService.isBuilt && !this._environmentService.isExtensionDevelopment && !this._environmentService.args['disable-telemetry'] && !!this._productService.enableTelemetry) { + private async addGAParameters(uri: URI, origin: string, experiment = '1'): Promise { + if (this._environmentService.isBuilt && !this._environmentService.isExtensionDevelopment && !this._environmentService.disableTelemetry && !!this._productService.enableTelemetry) { if (uri.scheme === 'https' && uri.authority === 'code.visualstudio.com') { const info = await this._telemetryService.getTelemetryInfo(); diff --git a/src/vs/workbench/contrib/update/browser/update.ts b/src/vs/workbench/contrib/update/browser/update.ts index 4aaf15704d..eebae8868b 100644 --- a/src/vs/workbench/contrib/update/browser/update.ts +++ b/src/vs/workbench/contrib/update/browser/update.ts @@ -137,7 +137,7 @@ export class ProductContribution implements IWorkbenchContribution { // was there an update? if so, open release notes const releaseNotesUrl = productService.releaseNotesUrl; - if (shouldShowReleaseNotes && !environmentService.args['skip-release-notes'] && releaseNotesUrl && lastVersion && productService.version !== lastVersion && productService.quality === 'stable') { // {{SQL CARBON EDIT}} Only show release notes for stable build) { + if (shouldShowReleaseNotes && releaseNotesUrl && lastVersion && productService.version !== lastVersion && productService.quality === 'stable') { // {{SQL CARBON EDIT}} Only show release notes for stable build) { /*showReleaseNotes(instantiationService, productService.version) {{SQL CARBON EDIT}} Prompt user to open release notes in browser until we can get ADS release notes from the web .then(undefined, () => {*/ notificationService.prompt( diff --git a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts index 764402c524..6cc22c39bb 100644 --- a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts +++ b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts @@ -24,7 +24,7 @@ import { Schemas } from 'vs/base/common/network'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { getInstalledExtensions, IExtensionStatus, onExtensionChanged, isKeymapExtension } from 'vs/workbench/contrib/extensions/common/extensionsUtils'; import { IExtensionManagementService, IExtensionGalleryService, ILocalExtension } from 'vs/platform/extensionManagement/common/extensionManagement'; -import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionTipsService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; +import { IWorkbenchExtensionEnablementService, EnablementState, IExtensionRecommendationsService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { ILifecycleService, StartupKind } from 'vs/platform/lifecycle/common/lifecycle'; import { Disposable } from 'vs/base/common/lifecycle'; import { splitName } from 'vs/base/common/labels'; @@ -261,7 +261,7 @@ class WelcomePage extends Disposable { @IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService, @IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService, @IExtensionManagementService private readonly extensionManagementService: IExtensionManagementService, - @IExtensionTipsService private readonly tipsService: IExtensionTipsService, + @IExtensionRecommendationsService private readonly tipsService: IExtensionRecommendationsService, @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, @ILifecycleService lifecycleService: ILifecycleService, @ITelemetryService private readonly telemetryService: ITelemetryService, diff --git a/src/vs/workbench/electron-browser/actions/media/actions.css b/src/vs/workbench/electron-browser/actions/media/actions.css index 9ec163bd7b..b8b56729c2 100644 --- a/src/vs/workbench/electron-browser/actions/media/actions.css +++ b/src/vs/workbench/electron-browser/actions/media/actions.css @@ -3,6 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -.quick-input-list .quick-input-list-entry.has-actions:hover .quick-input-list-entry-action-bar .action-label.dirty-window::before { +.monaco-workbench .quick-input-list .quick-input-list-entry.has-actions:hover .quick-input-list-entry-action-bar .action-label.dirty-window::before { content: "\ea76"; /* Close icon flips between black dot and "X" for dirty windows */ } diff --git a/src/vs/workbench/electron-browser/actions/windowActions.ts b/src/vs/workbench/electron-browser/actions/windowActions.ts index eab0818d1c..7aaf2f93fa 100644 --- a/src/vs/workbench/electron-browser/actions/windowActions.ts +++ b/src/vs/workbench/electron-browser/actions/windowActions.ts @@ -168,12 +168,12 @@ export abstract class BaseSwitchWindow extends Action { constructor( id: string, label: string, - private environmentService: INativeWorkbenchEnvironmentService, - private quickInputService: IQuickInputService, - private keybindingService: IKeybindingService, - private modelService: IModelService, - private modeService: IModeService, - private electronService: IElectronService + private readonly environmentService: INativeWorkbenchEnvironmentService, + private readonly quickInputService: IQuickInputService, + private readonly keybindingService: IKeybindingService, + private readonly modelService: IModelService, + private readonly modeService: IModeService, + private readonly electronService: IElectronService ) { super(id, label); } diff --git a/src/vs/workbench/electron-browser/desktop.contribution.ts b/src/vs/workbench/electron-browser/desktop.contribution.ts index 2f490ea4bb..29456361c1 100644 --- a/src/vs/workbench/electron-browser/desktop.contribution.ts +++ b/src/vs/workbench/electron-browser/desktop.contribution.ts @@ -24,6 +24,7 @@ import { IJSONContributionRegistry, Extensions as JSONExtensions } from 'vs/plat import product from 'vs/platform/product/common/product'; import { IJSONSchema } from 'vs/base/common/jsonSchema'; +// eslint-disable-next-line code-import-patterns import { InstallVSIXAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; // {{SQL CARBON EDIT}} add import // Actions diff --git a/src/vs/workbench/electron-browser/desktop.main.ts b/src/vs/workbench/electron-browser/desktop.main.ts index 9dec535d22..2d0aede696 100644 --- a/src/vs/workbench/electron-browser/desktop.main.ts +++ b/src/vs/workbench/electron-browser/desktop.main.ts @@ -53,13 +53,11 @@ import { IResourceIdentityService } from 'vs/platform/resource/common/resourceId class DesktopMain extends Disposable { - private readonly environmentService: NativeWorkbenchEnvironmentService; + private readonly environmentService = new NativeWorkbenchEnvironmentService(this.configuration, this.configuration.execPath); constructor(private configuration: INativeWindowConfiguration) { super(); - this.environmentService = new NativeWorkbenchEnvironmentService(configuration, configuration.execPath); - this.init(); } @@ -194,7 +192,7 @@ class DesktopMain extends Disposable { const signService = new SignService(); serviceCollection.set(ISignService, signService); - const remoteAgentService = this._register(new RemoteAgentService(this.environmentService.configuration, this.environmentService, remoteAuthorityResolverService, signService, logService)); + const remoteAgentService = this._register(new RemoteAgentService(this.environmentService, remoteAuthorityResolverService, signService, logService)); serviceCollection.set(IRemoteAgentService, remoteAgentService); // Files diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts index 324ef196a8..ec3cf943a2 100644 --- a/src/vs/workbench/electron-browser/window.ts +++ b/src/vs/workbench/electron-browser/window.ts @@ -14,7 +14,8 @@ import { IFileService } from 'vs/platform/files/common/files'; import { toResource, IUntitledTextResourceEditorInput, SideBySideEditor, pathsToEditors } from 'vs/workbench/common/editor'; import { IEditorService, IResourceEditorInputType } from 'vs/workbench/services/editor/common/editorService'; import { ITelemetryService, crashReporterIdStorageKey } from 'vs/platform/telemetry/common/telemetry'; -import { IWindowSettings, IOpenFileRequest, IWindowsConfiguration, IAddFoldersRequest, IRunActionInWindowRequest, IRunKeybindingInWindowRequest, getTitleBarStyle } from 'vs/platform/windows/common/windows'; +import { IWindowSettings, IOpenFileRequest, IWindowsConfiguration, getTitleBarStyle } from 'vs/platform/windows/common/windows'; +import { IRunActionInWindowRequest, IRunKeybindingInWindowRequest, IAddFoldersRequest, INativeOpenFileRequest } from 'vs/platform/windows/node/window'; import { ITitleService } from 'vs/workbench/services/title/common/titleService'; import { IWorkbenchThemeService, VS_HC_THEME } from 'vs/workbench/services/themes/common/workbenchThemeService'; import * as browser from 'vs/base/browser/browser'; @@ -77,7 +78,7 @@ export class NativeWindow extends Disposable { private readonly addFoldersScheduler = this._register(new RunOnceScheduler(() => this.doAddFolders(), 100)); private pendingFoldersToAdd: URI[] = []; - private readonly closeEmptyWindowScheduler: RunOnceScheduler = this._register(new RunOnceScheduler(() => this.onAllEditorsClosed(), 50)); + private readonly closeEmptyWindowScheduler = this._register(new RunOnceScheduler(() => this.onAllEditorsClosed(), 50)); private isDocumentedEdited = false; @@ -594,7 +595,7 @@ export class NativeWindow extends Disposable { this.workspaceEditingService.addFolders(foldersToAdd); } - private async onOpenFiles(request: IOpenFileRequest): Promise { + private async onOpenFiles(request: INativeOpenFileRequest): Promise { const inputs: IResourceEditorInputType[] = []; const diffMode = !!(request.filesToDiff && (request.filesToDiff.length === 2)); diff --git a/src/vs/workbench/services/backup/electron-browser/backup.ts b/src/vs/workbench/services/backup/electron-browser/backup.ts index 3c3b9887ee..c0d4525486 100644 --- a/src/vs/workbench/services/backup/electron-browser/backup.ts +++ b/src/vs/workbench/services/backup/electron-browser/backup.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from 'vs/base/common/uri'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; import { joinPath, relativePath } from 'vs/base/common/resources'; -export function toBackupWorkspaceResource(backupWorkspacePath: string, environmentService: IEnvironmentService): URI { +export function toBackupWorkspaceResource(backupWorkspacePath: string, environmentService: INativeEnvironmentService): URI { return joinPath(environmentService.userRoamingDataHome, relativePath(URI.file(environmentService.userDataPath), URI.file(backupWorkspacePath))!); } diff --git a/src/vs/workbench/services/configuration/browser/configurationService.ts b/src/vs/workbench/services/configuration/browser/configurationService.ts index 45b7e61e6c..7df049ca5e 100644 --- a/src/vs/workbench/services/configuration/browser/configurationService.ts +++ b/src/vs/workbench/services/configuration/browser/configurationService.ts @@ -548,22 +548,34 @@ export class WorkspaceService extends Disposable implements IConfigurationServic private async onWorkspaceConfigurationChanged(): Promise { if (this.workspace && this.workspace.configuration) { - const previous = { data: this._configuration.toData(), workspace: this.workspace }; - const change = this._configuration.compareAndUpdateWorkspaceConfiguration(this.workspaceConfiguration.getConfiguration()); - let configuredFolders = await this.toValidWorkspaceFolders(toWorkspaceFolders(this.workspaceConfiguration.getFolders(), this.workspace.configuration)); - const changes = this.compareFolders(this.workspace.folders, configuredFolders); - if (changes.added.length || changes.removed.length || changes.changed.length) { - this.workspace.folders = configuredFolders; - return this.onFoldersChanged() - .then(change => { - this.triggerConfigurationChange(change, previous, ConfigurationTarget.WORKSPACE_FOLDER); - this._onDidChangeWorkspaceFolders.fire(changes); - }); - } else { - this.triggerConfigurationChange(change, previous, ConfigurationTarget.WORKSPACE); + let newFolders = toWorkspaceFolders(this.workspaceConfiguration.getFolders(), this.workspace.configuration); + const { added, removed, changed } = this.compareFolders(this.workspace.folders, newFolders); + + /* If changed validate new folders */ + if (added.length || removed.length || changed.length) { + newFolders = await this.toValidWorkspaceFolders(newFolders); } + /* Otherwise use existing */ + else { + newFolders = this.workspace.folders; + } + + await this.updateWorkspaceConfiguration(newFolders, this.workspaceConfiguration.getConfiguration()); + } + } + + private async updateWorkspaceConfiguration(workspaceFolders: WorkspaceFolder[], configuration: ConfigurationModel): Promise { + const previous = { data: this._configuration.toData(), workspace: this.workspace }; + const change = this._configuration.compareAndUpdateWorkspaceConfiguration(configuration); + const changes = this.compareFolders(this.workspace.folders, workspaceFolders); + if (changes.added.length || changes.removed.length || changes.changed.length) { + this.workspace.folders = workspaceFolders; + const change = await this.onFoldersChanged(); + this.triggerConfigurationChange(change, previous, ConfigurationTarget.WORKSPACE_FOLDER); + this._onDidChangeWorkspaceFolders.fire(changes); + } else { + this.triggerConfigurationChange(change, previous, ConfigurationTarget.WORKSPACE); } - return Promise.resolve(undefined); } private onWorkspaceFolderConfigurationChanged(folder: IWorkspaceFolder, key?: string): Promise { @@ -619,7 +631,7 @@ export class WorkspaceService extends Disposable implements IConfigurationServic const validWorkspaceFolders = await this.toValidWorkspaceFolders(this.workspace.folders); const { removed } = this.compareFolders(this.workspace.folders, validWorkspaceFolders); if (removed.length) { - return this.onWorkspaceConfigurationChanged(); + await this.updateWorkspaceConfiguration(validWorkspaceFolders, this.workspaceConfiguration.getConfiguration()); } } diff --git a/src/vs/workbench/services/configuration/node/configurationCache.ts b/src/vs/workbench/services/configuration/node/configurationCache.ts index cd79394a9f..a8ad232ceb 100644 --- a/src/vs/workbench/services/configuration/node/configurationCache.ts +++ b/src/vs/workbench/services/configuration/node/configurationCache.ts @@ -4,16 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import * as pfs from 'vs/base/node/pfs'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { join } from 'vs/base/common/path'; +import { INativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; 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: IWorkbenchEnvironmentService) { + constructor(private readonly environmentService: INativeEnvironmentService) { } read(key: ConfigurationKey): Promise { @@ -48,7 +47,7 @@ class CachedConfiguration { constructor( { type, key }: ConfigurationKey, - environmentService: IEnvironmentService + environmentService: INativeEnvironmentService ) { this.cachedConfigurationFolderPath = join(environmentService.userDataPath, 'CachedConfigurations', type, key); this.cachedConfigurationFilePath = join(this.cachedConfigurationFolderPath, type === 'workspaces' ? 'workspace.json' : 'configuration.json'); 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 c8fc46032b..e15a2b6c08 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 @@ -49,7 +49,6 @@ class TestEnvironmentService extends NativeWorkbenchEnvironmentService { } get appSettingsHome() { return this._appSettingsHome; } - } suite('ConfigurationEditingService', () => { @@ -107,7 +106,7 @@ suite('ConfigurationEditingService', () => { instantiationService = workbenchInstantiationService(); const environmentService = new TestEnvironmentService(URI.file(workspaceDir)); instantiationService.stub(IEnvironmentService, environmentService); - const remoteAgentService = instantiationService.createInstance(RemoteAgentService, {}); + const remoteAgentService = instantiationService.createInstance(RemoteAgentService); const fileService = new FileService(new NullLogService()); const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService()); fileService.registerProvider(Schemas.file, diskFileSystemProvider); 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 6eab4998f8..c53f687530 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 @@ -111,7 +111,7 @@ suite.skip('WorkspaceContextService - Folder', () => { // {{SQL CARBON EDIT}} sk const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService()); fileService.registerProvider(Schemas.file, diskFileSystemProvider); fileService.registerProvider(Schemas.userData, new FileUserDataProvider(environmentService.appSettingsHome, environmentService.backupHome, new DiskFileSystemProvider(new NullLogService()), environmentService)); - workspaceContextService = new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, new RemoteAgentService(TestWindowConfiguration, environmentService, new RemoteAuthorityResolverService(), new SignService(undefined), new NullLogService())); + workspaceContextService = new WorkspaceService({ configurationCache: new ConfigurationCache(environmentService) }, environmentService, fileService, new RemoteAgentService(environmentService, new RemoteAuthorityResolverService(), new SignService(undefined), new NullLogService())); return (workspaceContextService).initialize(convertToWorkspacePayload(URI.file(folderDir))); }); }); @@ -171,7 +171,7 @@ suite.skip('WorkspaceContextService - Workspace', () => { // {{SQL CARBON EDIT}} instantiationService = workbenchInstantiationService(); const environmentService = new TestEnvironmentService(URI.file(parentDir)); - const remoteAgentService = instantiationService.createInstance(RemoteAgentService, {}); + const remoteAgentService = instantiationService.createInstance(RemoteAgentService); instantiationService.stub(IRemoteAgentService, remoteAgentService); const fileService = new FileService(new NullLogService()); const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService()); @@ -231,7 +231,7 @@ suite.skip('WorkspaceContextService - Workspace Editing', () => { // {{SQL CARBO instantiationService = workbenchInstantiationService(); const environmentService = new TestEnvironmentService(URI.file(parentDir)); - const remoteAgentService = instantiationService.createInstance(RemoteAgentService, {}); + const remoteAgentService = instantiationService.createInstance(RemoteAgentService); instantiationService.stub(IRemoteAgentService, remoteAgentService); const fileService = new FileService(new NullLogService()); const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService()); @@ -492,7 +492,7 @@ suite.skip('WorkspaceService - Initialization', () => { // {{SQL CARBON EDIT}} s const instantiationService = workbenchInstantiationService(); const environmentService = new TestEnvironmentService(URI.file(parentDir)); - const remoteAgentService = instantiationService.createInstance(RemoteAgentService, {}); + const remoteAgentService = instantiationService.createInstance(RemoteAgentService); instantiationService.stub(IRemoteAgentService, remoteAgentService); const fileService = new FileService(new NullLogService()); const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService()); @@ -769,7 +769,7 @@ suite.skip('WorkspaceConfigurationService - Folder', () => { // {{SQL CARBON EDI const instantiationService = workbenchInstantiationService(); const environmentService = new TestEnvironmentService(URI.file(parentDir)); - const remoteAgentService = instantiationService.createInstance(RemoteAgentService, {}); + const remoteAgentService = instantiationService.createInstance(RemoteAgentService); instantiationService.stub(IRemoteAgentService, remoteAgentService); fileService = new FileService(new NullLogService()); const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService()); @@ -1194,7 +1194,7 @@ suite.skip('WorkspaceConfigurationService-Multiroot', () => { // {{SQL CARBON ED const instantiationService = workbenchInstantiationService(); const environmentService = new TestEnvironmentService(URI.file(parentDir)); - const remoteAgentService = instantiationService.createInstance(RemoteAgentService, {}); + const remoteAgentService = instantiationService.createInstance(RemoteAgentService); instantiationService.stub(IRemoteAgentService, remoteAgentService); const fileService = new FileService(new NullLogService()); const diskFileSystemProvider = new DiskFileSystemProvider(new NullLogService()); diff --git a/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts b/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts index 6d67562561..7f6439239a 100644 --- a/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts +++ b/src/vs/workbench/services/configurationResolver/browser/configurationResolverService.ts @@ -28,9 +28,9 @@ export abstract class BaseConfigurationResolverService extends AbstractVariableR static readonly INPUT_OR_COMMAND_VARIABLES_PATTERN = /\${((input|command):(.*?))}/g; constructor( + context: { getExecPath: () => string | undefined }, envVariables: IProcessEnvironment, editorService: IEditorService, - environmentService: IWorkbenchEnvironmentService, private readonly configurationService: IConfigurationService, private readonly commandService: ICommandService, private readonly workspaceContextService: IWorkspaceContextService, @@ -48,7 +48,7 @@ export abstract class BaseConfigurationResolverService extends AbstractVariableR return configurationService.getValue(suffix, folderUri ? { resource: folderUri } : {}); }, getExecPath: (): string | undefined => { - return environmentService['execPath']; + return context.getExecPath(); }, getFilePath: (): string | undefined => { let activeEditor = editorService.activeEditor; @@ -349,7 +349,7 @@ export class ConfigurationResolverService extends BaseConfigurationResolverServi @IWorkspaceContextService workspaceContextService: IWorkspaceContextService, @IQuickInputService quickInputService: IQuickInputService ) { - super(Object.create(null), editorService, environmentService, configurationService, commandService, workspaceContextService, quickInputService); + super({ getExecPath: () => undefined }, Object.create(null), editorService, configurationService, commandService, workspaceContextService, quickInputService); } } diff --git a/src/vs/workbench/services/configurationResolver/electron-browser/configurationResolverService.ts b/src/vs/workbench/services/configurationResolver/electron-browser/configurationResolverService.ts index 318e58687f..24c50fc23b 100644 --- a/src/vs/workbench/services/configurationResolver/electron-browser/configurationResolverService.ts +++ b/src/vs/workbench/services/configurationResolver/electron-browser/configurationResolverService.ts @@ -13,18 +13,23 @@ import { IConfigurationResolverService } from 'vs/workbench/services/configurati import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IProcessEnvironment } from 'vs/base/common/platform'; import { BaseConfigurationResolverService } from 'vs/workbench/services/configurationResolver/browser/configurationResolverService'; +import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-browser/environmentService'; export class ConfigurationResolverService extends BaseConfigurationResolverService { constructor( @IEditorService editorService: IEditorService, - @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, + @IWorkbenchEnvironmentService environmentService: INativeWorkbenchEnvironmentService, @IConfigurationService configurationService: IConfigurationService, @ICommandService commandService: ICommandService, @IWorkspaceContextService workspaceContextService: IWorkspaceContextService, @IQuickInputService quickInputService: IQuickInputService ) { - super(process.env as IProcessEnvironment, editorService, environmentService, configurationService, commandService, workspaceContextService, quickInputService); + super({ + getExecPath: (): string | undefined => { + return environmentService.execPath; + } + }, process.env as IProcessEnvironment, editorService, configurationService, commandService, workspaceContextService, quickInputService); } } diff --git a/src/vs/workbench/services/configurationResolver/test/electron-browser/configurationResolverService.test.ts b/src/vs/workbench/services/configurationResolver/test/electron-browser/configurationResolverService.test.ts index 050034cbee..10222aad2d 100644 --- a/src/vs/workbench/services/configurationResolver/test/electron-browser/configurationResolverService.test.ts +++ b/src/vs/workbench/services/configurationResolver/test/electron-browser/configurationResolverService.test.ts @@ -62,7 +62,7 @@ suite('Configuration Resolver Service', () => { index: 0, toResource: (path: string) => uri.file(path) }; - configurationResolverService = new TestConfigurationResolverService(environmentService.userEnv, editorService, environmentService, new MockInputsConfigurationService(), mockCommandService, new TestContextService(), quickInputService); + configurationResolverService = new TestConfigurationResolverService({ getExecPath: () => undefined }, environmentService.userEnv, editorService, new MockInputsConfigurationService(), mockCommandService, new TestContextService(), quickInputService); }); teardown(() => { @@ -141,7 +141,7 @@ suite('Configuration Resolver Service', () => { } }); - let service = new TestConfigurationResolverService(environmentService.userEnv, new TestEditorServiceWithActiveEditor(), environmentService, configurationService, mockCommandService, new TestContextService(), quickInputService); + let service = new TestConfigurationResolverService({ getExecPath: () => undefined }, environmentService.userEnv, new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService); assert.strictEqual(service.resolve(workspace, 'abc ${config:editor.fontFamily} xyz'), 'abc foo xyz'); }); @@ -158,7 +158,7 @@ suite('Configuration Resolver Service', () => { } }); - let service = new TestConfigurationResolverService(environmentService.userEnv, new TestEditorServiceWithActiveEditor(), environmentService, configurationService, mockCommandService, new TestContextService(), quickInputService); + let service = new TestConfigurationResolverService({ getExecPath: () => undefined }, environmentService.userEnv, new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService); assert.strictEqual(service.resolve(workspace, 'abc ${config:editor.fontFamily} ${config:terminal.integrated.fontFamily} xyz'), 'abc foo bar xyz'); }); @@ -175,7 +175,7 @@ suite('Configuration Resolver Service', () => { } }); - let service = new TestConfigurationResolverService(environmentService.userEnv, new TestEditorServiceWithActiveEditor(), environmentService, configurationService, mockCommandService, new TestContextService(), quickInputService); + let service = new TestConfigurationResolverService({ getExecPath: () => undefined }, environmentService.userEnv, new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService); if (platform.isWindows) { assert.strictEqual(service.resolve(workspace, 'abc ${config:editor.fontFamily} ${workspaceFolder} ${env:key1} xyz'), 'abc foo \\VSCode\\workspaceLocation Value for key1 xyz'); } else { @@ -196,7 +196,7 @@ suite('Configuration Resolver Service', () => { } }); - let service = new TestConfigurationResolverService(environmentService.userEnv, new TestEditorServiceWithActiveEditor(), environmentService, configurationService, mockCommandService, new TestContextService(), quickInputService); + let service = new TestConfigurationResolverService({ getExecPath: () => undefined }, environmentService.userEnv, new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService); if (platform.isWindows) { assert.strictEqual(service.resolve(workspace, '${config:editor.fontFamily} ${config:terminal.integrated.fontFamily} ${workspaceFolder} - ${workspaceFolder} ${env:key1} - ${env:key2}'), 'foo bar \\VSCode\\workspaceLocation - \\VSCode\\workspaceLocation Value for key1 - Value for key2'); } else { @@ -230,7 +230,7 @@ suite('Configuration Resolver Service', () => { } }); - let service = new TestConfigurationResolverService(environmentService.userEnv, new TestEditorServiceWithActiveEditor(), environmentService, configurationService, mockCommandService, new TestContextService(), quickInputService); + let service = new TestConfigurationResolverService({ getExecPath: () => undefined }, environmentService.userEnv, new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService); assert.strictEqual(service.resolve(workspace, 'abc ${config:editor.fontFamily} ${config:editor.lineNumbers} ${config:editor.insertSpaces} xyz'), 'abc foo 123 false xyz'); }); @@ -240,7 +240,7 @@ suite('Configuration Resolver Service', () => { editor: {} }); - let service = new TestConfigurationResolverService(environmentService.userEnv, new TestEditorServiceWithActiveEditor(), environmentService, configurationService, mockCommandService, new TestContextService(), quickInputService); + let service = new TestConfigurationResolverService({ getExecPath: () => undefined }, environmentService.userEnv, new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService); assert.strictEqual(service.resolve(workspace, 'abc ${unknownVariable} xyz'), 'abc ${unknownVariable} xyz'); assert.strictEqual(service.resolve(workspace, 'abc ${env:unknownVariable} xyz'), 'abc xyz'); }); @@ -253,7 +253,7 @@ suite('Configuration Resolver Service', () => { } }); - let service = new TestConfigurationResolverService(environmentService.userEnv, new TestEditorServiceWithActiveEditor(), environmentService, configurationService, mockCommandService, new TestContextService(), quickInputService); + let service = new TestConfigurationResolverService({ getExecPath: () => undefined }, environmentService.userEnv, new TestEditorServiceWithActiveEditor(), configurationService, mockCommandService, new TestContextService(), quickInputService); assert.throws(() => service.resolve(workspace, 'abc ${env} xyz')); assert.throws(() => service.resolve(workspace, 'abc ${env:} xyz')); diff --git a/src/vs/workbench/services/decorations/browser/decorationsService.ts b/src/vs/workbench/services/decorations/browser/decorationsService.ts index 1ee872a747..b877642397 100644 --- a/src/vs/workbench/services/decorations/browser/decorationsService.ts +++ b/src/vs/workbench/services/decorations/browser/decorationsService.ts @@ -169,10 +169,10 @@ class DecorationStyles { class FileDecorationChangeEvent implements IResourceDecorationChangeEvent { - private readonly _data = TernarySearchTree.forPaths(); + private readonly _data = TernarySearchTree.forUris(); affectsResource(uri: URI): boolean { - return this._data.get(uri.toString()) || this._data.findSuperstr(uri.toString()) !== undefined; + return this._data.get(uri) || this._data.findSuperstr(uri) !== undefined; } static debouncer(last: FileDecorationChangeEvent | undefined, current: URI | URI[]) { @@ -182,11 +182,11 @@ class FileDecorationChangeEvent implements IResourceDecorationChangeEvent { if (Array.isArray(current)) { // many for (const uri of current) { - last._data.set(uri.toString(), true); + last._data.set(uri, true); } } else { // one - last._data.set(current.toString(), true); + last._data.set(current, true); } return last; @@ -202,7 +202,7 @@ class DecorationDataRequest { class DecorationProviderWrapper { - readonly data = TernarySearchTree.forPaths(); + readonly data = TernarySearchTree.forUris(); private readonly _dispoable: IDisposable; constructor( @@ -234,12 +234,12 @@ class DecorationProviderWrapper { } knowsAbout(uri: URI): boolean { - return Boolean(this.data.get(uri.toString())) || Boolean(this.data.findSuperstr(uri.toString())); + return Boolean(this.data.get(uri)) || Boolean(this.data.findSuperstr(uri)); } getOrRetrieve(uri: URI, includeChildren: boolean, callback: (data: IDecorationData, isChild: boolean) => void): void { - const key = uri.toString(); - let item = this.data.get(key); + + let item = this.data.get(uri); if (item === undefined) { // unknown -> trigger request @@ -253,7 +253,7 @@ class DecorationProviderWrapper { if (includeChildren) { // (resolved) children - const iter = this.data.findSuperstr(key); + const iter = this.data.findSuperstr(uri); if (iter) { for (let item = iter.next(); !item.done; item = iter.next()) { if (item.value && !(item.value instanceof DecorationDataRequest)) { @@ -267,10 +267,10 @@ class DecorationProviderWrapper { private _fetchData(uri: URI): IDecorationData | null { // check for pending request and cancel it - const pendingRequest = this.data.get(uri.toString()); + const pendingRequest = this.data.get(uri); if (pendingRequest instanceof DecorationDataRequest) { pendingRequest.source.cancel(); - this.data.delete(uri.toString()); + this.data.delete(uri); } const source = new CancellationTokenSource(); @@ -282,23 +282,23 @@ class DecorationProviderWrapper { } else { // async -> we have a result soon const request = new DecorationDataRequest(source, Promise.resolve(dataOrThenable).then(data => { - if (this.data.get(uri.toString()) === request) { + if (this.data.get(uri) === request) { this._keepItem(uri, data); } }).catch(err => { - if (!isPromiseCanceledError(err) && this.data.get(uri.toString()) === request) { - this.data.delete(uri.toString()); + if (!isPromiseCanceledError(err) && this.data.get(uri) === request) { + this.data.delete(uri); } })); - this.data.set(uri.toString(), request); + this.data.set(uri, request); return null; } } private _keepItem(uri: URI, data: IDecorationData | undefined): IDecorationData | null { const deco = data ? data : null; - const old = this.data.set(uri.toString(), deco); + const old = this.data.set(uri, deco); if (deco || old) { // only fire event when something changed this._uriEmitter.fire(uri); diff --git a/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts b/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts index 6a777aa991..936964adca 100644 --- a/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts +++ b/src/vs/workbench/services/dialogs/browser/simpleFileDialog.ts @@ -235,7 +235,7 @@ export class SimpleFileDialog { if (this.scheme !== Schemas.file) { return this.remotePathService.userHome; } - return URI.from({ scheme: this.scheme, path: this.environmentService.userHome }); + return this.environmentService.userHome!; } private async pickResource(isSave: boolean = false): Promise { diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index ff48d58321..3770e2e668 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -30,7 +30,6 @@ import { EditorsObserver } from 'vs/workbench/browser/parts/editor/editorsObserv import { IEditorViewState } from 'vs/editor/common/editorCommon'; import { IUntitledTextEditorModel } from 'vs/workbench/services/untitled/common/untitledTextEditorModel'; import { UntitledTextEditorInput } from 'vs/workbench/services/untitled/common/untitledTextEditorInput'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { timeout } from 'vs/base/common/async'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { indexOfPath } from 'vs/base/common/extpath'; @@ -68,7 +67,6 @@ export class EditorService extends Disposable implements EditorServiceImpl { @ILabelService private readonly labelService: ILabelService, @IFileService private readonly fileService: IFileService, @IConfigurationService private readonly configurationService: IConfigurationService, - @IEnvironmentService private readonly environmentService: IEnvironmentService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService ) { super(); @@ -348,8 +346,6 @@ export class EditorService extends Disposable implements EditorServiceImpl { if (!exists && !editor.isDisposed()) { editor.dispose(); - } else if (this.environmentService.verbose) { - console.warn(`File exists even though we received a delete event: ${resource.toString()}`); } } })(); diff --git a/src/vs/workbench/services/editor/test/browser/editorsObserver.test.ts b/src/vs/workbench/services/editor/test/browser/editorsObserver.test.ts index 10bca9f3e9..4c22b3edc9 100644 --- a/src/vs/workbench/services/editor/test/browser/editorsObserver.test.ts +++ b/src/vs/workbench/services/editor/test/browser/editorsObserver.test.ts @@ -17,6 +17,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { EditorsObserver } from 'vs/workbench/browser/parts/editor/editorsObserver'; import { timeout } from 'vs/base/common/async'; import { TestStorageService } from 'vs/workbench/test/common/workbenchTestServices'; +import { isWeb } from 'vs/base/common/platform'; const TEST_EDITOR_ID = 'MyTestEditorForEditorsObserver'; const TEST_EDITOR_INPUT_ID = 'testEditorInputForEditorsObserver'; @@ -204,7 +205,10 @@ suite.skip('EditorsObserver', function () { //{{SQL CARBON EDIT}} disable failin part.dispose(); }); - test('copy group', async () => { + test('copy group', async function () { + if (isWeb) { + this.skip(); + } const [part, observer] = await createEditorObserver(); const input1 = new TestFileEditorInput(URI.parse('foo://bar1'), TEST_SERIALIZABLE_EDITOR_INPUT_ID); diff --git a/src/vs/workbench/services/environment/browser/environmentService.ts b/src/vs/workbench/services/environment/browser/environmentService.ts index d3b58d7353..4f7c4cfafe 100644 --- a/src/vs/workbench/services/environment/browser/environmentService.ts +++ b/src/vs/workbench/services/environment/browser/environmentService.ts @@ -8,18 +8,19 @@ import { joinPath } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; import { BACKUPS, IExtensionHostDebugParams } from 'vs/platform/environment/common/environment'; -import { IPath, IWindowConfiguration } from 'vs/platform/windows/common/windows'; -import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { IPath } from 'vs/platform/windows/common/windows'; +import { IWorkbenchEnvironmentService, IEnvironmentConfiguration } from 'vs/workbench/services/environment/common/environmentService'; import { IWorkbenchConstructionOptions } from 'vs/workbench/workbench.web.api'; import product from 'vs/platform/product/common/product'; import { memoize } from 'vs/base/common/decorators'; +import { onUnexpectedError } from 'vs/base/common/errors'; -export class BrowserWindowConfiguration implements IWindowConfiguration { +export class BrowserEnvironmentConfiguration implements IEnvironmentConfiguration { constructor( private readonly options: IBrowserWorkbenchEnvironmentConstructionOptions, private readonly payload: Map | undefined, - private readonly environment: IWorkbenchEnvironmentService + private readonly backupHome: URI ) { } @memoize @@ -29,10 +30,7 @@ export class BrowserWindowConfiguration implements IWindowConfiguration { get remoteAuthority(): string | undefined { return this.options.remoteAuthority; } @memoize - get connectionToken(): string | undefined { return this.options.connectionToken || this.getCookieValue('vscode-tkn'); } - - @memoize - get backupWorkspaceResource(): URI { return joinPath(this.environment.backupHome, this.options.workspaceId); } + get backupWorkspaceResource(): URI { return joinPath(this.backupHome, this.options.workspaceId); } @memoize get filesToOpenOrCreate(): IPath[] | undefined { @@ -46,15 +44,24 @@ export class BrowserWindowConfiguration implements IWindowConfiguration { return undefined; } - // Currently unsupported in web but should look into support - get filesToDiff(): IPath[] | undefined { return undefined; } - highContrast = false; - _ = []; + @memoize + get filesToDiff(): IPath[] | undefined { + if (this.payload) { + const fileToDiffDetail = this.payload.get('diffFileDetail'); + const fileToDiffMaster = this.payload.get('diffFileMaster'); + if (fileToDiffDetail && fileToDiffMaster) { + return [ + { fileUri: URI.parse(fileToDiffDetail) }, + { fileUri: URI.parse(fileToDiffMaster) } + ]; + } + } - private getCookieValue(name: string): string | undefined { - const m = document.cookie.match('(^|[^;]+)\\s*' + name + '\\s*=\\s*([^;]+)'); // See https://stackoverflow.com/a/25490531 + return undefined; + } - return m ? m.pop() : undefined; + get highContrast() { + return false; // could investigate to detect high contrast theme automatically } } @@ -75,10 +82,10 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment _serviceBrand: undefined; - private _configuration: IWindowConfiguration | undefined = undefined; - get configuration(): IWindowConfiguration { + private _configuration: IEnvironmentConfiguration | undefined = undefined; + get configuration(): IEnvironmentConfiguration { if (!this._configuration) { - this._configuration = new BrowserWindowConfiguration(this.options, this.payload, this); + this._configuration = new BrowserEnvironmentConfiguration(this.options, this.payload, this.backupHome); } return this._configuration; @@ -90,6 +97,8 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment @memoize get logsPath(): string { return this.options.logsPath.path; } + get logLevel(): string | undefined { return this.payload?.get('logLevel'); } + @memoize get logFile(): URI { return joinPath(this.options.logsPath, 'window.log'); } @@ -111,6 +120,8 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment @memoize get userDataSyncLogResource(): URI { return joinPath(this.options.logsPath, 'userDataSync.log'); } + get sync(): 'on' | 'off' { return 'on'; } + @memoize get keybindingsResource(): URI { return joinPath(this.userRoamingDataHome, 'keybindings.json'); } @@ -123,6 +134,9 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment @memoize get untitledWorkspacesHome(): URI { return joinPath(this.userRoamingDataHome, 'Workspaces'); } + @memoize + get serviceMachineIdResource(): URI { return joinPath(this.userRoamingDataHome, 'machineid'); } + private _extensionHostDebugEnvironment: IExtensionHostDebugEnvironment | undefined = undefined; get debugExtensionHost(): IExtensionHostDebugParams { if (!this._extensionHostDebugEnvironment) { @@ -164,9 +178,11 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment return this._extensionHostDebugEnvironment.extensionEnabledProposedApi; } + get disableExtensions() { return this.payload?.get('disableExtensions') === 'true'; } + @memoize get webviewExternalEndpoint(): string { - // TODO: get fallback from product.json + // TODO@matt: get fallback from product.json return (this.options.webviewEndpoint || 'https://{{uuid}}.vscode-webview-test.com/{{commit}}').replace('{{commit}}', product.commit || '0d728c31ebdf03869d2687d9be0b017667c9ff37'); } @@ -180,19 +196,20 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment return this.webviewExternalEndpoint.replace('{{uuid}}', '*'); } - // Currently unsupported in web but should look into support - get disableExtensions() { return false; } - get extensionsPath(): string | undefined { return undefined; } - get verbose(): boolean { return false; } - get disableUpdates(): boolean { return false; } - get logExtensionHostCommunication(): boolean { return false; } - galleryMachineIdResource?: URI; + get disableTelemetry(): boolean { return false; } + + get verbose(): boolean { return this.payload?.get('verbose') === 'true'; } + get logExtensionHostCommunication(): boolean { return this.payload?.get('logExtensionHostCommunication') === 'true'; } private payload: Map | undefined; constructor(readonly options: IBrowserWorkbenchEnvironmentConstructionOptions) { if (options.workspaceProvider && Array.isArray(options.workspaceProvider.payload)) { - this.payload = new Map(options.workspaceProvider.payload); + try { + this.payload = new Map(options.workspaceProvider.payload); + } catch (error) { + onUnexpectedError(error); // possible invalid payload for map + } } } @@ -233,35 +250,4 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment return extensionHostDebugEnvironment; } - - //#region TODO MOVE TO NODE LAYER - - args = { _: [] }; - - mainIPCHandle!: string; - sharedIPCHandle!: string; - - nodeCachedDataDir?: string; - - driverHandle?: string; - driverVerbose!: boolean; - - installSourcePath!: string; - - builtinExtensionsPath!: string; - - globalStorageHome!: string; - workspaceStorageHome!: string; - - backupWorkspacesPath!: string; - - machineSettingsResource!: URI; - - userHome!: string; - userDataPath!: string; - appRoot!: string; - appSettingsHome!: URI; - execPath!: string; - - //#endregion } diff --git a/src/vs/workbench/services/environment/common/environmentService.ts b/src/vs/workbench/services/environment/common/environmentService.ts index 6511378831..bc34b5b4b1 100644 --- a/src/vs/workbench/services/environment/common/environmentService.ts +++ b/src/vs/workbench/services/environment/common/environmentService.ts @@ -11,11 +11,15 @@ import { URI } from 'vs/base/common/uri'; export const IWorkbenchEnvironmentService = createDecorator('environmentService'); +export interface IEnvironmentConfiguration extends IWindowConfiguration { + backupWorkspaceResource?: URI; +} + export interface IWorkbenchEnvironmentService extends IEnvironmentService { _serviceBrand: undefined; - readonly configuration: IWindowConfiguration; + readonly configuration: IEnvironmentConfiguration; readonly options?: IWorkbenchConstructionOptions; diff --git a/src/vs/workbench/services/environment/electron-browser/environmentService.ts b/src/vs/workbench/services/environment/electron-browser/environmentService.ts index 2b1808a3e6..b8f560cd0f 100644 --- a/src/vs/workbench/services/environment/electron-browser/environmentService.ts +++ b/src/vs/workbench/services/environment/electron-browser/environmentService.ts @@ -3,8 +3,8 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; -import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { EnvironmentService, INativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; +import { IWorkbenchEnvironmentService, IEnvironmentConfiguration } 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'; @@ -13,9 +13,9 @@ import { join } from 'vs/base/common/path'; import product from 'vs/platform/product/common/product'; import { INativeWindowConfiguration } from 'vs/platform/windows/node/window'; -export interface INativeWorkbenchEnvironmentService extends IWorkbenchEnvironmentService { +export interface INativeWorkbenchEnvironmentService extends IWorkbenchEnvironmentService, INativeEnvironmentService { - readonly configuration: INativeWindowConfiguration; + readonly configuration: INativeEnvironmentConfiguration; readonly disableCrashReporter: boolean; @@ -23,8 +23,12 @@ export interface INativeWorkbenchEnvironmentService extends IWorkbenchEnvironmen readonly log?: string; readonly extHostLogsPath: URI; + + readonly userHome: URI; } +export interface INativeEnvironmentConfiguration extends IEnvironmentConfiguration, INativeWindowConfiguration { } + export class NativeWorkbenchEnvironmentService extends EnvironmentService implements INativeWorkbenchEnvironmentService { _serviceBrand: undefined; @@ -52,7 +56,7 @@ export class NativeWorkbenchEnvironmentService extends EnvironmentService implem get extHostLogsPath(): URI { return URI.file(join(this.logsPath, `exthost${this.configuration.windowId}`)); } constructor( - readonly configuration: INativeWindowConfiguration, + readonly configuration: INativeEnvironmentConfiguration, execPath: string ) { super(configuration, execPath); diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts index 1c324c9a5a..929a5c39f3 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionManagement.ts @@ -94,9 +94,9 @@ export interface IExtensionRecommendation { sources: ExtensionRecommendationSource[]; } -export const IExtensionTipsService = createDecorator('extensionTipsService'); +export const IExtensionRecommendationsService = createDecorator('extensionRecommendationsService'); -export interface IExtensionTipsService { +export interface IExtensionRecommendationsService { _serviceBrand: undefined; getAllRecommendationsWithReason(): { [id: string]: { reasonId: ExtensionRecommendationReason, reasonText: string }; }; getFileBasedRecommendations(): IExtensionRecommendation[]; diff --git a/src/vs/workbench/services/extensionManagement/common/extensionTipsService.ts b/src/vs/workbench/services/extensionManagement/common/extensionTipsService.ts new file mode 100644 index 0000000000..1e2b7e7f72 --- /dev/null +++ b/src/vs/workbench/services/extensionManagement/common/extensionTipsService.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 { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IExtensionTipsService, IExecutableBasedExtensionTip, IWorkspaceTips } from 'vs/platform/extensionManagement/common/extensionManagement'; + +class WebExtensionTipsService implements IExtensionTipsService { + + _serviceBrand: any; + + constructor() { } + + async getImportantExecutableBasedTips(): Promise { + return []; + } + + async getOtherExecutableBasedTips(): Promise { + return []; + } + + async getAllWorkspacesTips(): Promise { + return []; + } + +} + +registerSingleton(IExtensionTipsService, WebExtensionTipsService); diff --git a/src/vs/workbench/services/extensionManagement/electron-browser/extensionTipsService.ts b/src/vs/workbench/services/extensionManagement/electron-browser/extensionTipsService.ts new file mode 100644 index 0000000000..ebde9fb403 --- /dev/null +++ b/src/vs/workbench/services/extensionManagement/electron-browser/extensionTipsService.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 { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; +import { IChannel } from 'vs/base/parts/ipc/common/ipc'; +import { IExtensionTipsService, IExecutableBasedExtensionTip, IWorkspaceTips } from 'vs/platform/extensionManagement/common/extensionManagement'; + +class NativeExtensionTipsService implements IExtensionTipsService { + + _serviceBrand: any; + + private readonly channel: IChannel; + + constructor( + @ISharedProcessService sharedProcessService: ISharedProcessService + ) { + this.channel = sharedProcessService.getChannel('extensionTipsService'); + } + + getImportantExecutableBasedTips(): Promise { + return this.channel.call('getImportantExecutableBasedTips'); + } + + getOtherExecutableBasedTips(): Promise { + return this.channel.call('getOtherExecutableBasedTips'); + } + + getAllWorkspacesTips(): Promise { + return this.channel.call('getAllWorkspacesTips'); + } + +} + +registerSingleton(IExtensionTipsService, NativeExtensionTipsService); diff --git a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts index 636e946a4a..9cb6c16c86 100644 --- a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts +++ b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts @@ -136,8 +136,6 @@ export class WebWorkerExtensionHostStarter implements IExtensionHostStarter { parentPid: -1, environment: { isExtensionDevelopmentDebug: false, - appRoot: this._environmentService.appRoot ? URI.file(this._environmentService.appRoot) : undefined, - appSettingsHome: this._environmentService.appSettingsHome ? this._environmentService.appSettingsHome : undefined, appName: this._productService.nameLong, appUriScheme: this._productService.urlProtocol, appLanguage: platform.language, diff --git a/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts b/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts index d880897d82..3779e9f90b 100644 --- a/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts +++ b/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts @@ -14,7 +14,8 @@ import * as platform from 'vs/base/common/platform'; import { originalFSPath } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import * as pfs from 'vs/base/node/pfs'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-browser/environmentService'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IWorkbenchExtensionEnablementService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; import { BUILTIN_MANIFEST_CACHE_FILE, MANIFEST_CACHE_FOLDER, USER_MANIFEST_CACHE_FILE, ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import product from 'vs/platform/product/common/product'; @@ -22,8 +23,6 @@ import { INotificationService, Severity } from 'vs/platform/notification/common/ import { IHostService } from 'vs/workbench/services/host/browser/host'; import { ExtensionScanner, ExtensionScannerInput, IExtensionReference, IExtensionResolver, IRelaxedExtensionDescription } from 'vs/workbench/services/extensions/node/extensionPoints'; import { Translations, ILog } from 'vs/workbench/services/extensions/common/extensionPoints'; -import { IProductService } from 'vs/platform/product/common/productService'; -import { parseBuiltInExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; interface IExtensionCacheData { input: ExtensionScannerInput; @@ -55,10 +54,9 @@ export class CachedExtensionScanner { constructor( @INotificationService private readonly _notificationService: INotificationService, - @IEnvironmentService private readonly _environmentService: IEnvironmentService, + @IWorkbenchEnvironmentService private readonly _environmentService: INativeWorkbenchEnvironmentService, @IWorkbenchExtensionEnablementService private readonly _extensionEnablementService: IWorkbenchExtensionEnablementService, @IHostService private readonly _hostService: IHostService, - @IProductService private readonly _productService: IProductService, ) { this.scannedExtensions = new Promise((resolve, reject) => { this._scannedExtensionsResolve = resolve; @@ -81,7 +79,7 @@ export class CachedExtensionScanner { public async startScanningExtensions(log: ILog): Promise { try { const translations = await this.translationConfig; - const { system, user, development } = await CachedExtensionScanner._scanInstalledExtensions(this._hostService, this._notificationService, this._environmentService, this._extensionEnablementService, this._productService, log, translations); + const { system, user, development } = await CachedExtensionScanner._scanInstalledExtensions(this._hostService, this._notificationService, this._environmentService, this._extensionEnablementService, log, translations); let result = new Map(); system.forEach((systemExtension) => { @@ -114,7 +112,7 @@ export class CachedExtensionScanner { } } - private static async _validateExtensionsCache(hostService: IHostService, notificationService: INotificationService, environmentService: IEnvironmentService, cacheKey: string, input: ExtensionScannerInput): Promise { + private static async _validateExtensionsCache(hostService: IHostService, notificationService: INotificationService, environmentService: INativeWorkbenchEnvironmentService, cacheKey: string, input: ExtensionScannerInput): Promise { const cacheFolder = path.join(environmentService.userDataPath, MANIFEST_CACHE_FOLDER); const cacheFile = path.join(cacheFolder, cacheKey); @@ -149,7 +147,7 @@ export class CachedExtensionScanner { ); } - private static async _readExtensionCache(environmentService: IEnvironmentService, cacheKey: string): Promise { + private static async _readExtensionCache(environmentService: INativeWorkbenchEnvironmentService, cacheKey: string): Promise { const cacheFolder = path.join(environmentService.userDataPath, MANIFEST_CACHE_FOLDER); const cacheFile = path.join(cacheFolder, cacheKey); @@ -163,7 +161,7 @@ export class CachedExtensionScanner { return null; } - private static async _writeExtensionCache(environmentService: IEnvironmentService, cacheKey: string, cacheContents: IExtensionCacheData): Promise { + private static async _writeExtensionCache(environmentService: INativeWorkbenchEnvironmentService, cacheKey: string, cacheContents: IExtensionCacheData): Promise { const cacheFolder = path.join(environmentService.userDataPath, MANIFEST_CACHE_FOLDER); const cacheFile = path.join(cacheFolder, cacheKey); @@ -180,7 +178,7 @@ export class CachedExtensionScanner { } } - private static async _scanExtensionsWithCache(hostService: IHostService, notificationService: INotificationService, environmentService: IEnvironmentService, cacheKey: string, input: ExtensionScannerInput, log: ILog): Promise { + private static async _scanExtensionsWithCache(hostService: IHostService, notificationService: INotificationService, environmentService: INativeWorkbenchEnvironmentService, cacheKey: string, input: ExtensionScannerInput, log: ILog): Promise { if (input.devMode) { // Do not cache when running out of sources... return ExtensionScanner.scanExtensions(input, log); @@ -239,9 +237,8 @@ export class CachedExtensionScanner { private static _scanInstalledExtensions( hostService: IHostService, notificationService: INotificationService, - environmentService: IEnvironmentService, + environmentService: INativeWorkbenchEnvironmentService, extensionEnablementService: IWorkbenchExtensionEnablementService, - productService: IProductService, log: ILog, translations: Translations ): Promise<{ system: IExtensionDescription[], user: IExtensionDescription[], development: IExtensionDescription[] }> { @@ -263,9 +260,7 @@ export class CachedExtensionScanner { let finalBuiltinExtensions: Promise = builtinExtensions; if (devMode) { - const builtInExtensionsFilePath = path.normalize(path.join(getPathFromAmdModule(require, ''), '..', 'build', 'builtInExtensions.json')); - const builtInExtensions = pfs.readFile(builtInExtensionsFilePath, 'utf8') - .then(raw => parseBuiltInExtensions(raw, productService.quality)); + const builtInExtensions = Promise.resolve(product.builtInExtensions || []); const controlFilePath = path.join(os.homedir(), '.vscode-oss-dev', 'extensions', 'control.json'); const controlFile = pfs.readFile(controlFilePath, 'utf8') @@ -325,7 +320,6 @@ interface IBuiltInExtension { name: string; version: string; repo: string; - forQualities?: ReadonlyArray; } interface IBuiltInExtensionControl { diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts index 556499ee5e..25b33b9fd9 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts @@ -418,7 +418,7 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { extensionDevelopmentLocationURI: this._environmentService.extensionDevelopmentLocationURI, extensionTestsLocationURI: this._environmentService.extensionTestsLocationURI, globalStorageHome: URI.file(this._environmentService.globalStorageHome), - userHome: URI.file(this._environmentService.userHome), + userHome: this._environmentService.userHome, webviewResourceRoot: this._environmentService.webviewResourceRoot, webviewCspSource: this._environmentService.webviewCspSource, }, diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts index 6edfa5bc2e..e9f3570373 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHostProfiler.ts @@ -30,10 +30,10 @@ export class ExtensionHostProfiler { } private distill(profile: Profile, extensions: IExtensionDescription[]): IExtensionHostProfile { - let searchTree = TernarySearchTree.forPaths(); + let searchTree = TernarySearchTree.forUris(); for (let extension of extensions) { if (extension.extensionLocation.scheme === Schemas.file) { - searchTree.set(URI.file(realpathSync(extension.extensionLocation.fsPath)).toString(), extension); + searchTree.set(URI.file(realpathSync(extension.extensionLocation.fsPath)), extension); } } @@ -62,7 +62,7 @@ export class ExtensionHostProfiler { } else if (segmentId === 'self' && node.callFrame.url) { let extension: IExtensionDescription | undefined; try { - extension = searchTree.findSubstr(URI.parse(node.callFrame.url).toString()); + extension = searchTree.findSubstr(URI.parse(node.callFrame.url)); } catch { // ignore } diff --git a/src/vs/workbench/services/keybinding/test/electron-browser/browserKeyboardMapper.test.ts b/src/vs/workbench/services/keybinding/test/electron-browser/browserKeyboardMapper.test.ts index 91523f45bb..52b68b3a33 100644 --- a/src/vs/workbench/services/keybinding/test/electron-browser/browserKeyboardMapper.test.ts +++ b/src/vs/workbench/services/keybinding/test/electron-browser/browserKeyboardMapper.test.ts @@ -6,8 +6,8 @@ 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 { BrowserKeyboardMapperFactoryBase } from 'vs/workbench/services/keybinding/browser/keymapService'; +import { KeymapInfo, IKeymapInfo } from 'vs/workbench/services/keybinding/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'; 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 9a62182b9d..71a366d76e 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 @@ -65,7 +65,6 @@ class TestEnvironmentService extends NativeWorkbenchEnvironmentService { } get appSettingsHome() { return this._appSettingsHome; } - } interface Modifiers { diff --git a/src/vs/workbench/services/label/common/labelService.ts b/src/vs/workbench/services/label/common/labelService.ts index 39c91ae229..26baef2843 100644 --- a/src/vs/workbench/services/label/common/labelService.ts +++ b/src/vs/workbench/services/label/common/labelService.ts @@ -21,6 +21,7 @@ import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/exte import { match } from 'vs/base/common/glob'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IRemotePathService } from 'vs/workbench/services/path/common/remotePathService'; const resourceLabelFormattersExtPoint = ExtensionsRegistry.registerExtensionPoint({ extensionPoint: 'resourceLabelFormatters', @@ -101,6 +102,7 @@ export class LabelService extends Disposable implements ILabelService { constructor( @IEnvironmentService private readonly environmentService: IEnvironmentService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, + @IRemotePathService private readonly remotePathService: IRemotePathService ) { super(); } @@ -264,7 +266,10 @@ export class LabelService extends Disposable implements ILabelService { } if (formatting.tildify && !forceNoTildify) { - label = tildify(label, this.environmentService.userHome); + const userHome = this.remotePathService.userHomeSync; + if (userHome) { + label = tildify(label, userHome.fsPath); + } } if (formatting.authorityPrefix && resource.authority) { label = formatting.authorityPrefix + label; diff --git a/src/vs/workbench/services/label/test/browser/label.test.ts b/src/vs/workbench/services/label/test/browser/label.test.ts index 89544a96cb..8523fe1d32 100644 --- a/src/vs/workbench/services/label/test/browser/label.test.ts +++ b/src/vs/workbench/services/label/test/browser/label.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { TestEnvironmentService } from 'vs/workbench/test/browser/workbenchTestServices'; +import { TestEnvironmentService, TestRemotePathService } from 'vs/workbench/test/browser/workbenchTestServices'; import { TestWorkspace } from 'vs/platform/workspace/test/common/testWorkspace'; import { URI } from 'vs/base/common/uri'; import { sep } from 'vs/base/common/path'; @@ -17,7 +17,7 @@ suite('URI Label', () => { let labelService: LabelService; setup(() => { - labelService = new LabelService(TestEnvironmentService, new TestContextService()); + labelService = new LabelService(TestEnvironmentService, new TestContextService(), new TestRemotePathService(TestEnvironmentService)); }); test('file scheme', function () { diff --git a/src/vs/workbench/services/path/common/remotePathService.ts b/src/vs/workbench/services/path/common/remotePathService.ts index 2b94327540..2ff70918aa 100644 --- a/src/vs/workbench/services/path/common/remotePathService.ts +++ b/src/vs/workbench/services/path/common/remotePathService.ts @@ -9,19 +9,35 @@ import { URI } from 'vs/base/common/uri'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; -import { Schemas } from 'vs/base/common/network'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; const REMOTE_PATH_SERVICE_ID = 'remotePath'; export const IRemotePathService = createDecorator(REMOTE_PATH_SERVICE_ID); export interface IRemotePathService { + _serviceBrand: undefined; + /** + * The path library to use for the target remote environment. + */ readonly path: Promise; + + /** + * Converts the given path to a file URI in the remote environment. + */ fileURI(path: string): Promise; + /** + * Resolves the user home of the remote environment if defined. + */ readonly userHome: Promise; + + /** + * Provides access to the user home of the remote environment + * if defined. + */ + readonly userHomeSync: URI | undefined; } /** @@ -31,12 +47,15 @@ export class RemotePathService implements IRemotePathService { _serviceBrand: undefined; private _extHostOS: Promise; + private _userHomeSync: URI | undefined; constructor( @IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService, @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService ) { this._extHostOS = remoteAgentService.getEnvironment().then(remoteEnvironment => { + this._userHomeSync = remoteEnvironment?.userHome; + return remoteEnvironment ? remoteEnvironment.os : platform.OS; }); } @@ -91,9 +110,13 @@ export class RemotePathService implements IRemotePathService { } // local: use the userHome from environment - return URI.from({ scheme: Schemas.file, path: this.environmentService.userHome }); + return this.environmentService.userHome!; }); } + + get userHomeSync(): URI | undefined { + return this._userHomeSync || this.environmentService.userHome; + } } registerSingleton(IRemotePathService, RemotePathService, true); diff --git a/src/vs/workbench/services/progress/browser/progressService.ts b/src/vs/workbench/services/progress/browser/progressService.ts index 5e7dbfbf72..cae99d24d0 100644 --- a/src/vs/workbench/services/progress/browser/progressService.ts +++ b/src/vs/workbench/services/progress/browser/progressService.ts @@ -380,11 +380,25 @@ export class ProgressService extends Disposable implements IProgressService { const listener = progressStateModel.onDidReport(step => updateNotification(step)); Event.once(progressStateModel.onDispose)(() => listener.dispose()); - // Show progress for at least 800ms and then hide once done or canceled - Promise.all([timeout(800), progressStateModel.promise]).finally(() => { - clearTimeout(notificationTimeout); - notificationHandle?.close(); - }); + // Clean up eventually + (async () => { + try { + + // with a delay we only wait for the finish of the promise + if (typeof options.delay === 'number' && options.delay > 0) { + await progressStateModel.promise; + } + + // without a delay we show the notification for at least 800ms + // to reduce the chance of the notification flashing up and hiding + else { + await Promise.all([timeout(800), progressStateModel.promise]); + } + } finally { + clearTimeout(notificationTimeout); + notificationHandle?.close(); + } + })(); return progressStateModel.promise; } diff --git a/src/vs/workbench/services/quickinput/browser/quickInputService.ts b/src/vs/workbench/services/quickinput/browser/quickInputService.ts index f78ea3c9c4..1574b2d3d9 100644 --- a/src/vs/workbench/services/quickinput/browser/quickInputService.ts +++ b/src/vs/workbench/services/quickinput/browser/quickInputService.ts @@ -7,7 +7,6 @@ import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; @@ -22,7 +21,6 @@ export class QuickInputService extends BaseQuickInputService { private readonly inQuickInputContext = InQuickPickContextKey.bindTo(this.contextKeyService); constructor( - @IEnvironmentService private readonly environmentService: IEnvironmentService, @IConfigurationService private readonly configurationService: IConfigurationService, @IInstantiationService instantiationService: IInstantiationService, @IKeybindingService private readonly keybindingService: IKeybindingService, @@ -43,7 +41,7 @@ export class QuickInputService extends BaseQuickInputService { protected createController(): QuickInputController { return super.createController(this.layoutService, { - ignoreFocusOut: () => this.environmentService.args['sticky-quickinput'] || !this.configurationService.getValue('workbench.quickOpen.closeOnFocusLost'), + ignoreFocusOut: () => !this.configurationService.getValue('workbench.quickOpen.closeOnFocusLost'), backKeybindingLabel: () => this.keybindingService.lookupKeybinding('workbench.action.quickInputBack')?.getLabel() || undefined, }); } diff --git a/src/vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl.ts b/src/vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl.ts index 881997341c..133121a29c 100644 --- a/src/vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl.ts +++ b/src/vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl.ts @@ -3,8 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; import { IRemoteAgentConnection, IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; import product from 'vs/platform/product/common/product'; @@ -13,6 +11,7 @@ import { AbstractRemoteAgentService, RemoteAgentConnection } from 'vs/workbench/ import { ISignService } from 'vs/platform/sign/common/sign'; import { ISocketFactory } from 'vs/platform/remote/common/remoteAgentConnection'; import { ILogService } from 'vs/platform/log/common/log'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; export class RemoteAgentService extends AbstractRemoteAgentService implements IRemoteAgentService { @@ -20,16 +19,16 @@ export class RemoteAgentService extends AbstractRemoteAgentService implements IR private readonly _connection: IRemoteAgentConnection | null = null; - constructor({ remoteAuthority }: IWindowConfiguration, - @IEnvironmentService environmentService: IEnvironmentService, + constructor( + @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, @IRemoteAuthorityResolverService remoteAuthorityResolverService: IRemoteAuthorityResolverService, @ISignService signService: ISignService, @ILogService logService: ILogService ) { super(environmentService); this.socketFactory = nodeSocketFactory; - if (remoteAuthority) { - this._connection = this._register(new RemoteAgentConnection(remoteAuthority, product.commit, nodeSocketFactory, remoteAuthorityResolverService, signService, logService)); + if (environmentService.configuration.remoteAuthority) { + this._connection = this._register(new RemoteAgentConnection(environmentService.configuration.remoteAuthority, product.commit, nodeSocketFactory, remoteAuthorityResolverService, signService, logService)); } } diff --git a/src/vs/workbench/services/search/node/searchService.ts b/src/vs/workbench/services/search/node/searchService.ts index 26fa92edaf..01555d1127 100644 --- a/src/vs/workbench/services/search/node/searchService.ts +++ b/src/vs/workbench/services/search/node/searchService.ts @@ -12,8 +12,8 @@ import { URI as uri } from 'vs/base/common/uri'; import { getNextTickChannel } from 'vs/base/parts/ipc/common/ipc'; import { Client, IIPCOptions } from 'vs/base/parts/ipc/node/ipc.cp'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IDebugParams } from 'vs/platform/environment/common/environment'; -import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { IDebugParams, IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { parseSearchPort, INativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; import { IFileService } from 'vs/platform/files/common/files'; import { ILogService } from 'vs/platform/log/common/log'; import { FileMatch, IFileMatch, IFileQuery, IProgressMessage, IRawSearchService, ISearchComplete, ISearchConfiguration, ISearchProgressItem, ISearchResultProvider, ISerializedFileMatch, ISerializedSearchComplete, ISerializedSearchProgressItem, isSerializedSearchComplete, isSerializedSearchSuccess, ITextQuery, ISearchService, isFileMatch } from 'vs/workbench/services/search/common/search'; @@ -25,7 +25,6 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic 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 { parseSearchPort } from 'vs/platform/environment/node/environmentService'; export class LocalSearchService extends SearchService { constructor( @@ -35,12 +34,11 @@ export class LocalSearchService extends SearchService { @ILogService logService: ILogService, @IExtensionService extensionService: IExtensionService, @IFileService fileService: IFileService, - @IWorkbenchEnvironmentService readonly environmentService: IWorkbenchEnvironmentService, + @IEnvironmentService readonly environmentService: INativeEnvironmentService, @IInstantiationService readonly instantiationService: IInstantiationService ) { super(modelService, editorService, telemetryService, logService, extensionService, fileService); - this.diskSearch = instantiationService.createInstance(DiskSearch, !environmentService.isBuilt || environmentService.verbose, parseSearchPort(environmentService.args, environmentService.isBuilt)); } } diff --git a/src/vs/workbench/services/telemetry/electron-browser/telemetryService.ts b/src/vs/workbench/services/telemetry/electron-browser/telemetryService.ts index 5a87c25908..5e59819b6c 100644 --- a/src/vs/workbench/services/telemetry/electron-browser/telemetryService.ts +++ b/src/vs/workbench/services/telemetry/electron-browser/telemetryService.ts @@ -35,7 +35,7 @@ export class TelemetryService extends Disposable implements ITelemetryService { ) { super(); - if (!environmentService.isExtensionDevelopment && !environmentService.args['disable-telemetry'] && !!productService.enableTelemetry) { + if (!environmentService.isExtensionDevelopment && !environmentService.disableTelemetry && !!productService.enableTelemetry) { const channel = sharedProcessService.getChannel('telemetryAppender'); const config: ITelemetryServiceConfig = { appender: combinedAppender(new TelemetryAppenderClient(channel), new LogAppender(logService)), diff --git a/src/vs/workbench/services/textfile/electron-browser/nativeTextFileService.ts b/src/vs/workbench/services/textfile/electron-browser/nativeTextFileService.ts index ae580b89ba..a41ad2b73b 100644 --- a/src/vs/workbench/services/textfile/electron-browser/nativeTextFileService.ts +++ b/src/vs/workbench/services/textfile/electron-browser/nativeTextFileService.ts @@ -33,7 +33,6 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IModelService } from 'vs/editor/common/services/modelService'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IDialogService, IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { assign } from 'vs/base/common/objects'; import { IFilesConfigurationService } from 'vs/workbench/services/filesConfiguration/common/filesConfigurationService'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; @@ -75,16 +74,15 @@ export class NativeTextFileService extends AbstractTextFileService { } async read(resource: URI, options?: IReadTextFileOptions): Promise { - const [bufferStream, decoder] = await this.doRead(resource, - assign({ - // optimization: since we know that the caller does not - // care about buffering, we indicate this to the reader. - // this reduces all the overhead the buffered reading - // has (open, read, close) if the provider supports - // unbuffered reading. - preferUnbuffered: true - }, options || Object.create(null)) - ); + const [bufferStream, decoder] = await this.doRead(resource, { + ...options, + // optimization: since we know that the caller does not + // care about buffering, we indicate this to the reader. + // this reduces all the overhead the buffered reading + // has (open, read, close) if the provider supports + // unbuffered reading. + preferUnbuffered: true + }); return { ...bufferStream, diff --git a/src/vs/workbench/services/themes/common/fileIconThemeSchema.ts b/src/vs/workbench/services/themes/common/fileIconThemeSchema.ts index db2032ceef..da06ad9c37 100644 --- a/src/vs/workbench/services/themes/common/fileIconThemeSchema.ts +++ b/src/vs/workbench/services/themes/common/fileIconThemeSchema.ts @@ -120,7 +120,7 @@ const schema: IJSONSchema = { properties: { path: { type: 'string', - description: nls.localize('schema.font-path', 'The font path, relative to the current icon theme file.'), + description: nls.localize('schema.font-path', 'The font path, relative to the current file icon theme file.'), }, format: { type: 'string', diff --git a/src/vs/workbench/services/themes/common/productIconThemeSchema.ts b/src/vs/workbench/services/themes/common/productIconThemeSchema.ts index a372ea34e3..bbd67d942f 100644 --- a/src/vs/workbench/services/themes/common/productIconThemeSchema.ts +++ b/src/vs/workbench/services/themes/common/productIconThemeSchema.ts @@ -34,7 +34,7 @@ const schema: IJSONSchema = { properties: { path: { type: 'string', - description: nls.localize('schema.font-path', 'The font path, relative to the current workbench icon theme file.'), + description: nls.localize('schema.font-path', 'The font path, relative to the current product icon theme file.'), }, format: { type: 'string', diff --git a/src/vs/workbench/services/themes/common/themeConfiguration.ts b/src/vs/workbench/services/themes/common/themeConfiguration.ts index 512184afba..2758dbc6ec 100644 --- a/src/vs/workbench/services/themes/common/themeConfiguration.ts +++ b/src/vs/workbench/services/themes/common/themeConfiguration.ts @@ -89,10 +89,10 @@ const fileIconThemeSettingSchema: IConfigurationPropertySchema = { const productIconThemeSettingSchema: IConfigurationPropertySchema = { type: ['string', 'null'], default: DEFAULT_PRODUCT_ICON_THEME_SETTING_VALUE, - description: nls.localize('workbenchIconTheme', "Specifies the workbench icon theme used."), + description: nls.localize('productIconTheme', "Specifies the product icon theme used."), enum: [DEFAULT_PRODUCT_ICON_THEME_SETTING_VALUE], - enumDescriptions: [nls.localize('defaultWorkbenchIconThemeDesc', 'Default')], - errorMessage: nls.localize('workbenchIconThemeError', "Workbench icon theme is unknown or not installed.") + enumDescriptions: [nls.localize('defaultProductIconThemeDesc', 'Default')], + errorMessage: nls.localize('productIconThemeError', "Product icon theme is unknown or not installed.") }; const themeSettingsConfiguration: IConfigurationNode = { diff --git a/src/vs/workbench/services/workspaces/browser/abstractWorkspaceEditingService.ts b/src/vs/workbench/services/workspaces/browser/abstractWorkspaceEditingService.ts index d7cad2fe9f..1c921fd4db 100644 --- a/src/vs/workbench/services/workspaces/browser/abstractWorkspaceEditingService.ts +++ b/src/vs/workbench/services/workspaces/browser/abstractWorkspaceEditingService.ts @@ -23,6 +23,7 @@ import { mnemonicButtonLabel } from 'vs/base/common/labels'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IHostService } from 'vs/workbench/services/host/browser/host'; +import { Schemas } from 'vs/base/common/network'; export abstract class AbstractWorkspaceEditingService implements IWorkspaceEditingService { @@ -127,13 +128,10 @@ export abstract class AbstractWorkspaceEditingService implements IWorkspaceEditi private async doAddFolders(foldersToAdd: IWorkspaceFolderCreationData[], index?: number, donotNotifyError: boolean = false): Promise { 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)) { - throw new Error(nls.localize('differentSchemeRoots', "Workspace folders from different providers are not allowed in the same workspace.")); - } + const remoteAuthority = this.environmentService.configuration.remoteAuthority; + if (remoteAuthority) { + // https://github.com/microsoft/vscode/issues/94191 + foldersToAdd = foldersToAdd.filter(f => f.uri.scheme !== Schemas.file && (f.uri.scheme !== Schemas.vscodeRemote || f.uri.authority === remoteAuthority)); } // If we are in no-workspace or single-folder workspace, adding folders has to diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index fa5737d6e4..cc9acfcfeb 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -168,7 +168,7 @@ export function workbenchInstantiationService(overrides?: { textFileService?: (i instantiationService.stub(ICodeEditorService, new CodeEditorService(editorService, themeService)); instantiationService.stub(IViewletService, new TestViewletService()); instantiationService.stub(IListService, new TestListService()); - instantiationService.stub(IQuickInputService, new QuickInputService(TestEnvironmentService, configService, instantiationService, keybindingService, contextKeyService, themeService, accessibilityService, layoutService)); + instantiationService.stub(IQuickInputService, new QuickInputService(configService, instantiationService, keybindingService, contextKeyService, themeService, accessibilityService, layoutService)); instantiationService.stub(IStorageKeysSyncRegistryService, new StorageKeysSyncRegistryService()); return instantiationService; @@ -261,7 +261,11 @@ export class TestTextFileService extends BrowserTextFileService { } } -export const TestEnvironmentService = new BrowserWorkbenchEnvironmentService(Object.create(null)); +class TestEnvironmentServiceWithArgs extends BrowserWorkbenchEnvironmentService { + args = []; +} + +export const TestEnvironmentService = new TestEnvironmentServiceWithArgs(Object.create(null)); export class TestProgressService implements IProgressService { @@ -1105,7 +1109,8 @@ export class TestRemotePathService implements IRemotePathService { get path() { return Promise.resolve(isWindows ? win32 : posix); } - get userHome() { return Promise.resolve(URI.file(this.environmentService.userHome)); } + get userHome() { return Promise.resolve(this.environmentService.userHome!); } + get userHomeSync() { return this.environmentService.userHome; } async fileURI(path: string): Promise { return URI.file(path); diff --git a/src/vs/workbench/test/common/api/semanticTokensDto.test.ts b/src/vs/workbench/test/common/api/semanticTokensDto.test.ts index 67fd9db295..e1155c4fb7 100644 --- a/src/vs/workbench/test/common/api/semanticTokensDto.test.ts +++ b/src/vs/workbench/test/common/api/semanticTokensDto.test.ts @@ -5,6 +5,7 @@ import * as assert from 'assert'; import { IFullSemanticTokensDto, IDeltaSemanticTokensDto, encodeSemanticTokensDto, ISemanticTokensDto, decodeSemanticTokensDto } from 'vs/workbench/api/common/shared/semanticTokensDto'; +import { VSBuffer } from 'vs/base/common/buffer'; suite('SemanticTokensDto', () => { @@ -107,4 +108,29 @@ suite('SemanticTokensDto', () => { }); }); + test('issue #94521: unusual backing array buffer', () => { + function wrapAndSliceUint8Arry(buff: Uint8Array, prefixLength: number, suffixLength: number): Uint8Array { + const wrapped = new Uint8Array(prefixLength + buff.byteLength + suffixLength); + wrapped.set(buff, prefixLength); + return wrapped.subarray(prefixLength, prefixLength + buff.byteLength); + } + function wrapAndSlice(buff: VSBuffer, prefixLength: number, suffixLength: number): VSBuffer { + return VSBuffer.wrap(wrapAndSliceUint8Arry(buff.buffer, prefixLength, suffixLength)); + } + const dto: ISemanticTokensDto = { + id: 5, + type: 'full', + data: new Uint32Array([1, 2, 3, 4, 5]) + }; + const encoded = encodeSemanticTokensDto(dto); + + // with misaligned prefix and misaligned suffix + assertEqualFull(decodeSemanticTokensDto(wrapAndSlice(encoded, 1, 1)), dto); + // with misaligned prefix and aligned suffix + assertEqualFull(decodeSemanticTokensDto(wrapAndSlice(encoded, 1, 4)), dto); + // with aligned prefix and misaligned suffix + assertEqualFull(decodeSemanticTokensDto(wrapAndSlice(encoded, 4, 1)), dto); + // with aligned prefix and aligned suffix + assertEqualFull(decodeSemanticTokensDto(wrapAndSlice(encoded, 4, 4)), dto); + }); }); diff --git a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts index a46d975a01..7e12e77d3a 100644 --- a/src/vs/workbench/test/electron-browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/electron-browser/workbenchTestServices.ts @@ -25,7 +25,7 @@ import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService import { URI } from 'vs/base/common/uri'; import { IReadTextFileOptions, ITextFileStreamContent, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { createTextBufferFactoryFromStream } from 'vs/editor/common/model/textModel'; -import { IOpenedWindow, IOpenEmptyWindowOptions, IWindowOpenable, IOpenWindowOptions } from 'vs/platform/windows/common/windows'; +import { IOpenEmptyWindowOptions, IWindowOpenable, IOpenWindowOptions } from 'vs/platform/windows/common/windows'; import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv'; import { LogLevel, ILogService } from 'vs/platform/log/common/log'; import { IRemotePathService } from 'vs/workbench/services/path/common/remotePathService'; @@ -37,7 +37,7 @@ import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; import { NodeTestBackupFileService } from 'vs/workbench/services/backup/test/electron-browser/backupFileService.test'; import { IWorkingCopyService } from 'vs/workbench/services/workingCopy/common/workingCopyService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { INativeWindowConfiguration } from 'vs/platform/windows/node/window'; +import { INativeWindowConfiguration, IOpenedWindow } from 'vs/platform/windows/node/window'; import { TestContextService } from 'vs/workbench/test/common/workbenchTestServices'; export const TestWindowConfiguration: INativeWindowConfiguration = { diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index 967bbab3a0..9035630b05 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -39,6 +39,7 @@ import 'vs/workbench/services/keybinding/electron-browser/keybinding.contributio import 'vs/workbench/services/extensions/electron-browser/extensionService'; import 'vs/workbench/services/contextmenu/electron-browser/contextmenuService'; import 'vs/workbench/services/extensionManagement/electron-browser/extensionManagementServerService'; +import 'vs/workbench/services/extensionManagement/electron-browser/extensionTipsService'; import 'vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl'; import 'vs/workbench/services/telemetry/electron-browser/telemetryService'; import 'vs/workbench/services/configurationResolver/electron-browser/configurationResolverService'; @@ -159,7 +160,7 @@ import 'vs/workbench/contrib/userDataSync/electron-browser/userDataSync.contribu import 'vs/workbench/contrib/welcome/telemetryOptOut/electron-browser/telemetryOptOut.contribution'; // Configuration Exporter -import 'vs/workbench/contrib/configExporter/node/configurationExportHelper.contribution'; +import 'vs/workbench/contrib/configExporter/electron-browser/configurationExportHelper.contribution'; //#endregion diff --git a/src/vs/workbench/workbench.web.main.ts b/src/vs/workbench/workbench.web.main.ts index f4c32d4af6..642cebaa5e 100644 --- a/src/vs/workbench/workbench.web.main.ts +++ b/src/vs/workbench/workbench.web.main.ts @@ -34,6 +34,7 @@ import 'vs/workbench/services/textfile/browser/browserTextFileService'; import 'vs/workbench/services/keybinding/browser/keymapService'; import 'vs/workbench/services/extensions/browser/extensionService'; import 'vs/workbench/services/extensionManagement/common/extensionManagementServerService'; +import 'vs/workbench/services/extensionManagement/common/extensionTipsService'; import 'vs/workbench/services/telemetry/browser/telemetryService'; import 'vs/workbench/services/configurationResolver/browser/configurationResolverService'; import 'vs/workbench/services/credentials/browser/credentialsService'; diff --git a/test/automation/src/code.ts b/test/automation/src/code.ts index 1ef57ecb67..ae9ddcbe26 100644 --- a/test/automation/src/code.ts +++ b/test/automation/src/code.ts @@ -133,8 +133,6 @@ export async function spawn(options: SpawnOptions): Promise { const args = [ options.workspacePath, '--skip-getting-started', - '--skip-release-notes', - '--sticky-quickinput', '--disable-telemetry', '--disable-updates', '--disable-crash-reporter', diff --git a/test/unit/browser/index.js b/test/unit/browser/index.js index bd1aaf7c64..4ab8e87b58 100644 --- a/test/unit/browser/index.js +++ b/test/unit/browser/index.js @@ -22,7 +22,7 @@ const optimist = require('optimist') .describe('run', 'only run tests matching ').string('run') .describe('glob', 'only run tests matching ').string('glob') .describe('debug', 'do not run browsers headless').boolean('debug') - .describe('browser', 'browsers in which tests should run').string('browser').default('browser', ['chromium']) + .describe('browser', 'browsers in which tests should run').string('browser').default('browser', ['chromium', 'firefox', 'webkit']) .describe('reporter', 'the mocha reporter').string('reporter').default('reporter', defaultReporterName) .describe('reporter-options', 'the mocha reporter options').string('reporter-options').default('reporter-options', '') .describe('tfs', 'tfs').string('tfs') diff --git a/yarn.lock b/yarn.lock index 73535d466d..b0b2c78076 100644 --- a/yarn.lock +++ b/yarn.lock @@ -6443,10 +6443,10 @@ node-pre-gyp@^0.10.0: semver "^5.3.0" tar "^4" -node-pty@^0.10.0-beta2: - version "0.10.0-beta2" - resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.10.0-beta2.tgz#6fd0d2fbbe881869e4e19795a05c557ac958da81" - integrity sha512-IU2lzlPUZ+gKG7pHJjzBHpnuwPTxWGgT3iyQicZfdL7dwLvP5cm00QxavAXCInBmRkOMhvM4aBSKvfzqQnCDBA== +node-pty@0.10.0-beta7: + version "0.10.0-beta7" + resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.10.0-beta7.tgz#7e383b2d1fe2f34509b57187f5a9a6ff90c46111" + integrity sha512-oC2VyIz9YaIvv6lWjAPZbUzmhLW1ouFmxOogNRNQrKeUzUi2yM/QRmybs+dW/Mhd3V89Yh61Ml0J5yuWiMIBbw== dependencies: nan "^2.14.0" From 06c1db5417c1a1e854ca892378078a89eead8fd4 Mon Sep 17 00:00:00 2001 From: Anthony Dresser Date: Wed, 8 Apr 2020 02:19:57 -0700 Subject: [PATCH 2/3] distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 1d5353a6fc..16e9615725 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "azuredatastudio", "version": "1.17.0", - "distro": "032712182226916808ad5fea8edbdf657dc8d297", + "distro": "cb5f694e82885048fde3f3236e77434b6fe31a1c", "author": { "name": "Microsoft Corporation" }, From c428a3aebf084e75c2b481e5e320648cc2a4b5d5 Mon Sep 17 00:00:00 2001 From: Anthony Dresser Date: Wed, 8 Apr 2020 03:05:09 -0700 Subject: [PATCH 3/3] distro --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 16e9615725..094fc4e114 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "azuredatastudio", "version": "1.17.0", - "distro": "cb5f694e82885048fde3f3236e77434b6fe31a1c", + "distro": "b5ba37f6b0c11bd7c826d81dec82c379633e0e27", "author": { "name": "Microsoft Corporation" },