From a07b10980164e08319c7a2b7281d92edc5e6cd2c Mon Sep 17 00:00:00 2001 From: ADS Merger Date: Wed, 18 Sep 2019 00:38:30 +0000 Subject: [PATCH] Merge from vscode 88ff4a7bdd9ad85ac8a44fafecdd9a103a6f8f28 --- .yarnrc | 2 +- build/azure-pipelines/exploration-build.yml | 8 +- .../linux/product-build-linux.yml | 2 +- build/gulpfile.editor.js | 2 + build/gulpfile.hygiene.js | 3 +- build/gulpfile.vscode.js | 2 +- build/gulpfile.vscode.web.js | 151 +- build/lib/i18n.js | 2 +- build/lib/i18n.resources.json | 8 + build/lib/i18n.ts | 2 +- build/npm/preinstall.js | 2 +- cglicenses.json | 52 - cgmanifest.json | 12 +- extensions/git/src/decorationProvider.ts | 16 +- extensions/image-preview/icon.png | Bin 0 -> 1979 bytes extensions/image-preview/icon.svg | 18 + extensions/image-preview/media/main.js | 8 +- extensions/image-preview/package.json | 4 +- extensions/image-preview/src/preview.ts | 12 +- extensions/image-preview/src/typings/ref.d.ts | 2 +- extensions/json/syntaxes/JSON.tmLanguage.json | 4 +- .../src/docIndex.ts | 50 - .../src/features/workspaceSymbolProvider.ts | 8 +- extensions/mssql/package.json | 2 +- extensions/mssql/yarn.lock | 102 +- extensions/package.json | 2 +- extensions/yarn.lock | 8 +- package.json | 4 +- remote/.yarnrc | 2 +- remote/package.json | 2 +- remote/yarn.lock | 818 +------- resources/win32/bin/code.sh | 10 +- scripts/test-integration.sh | 13 +- src/bootstrap.js | 18 +- src/main.js | 9 +- .../modelComponents/queryTextEditor.ts | 4 +- .../page/browser/az_data_welcome_page.ts | 3 - .../electron-browser/insightsUtils.test.ts | 1 + .../update/electron-browser/releaseNotes.ts | 5 +- src/typings/chokidar.d.ts | 4 +- src/typings/electron.d.ts | 1751 ++++++++++++----- src/vs/base/browser/dom.ts | 29 +- .../base/browser/ui/actionbar/actionbar.css | 4 +- src/vs/base/browser/ui/actionbar/actionbar.ts | 4 +- .../codicon/codicon-animations.css | 14 + .../ui/codiconLabel/codicon/codicon.css | 354 ++++ .../ui/codiconLabel/codicon/codicon.ttf | Bin 0 -> 52456 bytes .../ui/codiconLabel/codiconLabel.mock.ts | 24 + .../browser/ui/codiconLabel/codiconLabel.ts | 33 + .../ui/highlightedlabel/highlightedLabel.ts | 8 +- src/vs/base/browser/ui/inputbox/inputBox.css | 2 +- src/vs/base/common/network.ts | 9 +- .../contextmenu/electron-main/contextmenu.ts | 6 +- .../parts/tree/browser/collapse-all-dark.svg | 4 - .../parts/tree/browser/collapse-all-hc.svg | 4 - .../parts/tree/browser/collapse-all-light.svg | 4 - src/vs/base/parts/tree/browser/tree.css | 12 - .../code/browser/workbench/workbench-dev.html | 16 +- src/vs/code/browser/workbench/workbench.ts | 16 +- .../issue/issueReporterMain.ts | 13 +- .../processExplorer/processExplorerMain.ts | 2 +- .../contrib/languagePackCachedDataCleaner.ts | 2 +- .../contrib/nodeCachedDataCleaner.ts | 2 +- .../contrib/storageDataCleaner.ts | 2 +- .../sharedProcess/sharedProcessMain.ts | 15 +- src/vs/code/electron-main/app.ts | 81 +- src/vs/code/electron-main/main.ts | 26 +- src/vs/code/electron-main/sharedProcess.ts | 9 +- src/vs/code/electron-main/window.ts | 12 +- src/vs/code/electron-main/windows.ts | 119 +- src/vs/code/node/cli.ts | 7 +- src/vs/code/node/cliProcessMain.ts | 7 +- .../editor/browser/services/openerService.ts | 14 +- .../viewParts/lineNumbers/lineNumbers.ts | 18 +- src/vs/editor/common/commands/shiftCommand.ts | 4 +- src/vs/editor/contrib/find/findWidget.ts | 4 + .../test/linesOperations.test.ts | 22 + .../parameterHints/parameterHintsWidget.ts | 7 +- .../standalone/browser/simpleServices.ts | 4 + .../browser/commands/shiftCommand.test.ts | 2 +- src/vs/nls.d.ts | 10 +- .../{common => electron-main}/backup.ts | 21 +- .../backup/electron-main/backupMainService.ts | 3 +- src/vs/platform/backup/node/backup.ts | 22 + .../electron-main/backupMainService.test.ts | 49 +- .../debug/common/extensionHostDebug.ts | 14 +- .../debug/common/extensionHostDebugIpc.ts | 10 +- .../diagnostics/node/diagnosticsIpc.ts | 2 +- .../diagnostics/node/diagnosticsService.ts | 7 +- src/vs/platform/dialogs/common/dialogs.ts | 8 +- .../{node => electron-browser}/dialogIpc.ts | 21 +- .../platform/driver/electron-main/driver.ts | 14 +- .../electron-browser/electronService.ts | 28 + .../electron-main/electronMainService.ts | 59 + src/vs/platform/electron/node/electron.ts | 17 + .../environment/node/environmentService.ts | 9 +- .../common/extensionGalleryService.ts | 2 +- .../node/extensionManagementService.ts | 11 +- .../test/node/extensionGalleryService.test.ts | 6 +- .../watcher/unix/chokidarWatcherService.ts | 2 +- src/vs/platform/history/common/history.ts | 19 - .../electron-main/historyMainService.ts | 28 +- .../issue/{node => electron-main}/issueIpc.ts | 0 .../{issueService.ts => issueMainService.ts} | 64 +- .../common/{launchService.ts => launch.ts} | 3 +- .../launch/electron-main/launchService.ts | 18 +- .../electron-browser/lifecycleService.ts | 2 +- ...fecycleMain.ts => lifecycleMainService.ts} | 14 +- .../localizations/node/localizations.ts | 2 +- src/vs/platform/log/common/log.ts | 51 +- src/vs/platform/log/common/logIpc.ts | 31 +- .../platform/menubar/electron-main/menubar.ts | 16 +- ...enubarService.ts => menubarMainService.ts} | 5 +- src/vs/platform/opener/common/opener.ts | 18 +- src/vs/platform/product/browser/product.ts | 21 - src/vs/platform/product/common/product.ts | 156 +- .../platform/product/common/productService.ts | 127 ++ src/vs/platform/product/node/package.ts | 16 - src/vs/platform/product/node/product.ts | 28 - .../remote/browser/browserSocketFactory.ts | 82 +- .../browser/remoteAuthorityResolverService.ts | 4 +- ...equestService.ts => requestMainService.ts} | 2 +- .../storage/browser/storageService.ts | 2 +- .../telemetry/node/commonProperties.ts | 2 +- .../node/workbenchCommonProperties.ts | 2 +- src/vs/platform/theme/common/colorRegistry.ts | 2 + .../theme/electron-main/themeMainService.ts | 4 +- .../electron-main/abstractUpdateService.ts | 8 +- .../electron-main/updateService.darwin.ts | 6 +- .../electron-main/updateService.linux.ts | 8 +- .../electron-main/updateService.snap.ts | 12 +- .../electron-main/updateService.win32.ts | 8 +- .../url/electron-main/electronUrlListener.ts | 10 +- src/vs/platform/url/node/urlService.ts | 2 +- .../userDataSync/common/settingsSync.ts | 256 +++ .../userDataSync/common/userDataSync.ts | 118 ++ .../common/userDataSyncService.ts | 92 + .../common/userDataSyncStoreService.ts | 57 + src/vs/platform/windows/common/windows.ts | 2 - src/vs/platform/windows/common/windowsIpc.ts | 2 - .../electron-browser/windowsService.ts | 8 - .../windows/electron-main/windowsService.ts | 95 +- src/vs/platform/workspace/common/workspace.ts | 4 +- .../platform/workspaces/common/workspaces.ts | 23 +- .../{node => electron-main}/workspacesIpc.ts | 3 +- .../electron-main/workspacesMainService.ts | 28 +- .../workspacesMainService.test.ts | 16 +- src/vs/vscode.d.ts | 5 + src/vs/vscode.proposed.d.ts | 113 +- .../api/browser/extensionHost.contribution.ts | 1 + .../api/browser/mainThreadConsole.ts | 9 +- .../workbench/api/browser/mainThreadTask.ts | 1 + .../api/browser/mainThreadUserData.ts | 43 + .../api/browser/mainThreadWebview.ts | 83 +- .../api/browser/mainThreadWorkspace.ts | 35 +- .../workbench/api/common/extHost.api.impl.ts | 14 +- .../workbench/api/common/extHost.protocol.ts | 21 + src/vs/workbench/api/common/extHostTask.ts | 716 ++++++- src/vs/workbench/api/common/extHostTypes.ts | 23 + .../workbench/api/common/extHostUserData.ts | 48 + src/vs/workbench/api/common/extHostWebview.ts | 22 +- src/vs/workbench/api/common/shared/webview.ts | 2 - src/vs/workbench/api/node/extHostTask.ts | 631 +----- .../api/node/extHostTerminalService.ts | 4 +- .../browser/actions/developerActions.ts | 8 +- .../workbench/browser/actions/helpActions.ts | 388 ++++ .../browser/actions/layoutActions.ts | 10 - .../browser/actions/windowActions.ts | 13 +- .../browser/actions/workspaceActions.ts | 236 +-- src/vs/workbench/browser/contextkeys.ts | 6 +- src/vs/workbench/browser/legacyLayout.ts | 22 +- src/vs/workbench/browser/part.ts | 9 +- .../parts/panel/media/chevron-down-dark.svg | 3 - .../parts/panel/media/chevron-down-hc.svg | 3 - .../parts/panel/media/chevron-down-light.svg | 3 - .../parts/panel/media/chevron-up-dark.svg | 3 - .../parts/panel/media/chevron-up-hc.svg | 3 - .../parts/panel/media/chevron-up-light.svg | 3 - .../browser/parts/panel/media/close-dark.svg | 4 - .../browser/parts/panel/media/close-hc.svg | 4 - .../browser/parts/panel/media/close-light.svg | 4 - .../browser/parts/panel/media/panelpart.css | 14 - .../browser/parts/panel/panelActions.ts | 6 +- .../browser/parts/views/customView.ts | 1 + src/vs/workbench/browser/style.ts | 9 +- src/vs/workbench/browser/web.main.ts | 17 +- .../workbench/browser/web.simpleservices.ts | 102 +- .../contrib/cli/node/cli.contribution.ts | 2 +- .../customEditor/browser/customEditorInput.ts | 45 +- .../customEditor/browser/customEditors.ts | 141 +- .../customEditor/common/customEditor.ts | 6 +- .../browser/breakpointEditorContribution.ts | 559 ++++++ .../contrib/debug/browser/breakpointWidget.ts | 15 +- .../contrib/debug/browser/breakpointsView.ts | 6 +- .../debug/browser/debug.contribution.ts | 4 +- .../contrib/debug/browser/debugActions.ts | 6 +- .../browser/debugCallStackContribution.ts | 182 ++ .../debug/browser/debugEditorActions.ts | 6 +- .../debug/browser/debugEditorContribution.ts | 257 +-- .../debug/browser/debugEditorModelManager.ts | 334 ---- .../contrib/debug/browser/debugSession.ts | 43 +- .../browser/extensionHostDebugService.ts | 52 +- .../debug/browser/media/clear-dark.svg | 7 - .../contrib/debug/browser/media/clear-hc.svg | 7 - .../debug/browser/media/clear-light.svg | 7 - .../browser/media/debug.contribution.css | 31 +- .../contrib/debug/browser/media/repl.css | 13 - .../contrib/debug/browser/rawDebugSession.ts | 13 +- .../workbench/contrib/debug/browser/repl.ts | 2 +- .../workbench/contrib/debug/common/debug.ts | 9 +- .../extensionHostDebugService.ts | 13 +- .../contrib/debug/test/common/mockDebug.ts | 7 +- .../experiments/common/experimentService.ts | 2 +- .../browser/extensionTipsService.ts | 2 +- .../extensions/browser/extensionsActions.ts | 16 +- .../extensions/browser/extensionsViews.ts | 2 +- .../browser/extensionsWorkbenchService.ts | 2 +- .../browser/media/extensionActions.css | 2 +- .../extensionProfileService.ts | 2 +- .../electron-browser/extensionsSlowActions.ts | 4 +- .../runtimeExtensionsEditor.ts | 5 +- .../extensionsActions.test.ts | 2 +- .../extensionsTipsService.test.ts | 2 +- .../electron-browser/extensionsViews.test.ts | 2 +- .../contrib/feedback/browser/feedback.ts | 2 +- .../feedback/browser/feedbackStatusbarItem.ts | 2 +- .../files/browser/fileActions.contribution.ts | 61 +- .../files/browser/views/openEditorsView.ts | 15 +- .../files/common/editors/fileEditorInput.ts | 30 +- .../electron-browser/issue.contribution.ts | 2 +- .../browser/localizations.contribution.ts | 2 +- .../browser/localizationsActions.ts | 2 +- .../contrib/markers/browser/markersPanel.ts | 2 +- .../markers/browser/markersPanelActions.ts | 2 +- .../browser/media/exclude-settings-dark.svg | 3 - .../browser/media/exclude-settings-hc.svg | 3 - .../browser/media/exclude-settings-light.svg | 3 - .../contrib/markers/browser/media/markers.css | 13 - .../output/browser/media/clear-dark.svg | 7 - .../contrib/output/browser/media/clear-hc.svg | 7 - .../output/browser/media/clear-light.svg | 7 - .../output/browser/media/locked-dark.svg | 3 - .../output/browser/media/locked-hc.svg | 3 - .../output/browser/media/locked-light.svg | 3 - .../output/browser/media/open-file-dark.svg | 3 - .../output/browser/media/open-file-hc.svg | 3 - .../output/browser/media/open-file-light.svg | 3 - .../contrib/output/browser/media/output.css | 52 - .../output/browser/media/unlocked-dark.svg | 3 - .../output/browser/media/unlocked-hc.svg | 3 - .../output/browser/media/unlocked-light.svg | 3 - .../contrib/output/browser/outputActions.ts | 10 +- .../contrib/output/browser/outputPanel.ts | 1 - .../electron-browser/perfviewEditor.ts | 5 +- .../electron-browser/startupProfiler.ts | 2 +- .../electron-browser/startupTimings.ts | 2 +- .../preferences/browser/preferencesSearch.ts | 2 +- .../preferences/browser/settingsLayout.ts | 7 + .../common/relauncher.contribution.ts | 2 +- .../contrib/remote/browser/remote.ts | 199 +- .../remote/common/remote.contribution.ts | 8 +- .../electron-browser/remote.contribution.ts | 251 +-- .../search/browser/media/searchview.css | 2 +- .../contrib/search/browser/searchWidget.ts | 3 +- .../languageSurveys.contribution.ts | 29 +- .../nps.contribution.ts | 21 +- .../tasks/browser/abstractTaskService.ts | 12 +- .../contrib/tasks/common/taskService.ts | 1 + .../terminal/browser/media/kill-dark.svg | 3 - .../terminal/browser/media/kill-hc.svg | 3 - .../terminal/browser/media/kill-light.svg | 3 - .../terminal/browser/media/new-dark.svg | 3 - .../contrib/terminal/browser/media/new-hc.svg | 3 - .../terminal/browser/media/new-light.svg | 3 - .../media/split-editor-horizontal-dark.svg | 3 - .../media/split-editor-horizontal-hc.svg | 3 - .../media/split-editor-horizontal-light.svg | 3 - .../media/split-editor-vertical-dark.svg | 3 - .../media/split-editor-vertical-hc.svg | 3 - .../media/split-editor-vertical-light.svg | 3 - .../terminal/browser/media/terminal.css | 18 - .../terminal/browser/terminal.contribution.ts | 38 +- .../terminal/browser/terminalActions.ts | 32 +- .../terminal/browser/terminalConfigHelper.ts | 2 +- .../browser/terminalProcessManager.ts | 2 +- .../contrib/terminal/common/terminal.ts | 1 + .../update/browser/releaseNotesEditor.ts | 6 +- .../contrib/update/browser/update.ts | 16 +- .../contrib/update/electron-browser/update.ts | 2 +- .../contrib/url/common/externalUriResolver.ts | 26 + .../contrib/url/common/trustedDomains.ts | 106 + .../trustedDomainsFileSystemProvider.ts | 126 ++ .../url/common/trustedDomainsValidator.ts | 147 ++ .../contrib/url/common/url.contribution.ts | 260 +-- .../userData/browser/media/sync-push-dark.svg | 3 + .../browser/media/sync-push-light.svg | 3 + .../userData/browser/userData.contribution.ts | 250 +++ .../contrib/watermark/browser/watermark.ts | 2 +- .../webview/browser/webview.contribution.ts | 4 +- .../contrib/webview/browser/webviewEditor.ts | 23 +- .../webview/browser/webviewEditorInput.ts | 10 +- .../browser/webviewEditorInputFactory.ts | 10 +- .../webview/browser/webviewEditorService.ts | 38 +- .../contrib/webview/browser/webviewElement.ts | 27 +- .../electron-browser/pre/electron-index.js | 9 - .../gettingStarted.contribution.ts | 0 .../gettingStarted.ts | 23 +- .../telemetryOptOut.ts | 15 +- .../page/browser/vs_code_welcome_page.ts | 3 - .../welcome/page/browser/welcomePage.ts | 5 +- .../browser/editor/editorWalkThrough.ts | 6 +- ...rough.md => vs_code_editor_walkthrough.ts} | 102 +- .../common/walkThroughContentProvider.ts | 123 +- .../electron-browser/actions/helpActions.ts | 236 --- .../actions/workspaceActions.ts | 97 + .../electron-browser/desktop.contribution.ts | 167 +- .../electron-browser/desktop.main.ts | 31 +- src/vs/workbench/electron-browser/window.ts | 9 +- .../configuration/browser/configuration.ts | 2 +- .../configuration/common/jsonEditing.ts | 2 +- .../common/jsonEditingService.ts | 20 +- .../node/configurationExportHelper.ts | 2 +- .../configurationService.test.ts | 20 +- .../credentials/browser/credentialsService.ts | 2 + .../dialogs/browser/dialogService.ts | 26 +- .../dialogs/browser/remoteFileDialog.ts | 55 +- .../dialogs/electron-browser/dialogService.ts | 74 +- .../environment/browser/environmentService.ts | 28 +- .../environment/common/environmentService.ts | 1 + .../environment/node/environmentService.ts | 6 + .../common/extensionEnablementService.ts | 4 +- .../common/extensionManagementService.ts | 2 +- .../extensionManagementServerService.ts | 4 +- .../extensions/browser/extensionService.ts | 2 +- .../browser/webWorkerExtensionHostStarter.ts | 5 +- .../common/abstractExtensionService.ts | 2 +- .../extensions/common/extensionsRegistry.ts | 5 + .../extensions/common/extensionsUtil.ts | 2 +- .../extensions/common/remoteConsoleUtil.ts | 32 + .../common/remoteExtensionHostClient.ts | 5 +- .../extensions/common/staticExtensions.ts | 3 +- .../cachedExtensionScanner.ts | 7 +- .../electron-browser/extensionHost.ts | 18 +- .../electron-browser/extensionService.ts | 2 +- .../remoteExtensionManagementIpc.ts | 2 +- .../node/extensionHostProcessSetup.ts | 2 +- .../extensions/worker/extHost.services.ts | 4 +- .../integrity/node/integrityService.ts | 2 +- .../preferences/browser/preferencesService.ts | 2 +- .../remote/browser/remoteAgentServiceImpl.ts | 2 +- .../remoteAgentServiceImpl.ts | 2 +- .../services/remote/node/tunnelService.ts | 2 +- .../services/search/common/search.ts | 12 +- .../search/node/ripgrepTextSearchEngine.ts | 17 +- .../search/test/common/search.test.ts | 26 +- .../test/node/textSearch.integrationTest.ts | 23 +- .../telemetry/browser/telemetryService.ts | 2 +- .../electron-browser/telemetryService.ts | 2 +- .../textfile/common/textFileService.ts | 45 +- .../common/textResourcePropertiesService.ts | 6 +- .../services/textfile/node/textFileService.ts | 2 +- .../url/electron-browser/urlService.ts | 2 +- .../common/inMemoryUserDataProvider.ts | 2 +- .../userData/common/settingsMergeService.ts | 206 ++ .../userDataSync/common/userDataSyncStores.ts | 90 + .../browser/workspaceEditingService.ts | 2 +- .../workspace/browser/workspacesService.ts | 27 + .../electron-browser/workspacesService.ts | 3 + .../test/contrib/linkProtection.test.ts | 8 +- .../api/extHostMessagerService.test.ts | 4 + .../workbench/test/workbenchTestServices.ts | 16 +- src/vs/workbench/workbench.common.main.ts | 25 +- src/vs/workbench/workbench.desktop.main.ts | 16 +- src/vs/workbench/workbench.web.api.ts | 62 +- src/vs/workbench/workbench.web.main.ts | 5 +- test/automation/src/keybindings.ts | 2 +- test/electron/index.js | 15 +- yarn.lock | 134 +- 378 files changed, 8469 insertions(+), 5734 deletions(-) create mode 100644 extensions/image-preview/icon.png create mode 100644 extensions/image-preview/icon.svg delete mode 100644 extensions/markdown-language-features/src/docIndex.ts create mode 100644 src/vs/base/browser/ui/codiconLabel/codicon/codicon-animations.css create mode 100644 src/vs/base/browser/ui/codiconLabel/codicon/codicon.css create mode 100644 src/vs/base/browser/ui/codiconLabel/codicon/codicon.ttf create mode 100644 src/vs/base/browser/ui/codiconLabel/codiconLabel.mock.ts create mode 100644 src/vs/base/browser/ui/codiconLabel/codiconLabel.ts delete mode 100644 src/vs/base/parts/tree/browser/collapse-all-dark.svg delete mode 100644 src/vs/base/parts/tree/browser/collapse-all-hc.svg delete mode 100644 src/vs/base/parts/tree/browser/collapse-all-light.svg rename src/vs/platform/backup/{common => electron-main}/backup.ts (71%) create mode 100644 src/vs/platform/backup/node/backup.ts rename src/vs/platform/dialogs/{node => electron-browser}/dialogIpc.ts (58%) create mode 100644 src/vs/platform/electron/electron-browser/electronService.ts create mode 100644 src/vs/platform/electron/electron-main/electronMainService.ts create mode 100644 src/vs/platform/electron/node/electron.ts rename src/vs/platform/issue/{node => electron-main}/issueIpc.ts (100%) rename src/vs/platform/issue/electron-main/{issueService.ts => issueMainService.ts} (84%) rename src/vs/platform/launch/common/{launchService.ts => launch.ts} (99%) rename src/vs/platform/lifecycle/electron-main/{lifecycleMain.ts => lifecycleMainService.ts} (96%) rename src/vs/platform/menubar/electron-main/{menubarService.ts => menubarMainService.ts} (94%) delete mode 100644 src/vs/platform/product/browser/product.ts create mode 100644 src/vs/platform/product/common/productService.ts delete mode 100644 src/vs/platform/product/node/package.ts delete mode 100644 src/vs/platform/product/node/product.ts rename src/vs/platform/request/electron-main/{requestService.ts => requestMainService.ts} (94%) create mode 100644 src/vs/platform/userDataSync/common/settingsSync.ts create mode 100644 src/vs/platform/userDataSync/common/userDataSync.ts create mode 100644 src/vs/platform/userDataSync/common/userDataSyncService.ts create mode 100644 src/vs/platform/userDataSync/common/userDataSyncStoreService.ts rename src/vs/platform/workspaces/{node => electron-main}/workspacesIpc.ts (88%) create mode 100644 src/vs/workbench/api/browser/mainThreadUserData.ts create mode 100644 src/vs/workbench/api/common/extHostUserData.ts create mode 100644 src/vs/workbench/browser/actions/helpActions.ts delete mode 100644 src/vs/workbench/browser/parts/panel/media/chevron-down-dark.svg delete mode 100644 src/vs/workbench/browser/parts/panel/media/chevron-down-hc.svg delete mode 100644 src/vs/workbench/browser/parts/panel/media/chevron-down-light.svg delete mode 100644 src/vs/workbench/browser/parts/panel/media/chevron-up-dark.svg delete mode 100644 src/vs/workbench/browser/parts/panel/media/chevron-up-hc.svg delete mode 100644 src/vs/workbench/browser/parts/panel/media/chevron-up-light.svg delete mode 100644 src/vs/workbench/browser/parts/panel/media/close-dark.svg delete mode 100644 src/vs/workbench/browser/parts/panel/media/close-hc.svg delete mode 100644 src/vs/workbench/browser/parts/panel/media/close-light.svg create mode 100644 src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts create mode 100644 src/vs/workbench/contrib/debug/browser/debugCallStackContribution.ts delete mode 100644 src/vs/workbench/contrib/debug/browser/debugEditorModelManager.ts delete mode 100644 src/vs/workbench/contrib/debug/browser/media/clear-dark.svg delete mode 100644 src/vs/workbench/contrib/debug/browser/media/clear-hc.svg delete mode 100644 src/vs/workbench/contrib/debug/browser/media/clear-light.svg delete mode 100644 src/vs/workbench/contrib/markers/browser/media/exclude-settings-dark.svg delete mode 100644 src/vs/workbench/contrib/markers/browser/media/exclude-settings-hc.svg delete mode 100644 src/vs/workbench/contrib/markers/browser/media/exclude-settings-light.svg delete mode 100644 src/vs/workbench/contrib/output/browser/media/clear-dark.svg delete mode 100644 src/vs/workbench/contrib/output/browser/media/clear-hc.svg delete mode 100644 src/vs/workbench/contrib/output/browser/media/clear-light.svg delete mode 100644 src/vs/workbench/contrib/output/browser/media/locked-dark.svg delete mode 100644 src/vs/workbench/contrib/output/browser/media/locked-hc.svg delete mode 100644 src/vs/workbench/contrib/output/browser/media/locked-light.svg delete mode 100644 src/vs/workbench/contrib/output/browser/media/open-file-dark.svg delete mode 100644 src/vs/workbench/contrib/output/browser/media/open-file-hc.svg delete mode 100644 src/vs/workbench/contrib/output/browser/media/open-file-light.svg delete mode 100644 src/vs/workbench/contrib/output/browser/media/output.css delete mode 100644 src/vs/workbench/contrib/output/browser/media/unlocked-dark.svg delete mode 100644 src/vs/workbench/contrib/output/browser/media/unlocked-hc.svg delete mode 100644 src/vs/workbench/contrib/output/browser/media/unlocked-light.svg rename src/vs/workbench/contrib/surveys/{electron-browser => browser}/languageSurveys.contribution.ts (87%) rename src/vs/workbench/contrib/surveys/{electron-browser => browser}/nps.contribution.ts (81%) delete mode 100644 src/vs/workbench/contrib/terminal/browser/media/kill-dark.svg delete mode 100644 src/vs/workbench/contrib/terminal/browser/media/kill-hc.svg delete mode 100644 src/vs/workbench/contrib/terminal/browser/media/kill-light.svg delete mode 100644 src/vs/workbench/contrib/terminal/browser/media/new-dark.svg delete mode 100644 src/vs/workbench/contrib/terminal/browser/media/new-hc.svg delete mode 100644 src/vs/workbench/contrib/terminal/browser/media/new-light.svg delete mode 100644 src/vs/workbench/contrib/terminal/browser/media/split-editor-horizontal-dark.svg delete mode 100644 src/vs/workbench/contrib/terminal/browser/media/split-editor-horizontal-hc.svg delete mode 100644 src/vs/workbench/contrib/terminal/browser/media/split-editor-horizontal-light.svg delete mode 100644 src/vs/workbench/contrib/terminal/browser/media/split-editor-vertical-dark.svg delete mode 100644 src/vs/workbench/contrib/terminal/browser/media/split-editor-vertical-hc.svg delete mode 100644 src/vs/workbench/contrib/terminal/browser/media/split-editor-vertical-light.svg create mode 100644 src/vs/workbench/contrib/url/common/externalUriResolver.ts create mode 100644 src/vs/workbench/contrib/url/common/trustedDomains.ts create mode 100644 src/vs/workbench/contrib/url/common/trustedDomainsFileSystemProvider.ts create mode 100644 src/vs/workbench/contrib/url/common/trustedDomainsValidator.ts create mode 100644 src/vs/workbench/contrib/userData/browser/media/sync-push-dark.svg create mode 100644 src/vs/workbench/contrib/userData/browser/media/sync-push-light.svg create mode 100644 src/vs/workbench/contrib/userData/browser/userData.contribution.ts rename src/vs/workbench/contrib/welcome/gettingStarted/{electron-browser => browser}/gettingStarted.contribution.ts (100%) rename src/vs/workbench/contrib/welcome/gettingStarted/{electron-browser => browser}/gettingStarted.ts (87%) rename src/vs/workbench/contrib/welcome/gettingStarted/{electron-browser => browser}/telemetryOptOut.ts (90%) rename src/vs/workbench/contrib/welcome/walkThrough/browser/editor/{vs_code_editor_walkthrough.md => vs_code_editor_walkthrough.ts} (83%) delete mode 100644 src/vs/workbench/electron-browser/actions/helpActions.ts create mode 100644 src/vs/workbench/electron-browser/actions/workspaceActions.ts rename src/vs/{platform => workbench/services}/dialogs/browser/dialogService.ts (82%) create mode 100644 src/vs/workbench/services/extensions/common/remoteConsoleUtil.ts create mode 100644 src/vs/workbench/services/userData/common/settingsMergeService.ts create mode 100644 src/vs/workbench/services/userDataSync/common/userDataSyncStores.ts create mode 100644 src/vs/workbench/services/workspace/browser/workspacesService.ts rename src/vs/{platform/workspaces => workbench/services/workspace}/electron-browser/workspacesService.ts (91%) diff --git a/.yarnrc b/.yarnrc index ff946c7a2503..e49da7f447d5 100644 --- a/.yarnrc +++ b/.yarnrc @@ -1,3 +1,3 @@ disturl "https://atom.io/download/electron" -target "4.2.10" +target "6.0.9" runtime "electron" diff --git a/build/azure-pipelines/exploration-build.yml b/build/azure-pipelines/exploration-build.yml index 39a8f23b2621..305852528bcf 100644 --- a/build/azure-pipelines/exploration-build.yml +++ b/build/azure-pipelines/exploration-build.yml @@ -1,12 +1,8 @@ pool: vmImage: 'Ubuntu-16.04' -trigger: - branches: - include: ['master'] -pr: - branches: - include: ['master'] +trigger: none +pr: none steps: - task: NodeTool@0 diff --git a/build/azure-pipelines/linux/product-build-linux.yml b/build/azure-pipelines/linux/product-build-linux.yml index 0d01ba8a6083..0d0b26087577 100644 --- a/build/azure-pipelines/linux/product-build-linux.yml +++ b/build/azure-pipelines/linux/product-build-linux.yml @@ -100,7 +100,7 @@ steps: - script: | set -e - DISPLAY=:10 ./scripts/test.sh --build --tfs "Unit Tests" + DISPLAY=:10 ./scripts/test.sh --build --tfs --no-sandbox "Unit Tests" displayName: Run unit tests condition: and(succeeded(), eq(variables['VSCODE_STEP_ON_IT'], 'false')) diff --git a/build/gulpfile.editor.js b/build/gulpfile.editor.js index 4ecced99f340..0ca58327e7f4 100644 --- a/build/gulpfile.editor.js +++ b/build/gulpfile.editor.js @@ -45,6 +45,7 @@ var editorResources = [ '!out-build/vs/base/browser/ui/splitview/**/*', '!out-build/vs/base/browser/ui/toolbar/**/*', '!out-build/vs/base/browser/ui/octiconLabel/**/*', + '!out-build/vs/base/browser/ui/codiconLabel/**/*', '!out-build/vs/workbench/**', '!**/test/**' ]; @@ -91,6 +92,7 @@ const extractEditorSrcTask = task.define('extract-editor-src', () => { ], redirects: { 'vs/base/browser/ui/octiconLabel/octiconLabel': 'vs/base/browser/ui/octiconLabel/octiconLabel.mock', + 'vs/base/browser/ui/codiconLabel/codiconLabel': 'vs/base/browser/ui/codiconLabel/codiconLabel.mock', }, shakeLevel: 2, // 0-Files, 1-InnerFile, 2-ClassMembers importIgnorePattern: /(^vs\/css!)|(promise-polyfill\/polyfill)/, diff --git a/build/gulpfile.hygiene.js b/build/gulpfile.hygiene.js index dc2163c1c0d0..bc6049809fe2 100644 --- a/build/gulpfile.hygiene.js +++ b/build/gulpfile.hygiene.js @@ -72,6 +72,7 @@ const indentationFilter = [ // except multiple specific folders '!**/octicons/**', + '!**/codicon/**', '!**/fixtures/**', '!**/lib/**', '!extensions/**/out/**', @@ -381,8 +382,6 @@ function hygiene(some) { const sqlTsl = es.through(function (file) { //TODO restore const contents = file.contents.toString('utf8'); sqlTsLinter.lint(file.relative, contents, tslintSqlConfiguration.results); - - this.emit('data', file); }); const productJsonFilter = filter('product.json', { restore: true }); diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index eaeaad94e9e1..638e0ae8e743 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -79,6 +79,7 @@ const vscodeResources = [ 'out-build/vs/base/node/languagePacks.js', 'out-build/vs/base/node/{stdForkStart.js,terminateProcess.sh,cpuUsage.sh,ps.sh}', 'out-build/vs/base/browser/ui/octiconLabel/octicons/**', + 'out-build/vs/base/browser/ui/codiconLabel/codicon/**', 'out-build/vs/workbench/browser/media/*-theme.css', 'out-build/vs/workbench/contrib/debug/**/*.json', 'out-build/vs/workbench/contrib/externalTerminal/**/*.scpt', @@ -86,7 +87,6 @@ const vscodeResources = [ 'out-build/vs/workbench/contrib/webview/electron-browser/pre/*.js', 'out-build/vs/**/markdown.css', 'out-build/vs/workbench/contrib/tasks/**/*.json', - 'out-build/vs/workbench/contrib/welcome/walkThrough/**/*.md', 'out-build/vs/platform/files/**/*.exe', 'out-build/vs/platform/files/**/*.md', 'out-build/vs/code/electron-browser/workbench/**', diff --git a/build/gulpfile.vscode.web.js b/build/gulpfile.vscode.web.js index 7978d3520e00..e6549798443b 100644 --- a/build/gulpfile.vscode.web.js +++ b/build/gulpfile.vscode.web.js @@ -6,150 +6,11 @@ 'use strict'; const gulp = require('gulp'); -const path = require('path'); -const es = require('event-stream'); -const util = require('./lib/util'); -const task = require('./lib/task'); -const common = require('./lib/optimize'); -const product = require('../product.json'); -const rename = require('gulp-rename'); -const filter = require('gulp-filter'); -const json = require('gulp-json-editor'); -const _ = require('underscore'); -const deps = require('./dependencies'); -const vfs = require('vinyl-fs'); -const packageJson = require('../package.json'); -const { compileBuildTask } = require('./gulpfile.compile'); -const REPO_ROOT = path.dirname(__dirname); -const commit = util.getVersion(REPO_ROOT); -const BUILD_ROOT = path.dirname(REPO_ROOT); -const WEB_FOLDER = path.join(REPO_ROOT, 'remote', 'web'); +const noop = () => { return Promise.resolve(); }; -const productionDependencies = deps.getProductionDependencies(WEB_FOLDER); - -const nodeModules = Object.keys(product.dependencies || {}) - .concat(_.uniq(productionDependencies.map(d => d.name))); - -const vscodeWebResources = [ - - // Workbench - 'out-build/vs/{base,platform,editor,workbench}/**/*.{svg,png,html}', - 'out-build/vs/base/browser/ui/octiconLabel/octicons/**', - 'out-build/vs/**/markdown.css', - - // Webview - 'out-build/vs/workbench/contrib/webview/browser/pre/*.js', - - // Extension Worker - 'out-build/vs/workbench/services/extensions/worker/extensionHostWorkerMain.js', - - // Excludes - '!out-build/vs/**/{node,electron-browser,electron-main}/**', - '!out-build/vs/editor/standalone/**', - '!out-build/vs/workbench/**/*-tb.png', - '!**/test/**' -]; - -const buildfile = require('../src/buildfile'); - -const vscodeWebEntryPoints = [ - buildfile.workbenchWeb, - buildfile.serviceWorker, - buildfile.workerExtensionHost, - buildfile.keyboardMaps, - buildfile.base -]; - -const optimizeVSCodeWebTask = task.define('optimize-vscode-web', task.series( - util.rimraf('out-vscode-web'), - common.optimizeTask({ - src: 'out-build', - entryPoints: _.flatten(vscodeWebEntryPoints), - otherSources: [], - resources: vscodeWebResources, - loaderConfig: common.loaderConfig(nodeModules), - out: 'out-vscode-web', - bundleInfo: undefined - }) -)); - -const minifyVSCodeWebTask = task.define('minify-vscode-web', task.series( - optimizeVSCodeWebTask, - util.rimraf('out-vscode-web-min'), - common.minifyTask('out-vscode-web', `https://ticino.blob.core.windows.net/sourcemaps/${commit}/core`) -)); -gulp.task(minifyVSCodeWebTask); - -function packageTask(sourceFolderName, destinationFolderName) { - const destination = path.join(BUILD_ROOT, destinationFolderName); - - return () => { - const src = gulp.src(sourceFolderName + '/**', { base: '.' }) - .pipe(rename(function (path) { path.dirname = path.dirname.replace(new RegExp('^' + sourceFolderName), 'out'); })) - .pipe(filter(['**', '!**/*.js.map'])); - - const sources = es.merge(src); - - let version = packageJson.version; - const quality = product.quality; - - if (quality && quality !== 'stable') { - version += '-' + quality; - } - - const name = product.nameShort; - const packageJsonStream = gulp.src(['remote/web/package.json'], { base: 'remote/web' }) - .pipe(json({ name, version })); - - const date = new Date().toISOString(); - - const productJsonStream = gulp.src(['product.json'], { base: '.' }) - .pipe(json({ commit, date })); - - const license = gulp.src(['remote/LICENSE'], { base: 'remote' }); - - const dependenciesSrc = _.flatten(productionDependencies.map(d => path.relative(REPO_ROOT, d.path)).map(d => [`${d}/**`, `!${d}/**/{test,tests}/**`, `!${d}/.bin/**`])); - - const deps = gulp.src(dependenciesSrc, { base: 'remote/web', dot: true }) - .pipe(filter(['**', '!**/package-lock.json'])) - .pipe(util.cleanNodeModules(path.join(__dirname, '.nativeignore'))); - - const favicon = gulp.src('resources/server/favicon.ico', { base: 'resources/server' }); - - let all = es.merge( - packageJsonStream, - productJsonStream, - license, - sources, - deps, - favicon - ); - - let result = all - .pipe(util.skipDirectories()) - .pipe(util.fixWin32DirectoryPermissions()); - - return result.pipe(vfs.dest(destination)); - }; -} - -const dashed = (str) => (str ? `-${str}` : ``); - -['', 'min'].forEach(minified => { - const sourceFolderName = `out-vscode-web${dashed(minified)}`; - const destinationFolderName = `vscode-web`; - - const vscodeWebTaskCI = task.define(`vscode-web${dashed(minified)}-ci`, task.series( - minified ? minifyVSCodeWebTask : optimizeVSCodeWebTask, - util.rimraf(path.join(BUILD_ROOT, destinationFolderName)), - packageTask(sourceFolderName, destinationFolderName) - )); - gulp.task(vscodeWebTaskCI); - - const vscodeWebTask = task.define(`vscode-web${dashed(minified)}`, task.series( - compileBuildTask, - vscodeWebTaskCI - )); - gulp.task(vscodeWebTask); -}); +gulp.task('minify-vscode-web', noop); +gulp.task('vscode-web', noop); +gulp.task('vscode-web-min', noop); +gulp.task('vscode-web-ci', noop); +gulp.task('vscode-web-min-ci', noop); diff --git a/build/lib/i18n.js b/build/lib/i18n.js index d65b4a7ce485..4ee4e9c525d2 100644 --- a/build/lib/i18n.js +++ b/build/lib/i18n.js @@ -1086,7 +1086,7 @@ function prepareI18nPackFiles(externalExtensions, resultingTranslationPaths, pse resultingTranslationPaths.push({ id: 'vscode', resourceName: 'main.i18n.json' }); this.queue(translatedMainFile); for (let extension in extensionsPacks) { - const translatedExtFile = createI18nFile(`.build/extensions/${extension}`, extensionsPacks[extension]); + const translatedExtFile = createI18nFile(`extensions/${extension}`, extensionsPacks[extension]); this.queue(translatedExtFile); const externalExtensionId = externalExtensions[extension]; if (externalExtensionId) { diff --git a/build/lib/i18n.resources.json b/build/lib/i18n.resources.json index da50b1eb2d86..e8301efbeca0 100644 --- a/build/lib/i18n.resources.json +++ b/build/lib/i18n.resources.json @@ -106,6 +106,10 @@ "name": "vs/workbench/contrib/quickopen", "project": "vscode-workbench" }, + { + "name": "vs/workbench/contrib/userData", + "project": "vscode-workbench" + }, { "name": "vs/workbench/contrib/remote", "project": "vscode-workbench" @@ -277,6 +281,10 @@ { "name": "vs/workbench/services/notification", "project": "vscode-workbench" + }, + { + "name": "vs/workbench/services/userData", + "project": "vscode-workbench" } ] } diff --git a/build/lib/i18n.ts b/build/lib/i18n.ts index c3d7228f42c2..60997611eaf3 100644 --- a/build/lib/i18n.ts +++ b/build/lib/i18n.ts @@ -1253,7 +1253,7 @@ export function prepareI18nPackFiles(externalExtensions: Map, resultingT this.queue(translatedMainFile); for (let extension in extensionsPacks) { - const translatedExtFile = createI18nFile(`.build/extensions/${extension}`, extensionsPacks[extension]); + const translatedExtFile = createI18nFile(`extensions/${extension}`, extensionsPacks[extension]); this.queue(translatedExtFile); const externalExtensionId = externalExtensions[extension]; diff --git a/build/npm/preinstall.js b/build/npm/preinstall.js index 1cbc34b916bf..6b1f5982b967 100644 --- a/build/npm/preinstall.js +++ b/build/npm/preinstall.js @@ -31,4 +31,4 @@ if (!/yarn\.js$|yarnpkg$/.test(process.env['npm_execpath'])) { if (err) { console.error(''); process.exit(1); -} \ No newline at end of file +} diff --git a/cglicenses.json b/cglicenses.json index df57f699bd20..1e9287cbaf28 100644 --- a/cglicenses.json +++ b/cglicenses.json @@ -46,58 +46,6 @@ "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE." ] }, - { - // Reason: The npm module does not contain a repository field. - // waiting for https://github.com/xtermjs/xterm.js/issues/2395 - "name": "xterm-addon-search", - "fullLicenseText": [ - "Copyright (c) 2017, The xterm.js authors (https://github.com/xtermjs/xterm.js)", - "", - "Permission is hereby granted, free of charge, to any person obtaining a copy", - "of this software and associated documentation files (the \"Software\"), to deal", - "in the Software without restriction, including without limitation the rights", - "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", - "copies of the Software, and to permit persons to whom the Software is", - "furnished to do so, subject to the following conditions:", - "", - "The above copyright notice and this permission notice shall be included in", - "all copies or substantial portions of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", - "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", - "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", - "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", - "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", - "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN", - "THE SOFTWARE." - ] - }, - { - // Reason: The npm module does not contain a repository field. - // waiting for https://github.com/xtermjs/xterm.js/issues/2395 - "name": "xterm-addon-web-links", - "fullLicenseText": [ - "Copyright (c) 2017, The xterm.js authors (https://github.com/xtermjs/xterm.js)", - "", - "Permission is hereby granted, free of charge, to any person obtaining a copy", - "of this software and associated documentation files (the \"Software\"), to deal", - "in the Software without restriction, including without limitation the rights", - "to use, copy, modify, merge, publish, distribute, sublicense, and/or sell", - "copies of the Software, and to permit persons to whom the Software is", - "furnished to do so, subject to the following conditions:", - "", - "The above copyright notice and this permission notice shall be included in", - "all copies or substantial portions of the Software.", - "", - "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR", - "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,", - "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE", - "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER", - "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,", - "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN", - "THE SOFTWARE." - ] - }, { // Reason: The license at https://git.coolaj86.com/coolaj86/atob.js/src/branch/master/LICENSE // cannot be found by the OSS tool automatically. diff --git a/cgmanifest.json b/cgmanifest.json index d26f37b62040..8e8fc3484ff7 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "chromium", "repositoryUrl": "https://chromium.googlesource.com/chromium/src", - "commitHash": "c6a08e5368de4352903e702cde750b33239a50ab" + "commitHash": "91f08db83c2ce8c722ddf0911ead8f7c473bedfa" } }, "licenseDetail": [ @@ -40,7 +40,7 @@ "SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ], "isOnlyProductionDependency": true, - "version": "69.0.3497.128" + "version": "76.0.3809.146" }, { "component": { @@ -48,11 +48,11 @@ "git": { "name": "nodejs", "repositoryUrl": "https://github.com/nodejs/node", - "commitHash": "8c70b2084ce5f76ea1e3b3c4ccdeee4483fe338b" + "commitHash": "64219741218aa87e259cf8257596073b8e747f0a" } }, "isOnlyProductionDependency": true, - "version": "10.11.0" + "version": "12.4.0" }, { "component": { @@ -60,12 +60,12 @@ "git": { "name": "electron", "repositoryUrl": "https://github.com/electron/electron", - "commitHash": "4e4c7527c63fcf27dffaeb58bde996b8d859c0ed" + "commitHash": "407747b48c47cdeed156a73dde1c47609470c95a" } }, "isOnlyProductionDependency": true, "license": "MIT", - "version": "4.2.10" + "version": "6.0.9" }, { "component": { diff --git a/extensions/git/src/decorationProvider.ts b/extensions/git/src/decorationProvider.ts index e022411cc92c..1f0a02567ae0 100644 --- a/extensions/git/src/decorationProvider.ts +++ b/extensions/git/src/decorationProvider.ts @@ -9,7 +9,7 @@ import { Repository, GitResourceGroup } from './repository'; import { Model } from './model'; import { debounce } from './decorators'; import { filterEvent, dispose, anyEvent, fireEvent } from './util'; -import { GitErrorCodes } from './api/git'; +import { GitErrorCodes, Status } from './api/git'; type Callback = { resolve: (status: boolean) => void, reject: (err: any) => void }; @@ -122,12 +122,18 @@ class GitDecorationProvider implements DecorationProvider { } private collectDecorationData(group: GitResourceGroup, bucket: Map): void { - group.resourceStates.forEach(r => { - if (r.resourceDecoration) { + for (const r of group.resourceStates) { + const decoration = r.resourceDecoration; + + if (decoration) { // not deleted and has a decoration - bucket.set(r.original.toString(), r.resourceDecoration); + bucket.set(r.original.toString(), decoration); + + if (r.type === Status.INDEX_RENAMED) { + bucket.set(r.resourceUri.toString(), decoration); + } } - }); + } } private collectSubmoduleDecorationData(bucket: Map): void { diff --git a/extensions/image-preview/icon.png b/extensions/image-preview/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..64dcf7d463c2678de271c4bae155092982e9bd50 GIT binary patch literal 1979 zcmbtVX;4#F6ut>TmI@IRWRF!`DuPf(D_VsXESnLG?1Bj@s0gy8s3=(kg@R70r3xqn zoe^X+2}Qwx1ZWy@VHstK$daV22Ez+QAdv9V2X>~`;YY1M?woVx-h1x(?tI@lY2JHX zb+k>i0RTGgZY~4>P>72HnoAH999I;M7_At$Kr#U38|N1)^v)&-0F5Yjmv4N|(gIJ;PL8rlxVHDi0>aZa zE<`Jhz2~cD+b$jNZ;bAHm@ttlZf(tX+}zmM*l`edZD?TNb}oZKP4wwl?JdbD zsAwK8)K{zciicHnv-ORn%!cQFfY;E&ArG@~yAtJOJEiS)9jo)T(MBd=>LhvSc2hsJ zkV(ZqnD8J$2K5p?k*WJg#&g+PhB?IzJ;pMii&M`cflaj0tJzL*o*GGhi(9^iJ-N1C zqMjn$P2KM?Q*A^oI@vbpB%H3IdXciM8^(yePa0*;lq~zx&KTWFhwKof2#w_}HY%m0 zqqLSQU+=|uGYQ=a+u!6RobHWM)rOq^9AOT-t5TiV&u1@B$GLGyyc;8qn+Bg92m&p` zuW;rOBh?3p41clyqP+5~!HO#UaLx?n6?b;>L=bk;%#(}?&{1_+!xrsRk@sD{vkyiu zaMSuAI=BKFE)K=rg^q@*e-u;ML(^(6{YUeM!yR8M+yhVx3CFVlF2OER`mkutp~y>T zcd2?XqUuoMbYW$D_zur=Nc{K9AE=^<0tDu>JI7Z-wsV-^wLrjezP!N(HLoGI+>`#c zH950cdr`C>B>G2p&5(v@24?TDT992C-;*w$nQJwSF_V=~bhfyWQ6mbtijaO(&eL4H zrZSEKR5NQJW9UR4Yb-PS@JxNcv!a3L)eC!Q7!&8thfoz@vckSL>{|z z97{a~mvTVaR7iBhXxPc9m;SUDP?R8^YwG{tT^XO&a*$sPAh)WIRFArE5~b?5@V+T^?R|P9g;zlg{=RrKIZ|sKn(fztcv7tmXjz(sM;z)|;e!#;!i`~q s0`Ei$jQmXZ6DJfxm-mMq;VJ=Ei{i)j|LLooAJf2n_g)v~x1^-M04RAv?EnA( literal 0 HcmV?d00001 diff --git a/extensions/image-preview/icon.svg b/extensions/image-preview/icon.svg new file mode 100644 index 000000000000..0eb0f4c103b1 --- /dev/null +++ b/extensions/image-preview/icon.svg @@ -0,0 +1,18 @@ + + + + + + + + + diff --git a/extensions/image-preview/media/main.js b/extensions/image-preview/media/main.js index f2bb9e4eb726..70ed69aad87e 100644 --- a/extensions/image-preview/media/main.js +++ b/extensions/image-preview/media/main.js @@ -58,7 +58,8 @@ 20 ]; - const isMac = getSettings().isMac; + const settings = getSettings(); + const isMac = settings.isMac; const vscode = acquireVsCodeApi(); @@ -71,7 +72,7 @@ // Elements const container = /** @type {HTMLElement} */(document.querySelector('body')); - const image = document.querySelector('img'); + const image = document.createElement('img'); function updateScale(newScale) { if (!image || !image.parentElement) { @@ -248,6 +249,9 @@ } }); + image.src = decodeURI(settings.src); + document.body.append(image); + window.addEventListener('message', e => { switch (e.data.type) { case 'setScale': diff --git a/extensions/image-preview/package.json b/extensions/image-preview/package.json index aad1416c7b28..b7f2aeb6b8e1 100644 --- a/extensions/image-preview/package.json +++ b/extensions/image-preview/package.json @@ -4,6 +4,7 @@ "description": "%description%", "version": "1.0.0", "publisher": "vscode", + "icon": "icon.png", "enableProposedApi": true, "license": "MIT", "aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217", @@ -24,7 +25,8 @@ "displayName": "%webviewEditors.displayName%", "selector": [ { - "filenamePattern": "*.{jpg,jpe,jpeg,png,bmp,gif,ico,tga,tif,tiff,webp}" + "filenamePattern": "*.{jpg,jpe,jpeg,png,bmp,gif,ico,tga,tif,tiff,webp}", + "mime": "image/*" } ] } diff --git a/extensions/image-preview/src/preview.ts b/extensions/image-preview/src/preview.ts index 97d4d7c29b47..daf7bc520deb 100644 --- a/extensions/image-preview/src/preview.ts +++ b/extensions/image-preview/src/preview.ts @@ -80,7 +80,8 @@ export class Preview extends Disposable { private getWebiewContents(webviewEditor: vscode.WebviewEditor, resource: vscode.Uri): string { const settings = { - isMac: process.platform === 'darwin' + isMac: process.platform === 'darwin', + src: this.getResourcePath(webviewEditor, resource) }; return /* html */` @@ -95,12 +96,19 @@ export class Preview extends Disposable { - `; } + private getResourcePath(webviewEditor: vscode.WebviewEditor, resource: vscode.Uri) { + if (resource.scheme === 'data') { + return encodeURI(resource.toString(true)); + } + + return encodeURI(webviewEditor.webview.asWebviewUri(resource).toString(true)); + } + private extensionResource(path: string) { return this.webviewEditor.webview.asWebviewUri(this.extensionRoot.with({ path: this.extensionRoot.path + path diff --git a/extensions/image-preview/src/typings/ref.d.ts b/extensions/image-preview/src/typings/ref.d.ts index 954bab971e33..37d9f00e1106 100644 --- a/extensions/image-preview/src/typings/ref.d.ts +++ b/extensions/image-preview/src/typings/ref.d.ts @@ -1,6 +1,6 @@ /*--------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. + * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ /// diff --git a/extensions/json/syntaxes/JSON.tmLanguage.json b/extensions/json/syntaxes/JSON.tmLanguage.json index 910045be39ee..d296aac33ebc 100644 --- a/extensions/json/syntaxes/JSON.tmLanguage.json +++ b/extensions/json/syntaxes/JSON.tmLanguage.json @@ -5,7 +5,7 @@ "Once accepted there, we are happy to receive an update request." ], "version": "https://github.com/Microsoft/vscode-JSON.tmLanguage/commit/9bd83f1c252b375e957203f21793316203f61f70", - "name": "JSON (Javascript Next)", + "name": "JSON (JavaScript Next)", "scopeName": "source.json", "patterns": [ { @@ -210,4 +210,4 @@ ] } } -} \ No newline at end of file +} diff --git a/extensions/markdown-language-features/src/docIndex.ts b/extensions/markdown-language-features/src/docIndex.ts deleted file mode 100644 index 727682111016..000000000000 --- a/extensions/markdown-language-features/src/docIndex.ts +++ /dev/null @@ -1,50 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as vscode from 'vscode'; -import { Disposable } from './util/dispose'; - - -export class DocumentIndex extends Disposable { - private readonly _uriMap = new Map(); - - constructor() { - super(); - - for (let doc of vscode.workspace.textDocuments) { - this._registerDoc(doc); - } - - this._register( - vscode.workspace.onDidOpenTextDocument((doc) => { - this._registerDoc(doc); - }) - ); - this._register( - vscode.workspace.onDidCloseTextDocument((doc) => { - this._unregisterDoc(doc.uri); - }) - ); - } - - getByUri(uri: vscode.Uri): vscode.TextDocument | undefined { - return this._uriMap.get(uri.toString()); - } - - private _registerDoc(doc: vscode.TextDocument) { - const uri = doc.uri.toString(); - if (this._uriMap.has(uri)) { - throw new Error(`The document ${uri} is already registered.`); - } - this._uriMap.set(uri, doc); - } - - private _unregisterDoc(uri: vscode.Uri) { - if (!this._uriMap.has(uri.toString())) { - throw new Error(`The document ${uri.toString()} is not registered.`); - } - this._uriMap.delete(uri.toString()); - } -} diff --git a/extensions/markdown-language-features/src/features/workspaceSymbolProvider.ts b/extensions/markdown-language-features/src/features/workspaceSymbolProvider.ts index 6a5ab7af9f93..a406711c5d8b 100644 --- a/extensions/markdown-language-features/src/features/workspaceSymbolProvider.ts +++ b/extensions/markdown-language-features/src/features/workspaceSymbolProvider.ts @@ -10,7 +10,6 @@ import { Lazy, lazy } from '../util/lazy'; import MDDocumentSymbolProvider from './documentSymbolProvider'; import { SkinnyTextDocument, SkinnyTextLine } from '../tableOfContentsProvider'; import { flatten } from '../util/arrays'; -import { DocumentIndex } from '../docIndex'; export interface WorkspaceMarkdownDocumentProvider { getAllMarkdownDocuments(): Thenable>; @@ -27,7 +26,6 @@ class VSCodeWorkspaceMarkdownDocumentProvider extends Disposable implements Work private readonly _onDidDeleteMarkdownDocumentEmitter = this._register(new vscode.EventEmitter()); private _watcher: vscode.FileSystemWatcher | undefined; - private _docIndex: DocumentIndex = this._register(new DocumentIndex()); async getAllMarkdownDocuments() { const resources = await vscode.workspace.findFiles('**/*.md', '**/node_modules/**'); @@ -83,9 +81,9 @@ class VSCodeWorkspaceMarkdownDocumentProvider extends Disposable implements Work } private async getMarkdownDocument(resource: vscode.Uri): Promise { - const existingDocument = this._docIndex.getByUri(resource); - if (existingDocument) { - return existingDocument; + const matchingDocuments = vscode.workspace.textDocuments.filter((doc) => doc.uri.toString() === resource.toString()); + if (matchingDocuments.length !== 0) { + return matchingDocuments[0]; } const bytes = await vscode.workspace.fs.readFile(resource); diff --git a/extensions/mssql/package.json b/extensions/mssql/package.json index c1720ef58250..a835b501c860 100644 --- a/extensions/mssql/package.json +++ b/extensions/mssql/package.json @@ -941,7 +941,7 @@ "figures": "^2.0.0", "find-remove": "1.2.1", "fs-extra": "^3.0.1", - "kerberos": "^1.1.2", + "kerberos": "^1.1.3", "request": "^2.88.0", "request-promise": "^4.2.2", "service-downloader": "github:anthonydresser/service-downloader#0.1.6", diff --git a/extensions/mssql/yarn.lock b/extensions/mssql/yarn.lock index 852e5ff1b992..2fffe6c23194 100644 --- a/extensions/mssql/yarn.lock +++ b/extensions/mssql/yarn.lock @@ -132,7 +132,7 @@ bcrypt-pbkdf@^1.0.0: dependencies: tweetnacl "^0.14.3" -bindings@^1.3.0: +bindings@^1.5.0: version "1.5.0" resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.5.0.tgz#10353c9e945334bc0511a6d90b38fbc7c9c504df" integrity sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ== @@ -147,6 +147,13 @@ bl@^1.0.0: readable-stream "^2.3.5" safe-buffer "^5.1.1" +bl@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-3.0.0.tgz#3611ec00579fd18561754360b21e9f784500ff88" + integrity sha512-EUAyP5UHU5hxF8BPT0LKW8gjYLhq1DQIcneOX/pL/m2Alo+OYDQAJlHq+yseMP50Os2nHXOSic6Ss3vSQeyf4A== + dependencies: + readable-stream "^3.0.1" + bluebird@^3.5.0: version "3.5.3" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.3.tgz#7d01c6f9616c9a51ab0f8c549a79dfe6ec33efa7" @@ -206,7 +213,7 @@ caseless@~0.12.0: resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw= -chownr@^1.0.1: +chownr@^1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.2.tgz#a18f1e0b269c8a6a5d3c86eb298beb14c3dd7bf6" integrity sha512-GkfeAQh+QNy3wquu9oIZr6SS5x7wGdSgNQvD10X3r+AZr1Oys22HW8kAmDMvNg2+Dm0TeGaEuO8gFwdBXxwO8A== @@ -372,7 +379,7 @@ ecc-jsbn@~0.1.1: jsbn "~0.1.0" safer-buffer "^2.1.0" -end-of-stream@^1.0.0, end-of-stream@^1.1.0: +end-of-stream@^1.0.0, end-of-stream@^1.1.0, end-of-stream@^1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.1.tgz#ed29634d19baba463b6ce6b80a37213eab71ec43" integrity sha512-1MkrZNvWTKCaigbn+W15elq2BB/L22nqrSY5DKlo3X6+vclJm8Bb5djXJBmEX6fS3+zCh/F4VBK5Z2KxJt4s2Q== @@ -738,14 +745,14 @@ jsprim@^1.2.2: json-schema "0.2.3" verror "1.10.0" -kerberos@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/kerberos/-/kerberos-1.1.2.tgz#f213dae89c57e729786664fcf99117221e18e257" - integrity sha512-N+CeRTi0f6ov85Fx+w/epVPTBy6bovoWjUCD6pvXoHWBWeqI6+ViV2psdoSc4CRtiZfJkhvYO3b47I4hJfOu7Q== +kerberos@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/kerberos/-/kerberos-1.1.3.tgz#74c5620d09289e4a53beddc2d9a93eccfebd964d" + integrity sha512-R6LdXnkSTALLnrKaByYdIc+eRNhDf+ezA83BeqHiCcKhKE9R6m1PBD579YuX9KANPAakhWHEu/u6H7ZQBc/+Wg== dependencies: - bindings "^1.3.0" - nan "^2.10.0" - prebuild-install "^5.0.0" + bindings "^1.5.0" + nan "^2.14.0" + prebuild-install "^5.3.0" lodash@^4.13.1: version "4.17.11" @@ -810,7 +817,7 @@ ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.1.tgz#30a5864eb3ebb0a66f2ebe6d727af06a09d86e0a" integrity sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg== -nan@^2.10.0: +nan@^2.14.0: version "2.14.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== @@ -864,11 +871,6 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0: dependencies: wrappy "1" -os-homedir@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" - integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= - os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" @@ -911,10 +913,10 @@ pinkie@^2.0.0: resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" integrity sha1-clVrgM+g1IqXToDnckjoDtT3+HA= -prebuild-install@^5.0.0: - version "5.3.0" - resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-5.3.0.tgz#58b4d8344e03590990931ee088dd5401b03004c8" - integrity sha512-aaLVANlj4HgZweKttFNUVNRxDukytuIuxeK2boIMHjagNJCiVKWFsKF4tCE3ql3GbrD2tExPQ7/pwtEJcHNZeg== +prebuild-install@^5.3.0: + version "5.3.2" + resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-5.3.2.tgz#6392e9541ac0b879ef0f22b3d65037417eb2035e" + integrity sha512-INDfXzTPnhT+WYQemqnAXlP7SvfiFMopMozSgXCZ+RDLb279gKfIuLk4o7PgEawLp3WrMgIYGBpkxpraROHsSA== dependencies: detect-libc "^1.0.3" expand-template "^2.0.3" @@ -925,11 +927,10 @@ prebuild-install@^5.0.0: node-abi "^2.7.0" noop-logger "^0.1.1" npmlog "^4.0.1" - os-homedir "^1.0.1" - pump "^2.0.1" + pump "^3.0.0" rc "^1.2.7" - simple-get "^2.7.0" - tar-fs "^1.13.0" + simple-get "^3.0.3" + tar-fs "^2.0.0" tunnel-agent "^0.6.0" which-pm-runs "^1.0.0" @@ -943,18 +944,10 @@ psl@^1.1.24, psl@^1.1.28: resolved "https://registry.yarnpkg.com/psl/-/psl-1.1.31.tgz#e9aa86d0101b5b105cbe93ac6b784cd547276184" integrity sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw== -pump@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/pump/-/pump-1.0.3.tgz#5dfe8311c33bbf6fc18261f9f34702c47c08a954" - integrity sha512-8k0JupWme55+9tCVE+FS5ULT3K6AbgqrGa58lTT49RpyfwwcGedHqaC5LlQNdEAumn/wFsu6aPwkuPMioy8kqw== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.1" - -pump@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/pump/-/pump-2.0.1.tgz#12399add6e4cf7526d973cbc8b5ce2e2908b3909" - integrity sha512-ruPMNRkN3MHP1cWJc9OWr+T/xDP0jhXYCLfJcBuX54hhfIBnaQmAUMfDcG4DM5UMWByBbJY69QSphm3jtDKIkA== +pump@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/pump/-/pump-3.0.0.tgz#b4a2116815bde2f4e1ea602354e8c75565107a64" + integrity sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww== dependencies: end-of-stream "^1.1.0" once "^1.3.1" @@ -984,7 +977,7 @@ rc@^1.2.7: minimist "^1.2.0" strip-json-comments "~2.0.1" -"readable-stream@2 || 3": +"readable-stream@2 || 3", readable-stream@^3.0.1, readable-stream@^3.1.1: version "3.4.0" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.4.0.tgz#a51c26754658e0a3c21dbf59163bd45ba6f447fc" integrity sha512-jItXPLmrSR8jmTRmRWJXCnGJsfy85mB3Wd/uINMXA65yrnFo0cPClFIUWzo2najVNSl+mx7/4W8ttlLWJe99pQ== @@ -1119,10 +1112,10 @@ simple-concat@^1.0.0: resolved "https://registry.yarnpkg.com/simple-concat/-/simple-concat-1.0.0.tgz#7344cbb8b6e26fb27d66b2fc86f9f6d5997521c6" integrity sha1-c0TLuLbib7J9ZrL8hvn21Zl1IcY= -simple-get@^2.7.0: - version "2.8.1" - resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-2.8.1.tgz#0e22e91d4575d87620620bc91308d57a77f44b5d" - integrity sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw== +simple-get@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/simple-get/-/simple-get-3.0.3.tgz#924528ac3f9d7718ce5e9ec1b1a69c0be4d62efa" + integrity sha512-Wvre/Jq5vgoz31Z9stYWPLn0PqRqmBDpFSdypAnHu5AvRVCYPRYGnvryNLiXu8GOBNDH82J2FRHUGMjjHUpXFw== dependencies: decompress-response "^3.3.0" once "^1.3.1" @@ -1212,17 +1205,17 @@ strip-json-comments@~2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= -tar-fs@^1.13.0: - version "1.16.3" - resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-1.16.3.tgz#966a628841da2c4010406a82167cbd5e0c72d509" - integrity sha512-NvCeXpYx7OsmOh8zIOP/ebG55zZmxLE0etfWRbWok+q2Qo8x/vOR/IJT1taADXPe+jsiu9axDb3X4B+iIgNlKw== +tar-fs@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/tar-fs/-/tar-fs-2.0.0.tgz#677700fc0c8b337a78bee3623fdc235f21d7afad" + integrity sha512-vaY0obB6Om/fso8a8vakQBzwholQ7v5+uy+tF3Ozvxv1KNezmVQAiWtcNmMHFSFPqL3dJA8ha6gdtFbfX9mcxA== dependencies: - chownr "^1.0.1" + chownr "^1.1.1" mkdirp "^0.5.1" - pump "^1.0.0" - tar-stream "^1.1.2" + pump "^3.0.0" + tar-stream "^2.0.0" -tar-stream@^1.1.2, tar-stream@^1.5.2: +tar-stream@^1.5.2: version "1.6.2" resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-1.6.2.tgz#8ea55dab37972253d9a9af90fdcd559ae435c555" integrity sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A== @@ -1235,6 +1228,17 @@ tar-stream@^1.1.2, tar-stream@^1.5.2: to-buffer "^1.1.1" xtend "^4.0.0" +tar-stream@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/tar-stream/-/tar-stream-2.1.0.tgz#d1aaa3661f05b38b5acc9b7020efdca5179a2cc3" + integrity sha512-+DAn4Nb4+gz6WZigRzKEZl1QuJVOLtAwwF+WUxy1fJ6X63CaGaUAxJRD2KEn1OMfcbCjySTYpNC6WmfQoIEOdw== + dependencies: + bl "^3.0.0" + end-of-stream "^1.4.1" + fs-constants "^1.0.0" + inherits "^2.0.3" + readable-stream "^3.1.1" + through2@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/through2/-/through2-3.0.1.tgz#39276e713c3302edf9e388dd9c812dd3b825bd5a" diff --git a/extensions/package.json b/extensions/package.json index cbdafd7231d8..d70cdbe0ca12 100644 --- a/extensions/package.json +++ b/extensions/package.json @@ -3,7 +3,7 @@ "version": "0.0.1", "description": "Dependencies shared by all extensions", "dependencies": { - "typescript": "3.6.3-insiders.20190909" + "typescript": "3.6.3" }, "scripts": { "postinstall": "node ./postinstall" diff --git a/extensions/yarn.lock b/extensions/yarn.lock index f6ffdca517ac..2595e3b44163 100644 --- a/extensions/yarn.lock +++ b/extensions/yarn.lock @@ -2,7 +2,7 @@ # yarn lockfile v1 -typescript@3.6.3-insiders.20190909: - version "3.6.3-insiders.20190909" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.6.3-insiders.20190909.tgz#65b6b2d809288311a970819849e1964ac73e1fda" - integrity sha512-Lr7ONd8Y05EhrI+zKoI5tgvO5dhuRDrK5pyOLG33DeMln8zb8w7Yc8AoIEyqvxB5Btj9F7zBmXBXJdTI3SuX0Q== +typescript@3.6.3: + version "3.6.3" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.6.3.tgz#fea942fabb20f7e1ca7164ff626f1a9f3f70b4da" + integrity sha512-N7bceJL1CtRQ2RiG0AQME13ksR7DiuQh/QehubYcghzv20tnh+MQnQIuJddTmsbqYj+dztchykemz0zFzlvdQw== diff --git a/package.json b/package.json index 926d64b72339..452a31e3eeed 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "ansi_up": "^3.0.0", "applicationinsights": "1.0.8", "chart.js": "^2.6.0", + "chokidar": "3.1.0", "graceful-fs": "4.1.11", "html-query-plan": "git://github.com/anthonydresser/html-query-plan.git#2.6", "http-proxy-agent": "^2.1.0", @@ -71,7 +72,6 @@ "sudo-prompt": "9.0.0", "underscore": "^1.8.2", "v8-inspect-profiler": "^0.0.20", - "vscode-chokidar": "2.1.7", "vscode-minimist": "^1.2.1", "vscode-proxy-agent": "0.4.0", "vscode-ripgrep": "^1.5.7", @@ -128,7 +128,7 @@ "gulp-rename": "^1.2.0", "gulp-replace": "^0.5.4", "gulp-shell": "^0.6.5", - "gulp-tsb": "4.0.2", + "gulp-tsb": "4.0.4", "gulp-tslint": "^8.1.3", "gulp-untar": "^0.0.7", "gulp-vinyl-zip": "^2.1.2", diff --git a/remote/.yarnrc b/remote/.yarnrc index b28191e6bae4..1e16cde724c7 100644 --- a/remote/.yarnrc +++ b/remote/.yarnrc @@ -1,3 +1,3 @@ disturl "http://nodejs.org/dist" -target "10.11.0" +target "12.4.0" runtime "node" diff --git a/remote/package.json b/remote/package.json index 0d57945f25b8..4c37100ec67b 100644 --- a/remote/package.json +++ b/remote/package.json @@ -4,6 +4,7 @@ "dependencies": { "@microsoft/applicationinsights-web": "^2.1.1", "applicationinsights": "1.0.8", + "chokidar": "3.1.0", "cookie": "^0.4.0", "graceful-fs": "4.1.11", "http-proxy-agent": "^2.1.0", @@ -16,7 +17,6 @@ "onigasm-umd": "^2.2.2", "semver-umd": "^5.5.3", "spdlog": "^0.9.0", - "vscode-chokidar": "2.1.7", "vscode-minimist": "^1.2.1", "vscode-proxy-agent": "0.4.0", "vscode-ripgrep": "^1.5.7", diff --git a/remote/yarn.lock b/remote/yarn.lock index 6a545c782983..037aedc8cfd1 100644 --- a/remote/yarn.lock +++ b/remote/yarn.lock @@ -79,6 +79,14 @@ agent-base@~4.2.0: dependencies: es6-promisify "^5.0.0" +anymatch@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.0.tgz#e609350e50a9313b472789b2f14ef35808ee14d6" + integrity sha512-Ozz7l4ixzI7Oxj2+cw+p0tVUt27BpaJ+1+q1TCeANWxHpvyn2+Un+YamBdfKu0uh8xLodGhoa1v7595NhKDAuA== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + applicationinsights@1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/applicationinsights/-/applicationinsights-1.0.8.tgz#db6e3d983cf9f9405fe1ee5ba30ac6e1914537b5" @@ -88,58 +96,10 @@ applicationinsights@1.0.8: diagnostic-channel-publishers "0.2.1" zone.js "0.7.6" -arr-diff@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" - integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= - -arr-flatten@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" - integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== - -arr-union@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" - integrity sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= - -array-unique@^0.3.2: - version "0.3.2" - resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428" - integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= - -assign-symbols@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/assign-symbols/-/assign-symbols-1.0.0.tgz#59667f41fadd4f20ccbc2bb96b8d4f7f78ec0367" - integrity sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= - -async-each@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" - integrity sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ== - -atob@^2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" - integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== - -base@^0.11.1: - version "0.11.2" - resolved "https://registry.yarnpkg.com/base/-/base-0.11.2.tgz#7bde5ced145b6d551a90db87f83c558b4eb48a8f" - integrity sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg== - dependencies: - cache-base "^1.0.1" - class-utils "^0.3.5" - component-emitter "^1.2.1" - define-property "^1.0.0" - isobject "^3.0.1" - mixin-deep "^1.2.0" - pascalcase "^0.1.1" - -binary-extensions@^1.0.0: - version "1.10.0" - resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.10.0.tgz#9aeb9a6c5e88638aad171e167f5900abe24835d0" - integrity sha1-muuabF6IY4qtFx4Wf1kAq+JINdA= +binary-extensions@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.0.0.tgz#23c0df14f6a88077f5f986c0d167ec03c3d5537c" + integrity sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow== bindings@^1.5.0: version "1.5.0" @@ -148,80 +108,38 @@ bindings@^1.5.0: dependencies: file-uri-to-path "1.0.0" -braces@^2.3.1, braces@^2.3.2: - version "2.3.2" - resolved "https://registry.yarnpkg.com/braces/-/braces-2.3.2.tgz#5979fd3f14cd531565e5fa2df1abfff1dfaee729" - integrity sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w== +braces@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== dependencies: - arr-flatten "^1.1.0" - array-unique "^0.3.2" - extend-shallow "^2.0.1" - fill-range "^4.0.0" - isobject "^3.0.1" - repeat-element "^1.1.2" - snapdragon "^0.8.1" - snapdragon-node "^2.0.1" - split-string "^3.0.2" - to-regex "^3.0.1" + fill-range "^7.0.1" buffer-crc32@~0.2.3: version "0.2.13" resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= -cache-base@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/cache-base/-/cache-base-1.0.1.tgz#0a7f46416831c8b662ee36fe4e7c59d76f666ab2" - integrity sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ== - dependencies: - collection-visit "^1.0.0" - component-emitter "^1.2.1" - get-value "^2.0.6" - has-value "^1.0.0" - isobject "^3.0.1" - set-value "^2.0.0" - to-object-path "^0.3.0" - union-value "^1.0.0" - unset-value "^1.0.0" - -class-utils@^0.3.5: - version "0.3.6" - resolved "https://registry.yarnpkg.com/class-utils/-/class-utils-0.3.6.tgz#f93369ae8b9a7ce02fd41faad0ca83033190c463" - integrity sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg== - dependencies: - arr-union "^3.1.0" - define-property "^0.2.5" - isobject "^3.0.0" - static-extend "^0.1.1" - -collection-visit@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" - integrity sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= - dependencies: - map-visit "^1.0.0" - object-visit "^1.0.0" - -component-emitter@^1.2.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" - integrity sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg== +chokidar@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.1.0.tgz#ff23d077682a90eadd209bfa76eb10ed6d359668" + integrity sha512-6vZfo+7W0EOlbSo0nhVKMz4yyssrwiPbBZ8wj1lq8/+l4ZhGZ2U4Md7PspvmijXp1a26D3B7AHEBmIB7aVtaOQ== + dependencies: + anymatch "^3.1.0" + braces "^3.0.2" + glob-parent "^5.0.0" + is-binary-path "^2.1.0" + is-glob "^4.0.1" + normalize-path "^3.0.0" + readdirp "^3.1.1" + optionalDependencies: + fsevents "^2.0.6" cookie@^0.4.0: version "0.4.0" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.0.tgz#beb437e7022b3b6d49019d088665303ebe9c14ba" integrity sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg== -copy-descriptor@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" - integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= - -core-util-is@~1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" - integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= - debug@3.1.0, debug@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/debug/-/debug-3.1.0.tgz#5bb5a0672628b64149566ba16819e61518c67261" @@ -229,40 +147,6 @@ debug@3.1.0, debug@^3.1.0: dependencies: ms "2.0.0" -debug@^2.2.0, debug@^2.3.3: - version "2.6.9" - resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" - integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== - dependencies: - ms "2.0.0" - -decode-uri-component@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" - integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU= - -define-property@^0.2.5: - version "0.2.5" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-0.2.5.tgz#c35b1ef918ec3c990f9a5bc57be04aacec5c8116" - integrity sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= - dependencies: - is-descriptor "^0.1.0" - -define-property@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-1.0.0.tgz#769ebaaf3f4a63aad3af9e8d304c9bbe79bfb0e6" - integrity sha1-dp66rz9KY6rTr56NMEybvnm/sOY= - dependencies: - is-descriptor "^1.0.0" - -define-property@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/define-property/-/define-property-2.0.2.tgz#d459689e8d654ba77e02a817f8710d702cb16e9d" - integrity sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ== - dependencies: - is-descriptor "^1.0.2" - isobject "^3.0.1" - diagnostic-channel-publishers@0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/diagnostic-channel-publishers/-/diagnostic-channel-publishers-0.2.1.tgz#8e2d607a8b6d79fe880b548bc58cc6beb288c4f3" @@ -287,48 +171,6 @@ es6-promisify@^5.0.0: dependencies: es6-promise "^4.0.3" -expand-brackets@^2.1.4: - version "2.1.4" - resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-2.1.4.tgz#b77735e315ce30f6b6eff0f83b04151a22449622" - integrity sha1-t3c14xXOMPa27/D4OwQVGiJEliI= - dependencies: - debug "^2.3.3" - define-property "^0.2.5" - extend-shallow "^2.0.1" - posix-character-classes "^0.1.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - -extend-shallow@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-2.0.1.tgz#51af7d614ad9a9f610ea1bafbb989d6b1c56890f" - integrity sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= - dependencies: - is-extendable "^0.1.0" - -extend-shallow@^3.0.0, extend-shallow@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/extend-shallow/-/extend-shallow-3.0.2.tgz#26a71aaf073b39fb2127172746131c2704028db8" - integrity sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= - dependencies: - assign-symbols "^1.0.0" - is-extendable "^1.0.1" - -extglob@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" - integrity sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw== - dependencies: - array-unique "^0.3.2" - define-property "^1.0.0" - expand-brackets "^2.1.4" - extend-shallow "^2.0.1" - fragment-cache "^0.2.1" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - fd-slicer@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/fd-slicer/-/fd-slicer-1.1.0.tgz#25c7c89cb1f9077f8891bbe61d8f390eae256f1e" @@ -341,27 +183,12 @@ file-uri-to-path@1.0.0: resolved "https://registry.yarnpkg.com/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz#553a7b8446ff6f684359c445f1e37a05dacc33dd" integrity sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw== -fill-range@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-4.0.0.tgz#d544811d428f98eb06a63dc402d2403c328c38f7" - integrity sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= - dependencies: - extend-shallow "^2.0.1" - is-number "^3.0.0" - repeat-string "^1.6.1" - to-regex-range "^2.1.0" - -for-in@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" - integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= - -fragment-cache@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19" - integrity sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== dependencies: - map-cache "^0.2.2" + to-regex-range "^5.0.1" fs-extra@^7.0.0: version "7.0.1" @@ -372,60 +199,28 @@ fs-extra@^7.0.0: jsonfile "^4.0.0" universalify "^0.1.0" -get-value@^2.0.3, get-value@^2.0.6: - version "2.0.6" - resolved "https://registry.yarnpkg.com/get-value/-/get-value-2.0.6.tgz#dc15ca1c672387ca76bd37ac0a395ba2042a2c28" - integrity sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= +fsevents@^2.0.6: + version "2.0.7" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.0.7.tgz#382c9b443c6cbac4c57187cdda23aa3bf1ccfc2a" + integrity sha512-a7YT0SV3RB+DjYcppwVDLtn13UQnmg0SWZS7ezZD0UjnLwXmy8Zm21GMVGLaFGimIqcvyMQaOJBrop8MyOp1kQ== -glob-parent@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-3.1.0.tgz#9e6af6299d8d3bd2bd40430832bd113df906c5ae" - integrity sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4= +glob-parent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.0.0.tgz#1dc99f0f39b006d3e92c2c284068382f0c20e954" + integrity sha512-Z2RwiujPRGluePM6j699ktJYxmPpJKCfpGA13jz2hmFZC7gKetzrWvg5KN3+OsIFmydGyZ1AVwERCq1w/ZZwRg== dependencies: - is-glob "^3.1.0" - path-dirname "^1.0.0" + is-glob "^4.0.1" graceful-fs@4.1.11, graceful-fs@^4.1.2: version "4.1.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" integrity sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg= -graceful-fs@^4.1.11, graceful-fs@^4.1.6: +graceful-fs@^4.1.6: version "4.2.0" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.0.tgz#8d8fdc73977cb04104721cb53666c1ca64cd328b" integrity sha512-jpSvDPV4Cq/bgtpndIWbI5hmYxhQGHPC4d4cqBPb4DLniCfhJokdXhwhaDuLBGLQdvvRum/UiX6ECVIPvDXqdg== -has-value@^0.3.1: - version "0.3.1" - resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f" - integrity sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= - dependencies: - get-value "^2.0.3" - has-values "^0.1.4" - isobject "^2.0.0" - -has-value@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-value/-/has-value-1.0.0.tgz#18b281da585b1c5c51def24c930ed29a0be6b177" - integrity sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= - dependencies: - get-value "^2.0.6" - has-values "^1.0.0" - isobject "^3.0.0" - -has-values@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/has-values/-/has-values-0.1.4.tgz#6d61de95d91dfca9b9a02089ad384bff8f62b771" - integrity sha1-bWHeldkd/Km5oCCJrThL/49it3E= - -has-values@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/has-values/-/has-values-1.0.0.tgz#95b0b63fec2146619a6fe57fe75628d5a39efe4f" - integrity sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= - dependencies: - is-number "^3.0.0" - kind-of "^4.0.0" - http-proxy-agent@2.1.0, http-proxy-agent@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/http-proxy-agent/-/http-proxy-agent-2.1.0.tgz#e4821beef5b2142a2026bd73926fe537631c5405" @@ -449,150 +244,34 @@ iconv-lite@0.5.0: dependencies: safer-buffer ">= 2.1.2 < 3" -inherits@^2.0.3: - version "2.0.4" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" - integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== - -inherits@~2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" - integrity sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4= - ip@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" integrity sha1-vd7XARQpCCjAoDnnLvJfWq7ENUo= -is-accessor-descriptor@^0.1.6: - version "0.1.6" - resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" - integrity sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= - dependencies: - kind-of "^3.0.2" - -is-accessor-descriptor@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz#169c2f6d3df1f992618072365c9b0ea1f6878656" - integrity sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ== - dependencies: - kind-of "^6.0.0" - -is-binary-path@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" - integrity sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg= - dependencies: - binary-extensions "^1.0.0" - -is-buffer@^1.0.2: - version "1.1.4" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.4.tgz#cfc86ccd5dc5a52fa80489111c6920c457e2d98b" - integrity sha1-z8hszV3FpS+oBIkRHGkgxFfi2Ys= - -is-buffer@^1.1.5: - version "1.1.6" - resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" - integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w== - -is-data-descriptor@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz#0b5ee648388e2c860282e793f1856fec3f301b56" - integrity sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= - dependencies: - kind-of "^3.0.2" - -is-data-descriptor@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz#d84876321d0e7add03990406abbbbd36ba9268c7" - integrity sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ== - dependencies: - kind-of "^6.0.0" - -is-descriptor@^0.1.0: - version "0.1.6" - resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" - integrity sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg== - dependencies: - is-accessor-descriptor "^0.1.6" - is-data-descriptor "^0.1.4" - kind-of "^5.0.0" - -is-descriptor@^1.0.0, is-descriptor@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-1.0.2.tgz#3b159746a66604b04f8c81524ba365c5f14d86ec" - integrity sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg== - dependencies: - is-accessor-descriptor "^1.0.0" - is-data-descriptor "^1.0.0" - kind-of "^6.0.2" - -is-extendable@^0.1.0, is-extendable@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" - integrity sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= - -is-extendable@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" - integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== +is-binary-path@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== dependencies: - is-plain-object "^2.0.4" + binary-extensions "^2.0.0" -is-extglob@^2.1.0, is-extglob@^2.1.1: +is-extglob@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI= -is-glob@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" - integrity sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo= - dependencies: - is-extglob "^2.1.0" - -is-glob@^4.0.0: +is-glob@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== dependencies: is-extglob "^2.1.1" -is-number@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-3.0.0.tgz#24fd6201a4782cf50561c810276afc7d12d71195" - integrity sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= - dependencies: - kind-of "^3.0.2" - -is-plain-object@^2.0.3, is-plain-object@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" - integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== - dependencies: - isobject "^3.0.1" - -is-windows@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" - integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== - -isarray@1.0.0, isarray@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" - integrity sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE= - -isobject@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" - integrity sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= - dependencies: - isarray "1.0.0" - -isobject@^3.0.0, isobject@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" - integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== jschardet@1.6.0: version "1.6.0" @@ -606,37 +285,6 @@ jsonfile@^4.0.0: optionalDependencies: graceful-fs "^4.1.6" -kind-of@^3.0.2: - version "3.0.4" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.0.4.tgz#7b8ecf18a4e17f8269d73b501c9f232c96887a74" - integrity sha1-e47PGKThf4Jp1ztQHJ8jLJaIenQ= - dependencies: - is-buffer "^1.0.2" - -kind-of@^3.0.3, kind-of@^3.2.0: - version "3.2.2" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" - integrity sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= - dependencies: - is-buffer "^1.1.5" - -kind-of@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-4.0.0.tgz#20813df3d712928b207378691a45066fae72dd57" - integrity sha1-IIE989cSkosgc3hpGkUGb65y3Vc= - dependencies: - is-buffer "^1.1.5" - -kind-of@^5.0.0: - version "5.1.0" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" - integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== - -kind-of@^6.0.0, kind-of@^6.0.2: - version "6.0.2" - resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.2.tgz#01146b36a6218e64e58f3a8d66de5d7fc6f6d051" - integrity sha512-s5kLOcnH0XqDO+FvuaLX8DDjZ18CGFk7VygH40QoKPUQhW4e2rvM0rwUq0t8IQDOwYSeLK01U90OjzBTme2QqA== - lodash.isinteger@^4.0.4: version "4.0.4" resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" @@ -647,50 +295,11 @@ lodash.isundefined@^3.0.1: resolved "https://registry.yarnpkg.com/lodash.isundefined/-/lodash.isundefined-3.0.1.tgz#23ef3d9535565203a66cefd5b830f848911afb48" integrity sha1-I+89lTVWUgOmbO/VuDD4SJEa+0g= -map-cache@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" - integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= - -map-visit@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/map-visit/-/map-visit-1.0.0.tgz#ecdca8f13144e660f1b5bd41f12f3479d98dfb8f" - integrity sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= - dependencies: - object-visit "^1.0.0" - -micromatch@^3.1.10: - version "3.1.10" - resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" - integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== - dependencies: - arr-diff "^4.0.0" - array-unique "^0.3.2" - braces "^2.3.1" - define-property "^2.0.2" - extend-shallow "^3.0.2" - extglob "^2.0.4" - fragment-cache "^0.2.1" - kind-of "^6.0.2" - nanomatch "^1.2.9" - object.pick "^1.3.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.2" - minimist@0.0.8: version "0.0.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0= -mixin-deep@^1.2.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" - integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== - dependencies: - for-in "^1.0.2" - is-extendable "^1.0.1" - mkdirp@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" @@ -708,23 +317,6 @@ nan@^2.0.0, nan@^2.13.2, nan@^2.14.0: resolved "https://registry.yarnpkg.com/nan/-/nan-2.14.0.tgz#7818f722027b2459a86f0295d434d1fc2336c52c" integrity sha512-INOFj37C7k3AfaNTtX8RhsTw7qRy7eLET14cROi9+5HAVbbHuIWUHEauBv5qT4Av2tWasiTY1Jw6puUNqRJXQg== -nanomatch@^1.2.9: - version "1.2.13" - resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119" - integrity sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA== - dependencies: - arr-diff "^4.0.0" - array-unique "^0.3.2" - define-property "^2.0.2" - extend-shallow "^3.0.2" - fragment-cache "^0.2.1" - is-windows "^1.0.2" - kind-of "^6.0.2" - object.pick "^1.3.0" - regex-not "^1.0.0" - snapdragon "^0.8.1" - to-regex "^3.0.1" - native-watchdog@1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/native-watchdog/-/native-watchdog-1.2.0.tgz#9c710093ac6e9e60b19517cb1ef4ac9d7c997395" @@ -757,29 +349,6 @@ nsfw@1.2.5: lodash.isundefined "^3.0.1" nan "^2.0.0" -object-copy@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/object-copy/-/object-copy-0.1.0.tgz#7e7d858b781bd7c991a41ba975ed3812754e998c" - integrity sha1-fn2Fi3gb18mRpBupde04EnVOmYw= - dependencies: - copy-descriptor "^0.1.0" - define-property "^0.2.5" - kind-of "^3.0.3" - -object-visit@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/object-visit/-/object-visit-1.0.1.tgz#f79c4493af0c5377b59fe39d395e41042dd045bb" - integrity sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= - dependencies: - isobject "^3.0.0" - -object.pick@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" - integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= - dependencies: - isobject "^3.0.1" - onigasm-umd@^2.2.2: version "2.2.2" resolved "https://registry.yarnpkg.com/onigasm-umd/-/onigasm-umd-2.2.2.tgz#b989d762df61f899a3052ac794a50bd93fe20257" @@ -792,21 +361,6 @@ oniguruma@^7.2.0: dependencies: nan "^2.14.0" -pascalcase@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/pascalcase/-/pascalcase-0.1.1.tgz#b363e55e8006ca6fe21784d2db22bd15d7917f14" - integrity sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= - -path-dirname@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/path-dirname/-/path-dirname-1.0.2.tgz#cc33d24d525e099a5388c0336c6e32b9160609e0" - integrity sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA= - -path-is-absolute@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" - integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= - pend@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/pend/-/pend-1.2.0.tgz#7a57eb550a6783f9115331fcf4663d5c8e007a50" @@ -817,77 +371,12 @@ picomatch@^2.0.4: resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.0.7.tgz#514169d8c7cd0bdbeecc8a2609e34a7163de69f6" integrity sha512-oLHIdio3tZ0qH76NybpeneBhYVj0QFTfXEFTc/B3zKQspYfYYkWYgFsmzo+4kvId/bQRcNkVeguI3y+CD22BtA== -posix-character-classes@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/posix-character-classes/-/posix-character-classes-0.1.1.tgz#01eac0fe3b5af71a2a6c02feabb8c1fef7e00eab" - integrity sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= - -process-nextick-args@~1.0.6: - version "1.0.7" - resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" - integrity sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M= - -readable-stream@^2.0.2: - version "2.3.3" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c" - integrity sha512-m+qzzcn7KUxEmd1gMbchF+Y2eIUbieUaxkWtptyHywrX0rE8QEYqPC07Vuy4Wm32/xE16NcdBctb8S0Xe/5IeQ== - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~1.0.6" - safe-buffer "~5.1.1" - string_decoder "~1.0.3" - util-deprecate "~1.0.1" - -readdirp@^2.2.1: - version "2.2.1" - resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.2.1.tgz#0e87622a3325aa33e892285caf8b4e846529a525" - integrity sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ== +readdirp@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.1.2.tgz#fa85d2d14d4289920e4671dead96431add2ee78a" + integrity sha512-8rhl0xs2cxfVsqzreYCvs8EwBfn/DhVdqtoLmw19uI3SC5avYX9teCurlErfpPXGmYtMHReGaP2RsLnFvz/lnw== dependencies: - graceful-fs "^4.1.11" - micromatch "^3.1.10" - readable-stream "^2.0.2" - -regex-not@^1.0.0, regex-not@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" - integrity sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A== - dependencies: - extend-shallow "^3.0.2" - safe-regex "^1.1.0" - -repeat-element@^1.1.2: - version "1.1.2" - resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" - integrity sha1-7wiaF40Ug7quTZPrmLT55OEdmQo= - -repeat-string@^1.6.1: - version "1.6.1" - resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" - integrity sha1-jcrkcOHIirwtYA//Sndihtp15jc= - -resolve-url@^0.2.1: - version "0.2.1" - resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" - integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= - -ret@~0.1.10: - version "0.1.15" - resolved "https://registry.yarnpkg.com/ret/-/ret-0.1.15.tgz#b8a4825d5bdb1fc3f6f53c2bc33f81388681c7bc" - integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== - -safe-buffer@~5.1.0, safe-buffer@~5.1.1: - version "5.1.1" - resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" - integrity sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg== - -safe-regex@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/safe-regex/-/safe-regex-1.1.0.tgz#40a3669f3b077d1e943d44629e157dd48023bf2e" - integrity sha1-QKNmnzsHfR6UPURinhV91IAjvy4= - dependencies: - ret "~0.1.10" + picomatch "^2.0.4" "safer-buffer@>= 2.1.2 < 3": version "2.1.2" @@ -904,51 +393,11 @@ semver@^5.3.0: resolved "https://registry.yarnpkg.com/semver/-/semver-5.6.0.tgz#7e74256fbaa49c75aa7c7a205cc22799cac80004" integrity sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg== -set-value@^2.0.0, set-value@^2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b" - integrity sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw== - dependencies: - extend-shallow "^2.0.1" - is-extendable "^0.1.1" - is-plain-object "^2.0.3" - split-string "^3.0.1" - smart-buffer@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-4.0.1.tgz#07ea1ca8d4db24eb4cac86537d7d18995221ace3" integrity sha512-RFqinRVJVcCAL9Uh1oVqE6FZkqsyLiVOYEZ20TqIOjuX7iFVJ+zsbs4RIghnw/pTs7mZvt8ZHhvm1ZUrR4fykg== -snapdragon-node@^2.0.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/snapdragon-node/-/snapdragon-node-2.1.1.tgz#6c175f86ff14bdb0724563e8f3c1b021a286853b" - integrity sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw== - dependencies: - define-property "^1.0.0" - isobject "^3.0.0" - snapdragon-util "^3.0.1" - -snapdragon-util@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/snapdragon-util/-/snapdragon-util-3.0.1.tgz#f956479486f2acd79700693f6f7b805e45ab56e2" - integrity sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ== - dependencies: - kind-of "^3.2.0" - -snapdragon@^0.8.1: - version "0.8.2" - resolved "https://registry.yarnpkg.com/snapdragon/-/snapdragon-0.8.2.tgz#64922e7c565b0e14204ba1aa7d6964278d25182d" - integrity sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg== - dependencies: - base "^0.11.1" - debug "^2.2.0" - define-property "^0.2.5" - extend-shallow "^2.0.1" - map-cache "^0.2.2" - source-map "^0.5.6" - source-map-resolve "^0.5.0" - use "^3.1.0" - socks-proxy-agent@4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/socks-proxy-agent/-/socks-proxy-agent-4.0.1.tgz#5936bf8b707a993079c6f37db2091821bffa6473" @@ -965,27 +414,6 @@ socks@~2.2.0: ip "^1.1.5" smart-buffer "^4.0.1" -source-map-resolve@^0.5.0: - version "0.5.2" - resolved "https://registry.yarnpkg.com/source-map-resolve/-/source-map-resolve-0.5.2.tgz#72e2cc34095543e43b2c62b2c4c10d4a9054f259" - integrity sha512-MjqsvNwyz1s0k81Goz/9vRBe9SZdB09Bdw+/zYyO+3CuPk6fouTaxscHkgtE8jKvf01kVfl8riHzERQ/kefaSA== - dependencies: - atob "^2.1.1" - decode-uri-component "^0.2.0" - resolve-url "^0.2.1" - source-map-url "^0.4.0" - urix "^0.1.0" - -source-map-url@^0.4.0: - version "0.4.0" - resolved "https://registry.yarnpkg.com/source-map-url/-/source-map-url-0.4.0.tgz#3e935d7ddd73631b97659956d55128e87b5084a3" - integrity sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= - -source-map@^0.5.6: - version "0.5.7" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" - integrity sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= - spdlog@^0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/spdlog/-/spdlog-0.9.0.tgz#c85dd9d0b9cd385f6f3f5b92dc9d2e1691092b5c" @@ -995,135 +423,23 @@ spdlog@^0.9.0: mkdirp "^0.5.1" nan "^2.14.0" -split-string@^3.0.1, split-string@^3.0.2: - version "3.1.0" - resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2" - integrity sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw== - dependencies: - extend-shallow "^3.0.0" - -static-extend@^0.1.1: - version "0.1.2" - resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" - integrity sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= - dependencies: - define-property "^0.2.5" - object-copy "^0.1.0" - -string_decoder@~1.0.3: - version "1.0.3" - resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab" - integrity sha512-4AH6Z5fzNNBcH+6XDMfA/BTt87skxqJlO0lAh3Dker5zThcAxG6mKz+iGu308UKoPPQ8Dcqx/4JhujzltRa+hQ== - dependencies: - safe-buffer "~5.1.0" - -to-object-path@^0.3.0: - version "0.3.0" - resolved "https://registry.yarnpkg.com/to-object-path/-/to-object-path-0.3.0.tgz#297588b7b0e7e0ac08e04e672f85c1f4999e17af" - integrity sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= - dependencies: - kind-of "^3.0.2" - -to-regex-range@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-2.1.1.tgz#7c80c17b9dfebe599e27367e0d4dd5590141db38" - integrity sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= - dependencies: - is-number "^3.0.0" - repeat-string "^1.6.1" - -to-regex@^3.0.1, to-regex@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" - integrity sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw== +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== dependencies: - define-property "^2.0.2" - extend-shallow "^3.0.2" - regex-not "^1.0.2" - safe-regex "^1.1.0" + is-number "^7.0.0" tslib@^1.9.3: version "1.10.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a" integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ== -union-value@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" - integrity sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg== - dependencies: - arr-union "^3.1.0" - get-value "^2.0.6" - is-extendable "^0.1.1" - set-value "^2.0.1" - universalify@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== -unset-value@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" - integrity sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= - dependencies: - has-value "^0.3.1" - isobject "^3.0.0" - -upath@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.2.tgz#3db658600edaeeccbe6db5e684d67ee8c2acd068" - integrity sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q== - -urix@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" - integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= - -use@^3.1.0: - version "3.1.1" - resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" - integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== - -util-deprecate@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" - integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= - -vscode-anymatch@3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/vscode-anymatch/-/vscode-anymatch-3.0.3.tgz#5a79101e6df7e659a1f070367bc42f190eb4ae76" - integrity sha512-qQgfbzJJ5nNShh4jjC3BBekY4d8emcxHFgnqcXwsB/PUKvJPCg7AZYXM7hqS7EDnKrX9tsIFwFMihZ7yut92Qg== - dependencies: - normalize-path "^3.0.0" - picomatch "^2.0.4" - -vscode-chokidar@2.1.7: - version "2.1.7" - resolved "https://registry.yarnpkg.com/vscode-chokidar/-/vscode-chokidar-2.1.7.tgz#c5b31eb87402f4779bb4170915245bdcb6f7854b" - integrity sha512-uSNEQetPjAlgIAHmcF9E6M+KCw0f842rsEnJ64aamUAV6TO7gkXNCvLSzb4MuLsPU7ZQyCa++DrLQFjvciK5dg== - dependencies: - async-each "^1.0.1" - braces "^2.3.2" - glob-parent "^3.1.0" - inherits "^2.0.3" - is-binary-path "^1.0.0" - is-glob "^4.0.0" - normalize-path "^3.0.0" - path-is-absolute "^1.0.0" - readdirp "^2.2.1" - upath "^1.1.1" - vscode-anymatch "3.0.3" - optionalDependencies: - vscode-fsevents "1.2.12" - -vscode-fsevents@1.2.12: - version "1.2.12" - resolved "https://registry.yarnpkg.com/vscode-fsevents/-/vscode-fsevents-1.2.12.tgz#01a71a01f90ee95ca822c34427aba437a17c03a7" - integrity sha512-bH/jRdDpSesGpqiVLjp6gHLSKUOh7oNvppzZ17JIrdbRYCcDmV7dIWR5gQc27DFy0RD9JDT+t+ixMid94MkM1A== - dependencies: - nan "^2.14.0" - vscode-minimist@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/vscode-minimist/-/vscode-minimist-1.2.1.tgz#e63d3f4a9bf3680dcb8f9304eed612323fd6926a" diff --git a/resources/win32/bin/code.sh b/resources/win32/bin/code.sh index 54f9429b7dd7..a915930e70cc 100644 --- a/resources/win32/bin/code.sh +++ b/resources/win32/bin/code.sh @@ -27,15 +27,15 @@ if grep -qi Microsoft /proc/version; then WSL_EXT_ID="ms-vscode-remote.remote-wsl" if [ $WSL_BUILD -ge 41955 -a $WSL_BUILD -lt 41959 ]; then - # WSL2 in workaround for https://github.com/microsoft/WSL/issues/4337 + # WSL2 workaround for https://github.com/microsoft/WSL/issues/4337 CWD="$(pwd)" cd "$VSCODE_PATH" - cmd.exe /C ".\\bin\\$APP_NAME.cmd --locate-extension $WSL_EXT_ID >remote-wsl-loc.txt" - WSL_EXT_WLOC="$(cat ./remote-wsl-loc.txt)" - rm remote-wsl-loc.txt + cmd.exe /C ".\\bin\\$APP_NAME.cmd --locate-extension $WSL_EXT_ID >%TEMP%\\remote-wsl-loc.txt" + WSL_EXT_WLOC=$(cmd.exe /C type %TEMP%\\remote-wsl-loc.txt) cd "$CWD" else - WSL_EXT_WLOC=$(ELECTRON_RUN_AS_NODE=1 "$ELECTRON" "$CLI" --locate-extension $WSL_EXT_ID) + ELECTRON_RUN_AS_NODE=1 "$ELECTRON" "$CLI" --locate-extension $WSL_EXT_ID >/tmp/remote-wsl-loc.txt + WSL_EXT_WLOC=$(cat /tmp/remote-wsl-loc.txt) fi if [ -n "$WSL_EXT_WLOC" ]; then # replace \r\n with \n in WSL_EXT_WLOC diff --git a/scripts/test-integration.sh b/scripts/test-integration.sh index 9dd5449c32fc..3550993cc4c7 100755 --- a/scripts/test-integration.sh +++ b/scripts/test-integration.sh @@ -8,6 +8,7 @@ if [[ "$OSTYPE" == "darwin"* ]]; then else ROOT=$(dirname $(dirname $(readlink -f $0))) VSCODEUSERDATADIR=`mktemp -d 2>/dev/null` + LINUX_NO_SANDBOX="--no-sandbox" # workaround Electron 6 issue on Linux when running tests in container fi cd $ROOT @@ -33,17 +34,17 @@ else fi # Integration tests in AMD -./scripts/test.sh --runGlob **/*.integrationTest.js "$@" +./scripts/test.sh $LINUX_NO_SANDBOX --runGlob **/*.integrationTest.js "$@" # Tests in the extension host -# "$INTEGRATION_TEST_ELECTRON_PATH" $ROOT/extensions/vscode-api-tests/testWorkspace --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/singlefolder-tests --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --skip-getting-started --disable-inspect --user-data-dir=$VSCODEUSERDATADIR -# "$INTEGRATION_TEST_ELECTRON_PATH" $ROOT/extensions/vscode-api-tests/testworkspace.code-workspace --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/workspace-tests --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --skip-getting-started --disable-inspect --user-data-dir=$VSCODEUSERDATADIR +# "$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/vscode-api-tests/testWorkspace --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/singlefolder-tests --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --skip-getting-started --disable-inspect --user-data-dir=$VSCODEUSERDATADIR +# "$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/vscode-api-tests/testworkspace.code-workspace --extensionDevelopmentPath=$ROOT/extensions/vscode-api-tests --extensionTestsPath=$ROOT/extensions/vscode-api-tests/out/workspace-tests --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --skip-getting-started --disable-inspect --user-data-dir=$VSCODEUSERDATADIR "$INTEGRATION_TEST_ELECTRON_PATH" $ROOT/extensions/vscode-colorize-tests/test --extensionDevelopmentPath=$ROOT/extensions/vscode-colorize-tests --extensionTestsPath=$ROOT/extensions/vscode-colorize-tests/out --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --skip-getting-started --disable-inspect --user-data-dir=$VSCODEUSERDATADIR -"$INTEGRATION_TEST_ELECTRON_PATH" $ROOT/extensions/markdown-language-features/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/markdown-language-features --extensionTestsPath=$ROOT/extensions/markdown-language-features/out/test --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --skip-getting-started --disable-inspect --user-data-dir=$VSCODEUSERDATADIR -"$INTEGRATION_TEST_ELECTRON_PATH" $ROOT/extensions/azurecore/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/azurecore --extensionTestsPath=$ROOT/extensions/azurecore/out/test --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --skip-getting-started --disable-inspect --user-data-dir=$VSCODEUSERDATADIR +"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/markdown-language-features/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/markdown-language-features --extensionTestsPath=$ROOT/extensions/markdown-language-features/out/test --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --skip-getting-started --disable-inspect --user-data-dir=$VSCODEUSERDATADIR +"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/azurecore/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/azurecore --extensionTestsPath=$ROOT/extensions/azurecore/out/test --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --skip-getting-started --disable-inspect --user-data-dir=$VSCODEUSERDATADIR mkdir -p $ROOT/extensions/emmet/test-fixtures -"$INTEGRATION_TEST_ELECTRON_PATH" $ROOT/extensions/emmet/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/emmet --extensionTestsPath=$ROOT/extensions/emmet/out/test --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --skip-getting-started --disable-inspect --user-data-dir=$VSCODEUSERDATADIR +"$INTEGRATION_TEST_ELECTRON_PATH" $LINUX_NO_SANDBOX $ROOT/extensions/emmet/test-fixtures --extensionDevelopmentPath=$ROOT/extensions/emmet --extensionTestsPath=$ROOT/extensions/emmet/out/test --disable-telemetry --disable-crash-reporter --disable-updates --disable-extensions --skip-getting-started --disable-inspect --user-data-dir=$VSCODEUSERDATADIR rm -rf $ROOT/extensions/emmet/test-fixtures # Remote Integration Tests diff --git a/src/bootstrap.js b/src/bootstrap.js index 8d95ffee952f..8a6a02ab5ac1 100644 --- a/src/bootstrap.js +++ b/src/bootstrap.js @@ -21,6 +21,7 @@ process.on('SIGPIPE', () => { //#endregion //#region Add support for redirecting the loading of node modules + exports.injectNodeModuleLookupPath = function (injectPath) { if (!injectPath) { throw new Error('Missing injectPath'); @@ -36,10 +37,8 @@ exports.injectNodeModuleLookupPath = function (injectPath) { const originalResolveLookupPaths = Module._resolveLookupPaths; // @ts-ignore - Module._resolveLookupPaths = function (moduleName, parent, newReturn) { - const result = originalResolveLookupPaths(moduleName, parent, newReturn); - - const paths = newReturn ? result : result[1]; + Module._resolveLookupPaths = function (moduleName, parent) { + const paths = originalResolveLookupPaths(moduleName, parent); for (let i = 0, len = paths.length; i < len; i++) { if (paths[i] === nodeModulesPath) { paths.splice(i, 0, injectPath); @@ -47,7 +46,7 @@ exports.injectNodeModuleLookupPath = function (injectPath) { } } - return result; + return paths; }; }; //#endregion @@ -71,11 +70,10 @@ exports.enableASARSupport = function (nodeModulesPath) { // @ts-ignore const originalResolveLookupPaths = Module._resolveLookupPaths; - // @ts-ignore - Module._resolveLookupPaths = function (request, parent, newReturn) { - const result = originalResolveLookupPaths(request, parent, newReturn); - const paths = newReturn ? result : result[1]; + // @ts-ignore + Module._resolveLookupPaths = function (request, parent) { + const paths = originalResolveLookupPaths(request, parent); for (let i = 0, len = paths.length; i < len; i++) { if (paths[i] === NODE_MODULES_PATH) { paths.splice(i, 0, NODE_MODULES_ASAR_PATH); @@ -83,7 +81,7 @@ exports.enableASARSupport = function (nodeModulesPath) { } } - return result; + return paths; }; }; //#endregion diff --git a/src/main.js b/src/main.js index d50913a2654a..1059583cd44b 100644 --- a/src/main.js +++ b/src/main.js @@ -17,7 +17,7 @@ const paths = require('./paths'); // @ts-ignore const product = require('../product.json'); // @ts-ignore -const app = require('electron').app; +const { app, protocol } = require('electron'); // Enable portable support const portable = bootstrap.configurePortable(); @@ -33,6 +33,11 @@ app.setPath('userData', userDataPath); // Update cwd based on environment and platform setCurrentWorkingDirectory(); +// Register custom schemes with privileges +protocol.registerSchemesAsPrivileged([ + { scheme: 'vscode-resource', privileges: { secure: true, supportFetchAPI: true, corsEnabled: true } } +]); + // Global app listeners registerListeners(); @@ -102,7 +107,7 @@ function onReady() { }); }; - // We recevied a valid nlsConfig from a user defined locale + // We received a valid nlsConfig from a user defined locale if (nlsConfig) { startup(nlsConfig); } diff --git a/src/sql/workbench/browser/modelComponents/queryTextEditor.ts b/src/sql/workbench/browser/modelComponents/queryTextEditor.ts index 5933b8ef3c43..ea8e369971c6 100644 --- a/src/sql/workbench/browser/modelComponents/queryTextEditor.ts +++ b/src/sql/workbench/browser/modelComponents/queryTextEditor.ts @@ -112,14 +112,14 @@ export class QueryTextEditor extends BaseTextEditor { public setWidth(width: number) { if (this._dimension) { - this._dimension.width = width; + this._dimension = new DOM.Dimension(width, this._dimension.height); this.layout(); } } public setHeight(height: number) { if (this._dimension) { - this._dimension.height = height; + this._dimension = new DOM.Dimension(this._dimension.width, height); this.layout(this._dimension); } } diff --git a/src/sql/workbench/contrib/welcome/page/browser/az_data_welcome_page.ts b/src/sql/workbench/contrib/welcome/page/browser/az_data_welcome_page.ts index 3b218b1bdff4..3f86fa7a0e6f 100644 --- a/src/sql/workbench/contrib/welcome/page/browser/az_data_welcome_page.ts +++ b/src/sql/workbench/contrib/welcome/page/browser/az_data_welcome_page.ts @@ -6,9 +6,6 @@ import { escape } from 'vs/base/common/strings'; import { localize } from 'vs/nls'; -export function used() { -} - let productQuality: string; export function setProductQuality(quality: string): void { 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 a750245a9441..f0ccd44d8578 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 @@ -25,6 +25,7 @@ import { getRandomTestPath } from 'vs/base/test/node/testUtils'; import { IWorkbenchConstructionOptions } from 'vs/workbench/workbench.web.api'; class TestEnvironmentService implements IWorkbenchEnvironmentService { + webviewExternalEndpoint: string; logFile: URI; options?: IWorkbenchConstructionOptions; galleryMachineIdResource?: URI; diff --git a/src/sql/workbench/update/electron-browser/releaseNotes.ts b/src/sql/workbench/update/electron-browser/releaseNotes.ts index c3cc330d881d..9bf4fc4413aa 100644 --- a/src/sql/workbench/update/electron-browser/releaseNotes.ts +++ b/src/sql/workbench/update/electron-browser/releaseNotes.ts @@ -5,8 +5,7 @@ import nls = require('vs/nls'); import { Action } from 'vs/base/common/actions'; -import pkg from 'vs/platform/product/node/package'; -import product from 'vs/platform/product/node/product'; +import product from 'vs/platform/product/common/product'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { URI } from 'vs/base/common/uri'; import { IOpenerService } from 'vs/platform/opener/common/opener'; @@ -37,7 +36,7 @@ export class ShowCurrentReleaseNotesAction extends AbstractShowReleaseNotesActio label = ShowCurrentReleaseNotesAction.LABEL, @IInstantiationService instantiationService: IInstantiationService ) { - super(id, label, pkg.version, instantiationService); + super(id, label, product.version, instantiationService); } } diff --git a/src/typings/chokidar.d.ts b/src/typings/chokidar.d.ts index 1584d20fbbd6..87d93fd5ab1a 100644 --- a/src/typings/chokidar.d.ts +++ b/src/typings/chokidar.d.ts @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -declare module 'vscode-chokidar' { +declare module 'chokidar' { // TypeScript Version: 3.0 @@ -197,4 +197,4 @@ declare module 'vscode-chokidar' { paths: string | string[], options?: WatchOptions ): FSWatcher; -} \ No newline at end of file +} diff --git a/src/typings/electron.d.ts b/src/typings/electron.d.ts index 6f44785756c4..3e55cdc4bf19 100644 --- a/src/typings/electron.d.ts +++ b/src/typings/electron.d.ts @@ -1,4 +1,4 @@ -// Type definitions for Electron 4.2.10 +// Type definitions for Electron 6.0.9 // Project: http://electronjs.org/ // Definitions by: The Electron Team // Definitions: https://github.com/electron/electron-typescript-definitions @@ -8,6 +8,7 @@ type GlobalEvent = Event; declare namespace Electron { + // TODO: Replace this declaration with NodeJS.EventEmitter class EventEmitter { addListener(event: string, listener: Function): this; on(event: string, listener: Function): this; @@ -21,28 +22,17 @@ declare namespace Electron { listenerCount(type: string): number; prependListener(event: string, listener: Function): this; prependOnceListener(event: string, listener: Function): this; - eventNames(): string[]; + eventNames(): Array<(string | symbol)>; } class Accelerator extends String { } - interface Event extends GlobalEvent { - preventDefault: () => void; - sender: WebContents; - returnValue: any; - ctrlKey?: boolean; - metaKey?: boolean; - shiftKey?: boolean; - altKey?: boolean; - } - interface CommonInterface { clipboard: Clipboard; crashReporter: CrashReporter; nativeImage: typeof NativeImage; - screen: Screen; shell: Shell; } @@ -69,6 +59,7 @@ declare namespace Electron { powerMonitor: PowerMonitor; powerSaveBlocker: PowerSaveBlocker; protocol: Protocol; + screen: Screen; session: typeof Session; systemPreferences: SystemPreferences; TouchBar: typeof TouchBar; @@ -203,9 +194,9 @@ declare namespace Electron { userInfo: any) => void): this; /** * Emitted before the application starts closing its windows. Calling - * event.preventDefault() will prevent the default behaviour, which is terminating + * event.preventDefault() will prevent the default behavior, which is terminating * the application. Note: If application quit was initiated by - * autoUpdater.quitAndInstall() then before-quit is emitted after emitting close + * autoUpdater.quitAndInstall(), then before-quit is emitted after emitting close * event on all windows and closing them. Note: On Windows, this event will not be * emitted if the app is closed due to a shutdown/restart of the system or a user * logout. @@ -372,6 +363,18 @@ declare namespace Electron { * A string with the error's localized description. */ error: string) => void): this; + /** + * Emitted when desktopCapturer.getSources() is called in the renderer process of + * webContents. Calling event.preventDefault() will make it return empty sources. + */ + on(event: 'desktop-capturer-get-sources', listener: (event: Event, + webContents: WebContents) => void): this; + once(event: 'desktop-capturer-get-sources', listener: (event: Event, + webContents: WebContents) => void): this; + addListener(event: 'desktop-capturer-get-sources', listener: (event: Event, + webContents: WebContents) => void): this; + removeListener(event: 'desktop-capturer-get-sources', listener: (event: Event, + webContents: WebContents) => void): this; /** * Emitted when the gpu process crashes or is killed. */ @@ -385,7 +388,7 @@ declare namespace Electron { killed: boolean) => void): this; /** * Emitted when webContents wants to do basic auth. The default behavior is to - * cancel all authentications, to override this you should prevent the default + * cancel all authentications. To override this you should prevent the default * behavior with event.preventDefault() and call callback(username, password) with * the credentials. */ @@ -566,13 +569,30 @@ declare namespace Electron { removeListener(event: 'remote-require', listener: (event: Event, webContents: WebContents, moduleName: string) => void): this; + /** + * Emitted when the renderer process of webContents crashes or is killed. + */ + on(event: 'renderer-process-crashed', listener: (event: Event, + webContents: WebContents, + killed: boolean) => void): this; + once(event: 'renderer-process-crashed', listener: (event: Event, + webContents: WebContents, + killed: boolean) => void): this; + addListener(event: 'renderer-process-crashed', listener: (event: Event, + webContents: WebContents, + killed: boolean) => void): this; + removeListener(event: 'renderer-process-crashed', listener: (event: Event, + webContents: WebContents, + killed: boolean) => void): this; /** * This event will be emitted inside the primary instance of your application when - * a second instance has been executed. argv is an Array of the second instance's - * command line arguments, and workingDirectory is its current working directory. - * Usually applications respond to this by making their primary window focused and - * non-minimized. This event is guaranteed to be emitted after the ready event of - * app gets emitted. + * a second instance has been executed and calls app.requestSingleInstanceLock(). + * argv is an Array of the second instance's command line arguments, and + * workingDirectory is its current working directory. Usually applications respond + * to this by making their primary window focused and non-minimized. This event is + * guaranteed to be emitted after the ready event of app gets emitted. Note: Extra + * command line arguments might be added by Chromium, such as + * --original-process-start-time. */ on(event: 'second-instance', listener: (event: Event, /** @@ -647,8 +667,8 @@ declare namespace Electron { * Emitted when Handoff is about to be resumed on another device. If you need to * update the state to be transferred, you should call event.preventDefault() * immediately, construct a new userInfo dictionary and call - * app.updateCurrentActiviy() in a timely manner. Otherwise the operation will fail - * and continue-activity-error will be called. + * app.updateCurrentActiviy() in a timely manner. Otherwise, the operation will + * fail and continue-activity-error will be called. */ on(event: 'update-activity-state', listener: (event: Event, /** @@ -760,8 +780,8 @@ declare namespace Electron { removeListener(event: 'window-all-closed', listener: Function): this; /** * Adds path to the recent documents list. This list is managed by the OS. On - * Windows you can visit the list from the task bar, and on macOS you can visit it - * from dock menu. + * Windows, you can visit the list from the task bar, and on macOS, you can visit + * it from dock menu. */ addRecentDocument(path: string): void; /** @@ -779,11 +799,6 @@ declare namespace Electron { * before app is ready. */ disableHardwareAcceleration(): void; - /** - * Enables mixed sandbox mode on the app. This method can only be called before app - * is ready. - */ - enableMixedSandbox(): void; /** * Enables full sandbox mode on the app. This method can only be called before app * is ready. @@ -791,8 +806,8 @@ declare namespace Electron { enableSandbox(): void; /** * Exits immediately with exitCode. exitCode defaults to 0. All windows will be - * closed immediately without asking user and the before-quit and will-quit events - * will not be emitted. + * closed immediately without asking the user, and the before-quit and will-quit + * events will not be emitted. */ exit(exitCode?: number): void; /** @@ -808,12 +823,19 @@ declare namespace Electron { * Fetches a path's associated icon. On Windows, there a 2 kinds of icons: On Linux * and macOS, icons depend on the application associated with file mime type. */ - getFileIcon(path: string, callback: (error: Error, icon: NativeImage) => void): void; + getFileIcon(path: string, options?: FileIconOptions): Promise; /** - * Fetches a path's associated icon. On Windows, there a 2 kinds of icons: On Linux - * and macOS, icons depend on the application associated with file mime type. + * Fetches a path's associated icon. On Windows, there are 2 kinds of icons: On + * Linux and macOS, icons depend on the application associated with file mime type. + * Deprecated Soon */ getFileIcon(path: string, options: FileIconOptions, callback: (error: Error, icon: NativeImage) => void): void; + /** + * Fetches a path's associated icon. On Windows, there are 2 kinds of icons: On + * Linux and macOS, icons depend on the application associated with file mime type. + * Deprecated Soon + */ + getFileIcon(path: string, callback: (error: Error, icon: NativeImage) => void): void; getGPUFeatureStatus(): GPUFeatureStatus; /** * For infoType equal to complete: Promise is fulfilled with Object containing all @@ -828,12 +850,16 @@ declare namespace Electron { /** * To set the locale, you'll want to use a command line switch at app startup, * which may be found here. Note: When distributing your packaged app, you have to - * also ship the locales folder. Note: On Windows you have to call it after the + * also ship the locales folder. Note: On Windows, you have to call it after the * ready events gets emitted. */ getLocale(): string; /** - * If you provided path and args options to app.setLoginItemSettings then you need + * Note: When unable to detect locale country code, it returns empty string. + */ + getLocaleCountryCode(): string; + /** + * If you provided path and args options to app.setLoginItemSettings, then you need * to pass the same arguments here for openAtLogin to be set correctly. */ getLoginItemSettings(options?: LoginItemSettingsOptions): LoginItemSettings; @@ -870,6 +896,9 @@ declare namespace Electron { * Invalidates the current Handoff user activity. */ invalidateCurrentActivity(type: string): void; + /** + * Deprecated Soon + */ isAccessibilitySupportEnabled(): boolean; /** * This method checks if the current executable is the default handler for a @@ -881,15 +910,16 @@ declare namespace Electron { * the Windows Registry and LSCopyDefaultHandlerForURLScheme internally. */ isDefaultProtocolClient(protocol: string, path?: string, args?: string[]): boolean; + isEmojiPanelSupported(): boolean; isInApplicationsFolder(): boolean; isReady(): boolean; isUnityRunning(): boolean; /** - * No confirmation dialog will be presented by default, if you wish to allow the - * user to confirm the operation you may do so using the dialog API. NOTE: This + * No confirmation dialog will be presented by default. If you wish to allow the + * user to confirm the operation, you may do so using the dialog API. NOTE: This * method throws errors if anything other than the user causes the move to fail. - * For instance if the user cancels the authorization dialog this method returns - * false. If we fail to perform the copy then this method will throw an error. The + * For instance if the user cancels the authorization dialog, this method returns + * false. If we fail to perform the copy, then this method will throw an error. The * message in the error should be informative and tell you exactly what went wrong */ moveToApplicationsFolder(): boolean; @@ -903,14 +933,14 @@ declare namespace Electron { */ quit(): void; /** - * Relaunches the app when current instance exits. By default the new instance will - * use the same working directory and command line arguments with current instance. - * When args is specified, the args will be passed as command line arguments - * instead. When execPath is specified, the execPath will be executed for relaunch - * instead of current app. Note that this method does not quit the app when - * executed, you have to call app.quit or app.exit after calling app.relaunch to - * make the app restart. When app.relaunch is called for multiple times, multiple - * instances will be started after current instance exited. An example of + * Relaunches the app when current instance exits. By default, the new instance + * will use the same working directory and command line arguments with current + * instance. When args is specified, the args will be passed as command line + * arguments instead. When execPath is specified, the execPath will be executed for + * relaunch instead of current app. Note that this method does not quit the app + * when executed, you have to call app.quit or app.exit after calling app.relaunch + * to make the app restart. When app.relaunch is called for multiple times, + * multiple instances will be started after current instance exited. An example of * restarting current instance immediately and adding a new command line argument * to the new instance: */ @@ -926,27 +956,25 @@ declare namespace Electron { */ removeAsDefaultProtocolClient(protocol: string, path?: string, args?: string[]): boolean; /** - * This method makes your application a Single Instance Application - instead of - * allowing multiple instances of your app to run, this will ensure that only a - * single instance of your app is running, and other instances signal this instance - * and exit. The return value of this method indicates whether or not this instance - * of your application successfully obtained the lock. If it failed to obtain the - * lock you can assume that another instance of your application is already running - * with the lock and exit immediately. I.e. This method returns true if your - * process is the primary instance of your application and your app should continue - * loading. It returns false if your process should immediately quit as it has - * sent its parameters to another instance that has already acquired the lock. On - * macOS the system enforces single instance automatically when users try to open a - * second instance of your app in Finder, and the open-file and open-url events - * will be emitted for that. However when users start your app in command line the - * system's single instance mechanism will be bypassed and you have to use this + * The return value of this method indicates whether or not this instance of your + * application successfully obtained the lock. If it failed to obtain the lock, + * you can assume that another instance of your application is already running with + * the lock and exit immediately. I.e. This method returns true if your process is + * the primary instance of your application and your app should continue loading. + * It returns false if your process should immediately quit as it has sent its + * parameters to another instance that has already acquired the lock. On macOS, the + * system enforces single instance automatically when users try to open a second + * instance of your app in Finder, and the open-file and open-url events will be + * emitted for that. However when users start your app in command line, the + * system's single instance mechanism will be bypassed, and you have to use this * method to ensure single instance. An example of activating the window of primary * instance when a second instance starts: */ requestSingleInstanceLock(): boolean; /** * Set the about panel options. This will override the values defined in the app's - * .plist file. See the Apple docs for more details. + * .plist file on MacOS. See the Apple docs for more details. On Linux, values must + * be set in order to be shown; there are no defaults. */ setAboutPanelOptions(options: AboutPanelOptionsOptions): void; /** @@ -955,9 +983,17 @@ declare namespace Electron { * accessibility docs for more details. Disabled by default. This API must be * called after the ready event is emitted. Note: Rendering accessibility tree can * significantly affect the performance of your app. It should not be enabled by - * default. + * default. Deprecated Soon */ setAccessibilitySupportEnabled(enabled: boolean): void; + /** + * Sets or creates a directory your app's logs which can then be manipulated with + * app.getPath() or app.setPath(pathName, newPath). Calling app.setAppLogsPath() + * without a path parameter will result in this directory being set to + * /Library/Logs/YourAppName on macOS, and inside the userData directory on Linux + * and Windows. + */ + setAppLogsPath(path?: string): void; /** * Changes the Application User Model ID to id. */ @@ -967,20 +1003,23 @@ declare namespace Electron { * (aka URI scheme). It allows you to integrate your app deeper into the operating * system. Once registered, all links with your-protocol:// will be opened with the * current executable. The whole link, including protocol, will be passed to your - * application as a parameter. On Windows you can provide optional parameters path, - * the path to your executable, and args, an array of arguments to be passed to - * your executable when it launches. Note: On macOS, you can only register + * application as a parameter. On Windows, you can provide optional parameters + * path, the path to your executable, and args, an array of arguments to be passed + * to your executable when it launches. Note: On macOS, you can only register * protocols that have been added to your app's info.plist, which can not be * modified at runtime. You can however change the file with a simple text editor * or script during build time. Please refer to Apple's documentation for details. - * The API uses the Windows Registry and LSSetDefaultHandlerForURLScheme - * internally. + * Note: In a Windows Store environment (when packaged as an appx) this API will + * return true for all calls but the registry key it sets won't be accessible by + * other applications. In order to register your Windows Store application as a + * default protocol handler you must declare the protocol in your manifest. The API + * uses the Windows Registry and LSSetDefaultHandlerForURLScheme internally. */ setAsDefaultProtocolClient(protocol: string, path?: string, args?: string[]): boolean; /** * Sets the counter badge for current app. Setting the count to 0 will hide the - * badge. On macOS it shows on the dock icon. On Linux it only works for Unity - * launcher, Note: Unity launcher requires the existence of a .desktop file to + * badge. On macOS, it shows on the dock icon. On Linux, it only works for Unity + * launcher. Note: Unity launcher requires the existence of a .desktop file to * work, for more information please read Desktop Environment Integration. */ setBadgeCount(count: number): boolean; @@ -1012,12 +1051,12 @@ declare namespace Electron { setName(name: string): void; /** * Overrides the path to a special directory or file associated with name. If the - * path specifies a directory that does not exist, the directory will be created by - * this method. On failure an Error is thrown. You can only override paths of a - * name defined in app.getPath. By default, web pages' cookies and caches will be - * stored under the userData directory. If you want to change this location, you - * have to override the userData path before the ready event of the app module is - * emitted. + * path specifies a directory that does not exist, an Error is thrown. In that + * case, the directory should be created with fs.mkdirSync or similar. You can only + * override paths of a name defined in app.getPath. By default, web pages' cookies + * and caches will be stored under the userData directory. If you want to change + * this location, you have to override the userData path before the ready event of + * the app module is emitted. */ setPath(name: string, path: string): void; /** @@ -1037,10 +1076,14 @@ declare namespace Electron { */ show(): void; /** - * Show the about panel with the values defined in the app's .plist file or with - * the options set via app.setAboutPanelOptions(options). + * Show the app's about panel options. These options can be overridden with + * app.setAboutPanelOptions(options). */ showAboutPanel(): void; + /** + * Show the platform's native emoji picker. + */ + showEmojiPanel(): void; /** * Start accessing a security scoped resource. With this method Electron * applications that are packaged for the Mac App Store may reach outside their @@ -1054,6 +1097,34 @@ declare namespace Electron { */ updateCurrentActivity(type: string, userInfo: any): void; whenReady(): Promise; + /** + * A Boolean property that's true if Chrome's accessibility support is enabled, + * false otherwise. This property will be true if the use of assistive + * technologies, such as screen readers, has been detected. Setting this property + * to true manually enables Chrome's accessibility support, allowing developers to + * expose accessibility switch to users in application settings. See Chromium's + * accessibility docs for more details. Disabled by default. This API must be + * called after the ready event is emitted. Note: Rendering accessibility tree can + * significantly affect the performance of your app. It should not be enabled by + * default. + */ + accessibilitySupportEnabled?: boolean; + /** + * A Boolean which when true disables the overrides that Electron has in place to + * ensure renderer processes are restarted on every navigation. The current + * default value for this property is false. The intention is for these overrides + * to become disabled by default and then at some point in the future this property + * will be removed. This property impacts which native modules you can use in the + * renderer process. For more information on the direction Electron is going with + * renderer process restarts and usage of native modules in the renderer process + * please check out this Tracking Issue. + */ + allowRendererProcessReuse?: boolean; + /** + * A Menu property that return Menu if one has been set and null otherwise. Users + * can pass a Menu to set this property. + */ + applicationMenu?: Menu; commandLine: CommandLine; dock: Dock; /** @@ -1217,7 +1288,8 @@ declare namespace Electron { * media keys or browser commands, as well as the "Back" button built into some * mice on Windows. Commands are lowercased, underscores are replaced with hyphens, * and the APPCOMMAND_ prefix is stripped off. e.g. APPCOMMAND_BROWSER_BACKWARD is - * emitted as browser-backward. + * emitted as browser-backward. The following app commands are explictly supported + * on Linux: */ on(event: 'app-command', listener: (event: Event, command: string) => void): this; @@ -1551,6 +1623,10 @@ declare namespace Electron { * ready event of the app module is emitted. */ static removeExtension(name: string): void; + /** + * Replacement API for setBrowserView supporting work with multi browser views. + */ + addBrowserView(browserView: BrowserView): void; /** * Adds a window as a tab on this window, after the tab for the window instance. */ @@ -1561,11 +1637,22 @@ declare namespace Electron { blur(): void; blurWebView(): void; /** - * Same as webContents.capturePage([rect, ]callback). + * Captures a snapshot of the page within rect. Upon completion callback will be + * called with callback(image). The image is an instance of NativeImage that stores + * data of the snapshot. Omitting rect will capture the whole visible page. + * Deprecated Soon */ capturePage(callback: (image: NativeImage) => void): void; /** - * Same as webContents.capturePage([rect, ]callback). + * Captures a snapshot of the page within rect. Omitting rect will capture the + * whole visible page. + */ + capturePage(rect?: Rectangle): Promise; + /** + * Captures a snapshot of the page within rect. Upon completion callback will be + * called with callback(image). The image is an instance of NativeImage that stores + * data of the snapshot. Omitting rect will capture the whole visible page. + * Deprecated Soon */ capturePage(rect: Rectangle, callback: (image: NativeImage) => void): void; /** @@ -1598,11 +1685,13 @@ declare namespace Electron { focus(): void; focusOnWebView(): void; getBounds(): Rectangle; + getBrowserView(): (BrowserView) | (null); /** - * Note: The BrowserView API is currently experimental and may change or be removed - * in future Electron releases. + * Returns array of BrowserView what was an attached with addBrowserView or + * setBrowserView. Note: The BrowserView API is currently experimental and may + * change or be removed in future Electron releases. */ - getBrowserView(): (BrowserView) | (null); + getBrowserViews(): void; getChildWindows(): BrowserWindow[]; getContentBounds(): Rectangle; getContentSize(): number[]; @@ -1626,13 +1715,10 @@ declare namespace Electron { getRepresentedFilename(): string; getSize(): number[]; /** - * Note: The title of web page can be different from the title of the native + * Note: The title of the web page can be different from the title of the native * window. */ getTitle(): string; - /** - * On Windows and Linux always returns true. - */ hasShadow(): boolean; /** * Hides the window. @@ -1684,7 +1770,7 @@ declare namespace Electron { * Same as webContents.loadFile, filePath should be a path to an HTML file relative * to the root of your application. See the webContents docs for more information. */ - loadFile(filePath: string, options?: LoadFileOptions): void; + loadFile(filePath: string, options?: LoadFileOptions): Promise; /** * Same as webContents.loadURL(url[, options]). The url can be a remote address * (e.g. http://) or a path to a local HTML file using the file:// protocol. To @@ -1692,7 +1778,7 @@ declare namespace Electron { * url.format method: You can load a URL using a POST request with URL-encoded data * by doing the following: */ - loadURL(url: string, options?: LoadURLOptions): void; + loadURL(url: string, options?: LoadURLOptions): Promise; /** * Maximizes the window. This will also show (but not focus) the window if it isn't * being displayed already. @@ -1725,6 +1811,11 @@ declare namespace Electron { * Same as webContents.reload. */ reload(): void; + removeBrowserView(browserView: BrowserView): void; + /** + * Remove the window's menu bar. + */ + removeMenu(): void; /** * Restores the window from minimized state to its previous state. */ @@ -1829,7 +1920,7 @@ declare namespace Electron { */ setFullScreenable(fullscreenable: boolean): void; /** - * Sets whether the window should have a shadow. On Windows and Linux does nothing. + * Sets whether the window should have a shadow. */ setHasShadow(hasShadow: boolean): void; /** @@ -1856,8 +1947,7 @@ declare namespace Electron { */ setMaximumSize(width: number, height: number): void; /** - * Sets the menu as the window's menu bar, setting it to null will remove the menu - * bar. + * Sets the menu as the window's menu bar. */ setMenu(menu: (Menu) | (null)): void; /** @@ -2296,12 +2386,12 @@ declare namespace Electron { // Docs: http://electronjs.org/docs/api/clipboard - availableFormats(type?: string): string[]; + availableFormats(type?: 'selection' | 'clipboard'): string[]; /** * Clears the clipboard content. */ - clear(type?: string): void; - has(format: string, type?: string): boolean; + clear(type?: 'selection' | 'clipboard'): void; + has(format: string, type?: 'selection' | 'clipboard'): boolean; read(format: string): string; /** * Returns an Object containing title and url keys representing the bookmark in the @@ -2311,24 +2401,24 @@ declare namespace Electron { readBookmark(): ReadBookmark; readBuffer(format: string): Buffer; readFindText(): string; - readHTML(type?: string): string; - readImage(type?: string): NativeImage; - readRTF(type?: string): string; - readText(type?: string): string; + readHTML(type?: 'selection' | 'clipboard'): string; + readImage(type?: 'selection' | 'clipboard'): NativeImage; + readRTF(type?: 'selection' | 'clipboard'): string; + readText(type?: 'selection' | 'clipboard'): string; /** * Writes data to the clipboard. */ - write(data: Data, type?: string): void; + write(data: Data, type?: 'selection' | 'clipboard'): void; /** * Writes the title and url into the clipboard as a bookmark. Note: Most apps on * Windows don't support pasting bookmarks into them so you can use clipboard.write * to write both a bookmark and fallback text to the clipboard. */ - writeBookmark(title: string, url: string, type?: string): void; + writeBookmark(title: string, url: string, type?: 'selection' | 'clipboard'): void; /** * Writes the buffer into the clipboard as format. */ - writeBuffer(format: string, buffer: Buffer, type?: string): void; + writeBuffer(format: string, buffer: Buffer, type?: 'selection' | 'clipboard'): void; /** * Writes the text into the find pasteboard as plain text. This method uses * synchronous IPC when called from the renderer process. @@ -2337,66 +2427,61 @@ declare namespace Electron { /** * Writes markup to the clipboard. */ - writeHTML(markup: string, type?: string): void; + writeHTML(markup: string, type?: 'selection' | 'clipboard'): void; /** * Writes image to the clipboard. */ - writeImage(image: NativeImage, type?: string): void; + writeImage(image: NativeImage, type?: 'selection' | 'clipboard'): void; /** * Writes the text into the clipboard in RTF. */ - writeRTF(text: string, type?: string): void; + writeRTF(text: string, type?: 'selection' | 'clipboard'): void; /** * Writes the text into the clipboard as plain text. */ - writeText(text: string, type?: string): void; + writeText(text: string, type?: 'selection' | 'clipboard'): void; } interface ContentTracing extends EventEmitter { // Docs: http://electronjs.org/docs/api/content-tracing - /** - * Get the current monitoring traced data. Child processes typically cache trace - * data and only rarely flush and send trace data back to the main process. This is - * because it may be an expensive operation to send the trace data over IPC and we - * would like to avoid unneeded runtime overhead from tracing. So, to end tracing, - * we must asynchronously ask all child processes to flush any pending trace data. - * Once all child processes have acknowledged the captureMonitoringSnapshot request - * the callback will be called with a file that contains the traced data. - */ - captureMonitoringSnapshot(resultFilePath: string, callback: (resultFilePath: string) => void): void; /** * Get a set of category groups. The category groups can change as new code paths * are reached. Once all child processes have acknowledged the getCategories - * request the callback is invoked with an array of category groups. + * request the callback is invoked with an array of category groups. Deprecated + * Soon */ getCategories(callback: (categories: string[]) => void): void; + /** + * Get a set of category groups. The category groups can change as new code paths + * are reached. + */ + getCategories(): Promise; /** * Get the maximum usage across processes of trace buffer as a percentage of the * full state. When the TraceBufferUsage value is determined the callback is - * called. + * called. Deprecated Soon */ - getTraceBufferUsage(callback: (value: number, percentage: number) => void): void; + getTraceBufferUsage(callback: (value: number) => void): void; /** - * Start monitoring on all processes. Monitoring begins immediately locally and - * asynchronously on child processes as soon as they receive the startMonitoring - * request. Once all child processes have acknowledged the startMonitoring request - * the callback will be called. + * Get the maximum usage across processes of trace buffer as a percentage of the + * full state. */ - startMonitoring(options: StartMonitoringOptions, callback: Function): void; + getTraceBufferUsage(): Promise; /** * Start recording on all processes. Recording begins immediately locally and * asynchronously on child processes as soon as they receive the EnableRecording * request. The callback will be called once all child processes have acknowledged - * the startRecording request. + * the startRecording request. Deprecated Soon */ startRecording(options: (TraceCategoriesAndOptions) | (TraceConfig), callback: Function): void; /** - * Stop monitoring on all processes. Once all child processes have acknowledged the - * stopMonitoring request the callback is called. + * Start recording on all processes. Recording begins immediately locally and + * asynchronously on child processes as soon as they receive the EnableRecording + * request. */ - stopMonitoring(callback: Function): void; + startRecording(options: (TraceCategoriesAndOptions) | (TraceConfig)): Promise; /** * Stop recording on all processes. Child processes typically cache trace data and * only rarely flush and send trace data back to the main process. This helps to @@ -2406,9 +2491,18 @@ declare namespace Electron { * acknowledged the stopRecording request, callback will be called with a file that * contains the traced data. Trace data will be written into resultFilePath if it * is not empty or into a temporary file. The actual file path will be passed to - * callback if it's not null. + * callback if it's not null. Deprecated Soon */ stopRecording(resultFilePath: string, callback: (resultFilePath: string) => void): void; + /** + * Stop recording on all processes. Child processes typically cache trace data and + * only rarely flush and send trace data back to the main process. This helps to + * minimize the runtime overhead of tracing since sending trace data over IPC can + * be an expensive operation. So, to end tracing, we must asynchronously ask all + * child processes to flush any pending trace data. Trace data will be written into + * resultFilePath if it is not empty or into a temporary file. + */ + stopRecording(resultFilePath: string): Promise; } interface Cookie { @@ -2520,20 +2614,37 @@ declare namespace Electron { /** * Writes any unwritten cookies data to disk. */ + flushStore(): Promise; + /** + * Writes any unwritten cookies data to disk. Deprecated Soon + */ flushStore(callback: Function): void; + /** + * Sends a request to get all cookies matching filter, and resolves a promise with + * the response. + */ + get(filter: Filter): Promise; /** * Sends a request to get all cookies matching filter, callback will be called with - * callback(error, cookies) on complete. + * callback(error, cookies) on complete. Deprecated Soon */ get(filter: Filter, callback: (error: Error, cookies: Cookie[]) => void): void; + /** + * Removes the cookies matching url and name + */ + remove(url: string, name: string): Promise; /** * Removes the cookies matching url and name, callback will called with callback() - * on complete. + * on complete. Deprecated Soon */ remove(url: string, name: string, callback: Function): void; + /** + * Sets a cookie with details. + */ + set(details: Details): Promise; /** * Sets a cookie with details, callback will be called with callback(error) on - * complete. + * complete. Deprecated Soon */ set(details: Details, callback: (error: Error) => void): void; } @@ -2561,15 +2672,15 @@ declare namespace Electron { id: string; } - interface CrashReporter extends EventEmitter { + interface CrashReporter { // Docs: http://electronjs.org/docs/api/crash-reporter /** * Set an extra parameter to be sent with the crash report. The values specified * here will be sent in addition to any values set via the extra option when start - * was called. This API is only available on macOS, if you need to add/update extra - * parameters on Linux and Windows after your first call to start you can call + * was called. This API is only available on macOS and windows, if you need to + * add/update extra parameters on Linux after your first call to start you can call * start again with the updated extra options. */ addExtraParameter(key: string, value: string): void; @@ -2613,13 +2724,10 @@ declare namespace Electron { * reports from them, use process.crashReporter.start instead. Pass the same * options as above along with an additional one called crashesDirectory that * should point to a directory to store the crash reports temporarily. You can test - * this out by calling process.crash() to crash the child process. Note: To collect - * crash reports from child process in Windows, you need to add this extra code as - * well. This will start the process that will monitor and send the crash reports. - * Replace submitURL, productName and crashesDirectory with appropriate values. - * Note: If you need send additional/updated extra parameters after your first call - * start you can call addExtraParameter on macOS or call start again with the - * new/updated extra parameters on Linux and Windows. Note: On macOS, Electron uses + * this out by calling process.crash() to crash the child process. Note: If you + * need send additional/updated extra parameters after your first call start you + * can call addExtraParameter on macOS or call start again with the new/updated + * extra parameters on Linux and Windows. Note: On macOS and windows, Electron uses * a new crashpad client for crash collection and reporting. If you want to enable * crash reporting, initializing crashpad from the main process using * crashReporter.start is required regardless of which process you want to collect @@ -2631,6 +2739,17 @@ declare namespace Electron { start(options: CrashReporterStartOptions): void; } + interface CustomScheme { + + // Docs: http://electronjs.org/docs/api/structures/custom-scheme + + privileges?: Privileges; + /** + * Custom schemes to be registered with options. + */ + scheme: string; + } + class Debugger extends EventEmitter { // Docs: http://electronjs.org/docs/api/debugger @@ -2712,9 +2831,13 @@ declare namespace Electron { detach(): void; isAttached(): boolean; /** - * Send given command to the debugging target. + * Send given command to the debugging target. Deprecated Soon */ sendCommand(method: string, commandParams?: any, callback?: (error: any, result: any) => void): void; + /** + * Send given command to the debugging target. + */ + sendCommand(method: string, commandParams?: any): Promise; } interface DesktopCapturer extends EventEmitter { @@ -2725,15 +2848,22 @@ declare namespace Electron { * Starts gathering information about all available desktop media sources, and * calls callback(error, sources) when finished. sources is an array of * DesktopCapturerSource objects, each DesktopCapturerSource represents a screen or - * an individual window that can be captured. + * an individual window that can be captured. Deprecated Soon */ getSources(options: SourcesOptions, callback: (error: Error, sources: DesktopCapturerSource[]) => void): void; + getSources(options: SourcesOptions): Promise; } interface DesktopCapturerSource { // Docs: http://electronjs.org/docs/api/structures/desktop-capturer-source + /** + * An icon image of the application that owns the window or null if the source has + * a type screen. The size of the icon is not known in advance and depends on what + * the the application provides. + */ + appIcon: NativeImage; /** * A unique identifier that will correspond to the id of the matching returned by * the . On some platforms, this is equivalent to the XX portion of the id field @@ -2770,7 +2900,7 @@ declare namespace Electron { * information, and gives the user the option of trusting/importing the * certificate. If you provide a browserWindow argument the dialog will be attached * to the parent window, making it modal. On Windows the options are more limited, - * due to the Win32 APIs used: + * due to the Win32 APIs used: Deprecated Soon */ showCertificateTrustDialog(browserWindow: BrowserWindow, options: CertificateTrustDialogOptions, callback: Function): void; /** @@ -2780,6 +2910,14 @@ declare namespace Electron { * to the parent window, making it modal. On Windows the options are more limited, * due to the Win32 APIs used: */ + showCertificateTrustDialog(options: CertificateTrustDialogOptions): Promise; + /** + * On macOS, this displays a modal dialog that shows a message and certificate + * information, and gives the user the option of trusting/importing the + * certificate. If you provide a browserWindow argument the dialog will be attached + * to the parent window, making it modal. On Windows the options are more limited, + * due to the Win32 APIs used: Deprecated Soon + */ showCertificateTrustDialog(options: CertificateTrustDialogOptions, callback: Function): void; /** * On macOS, this displays a modal dialog that shows a message and certificate @@ -2788,6 +2926,14 @@ declare namespace Electron { * to the parent window, making it modal. On Windows the options are more limited, * due to the Win32 APIs used: */ + showCertificateTrustDialog(browserWindow: BrowserWindow, options: CertificateTrustDialogOptions): Promise; + /** + * On macOS, this displays a modal dialog that shows a message and certificate + * information, and gives the user the option of trusting/importing the + * certificate. If you provide a browserWindow argument the dialog will be attached + * to the parent window, making it modal. On Windows the options are more limited, + * due to the Win32 APIs used: Deprecated Soon + */ showCertificateTrustDialog(browserWindow: BrowserWindow, options: CertificateTrustDialogOptions, callback: Function): void; /** * Displays a modal dialog that shows an error message. This API can be called @@ -2796,75 +2942,142 @@ declare namespace Electron { * the message will be emitted to stderr, and no GUI dialog will appear. */ showErrorBox(title: string, content: string): void; + /** + * Shows a message box, it will block the process until the message box is closed. + * The browserWindow argument allows the dialog to attach itself to a parent + * window, making it modal. + */ + showMessageBox(browserWindow: BrowserWindow, options: MessageBoxOptions): Promise; + /** + * Shows a message box, it will block the process until the message box is closed. + * The browserWindow argument allows the dialog to attach itself to a parent + * window, making it modal. + */ + showMessageBox(options: MessageBoxOptions): Promise; /** * Shows a message box, it will block the process until the message box is closed. * It returns the index of the clicked button. The browserWindow argument allows - * the dialog to attach itself to a parent window, making it modal. If a callback - * is passed, the dialog will not block the process. The API call will be - * asynchronous and the result will be passed via callback(response). + * the dialog to attach itself to a parent window, making it modal. */ - showMessageBox(browserWindow: BrowserWindow, options: MessageBoxOptions, callback?: (response: number, checkboxChecked: boolean) => void): number; + showMessageBoxSync(browserWindow: BrowserWindow, options: MessageBoxSyncOptions): number; /** * Shows a message box, it will block the process until the message box is closed. * It returns the index of the clicked button. The browserWindow argument allows - * the dialog to attach itself to a parent window, making it modal. If a callback - * is passed, the dialog will not block the process. The API call will be - * asynchronous and the result will be passed via callback(response). + * the dialog to attach itself to a parent window, making it modal. + */ + showMessageBoxSync(options: MessageBoxSyncOptions): number; + /** + * The browserWindow argument allows the dialog to attach itself to a parent + * window, making it modal. The filters specifies an array of file types that can + * be displayed or selected when you want to limit the user to a specific type. For + * example: The extensions array should contain extensions without wildcards or + * dots (e.g. 'png' is good but '.png' and '*.png' are bad). To show all files, use + * the '*' wildcard (no other wildcard is supported). Note: On Windows and Linux an + * open dialog can not be both a file selector and a directory selector, so if you + * set properties to ['openFile', 'openDirectory'] on these platforms, a directory + * selector will be shown. */ - showMessageBox(options: MessageBoxOptions, callback?: (response: number, checkboxChecked: boolean) => void): number; + showOpenDialog(browserWindow: BrowserWindow, options: OpenDialogOptions, callback?: Function): Promise; /** * The browserWindow argument allows the dialog to attach itself to a parent * window, making it modal. The filters specifies an array of file types that can * be displayed or selected when you want to limit the user to a specific type. For * example: The extensions array should contain extensions without wildcards or * dots (e.g. 'png' is good but '.png' and '*.png' are bad). To show all files, use - * the '*' wildcard (no other wildcard is supported). If a callback is passed, the - * API call will be asynchronous and the result will be passed via - * callback(filenames). Note: On Windows and Linux an open dialog can not be both a - * file selector and a directory selector, so if you set properties to ['openFile', - * 'openDirectory'] on these platforms, a directory selector will be shown. + * the '*' wildcard (no other wildcard is supported). Note: On Windows and Linux an + * open dialog can not be both a file selector and a directory selector, so if you + * set properties to ['openFile', 'openDirectory'] on these platforms, a directory + * selector will be shown. */ - showOpenDialog(browserWindow: BrowserWindow, options: OpenDialogOptions, callback?: (filePaths: string[], bookmarks: string[]) => void): (string[]) | (undefined); + showOpenDialog(options: OpenDialogOptions, callback?: Function): Promise; /** * The browserWindow argument allows the dialog to attach itself to a parent * window, making it modal. The filters specifies an array of file types that can * be displayed or selected when you want to limit the user to a specific type. For * example: The extensions array should contain extensions without wildcards or * dots (e.g. 'png' is good but '.png' and '*.png' are bad). To show all files, use - * the '*' wildcard (no other wildcard is supported). If a callback is passed, the - * API call will be asynchronous and the result will be passed via - * callback(filenames). Note: On Windows and Linux an open dialog can not be both a - * file selector and a directory selector, so if you set properties to ['openFile', - * 'openDirectory'] on these platforms, a directory selector will be shown. + * the '*' wildcard (no other wildcard is supported). Note: On Windows and Linux an + * open dialog can not be both a file selector and a directory selector, so if you + * set properties to ['openFile', 'openDirectory'] on these platforms, a directory + * selector will be shown. + */ + showOpenDialogSync(browserWindow: BrowserWindow, options: OpenDialogSyncOptions): (string[]) | (undefined); + /** + * The browserWindow argument allows the dialog to attach itself to a parent + * window, making it modal. The filters specifies an array of file types that can + * be displayed or selected when you want to limit the user to a specific type. For + * example: The extensions array should contain extensions without wildcards or + * dots (e.g. 'png' is good but '.png' and '*.png' are bad). To show all files, use + * the '*' wildcard (no other wildcard is supported). Note: On Windows and Linux an + * open dialog can not be both a file selector and a directory selector, so if you + * set properties to ['openFile', 'openDirectory'] on these platforms, a directory + * selector will be shown. + */ + showOpenDialogSync(options: OpenDialogSyncOptions): (string[]) | (undefined); + /** + * The browserWindow argument allows the dialog to attach itself to a parent + * window, making it modal. The filters specifies an array of file types that can + * be displayed, see dialog.showOpenDialog for an example. Note: On macOS, using + * the asynchronous version is recommended to avoid issues when expanding and + * collapsing the dialog. + */ + showSaveDialog(options: SaveDialogOptions): Promise; + /** + * The browserWindow argument allows the dialog to attach itself to a parent + * window, making it modal. The filters specifies an array of file types that can + * be displayed, see dialog.showOpenDialog for an example. Note: On macOS, using + * the asynchronous version is recommended to avoid issues when expanding and + * collapsing the dialog. */ - showOpenDialog(options: OpenDialogOptions, callback?: (filePaths: string[], bookmarks: string[]) => void): (string[]) | (undefined); + showSaveDialog(browserWindow: BrowserWindow, options: SaveDialogOptions): Promise; /** * The browserWindow argument allows the dialog to attach itself to a parent * window, making it modal. The filters specifies an array of file types that can - * be displayed, see dialog.showOpenDialog for an example. If a callback is passed, - * the API call will be asynchronous and the result will be passed via - * callback(filename). + * be displayed, see dialog.showOpenDialog for an example. */ - showSaveDialog(browserWindow: BrowserWindow, options: SaveDialogOptions, callback?: (filename: string, bookmark: string) => void): (string) | (undefined); + showSaveDialogSync(options: SaveDialogSyncOptions): (string) | (undefined); /** * The browserWindow argument allows the dialog to attach itself to a parent * window, making it modal. The filters specifies an array of file types that can - * be displayed, see dialog.showOpenDialog for an example. If a callback is passed, - * the API call will be asynchronous and the result will be passed via - * callback(filename). + * be displayed, see dialog.showOpenDialog for an example. */ - showSaveDialog(options: SaveDialogOptions, callback?: (filename: string, bookmark: string) => void): (string) | (undefined); + showSaveDialogSync(browserWindow: BrowserWindow, options: SaveDialogSyncOptions): (string) | (undefined); } interface Display { // Docs: http://electronjs.org/docs/api/structures/display + /** + * Can be available, unavailable, unknown. + */ + accelerometerSupport: ('available' | 'unavailable' | 'unknown'); bounds: Rectangle; + /** + * The number of bits per pixel. + */ + colorDepth: number; + /** + * represent a color space (three-dimensional object which contains all realizable + * color combinations) for the purpose of color conversions + */ + colorSpace: string; + /** + * The number of bits per color component. + */ + depthPerComponent: number; /** * Unique identifier associated with the display. */ id: number; + /** + * true for an internal display and false for an external display + */ + internal: boolean; + /** + * Whether or not the display is a monochrome display. + */ + monochrome: boolean; /** * Can be 0, 90, 180, 270, represents screen rotation in clock-wise degrees. */ @@ -2951,6 +3164,7 @@ declare namespace Electron { getLastModifiedTime(): string; getMimeType(): string; getReceivedBytes(): number; + getSaveDialogOptions(): SaveDialogOptions; getSavePath(): string; getStartTime(): number; /** @@ -2977,6 +3191,12 @@ declare namespace Electron { * received bytes and restart the download from the beginning. */ resume(): void; + /** + * This API allows the user to set custom options for the save dialog that opens + * for the download item by default. The API is only available in session's + * will-download callback function. + */ + setSaveDialogOptions(options: SaveDialogOptions): void; /** * The API is only available in session's will-download callback function. If user * doesn't set the save path via the API, Electron will use the original routine to @@ -2985,6 +3205,13 @@ declare namespace Electron { setSavePath(path: string): void; } + interface Event extends GlobalEvent { + + // Docs: http://electronjs.org/docs/api/structures/event + + preventDefault: (() => void); + } + interface FileFilter { // Docs: http://electronjs.org/docs/api/structures/file-filter @@ -3012,7 +3239,17 @@ declare namespace Electron { * on macOS 10.14 Mojave unless the app has been authorized as a trusted * accessibility client: */ - register(accelerator: Accelerator, callback: Function): void; + register(accelerator: Accelerator, callback: Function): boolean; + /** + * Registers a global shortcut of all accelerator items in accelerators. The + * callback is called when any of the registered shortcuts are pressed by the user. + * When a given accelerator is already taken by other applications, this call will + * silently fail. This behavior is intended by operating systems, since they don't + * want applications to fight for global shortcuts. The following accelerators will + * not be registered successfully on macOS 10.14 Mojave unless the app has been + * authorized as a trusted accessibility client: + */ + registerAll(accelerators: string[], callback: Function): void; /** * Unregisters the global shortcut of accelerator. */ @@ -3118,15 +3355,24 @@ declare namespace Electron { */ finishTransactionByDate(date: string): void; /** - * Retrieves the product descriptions. + * Retrieves the product descriptions. Deprecated Soon */ getProducts(productIDs: string[], callback: (products: Product[]) => void): void; + /** + * Retrieves the product descriptions. + */ + getProducts(productIDs: string[]): Promise; getReceiptURL(): string; /** * You should listen for the transactions-updated event as soon as possible and - * certainly before you call purchaseProduct. + * certainly before you call purchaseProduct. Deprecated Soon */ purchaseProduct(productID: string, quantity?: number, callback?: (isProductValid: boolean) => void): void; + /** + * You should listen for the transactions-updated event as soon as possible and + * certainly before you call purchaseProduct. + */ + purchaseProduct(productID: string, quantity?: number): Promise; } class IncomingMessage extends EventEmitter { @@ -3228,12 +3474,12 @@ declare namespace Electron { * Listens to channel, when a new message arrives listener would be called with * listener(event, args...). */ - on(channel: string, listener: Function): this; + on(channel: string, listener: (event: IpcMainEvent, ...args: any[]) => void): this; /** * Adds a one time listener function for the event. This listener is invoked only * the next time a message is sent to channel, after which it is removed. */ - once(channel: string, listener: Function): this; + once(channel: string, listener: (event: IpcMainEvent, ...args: any[]) => void): this; /** * Removes listeners of the specified channel. */ @@ -3245,6 +3491,31 @@ declare namespace Electron { removeListener(channel: string, listener: Function): this; } + interface IpcMainEvent extends Event { + + // Docs: http://electronjs.org/docs/api/structures/ipc-main-event + + /** + * The ID of the renderer frame that sent this message + */ + frameId: number; + /** + * A function that will send an IPC message to the renderer frame that sent the + * original message that you are currently handling. You should use this method to + * "reply" to the sent message in order to guaruntee the reply will go to the + * correct process and frame. + */ + reply: Function; + /** + * Set this to the value to be returned in a syncronous message + */ + returnValue: any; + /** + * Returns the webContents that sent the message + */ + sender: WebContents; + } + interface IpcRenderer extends EventEmitter { // Docs: http://electronjs.org/docs/api/ipc-renderer @@ -3253,12 +3524,12 @@ declare namespace Electron { * Listens to channel, when a new message arrives listener would be called with * listener(event, args...). */ - on(channel: string, listener: Function): this; + on(channel: string, listener: (event: IpcRendererEvent, ...args: any[]) => void): this; /** * Adds a one time listener function for the event. This listener is invoked only * the next time a message is sent to channel, after which it is removed. */ - once(channel: string, listener: Function): this; + once(channel: string, listener: (event: IpcRendererEvent, ...args: any[]) => void): this; /** * Removes all listeners, or those of the specified channel. */ @@ -3295,6 +3566,23 @@ declare namespace Electron { sendToHost(channel: string, ...args: any[]): void; } + interface IpcRendererEvent extends Event { + + // Docs: http://electronjs.org/docs/api/structures/ipc-renderer-event + + /** + * The IpcRenderer instance that emitted the event originally + */ + sender: IpcRenderer; + /** + * The webContents.id that sent the message, you can call + * event.sender.sendTo(event.senderId, ...) to reply to the message, see for more + * information. This only applies to messages sent from a different renderer. + * Messages sent directly from the main process set event.senderId to 0. + */ + senderId: number; + } + interface JumpListCategory { // Docs: http://electronjs.org/docs/api/structures/jump-list-category @@ -3358,6 +3646,37 @@ declare namespace Electron { * One of the following: */ type?: ('task' | 'separator' | 'file'); + /** + * The working directory. Default is empty. + */ + workingDirectory?: string; + } + + interface KeyboardEvent extends Event { + + // Docs: http://electronjs.org/docs/api/structures/keyboard-event + + /** + * whether an Alt key was used in an accelerator to trigger the Event + */ + altKey?: boolean; + /** + * whether the Control key was used in an accelerator to trigger the Event + */ + ctrlKey?: boolean; + /** + * whether a meta key was used in an accelerator to trigger the Event + */ + metaKey?: boolean; + /** + * whether a Shift key was used in an accelerator to trigger the Event + */ + shiftKey?: boolean; + /** + * whether an accelerator was used to trigger the event as opposed to another user + * gesture like mouse click + */ + triggeredByAccelerator?: boolean; } interface MemoryUsageDetails { @@ -3393,7 +3712,7 @@ declare namespace Electron { * usage can be referenced above. You can also attach other fields to the element * of the template and they will become properties of the constructed menu items. */ - static buildFromTemplate(template: MenuItemConstructorOptions[]): Menu; + static buildFromTemplate(template: Array<(MenuItemConstructorOptions) | (MenuItem)>): Menu; /** * Note: The returned Menu instance doesn't support dynamic addition or removal of * menu items. Instance properties can still be dynamically modified. @@ -3408,9 +3727,16 @@ declare namespace Electron { static sendActionToFirstResponder(action: string): void; /** * Sets menu as the application menu on macOS. On Windows and Linux, the menu will - * be set as each window's top menu. Passing null will remove the menu bar on - * Windows and Linux but has no effect on macOS. Note: This API has to be called - * after the ready event of app module. + * be set as each window's top menu. Also on Windows and Linux, you can use a & in + * the top-level item name to indicate which letter should get a generated + * accelerator. For example, using &File for the file menu would result in a + * generated Alt-F accelerator that opens the associated menu. The indicated + * character in the button label gets an underline. The & character is not + * displayed on the button label. Passing null will suppress the default menu. On + * Windows and Linux, this has the additional effect of removing the menu bar from + * the window. Note: The default menu will be created automatically if the app does + * not set one. It contains standard items such as File, Edit, View, Window and + * Help. */ static setApplicationMenu(menu: (Menu) | (null)): void; /** @@ -3438,10 +3764,20 @@ declare namespace Electron { // Docs: http://electronjs.org/docs/api/menu-item constructor(options: MenuItemConstructorOptions); + accelerator: string; checked: boolean; click: Function; + commandId: number; enabled: boolean; + icon: NativeImage; + id: string; label: string; + menu: Menu; + registerAccelerator: boolean; + role: string; + sublabel: string; + submenu: Menu; + type: string; visible: boolean; } @@ -3468,7 +3804,13 @@ declare namespace Electron { */ static createEmpty(): NativeImage; /** - * Creates a new NativeImage instance from buffer. + * Creates a new NativeImage instance from buffer that contains the raw bitmap + * pixel data returned by toBitmap(). The specific format is platform-dependent. + */ + static createFromBitmap(buffer: Buffer, options: CreateFromBitmapOptions): NativeImage; + /** + * Creates a new NativeImage instance from buffer. Tries to decode as PNG or JPEG + * first. */ static createFromBuffer(buffer: Buffer, options?: CreateFromBufferOptions): NativeImage; /** @@ -3556,9 +3898,14 @@ declare namespace Electron { startLogging(path: string): void; /** * Stops recording network events. If not called, net logging will automatically - * end when app quits. + * end when app quits. Deprecated Soon */ stopLogging(callback?: (path: string) => void): void; + /** + * Stops recording network events. If not called, net logging will automatically + * end when app quits. + */ + stopLogging(): Promise; /** * A Boolean property that indicates whether network logs are recorded. */ @@ -3737,6 +4084,15 @@ declare namespace Electron { once(event: 'unlock-screen', listener: Function): this; addListener(event: 'unlock-screen', listener: Function): this; removeListener(event: 'unlock-screen', listener: Function): this; + /** + * Calculate the system idle state. idleThreshold is the amount of time (in + * seconds) before considered idle. locked is available on supported systems only. + */ + getSystemIdleState(idleThreshold: number): ('active' | 'idle' | 'locked' | 'unknown'); + /** + * Calculate system idle time in seconds. + */ + getSystemIdleTime(): number; /** * Calculate the system idle state. idleThreshold is the amount of time (in * seconds) before considered idle. callback will be called synchronously on some @@ -3782,22 +4138,42 @@ declare namespace Electron { status: number; } - interface ProcessMetric { + interface ProcessMemoryInfo { - // Docs: http://electronjs.org/docs/api/structures/process-metric + // Docs: http://electronjs.org/docs/api/structures/process-memory-info /** - * CPU usage of the process. + * The amount of memory not shared by other processes, such as JS heap or HTML + * content in Kilobytes. */ - cpu: CPUUsage; + private: number; /** - * Process id of the process. + * and The amount of memory currently pinned to actual physical RAM in Kilobytes. */ - pid: number; + residentSet: number; /** - * Process type (Browser or Tab or GPU etc). + * The amount of memory shared between processes, typically memory consumed by the + * Electron code itself in Kilobytes. */ - type: string; + shared: number; + } + + interface ProcessMetric { + + // Docs: http://electronjs.org/docs/api/structures/process-metric + + /** + * CPU usage of the process. + */ + cpu: CPUUsage; + /** + * Process id of the process. + */ + pid: number; + /** + * Process type. One of the following values: + */ + type: ('Browser' | 'Tab' | 'Utility' | 'Zygote' | 'GPU' | 'Unknown'); } interface Product { @@ -3812,15 +4188,16 @@ declare namespace Electron { * A string that identifies the version of the content. */ contentVersion: string; - /** - * A Boolean value that indicates whether the App Store has downloadable content - * for this product. - */ - downloadable: boolean; /** * The locale formatted price of the product. */ formattedPrice: string; + /** + * A Boolean value that indicates whether the App Store has downloadable content + * for this product. true if at least one file has been associated with the + * product. + */ + isDownloadable: boolean; /** * A description of the product. */ @@ -3870,9 +4247,10 @@ declare namespace Electron { interceptStringProtocol(scheme: string, handler: (request: InterceptStringProtocolRequest, callback: (data?: string) => void) => void, completion?: (error: Error) => void): void; /** * The callback will be called with a boolean that indicates whether there is - * already a handler for scheme. + * already a handler for scheme. Deprecated Soon */ - isProtocolHandled(scheme: string, callback: (error: Error) => void): void; + isProtocolHandled(scheme: string, callback: (handled: boolean) => void): void; + isProtocolHandled(scheme: string): Promise; /** * Registers a protocol of scheme that will send a Buffer as a response. The usage * is the same with registerFileProtocol, except that the callback should be called @@ -3887,13 +4265,14 @@ declare namespace Electron { * scheme is successfully registered or completion(error) when failed. To handle * the request, the callback should be called with either the file's path or an * object that has a path property, e.g. callback(filePath) or callback({ path: - * filePath }). When callback is called with nothing, a number, or an object that - * has an error property, the request will fail with the error number you - * specified. For the available error numbers you can use, please see the net error - * list. By default the scheme is treated like http:, which is parsed differently - * than protocols that follow the "generic URI syntax" like file:, so you probably - * want to call protocol.registerStandardSchemes to have your scheme treated as a - * standard scheme. + * filePath }). The object may also have a headers property which gives a map of + * headers to values for the response headers, e.g. callback({ path: filePath, + * headers: {"Content-Security-Policy": "default-src 'none'"]}). When callback is + * called with nothing, a number, or an object that has an error property, the + * request will fail with the error number you specified. For the available error + * numbers you can use, please see the net error list. By default the scheme is + * treated like http:, which is parsed differently than protocols that follow the + * "generic URI syntax" like file:. */ registerFileProtocol(scheme: string, handler: (request: RegisterFileProtocolRequest, callback: (filePath?: string) => void) => void, completion?: (error: Error) => void): void; /** @@ -3905,24 +4284,31 @@ declare namespace Electron { * set session to null. For POST requests the uploadData object must be provided. */ registerHttpProtocol(scheme: string, handler: (request: RegisterHttpProtocolRequest, callback: (redirectRequest: RedirectRequest) => void) => void, completion?: (error: Error) => void): void; - registerServiceWorkerSchemes(schemes: string[]): void; - /** - * A standard scheme adheres to what RFC 3986 calls generic URI syntax. For example - * http and https are standard schemes, while file is not. Registering a scheme as - * standard, will allow relative and absolute resources to be resolved correctly - * when served. Otherwise the scheme will behave like the file protocol, but - * without the ability to resolve relative URLs. For example when you load - * following page with custom protocol without registering it as standard scheme, - * the image will not be loaded because non-standard schemes can not recognize - * relative URLs: Registering a scheme as standard will allow access to files - * through the FileSystem API. Otherwise the renderer will throw a security error - * for the scheme. By default web storage apis (localStorage, sessionStorage, - * webSQL, indexedDB, cookies) are disabled for non standard schemes. So in general - * if you want to register a custom protocol to replace the http protocol, you have - * to register it as a standard scheme: Note: This method can only be used before - * the ready event of the app module gets emitted. - */ - registerStandardSchemes(schemes: string[], options?: RegisterStandardSchemesOptions): void; + /** + * Note: This method can only be used before the ready event of the app module gets + * emitted and can be called only once. Registers the scheme as standard, secure, + * bypasses content security policy for resources, allows registering ServiceWorker + * and supports fetch API. Specify a privilege with the value of true to enable the + * capability. An example of registering a privileged scheme, with bypassing + * Content Security Policy: A standard scheme adheres to what RFC 3986 calls + * generic URI syntax. For example http and https are standard schemes, while file + * is not. Registering a scheme as standard, will allow relative and absolute + * resources to be resolved correctly when served. Otherwise the scheme will behave + * like the file protocol, but without the ability to resolve relative URLs. For + * example when you load following page with custom protocol without registering it + * as standard scheme, the image will not be loaded because non-standard schemes + * can not recognize relative URLs: Registering a scheme as standard will allow + * access to files through the FileSystem API. Otherwise the renderer will throw a + * security error for the scheme. By default web storage apis (localStorage, + * sessionStorage, webSQL, indexedDB, cookies) are disabled for non standard + * schemes. So in general if you want to register a custom protocol to replace the + * http protocol, you have to register it as a standard scheme. + * protocol.registerSchemesAsPrivileged can be used to replicate the functionality + * of the previous protocol.registerStandardSchemes, webFrame.registerURLSchemeAs* + * and protocol.registerServiceWorkerSchemes functions that existed prior to + * Electron 5.0.0, for example: before (<= v4.x) after (>= v5.x) + */ + registerSchemesAsPrivileged(customSchemes: CustomScheme[]): void; /** * Registers a protocol of scheme that will send a Readable as a response. The * usage is similar to the other register{Any}Protocol, except that the callback @@ -4203,22 +4589,33 @@ declare namespace Electron { * authentication. */ allowNTLMCredentialsForDomains(domains: string): void; + clearAuthCache(): Promise; + clearAuthCache(options: (RemovePassword) | (RemoveClientCertificate)): Promise; /** - * Clears the session’s HTTP authentication cache. + * Clears the session’s HTTP authentication cache. Deprecated Soon */ - clearAuthCache(options: (RemovePassword) | (RemoveClientCertificate), callback?: Function): void; + clearAuthCache(options: (RemovePassword) | (RemoveClientCertificate), callback: Function): void; /** * Clears the session’s HTTP cache. */ - clearCache(callback: Function): void; + clearCache(): Promise; + /** + * Clears the session’s HTTP cache. Deprecated Soon + */ + clearCache(callback: (error: number) => void): void; /** * Clears the host resolver cache. */ + clearHostResolverCache(): Promise; + /** + * Clears the host resolver cache. Deprecated Soon + */ clearHostResolverCache(callback?: Function): void; /** - * Clears the data of web storages. + * Clears the storage data for the current session. Deprecated Soon */ clearStorageData(options?: ClearStorageDataOptions, callback?: Function): void; + clearStorageData(options?: ClearStorageDataOptions): Promise; /** * Allows resuming cancelled or interrupted downloads from previous Session. The * API will generate a DownloadItem that can be accessed with the will-download @@ -4240,16 +4637,22 @@ declare namespace Electron { * Writes any unwritten DOMStorage data to disk. */ flushStorageData(): void; + /** + * Deprecated Soon + */ getBlobData(identifier: string, callback: (result: Buffer) => void): void; + getBlobData(identifier: string): Promise; + getCacheSize(): Promise; /** - * Callback is invoked with the session's current cache size. + * Callback is invoked with the session's current cache size. Deprecated Soon */ - getCacheSize(callback: (size: number) => void): void; + getCacheSize(callback: (size: number, error: number) => void): void; getPreloads(): string[]; getUserAgent(): string; + resolveProxy(url: string): Promise; /** * Resolves the proxy information for url. The callback will be called with - * callback(proxy) when the request is performed. + * callback(proxy) when the request is performed. Deprecated Soon */ resolveProxy(url: string, callback: (proxy: string) => void): void; /** @@ -4288,6 +4691,13 @@ declare namespace Electron { * proxyRules has to follow the rules below: For example: The proxyBypassRules is a * comma separated list of rules described below: */ + setProxy(config: Config): Promise; + /** + * Sets the proxy settings. When pacScript and proxyRules are provided together, + * the proxyRules option is ignored and pacScript configuration is applied. The + * proxyRules has to follow the rules below: For example: The proxyBypassRules is a + * comma separated list of rules described below: Deprecated Soon + */ setProxy(config: Config, callback: Function): void; /** * Overrides the userAgent and acceptLanguages for this session. The @@ -4319,7 +4729,12 @@ declare namespace Electron { * Open the given external protocol URL in the desktop's default manner. (For * example, mailto: URLs in the user's default mail agent). */ - openExternal(url: string, options?: OpenExternalOptions, callback?: (error: Error) => void): boolean; + openExternal(url: string, options?: OpenExternalOptions): Promise; + /** + * Open the given external protocol URL in the desktop's default manner. (For + * example, mailto: URLs in the user's default mail agent). Deprecated + */ + openExternalSync(url: string, options?: OpenExternalSyncOptions): boolean; /** * Open the given file in the desktop's default manner. */ @@ -4332,7 +4747,7 @@ declare namespace Electron { /** * Show the given file in a file manager. If possible, select the file. */ - showItemInFolder(fullPath: string): boolean; + showItemInFolder(fullPath: string): void; /** * Creates or updates a shortcut link at shortcutPath. */ @@ -4433,28 +4848,48 @@ declare namespace Electron { once(event: 'color-changed', listener: (event: Event) => void): this; addListener(event: 'color-changed', listener: (event: Event) => void): this; removeListener(event: 'color-changed', listener: (event: Event) => void): this; + on(event: 'high-contrast-color-scheme-changed', listener: (event: Event, + /** + * `true` if a high contrast theme is being used, `false` otherwise. + */ + highContrastColorScheme: boolean) => void): this; + once(event: 'high-contrast-color-scheme-changed', listener: (event: Event, + /** + * `true` if a high contrast theme is being used, `false` otherwise. + */ + highContrastColorScheme: boolean) => void): this; + addListener(event: 'high-contrast-color-scheme-changed', listener: (event: Event, + /** + * `true` if a high contrast theme is being used, `false` otherwise. + */ + highContrastColorScheme: boolean) => void): this; + removeListener(event: 'high-contrast-color-scheme-changed', listener: (event: Event, + /** + * `true` if a high contrast theme is being used, `false` otherwise. + */ + highContrastColorScheme: boolean) => void): this; on(event: 'inverted-color-scheme-changed', listener: (event: Event, /** - * `true` if an inverted color scheme, such as a high contrast theme, is being - * used, `false` otherwise. + * `true` if an inverted color scheme (a high contrast color scheme with light text + * and dark backgrounds) is being used, `false` otherwise. */ invertedColorScheme: boolean) => void): this; once(event: 'inverted-color-scheme-changed', listener: (event: Event, /** - * `true` if an inverted color scheme, such as a high contrast theme, is being - * used, `false` otherwise. + * `true` if an inverted color scheme (a high contrast color scheme with light text + * and dark backgrounds) is being used, `false` otherwise. */ invertedColorScheme: boolean) => void): this; addListener(event: 'inverted-color-scheme-changed', listener: (event: Event, /** - * `true` if an inverted color scheme, such as a high contrast theme, is being - * used, `false` otherwise. + * `true` if an inverted color scheme (a high contrast color scheme with light text + * and dark backgrounds) is being used, `false` otherwise. */ invertedColorScheme: boolean) => void): this; removeListener(event: 'inverted-color-scheme-changed', listener: (event: Event, /** - * `true` if an inverted color scheme, such as a high contrast theme, is being - * used, `false` otherwise. + * `true` if an inverted color scheme (a high contrast color scheme with light text + * and dark backgrounds) is being used, `false` otherwise. */ invertedColorScheme: boolean) => void): this; /** @@ -4467,15 +4902,26 @@ declare namespace Electron { * was not required until macOS 10.14 Mojave, so this method will always return * true if your system is running 10.13 High Sierra or lower. */ - askForMediaAccess(mediaType: 'microphone' | 'camera'): Promise; + askForMediaAccess(mediaType: 'microphone' | 'camera'): Promise; + /** + * NOTE: This API will return false on macOS systems older than Sierra 10.12.2. + */ + canPromptTouchID(): boolean; + /** + * This API is only available on macOS 10.14 Mojave or newer. + */ getAccentColor(): string; + /** + * Returns an object with system animation settings. + */ + getAnimationSettings(): AnimationSettings; /** * Gets the macOS appearance setting that you have declared you want for your * application, maps to NSApplication.appearance. You can use the * setAppLevelAppearance API to set this value. */ getAppLevelAppearance(): ('dark' | 'light' | 'unknown'); - getColor(color: '3d-dark-shadow' | '3d-face' | '3d-highlight' | '3d-light' | '3d-shadow' | 'active-border' | 'active-caption' | 'active-caption-gradient' | 'app-workspace' | 'button-text' | 'caption-text' | 'desktop' | 'disabled-text' | 'highlight' | 'highlight-text' | 'hotlight' | 'inactive-border' | 'inactive-caption' | 'inactive-caption-gradient' | 'inactive-caption-text' | 'info-background' | 'info-text' | 'menu' | 'menu-highlight' | 'menubar' | 'menu-text' | 'scrollbar' | 'window' | 'window-frame' | 'window-text'): string; + getColor(color: '3d-dark-shadow' | '3d-dark-shadow' | '3d-face' | '3d-highlight' | '3d-light' | '3d-shadow' | 'active-border' | 'active-caption' | 'active-caption-gradient' | 'app-workspace' | 'button-text' | 'caption-text' | 'desktop' | 'disabled-text' | 'highlight' | 'highlight-text' | 'hotlight' | 'inactive-border' | 'inactive-caption' | 'inactive-caption-gradient' | 'inactive-caption-text' | 'info-background' | 'info-text' | 'menu' | 'menu-highlight' | 'menubar' | 'menu-text' | 'scrollbar' | 'window' | 'window-frame' | 'window-text' | 'alternate-selected-control-text' | 'alternate-selected-control-text' | 'control-background' | 'control' | 'control-text' | 'disabled-control-text' | 'find-highlight' | 'grid' | 'header-text' | 'highlight' | 'keyboard-focus-indicator' | 'label' | 'link' | 'placeholder-text' | 'quaternary-label' | 'scrubber-textured-background' | 'secondary-label' | 'selected-content-background' | 'selected-control' | 'selected-control-text' | 'selected-menu-item' | 'selected-text-background' | 'selected-text' | 'separator' | 'shadow' | 'tertiary-label' | 'text-background' | 'text' | 'under-page-background' | 'unemphasized-selected-content-background' | 'unemphasized-selected-text-background' | 'unemphasized-selected-text' | 'window-background' | 'window-frame-text'): string; /** * Gets the macOS appearance setting that is currently applied to your application, * maps to NSApplication.effectiveAppearance Please note that until Electron is @@ -4492,6 +4938,12 @@ declare namespace Electron { * always return granted if your system is running 10.13 High Sierra or lower. */ getMediaAccessStatus(mediaType: string): ('not-determined' | 'granted' | 'denied' | 'restricted' | 'unknown'); + /** + * Returns one of several standard system colors that automatically adapt to + * vibrancy and changes in accessibility settings like 'Increase contrast' and + * 'Reduce transparency'. See Apple Documentation for more details. + */ + getSystemColor(color: 'blue' | 'brown' | 'gray' | 'green' | 'orange' | 'pink' | 'purple' | 'red' | 'yellow'): void; /** * Some popular key and types are: */ @@ -4502,6 +4954,7 @@ declare namespace Electron { */ isAeroGlassEnabled(): boolean; isDarkMode(): boolean; + isHighContrastColorScheme(): boolean; isInvertedColorScheme(): boolean; isSwipeTrackingFromScrollEventsEnabled(): boolean; isTrustedAccessibilityClient(prompt: boolean): boolean; @@ -4514,12 +4967,22 @@ declare namespace Electron { * Posts event as native notifications of macOS. The userInfo is an Object that * contains the user information dictionary sent along with the notification. */ - postNotification(event: string, userInfo: any): void; + postNotification(event: string, userInfo: any, deliverImmediately?: boolean): void; /** * Posts event as native notifications of macOS. The userInfo is an Object that * contains the user information dictionary sent along with the notification. */ postWorkspaceNotification(event: string, userInfo: any): void; + /** + * This API itself will not protect your user data; rather, it is a mechanism to + * allow you to do so. Native apps will need to set Access Control Constants like + * kSecAccessControlUserPresence on the their keychain entry so that reading it + * would auto-prompt for Touch ID biometric consent. This could be done with + * node-keytar, such that one would store an encryption key with node-keytar and + * only fetch it if promptTouchID() resolves. NOTE: This API will return a rejected + * Promise on macOS systems older than Sierra 10.12.2. + */ + promptTouchID(reason: string): Promise; /** * Add the specified defaults to your application's NSUserDefaults. */ @@ -4608,6 +5071,10 @@ declare namespace Electron { * The string to be displayed in a JumpList. */ title: string; + /** + * The working directory. Default is empty. + */ + workingDirectory?: string; } interface ThumbarButton { @@ -4829,7 +5296,7 @@ declare namespace Electron { /** * Emitted when the tray icon is clicked. */ - on(event: 'click', listener: (event: Event, + on(event: 'click', listener: (event: KeyboardEvent, /** * The bounds of tray icon. */ @@ -4838,7 +5305,7 @@ declare namespace Electron { * The position of the event. */ position: Point) => void): this; - once(event: 'click', listener: (event: Event, + once(event: 'click', listener: (event: KeyboardEvent, /** * The bounds of tray icon. */ @@ -4847,7 +5314,7 @@ declare namespace Electron { * The position of the event. */ position: Point) => void): this; - addListener(event: 'click', listener: (event: Event, + addListener(event: 'click', listener: (event: KeyboardEvent, /** * The bounds of tray icon. */ @@ -4856,7 +5323,7 @@ declare namespace Electron { * The position of the event. */ position: Point) => void): this; - removeListener(event: 'click', listener: (event: Event, + removeListener(event: 'click', listener: (event: KeyboardEvent, /** * The bounds of tray icon. */ @@ -4868,22 +5335,22 @@ declare namespace Electron { /** * Emitted when the tray icon is double clicked. */ - on(event: 'double-click', listener: (event: Event, + on(event: 'double-click', listener: (event: KeyboardEvent, /** * The bounds of tray icon. */ bounds: Rectangle) => void): this; - once(event: 'double-click', listener: (event: Event, + once(event: 'double-click', listener: (event: KeyboardEvent, /** * The bounds of tray icon. */ bounds: Rectangle) => void): this; - addListener(event: 'double-click', listener: (event: Event, + addListener(event: 'double-click', listener: (event: KeyboardEvent, /** * The bounds of tray icon. */ bounds: Rectangle) => void): this; - removeListener(event: 'double-click', listener: (event: Event, + removeListener(event: 'double-click', listener: (event: KeyboardEvent, /** * The bounds of tray icon. */ @@ -4965,22 +5432,22 @@ declare namespace Electron { /** * Emitted when the mouse enters the tray icon. */ - on(event: 'mouse-enter', listener: (event: Event, + on(event: 'mouse-enter', listener: (event: KeyboardEvent, /** * The position of the event. */ position: Point) => void): this; - once(event: 'mouse-enter', listener: (event: Event, + once(event: 'mouse-enter', listener: (event: KeyboardEvent, /** * The position of the event. */ position: Point) => void): this; - addListener(event: 'mouse-enter', listener: (event: Event, + addListener(event: 'mouse-enter', listener: (event: KeyboardEvent, /** * The position of the event. */ position: Point) => void): this; - removeListener(event: 'mouse-enter', listener: (event: Event, + removeListener(event: 'mouse-enter', listener: (event: KeyboardEvent, /** * The position of the event. */ @@ -4988,22 +5455,22 @@ declare namespace Electron { /** * Emitted when the mouse exits the tray icon. */ - on(event: 'mouse-leave', listener: (event: Event, + on(event: 'mouse-leave', listener: (event: KeyboardEvent, /** * The position of the event. */ position: Point) => void): this; - once(event: 'mouse-leave', listener: (event: Event, + once(event: 'mouse-leave', listener: (event: KeyboardEvent, /** * The position of the event. */ position: Point) => void): this; - addListener(event: 'mouse-leave', listener: (event: Event, + addListener(event: 'mouse-leave', listener: (event: KeyboardEvent, /** * The position of the event. */ position: Point) => void): this; - removeListener(event: 'mouse-leave', listener: (event: Event, + removeListener(event: 'mouse-leave', listener: (event: KeyboardEvent, /** * The position of the event. */ @@ -5011,22 +5478,22 @@ declare namespace Electron { /** * Emitted when the mouse moves in the tray icon. */ - on(event: 'mouse-move', listener: (event: Event, + on(event: 'mouse-move', listener: (event: KeyboardEvent, /** * The position of the event. */ position: Point) => void): this; - once(event: 'mouse-move', listener: (event: Event, + once(event: 'mouse-move', listener: (event: KeyboardEvent, /** * The position of the event. */ position: Point) => void): this; - addListener(event: 'mouse-move', listener: (event: Event, + addListener(event: 'mouse-move', listener: (event: KeyboardEvent, /** * The position of the event. */ position: Point) => void): this; - removeListener(event: 'mouse-move', listener: (event: Event, + removeListener(event: 'mouse-move', listener: (event: KeyboardEvent, /** * The position of the event. */ @@ -5034,22 +5501,22 @@ declare namespace Electron { /** * Emitted when the tray icon is right clicked. */ - on(event: 'right-click', listener: (event: Event, + on(event: 'right-click', listener: (event: KeyboardEvent, /** * The bounds of tray icon. */ bounds: Rectangle) => void): this; - once(event: 'right-click', listener: (event: Event, + once(event: 'right-click', listener: (event: KeyboardEvent, /** * The bounds of tray icon. */ bounds: Rectangle) => void): this; - addListener(event: 'right-click', listener: (event: Event, + addListener(event: 'right-click', listener: (event: KeyboardEvent, /** * The bounds of tray icon. */ bounds: Rectangle) => void): this; - removeListener(event: 'right-click', listener: (event: Event, + removeListener(event: 'right-click', listener: (event: KeyboardEvent, /** * The bounds of tray icon. */ @@ -5068,6 +5535,7 @@ declare namespace Electron { */ getBounds(): Rectangle; getIgnoreDoubleClickEvents(): boolean; + getTitle(title: string): string; isDestroyed(): boolean; /** * Pops up the context menu of the tray icon. When menu is passed, the menu will be @@ -5080,9 +5548,9 @@ declare namespace Electron { */ setContextMenu(menu: (Menu) | (null)): void; /** - * Sets when the tray's icon background becomes highlighted (in blue). Note: You - * can use highlightMode with a BrowserWindow by toggling between 'never' and - * 'always' modes when the window visibility changes. + * Sets when the tray's icon background becomes highlighted (in blue). Deprecated + * Note: You can use highlightMode with a BrowserWindow by toggling between 'never' + * and 'always' modes when the window visibility changes. */ setHighlightMode(mode: 'selection' | 'always' | 'never'): void; /** @@ -5100,7 +5568,7 @@ declare namespace Electron { */ setPressedImage(image: (NativeImage) | (string)): void; /** - * Sets the title displayed aside of the tray icon in the status bar (Support ANSI + * Sets the title displayed next to the tray icon in the status bar (Support ANSI * colors). */ setTitle(title: string): void; @@ -5368,6 +5836,14 @@ declare namespace Electron { * coordinates of the custom cursor's hotspot. */ hotspot?: Point) => void): this; + /** + * Emitted when desktopCapturer.getSources() is called in the renderer process. + * Calling event.preventDefault() will make it return empty sources. + */ + on(event: 'desktop-capturer-get-sources', listener: (event: Event) => void): this; + once(event: 'desktop-capturer-get-sources', listener: (event: Event) => void): this; + addListener(event: 'desktop-capturer-get-sources', listener: (event: Event) => void): this; + removeListener(event: 'desktop-capturer-get-sources', listener: (event: Event) => void): this; /** * Emitted when webContents is destroyed. */ @@ -5716,6 +6192,13 @@ declare namespace Electron { once(event: 'dom-ready', listener: (event: Event) => void): this; addListener(event: 'dom-ready', listener: (event: Event) => void): this; removeListener(event: 'dom-ready', listener: (event: Event) => void): this; + /** + * Emitted when the window enters a full-screen state triggered by HTML API. + */ + on(event: 'enter-html-full-screen', listener: Function): this; + once(event: 'enter-html-full-screen', listener: Function): this; + addListener(event: 'enter-html-full-screen', listener: Function): this; + removeListener(event: 'enter-html-full-screen', listener: Function): this; /** * Emitted when a result is available for [webContents.findInPage] request. */ @@ -5727,6 +6210,45 @@ declare namespace Electron { result: Result) => void): this; removeListener(event: 'found-in-page', listener: (event: Event, result: Result) => void): this; + /** + * Emitted when the renderer process sends an asynchronous message via + * ipcRenderer.send(). + */ + on(event: 'ipc-message', listener: (event: Event, + channel: string, + ...args: any[]) => void): this; + once(event: 'ipc-message', listener: (event: Event, + channel: string, + ...args: any[]) => void): this; + addListener(event: 'ipc-message', listener: (event: Event, + channel: string, + ...args: any[]) => void): this; + removeListener(event: 'ipc-message', listener: (event: Event, + channel: string, + ...args: any[]) => void): this; + /** + * Emitted when the renderer process sends a synchronous message via + * ipcRenderer.sendSync(). + */ + on(event: 'ipc-message-sync', listener: (event: Event, + channel: string, + ...args: any[]) => void): this; + once(event: 'ipc-message-sync', listener: (event: Event, + channel: string, + ...args: any[]) => void): this; + addListener(event: 'ipc-message-sync', listener: (event: Event, + channel: string, + ...args: any[]) => void): this; + removeListener(event: 'ipc-message-sync', listener: (event: Event, + channel: string, + ...args: any[]) => void): this; + /** + * Emitted when the window leaves a full-screen state triggered by HTML API. + */ + on(event: 'leave-html-full-screen', listener: Function): this; + once(event: 'leave-html-full-screen', listener: Function): this; + addListener(event: 'leave-html-full-screen', listener: Function): this; + removeListener(event: 'leave-html-full-screen', listener: Function): this; /** * Emitted when webContents wants to do basic auth. The usage is the same with the * login event of app. @@ -5941,6 +6463,21 @@ declare namespace Electron { removeListener(event: 'plugin-crashed', listener: (event: Event, name: string, version: string) => void): this; + /** + * Emitted when the preload script preloadPath throws an unhandled exception error. + */ + on(event: 'preload-error', listener: (event: Event, + preloadPath: string, + error: Error) => void): this; + once(event: 'preload-error', listener: (event: Event, + preloadPath: string, + error: Error) => void): this; + addListener(event: 'preload-error', listener: (event: Event, + preloadPath: string, + error: Error) => void): this; + removeListener(event: 'preload-error', listener: (event: Event, + preloadPath: string, + error: Error) => void): this; /** * Emitted when remote.getBuiltin() is called in the renderer process. Calling * event.preventDefault() will prevent the module from being returned. Custom value @@ -6205,16 +6742,23 @@ declare namespace Electron { canGoBack(): boolean; canGoForward(): boolean; canGoToOffset(offset: number): boolean; + /** + * Captures a snapshot of the page within rect. Omitting rect will capture the + * whole visible page. + */ + capturePage(rect?: Rectangle): Promise; /** * Captures a snapshot of the page within rect. Upon completion callback will be * called with callback(image). The image is an instance of NativeImage that stores * data of the snapshot. Omitting rect will capture the whole visible page. + * Deprecated Soon */ capturePage(rect: Rectangle, callback: (image: NativeImage) => void): void; /** * Captures a snapshot of the page within rect. Upon completion callback will be * called with callback(image). The image is an instance of NativeImage that stores * data of the snapshot. Omitting rect will capture the whole visible page. + * Deprecated Soon */ capturePage(callback: (image: NativeImage) => void): void; /** @@ -6261,12 +6805,15 @@ declare namespace Electron { /** * Evaluates code in page. In the browser window some HTML APIs like * requestFullScreen can only be invoked by a gesture from the user. Setting - * userGesture to true will remove this limitation. If the result of the executed - * code is a promise the callback result will be the resolved value of the promise. - * We recommend that you use the returned Promise to handle code that results in a - * Promise. + * userGesture to true will remove this limitation. Deprecated Soon */ executeJavaScript(code: string, userGesture?: boolean, callback?: (result: any) => void): Promise; + /** + * Evaluates code in page. In the browser window some HTML APIs like + * requestFullScreen can only be invoked by a gesture from the user. Setting + * userGesture to true will remove this limitation. + */ + executeJavaScript(code: string, userGesture?: boolean): Promise; /** * Starts a request to find all matches for the text in the web page. The result of * the request can be obtained by subscribing to found-in-page event. @@ -6288,16 +6835,8 @@ declare namespace Electron { getURL(): string; getUserAgent(): string; getWebRTCIPHandlingPolicy(): string; - /** - * Sends a request to get current zoom factor, the callback will be called with - * callback(zoomFactor). - */ - getZoomFactor(callback: (zoomFactor: number) => void): void; - /** - * Sends a request to get current zoom level, the callback will be called with - * callback(zoomLevel). - */ - getZoomLevel(callback: (zoomLevel: number) => void): void; + getZoomFactor(): number; + getZoomLevel(): number; /** * Makes the browser go back a web page. */ @@ -6314,11 +6853,6 @@ declare namespace Electron { * Navigates to the specified offset from the "current entry". */ goToOffset(offset: number): void; - /** - * Checks if any ServiceWorker is registered and returns a boolean as response to - * callback. - */ - hasServiceWorker(callback: (hasWorker: boolean) => void): void; /** * Injects CSS into the current web page. */ @@ -6335,6 +6869,10 @@ declare namespace Electron { * Opens the developer tools for the service worker context. */ inspectServiceWorker(): void; + /** + * Opens the developer tools for the shared worker context. + */ + inspectSharedWorker(): void; /** * Schedules a full repaint of the window this web contents is in. If offscreen * rendering is enabled invalidates the frame and generates a new one through the @@ -6358,13 +6896,13 @@ declare namespace Electron { * relative to the root of your application. For instance an app structure like * this: Would require code like this */ - loadFile(filePath: string, options?: LoadFileOptions): void; + loadFile(filePath: string, options?: LoadFileOptions): Promise; /** * Loads the url in the window. The url must contain the protocol prefix, e.g. the * http:// or file://. If the load should bypass http cache then use the pragma * header to achieve it. */ - loadURL(url: string, options?: LoadURLOptions): void; + loadURL(url: string, options?: LoadURLOptions): Promise; /** * Opens the devtools. When contents is a tag, the mode would be detach * by default, explicitly passing an empty mode can force using last used dock @@ -6387,13 +6925,18 @@ declare namespace Electron { * Use page-break-before: always; CSS style to force to print to a new page. */ print(options?: PrintOptions, callback?: (success: boolean) => void): void; + /** + * Prints window's web page as PDF with Chromium's preview printing custom + * settings. The landscape will be ignored if @page CSS at-rule is used in the web + * page. By default, an empty options will be regarded as: Use page-break-before: + * always; CSS style to force to print to a new page. An example of + * webContents.printToPDF: + */ + printToPDF(options: PrintToPDFOptions): Promise; /** * Prints window's web page as PDF with Chromium's preview printing custom * settings. The callback will be called with callback(error, data) on completion. - * The data is a Buffer that contains the generated PDF data. The landscape will be - * ignored if @page CSS at-rule is used in the web page. By default, an empty - * options will be regarded as: Use page-break-before: always; CSS style to force - * to print to a new page. An example of webContents.printToPDF: + * The data is a Buffer that contains the generated PDF data. Deprecated Soon */ printToPDF(options: PrintToPDFOptions, callback: (error: Error, data: Buffer) => void): void; /** @@ -6420,7 +6963,7 @@ declare namespace Electron { * Executes the editing command replaceMisspelling in web page. */ replaceMisspelling(text: string): void; - savePage(fullPath: string, saveType: 'HTMLOnly' | 'HTMLComplete' | 'MHTML', callback: (error: Error) => void): boolean; + savePage(fullPath: string, saveType: 'HTMLOnly' | 'HTMLComplete' | 'MHTML'): Promise; /** * Executes the editing command selectAll in web page. */ @@ -6441,6 +6984,16 @@ declare namespace Electron { * object also have following properties: */ sendInputEvent(event: Event): void; + /** + * Send an asynchronous message to a specific frame in a renderer process via + * channel. Arguments will be serialized as JSON internally and as such no + * functions or prototype chains will be included. The renderer process can handle + * the message by listening to channel with the ipcRenderer module. If you want to + * get the frameId of a given renderer context you should use the + * webFrame.routingId value. E.g. You can also read frameId from all incoming IPC + * messages in the main process. + */ + sendToFrame(frameId: number, channel: string, ...args: any[]): void; /** * Mute the audio on the current web page. */ @@ -6539,12 +7092,6 @@ declare namespace Electron { * Executes the editing command undo in web page. */ undo(): void; - /** - * Unregisters any ServiceWorker if present and returns a boolean as response to - * callback when the JS promise is fulfilled or false when the JS promise is - * rejected. - */ - unregisterServiceWorker(callback: (success: boolean) => void): void; /** * Executes the editing command unselect in web page. */ @@ -6574,11 +7121,22 @@ declare namespace Electron { * requestFullScreen can only be invoked by a gesture from the user. Setting * userGesture to true will remove this limitation. */ + executeJavaScript(code: string, userGesture?: boolean): Promise; + /** + * Evaluates code in page. In the browser window some HTML APIs like + * requestFullScreen can only be invoked by a gesture from the user. Setting + * userGesture to true will remove this limitation. Deprecated Soon + */ executeJavaScript(code: string, userGesture?: boolean, callback?: (result: any) => void): Promise; /** - * Work like executeJavaScript but evaluates scripts in an isolated context. + * Works like executeJavaScript but evaluates scripts in an isolated context. + * Deprecated Soon + */ + executeJavaScriptInIsolatedWorld(worldId: number, scripts: WebSource[], userGesture?: boolean, callback?: (result: any) => void): Promise; + /** + * Works like executeJavaScript but evaluates scripts in an isolated context. */ - executeJavaScriptInIsolatedWorld(worldId: number, scripts: WebSource[], userGesture?: boolean, callback?: (result: any) => void): void; + executeJavaScriptInIsolatedWorld(worldId: number, scripts: WebSource[], userGesture?: boolean): Promise; findFrameByName(name: string): WebFrame; findFrameByRoutingId(routingId: number): WebFrame; getFrameForSelector(selector: string): WebFrame; @@ -6590,21 +7148,13 @@ declare namespace Electron { getZoomFactor(): number; getZoomLevel(): number; /** - * Inserts text to the focused element. + * Inserts css as a style sheet in the document. */ - insertText(text: string): void; - /** - * Resources will be loaded from this scheme regardless of the current page's - * Content Security Policy. - */ - registerURLSchemeAsBypassingCSP(scheme: string): void; + insertCSS(css: string): void; /** - * Registers the scheme as secure, bypasses content security policy for resources, - * allows registering ServiceWorker and supports fetch API. Specify an option with - * the value of false to omit it from the registration. An example of registering a - * privileged scheme, without bypassing Content Security Policy: + * Inserts text to the focused element. */ - registerURLSchemeAsPrivileged(scheme: string, options?: RegisterURLSchemeAsPrivilegedOptions): void; + insertText(text: string): void; /** * Set the content security policy of the isolated world. */ @@ -6613,6 +7163,11 @@ declare namespace Electron { * Set the name of the isolated world. Useful in devtools. */ setIsolatedWorldHumanReadableName(worldId: number, name: string): void; + /** + * Set the security origin, content security policy and name of the isolated world. + * Note: If the csp is specified, then the securityOrigin also has to be specified. + */ + setIsolatedWorldInfo(worldId: number, info: Info): void; /** * Set the security origin of the isolated world. */ @@ -6623,10 +7178,12 @@ declare namespace Electron { setLayoutZoomLevelLimits(minimumLevel: number, maximumLevel: number): void; /** * Sets a provider for spell checking in input fields and text areas. The provider - * must be an object that has a spellCheck method that returns whether the word - * passed is correctly spelled. An example of using node-spellchecker as provider: + * must be an object that has a spellCheck method that accepts an array of + * individual words for spellchecking. The spellCheck function runs asynchronously + * and calls the callback function with an array of misspelt words when complete. + * An example of using node-spellchecker as provider: */ - setSpellCheckProvider(language: string, autoCorrectWord: boolean, provider: Provider): void; + setSpellCheckProvider(language: string, provider: Provider): void; /** * Sets the maximum and minimum pinch-to-zoom level. */ @@ -6685,90 +7242,90 @@ declare namespace Electron { * The listener will be called with listener(details) when a server initiated * redirect is about to occur. */ - onBeforeRedirect(listener: (details: OnBeforeRedirectDetails) => void): void; + onBeforeRedirect(listener: ((details: OnBeforeRedirectDetails) => void) | (null)): void; /** * The listener will be called with listener(details) when a server initiated * redirect is about to occur. */ - onBeforeRedirect(filter: OnBeforeRedirectFilter, listener: (details: OnBeforeRedirectDetails) => void): void; + onBeforeRedirect(filter: OnBeforeRedirectFilter, listener: ((details: OnBeforeRedirectDetails) => void) | (null)): void; /** * The listener will be called with listener(details, callback) when a request is * about to occur. The uploadData is an array of UploadData objects. The callback - * has to be called with an response object. + * has to be called with an response object. Some examples of valid urls: */ - onBeforeRequest(listener: (details: OnBeforeRequestDetails, callback: (response: Response) => void) => void): void; + onBeforeRequest(listener: ((details: OnBeforeRequestDetails, callback: (response: Response) => void) => void) | (null)): void; /** * The listener will be called with listener(details, callback) when a request is * about to occur. The uploadData is an array of UploadData objects. The callback - * has to be called with an response object. + * has to be called with an response object. Some examples of valid urls: */ - onBeforeRequest(filter: OnBeforeRequestFilter, listener: (details: OnBeforeRequestDetails, callback: (response: Response) => void) => void): void; + onBeforeRequest(filter: OnBeforeRequestFilter, listener: ((details: OnBeforeRequestDetails, callback: (response: Response) => void) => void) | (null)): void; /** * The listener will be called with listener(details, callback) before sending an * HTTP request, once the request headers are available. This may occur after a TCP * connection is made to the server, but before any http data is sent. The callback * has to be called with an response object. */ - onBeforeSendHeaders(filter: OnBeforeSendHeadersFilter, listener: (details: OnBeforeSendHeadersDetails, callback: (response: OnBeforeSendHeadersResponse) => void) => void): void; + onBeforeSendHeaders(filter: OnBeforeSendHeadersFilter, listener: ((details: OnBeforeSendHeadersDetails, callback: (response: OnBeforeSendHeadersResponse) => void) => void) | (null)): void; /** * The listener will be called with listener(details, callback) before sending an * HTTP request, once the request headers are available. This may occur after a TCP * connection is made to the server, but before any http data is sent. The callback * has to be called with an response object. */ - onBeforeSendHeaders(listener: (details: OnBeforeSendHeadersDetails, callback: (response: OnBeforeSendHeadersResponse) => void) => void): void; + onBeforeSendHeaders(listener: ((details: OnBeforeSendHeadersDetails, callback: (response: OnBeforeSendHeadersResponse) => void) => void) | (null)): void; /** * The listener will be called with listener(details) when a request is completed. */ - onCompleted(filter: OnCompletedFilter, listener: (details: OnCompletedDetails) => void): void; + onCompleted(filter: OnCompletedFilter, listener: ((details: OnCompletedDetails) => void) | (null)): void; /** * The listener will be called with listener(details) when a request is completed. */ - onCompleted(listener: (details: OnCompletedDetails) => void): void; + onCompleted(listener: ((details: OnCompletedDetails) => void) | (null)): void; /** * The listener will be called with listener(details) when an error occurs. */ - onErrorOccurred(listener: (details: OnErrorOccurredDetails) => void): void; + onErrorOccurred(listener: ((details: OnErrorOccurredDetails) => void) | (null)): void; /** * The listener will be called with listener(details) when an error occurs. */ - onErrorOccurred(filter: OnErrorOccurredFilter, listener: (details: OnErrorOccurredDetails) => void): void; + onErrorOccurred(filter: OnErrorOccurredFilter, listener: ((details: OnErrorOccurredDetails) => void) | (null)): void; /** * The listener will be called with listener(details, callback) when HTTP response * headers of a request have been received. The callback has to be called with an * response object. */ - onHeadersReceived(filter: OnHeadersReceivedFilter, listener: (details: OnHeadersReceivedDetails, callback: (response: OnHeadersReceivedResponse) => void) => void): void; + onHeadersReceived(filter: OnHeadersReceivedFilter, listener: ((details: OnHeadersReceivedDetails, callback: (response: OnHeadersReceivedResponse) => void) => void) | (null)): void; /** * The listener will be called with listener(details, callback) when HTTP response * headers of a request have been received. The callback has to be called with an * response object. */ - onHeadersReceived(listener: (details: OnHeadersReceivedDetails, callback: (response: OnHeadersReceivedResponse) => void) => void): void; + onHeadersReceived(listener: ((details: OnHeadersReceivedDetails, callback: (response: OnHeadersReceivedResponse) => void) => void) | (null)): void; /** * The listener will be called with listener(details) when first byte of the * response body is received. For HTTP requests, this means that the status line * and response headers are available. */ - onResponseStarted(listener: (details: OnResponseStartedDetails) => void): void; + onResponseStarted(listener: ((details: OnResponseStartedDetails) => void) | (null)): void; /** * The listener will be called with listener(details) when first byte of the * response body is received. For HTTP requests, this means that the status line * and response headers are available. */ - onResponseStarted(filter: OnResponseStartedFilter, listener: (details: OnResponseStartedDetails) => void): void; + onResponseStarted(filter: OnResponseStartedFilter, listener: ((details: OnResponseStartedDetails) => void) | (null)): void; /** * The listener will be called with listener(details) just before a request is * going to be sent to the server, modifications of previous onBeforeSendHeaders * response are visible by the time this listener is fired. */ - onSendHeaders(filter: OnSendHeadersFilter, listener: (details: OnSendHeadersDetails) => void): void; + onSendHeaders(filter: OnSendHeadersFilter, listener: ((details: OnSendHeadersDetails) => void) | (null)): void; /** * The listener will be called with listener(details) just before a request is * going to be sent to the server, modifications of previous onBeforeSendHeaders * response are visible by the time this listener is fired. */ - onSendHeaders(listener: (details: OnSendHeadersDetails) => void): void; + onSendHeaders(listener: ((details: OnSendHeadersDetails) => void) | (null)): void; } interface WebSource { @@ -6963,15 +7520,24 @@ declare namespace Electron { canGoForward(): boolean; canGoToOffset(offset: number): boolean; /** - * Captures a snapshot of the webview's page. Same as - * webContents.capturePage([rect, ]callback). + * Captures a snapshot of the page within rect. Upon completion callback will be + * called with callback(image). The image is an instance of NativeImage that stores + * data of the snapshot. Omitting rect will capture the whole visible page. + * Deprecated Soon */ capturePage(callback: (image: NativeImage) => void): void; /** - * Captures a snapshot of the webview's page. Same as - * webContents.capturePage([rect, ]callback). + * Captures a snapshot of the page within rect. Upon completion callback will be + * called with callback(image). The image is an instance of NativeImage that stores + * data of the snapshot. Omitting rect will capture the whole visible page. + * Deprecated Soon */ capturePage(rect: Rectangle, callback: (image: NativeImage) => void): void; + /** + * Captures a snapshot of the page within rect. Omitting rect will capture the + * whole visible page. + */ + capturePage(rect?: Rectangle): Promise; /** * Clears the navigation history. */ @@ -6996,12 +7562,18 @@ declare namespace Electron { * Initiates a download of the resource at url without navigating. */ downloadURL(url: string): void; + /** + * Evaluates code in page. If userGesture is set, it will create the user gesture + * context in the page. HTML APIs like requestFullScreen, which require user + * action, can take advantage of this option for automation. Deprecated Soon + */ + executeJavaScript(code: string, userGesture?: boolean, callback?: (result: any) => void): Promise; /** * Evaluates code in page. If userGesture is set, it will create the user gesture * context in the page. HTML APIs like requestFullScreen, which require user * action, can take advantage of this option for automation. */ - executeJavaScript(code: string, userGesture?: boolean, callback?: (result: any) => void): void; + executeJavaScript(code: string, userGesture?: boolean): Promise; /** * Starts a request to find all matches for the text in the web page. The result of * the request can be obtained by subscribing to found-in-page event. @@ -7015,16 +7587,9 @@ declare namespace Electron { * is disabled. */ getWebContents(): WebContents; - /** - * Sends a request to get current zoom factor, the callback will be called with - * callback(zoomFactor). - */ - getZoomFactor(callback: (zoomFactor: number) => void): void; - /** - * Sends a request to get current zoom level, the callback will be called with - * callback(zoomLevel). - */ - getZoomLevel(callback: (zoomLevel: number) => void): void; + getWebContentsId(): number; + getZoomFactor(): number; + getZoomLevel(): number; /** * Makes the guest page go back. */ @@ -7057,6 +7622,10 @@ declare namespace Electron { * Opens the DevTools for the service worker context present in the guest page. */ inspectServiceWorker(): void; + /** + * Opens the DevTools for the shared worker context present in the guest page. + */ + inspectSharedWorker(): void; isAudioMuted(): boolean; isCrashed(): boolean; isCurrentlyAudible(): boolean; @@ -7069,7 +7638,7 @@ declare namespace Electron { * Loads the url in the webview, the url must contain the protocol prefix, e.g. the * http:// or file://. */ - loadURL(url: string, options?: LoadURLOptions): void; + loadURL(url: string, options?: LoadURLOptions): Promise; /** * Opens a DevTools window for guest page. */ @@ -7088,9 +7657,13 @@ declare namespace Electron { print(options?: PrintOptions): void; /** * Prints webview's web page as PDF, Same as webContents.printToPDF(options, - * callback). + * callback). Deprecated Soon */ printToPDF(options: PrintToPDFOptions, callback: (error: Error, data: Buffer) => void): void; + /** + * Prints webview's web page as PDF, Same as webContents.printToPDF(options). + */ + printToPDF(options: PrintToPDFOptions): Promise; /** * Executes editing command redo in page. */ @@ -7180,14 +7753,6 @@ declare namespace Electron { * windows. Popups are disabled by default. */ // allowpopups?: string; ### VSCODE CHANGE (https://github.com/electron/electron/blob/master/docs/tutorial/security.md) ### - /** - * When this attribute is present the webview container will automatically resize - * within the bounds specified by the attributes minwidth, minheight, maxwidth, and - * maxheight. These constraints do not impact the webview unless autosize is - * enabled. When autosize is enabled, the webview container size cannot be less - * than the minimum values or greater than the maximum. - */ - autosize?: string; /** * A list of strings which specifies the blink features to be disabled separated by * ,. The full list of supported feature strings can be found in the @@ -7207,7 +7772,7 @@ declare namespace Electron { enableblinkfeatures?: string; /** * When this attribute is false the guest page in webview will not have access to - * the remote module. The remote module is avaiable by default. + * the remote module. The remote module is available by default. */ enableremotemodule?: string; /** @@ -7220,6 +7785,13 @@ declare namespace Electron { * system resources. Node integration is disabled by default in the guest page. */ nodeintegration?: string; + /** + * Experimental option for enabling NodeJS support in sub-frames such as iframes + * inside the webview. All your preloads will load for every iframe, you can use + * process.isMainFrame to determine if you are in the main frame or not. This + * option is disabled by default in the guest page. + */ + nodeintegrationinsubframes?: string; /** * Sets the session used by the page. If partition starts with persist:, the page * will use a persistent session available to all pages in the app with the same @@ -7281,14 +7853,23 @@ declare namespace Electron { * Copyright information. */ copyright?: string; + /** + * The app's build version number. + */ + version?: string; /** * Credit information. */ credits?: string; /** - * The app's build version number. + * The app's website. */ - version?: string; + website?: string; + /** + * Path to the app's icon. Will be shown as 64x64 pixels while retaining aspect + * ratio. + */ + iconPath?: string; } interface AddRepresentationOptions { @@ -7314,6 +7895,24 @@ declare namespace Electron { dataURL?: string; } + interface AnimationSettings { + /** + * Returns true if rich animations should be rendered. Looks at session type (e.g. + * remote desktop) and accessibility settings to give guidance for heavy + * animations. + */ + shouldRenderRichAnimation: boolean; + /** + * Determines on a per-platform basis whether scroll animations (e.g. produced by + * home/end key) should be enabled. + */ + scrollAnimationsEnabledBySystem: boolean; + /** + * Determines whether the user desires reduced motion based on platform APIs. + */ + prefersReducedMotion: boolean; + } + interface AppDetailsOptions { /** * Window's . It has to be set, otherwise the other options will have no effect. @@ -7357,6 +7956,16 @@ declare namespace Electron { * by default. */ height: boolean; + /** + * If true, the view's x position and width will grow and shrink proportionly with + * the window. false by default. + */ + horizontal: boolean; + /** + * If true, the view's y position and height will grow and shrink proportinaly with + * the window. false by default. + */ + vertical: boolean; } interface BitmapOptions { @@ -7475,7 +8084,9 @@ declare namespace Electron { */ kiosk?: boolean; /** - * Default window title. Default is "Electron". + * Default window title. Default is "Electron". If the HTML tag is defined + * in the HTML file loaded by loadURL(), this property will be + * ignored. */ title?: string; /** @@ -7520,8 +8131,8 @@ declare namespace Electron { enableLargerThanScreen?: boolean; /** * Window's background color as a hexadecimal value, like #66CD00 or #FFF or - * #80FFFFFF (alpha is supported if transparent is set to true). Default is #FFF - * (white). + * #80FFFFFF (alpha in #AARRGGBB format is supported if transparent is set to + * true). Default is #FFF (white). */ backgroundColor?: string; /** @@ -7633,15 +8244,24 @@ declare namespace Electron { interface CommandLine { /** * Append a switch (with optional value) to Chromium's command line. Note: This - * will not affect process.argv, and is mainly used by developers to control some - * low-level Chromium behaviors. + * will not affect process.argv. The intended usage of this function is to control + * Chromium's behavior. */ appendSwitch: (the_switch: string, value?: string) => void; /** * Append an argument to Chromium's command line. The argument will be quoted - * correctly. Note: This will not affect process.argv. + * correctly. Switches will precede arguments regardless of appending order. If + * you're appending an argument like --switch=value, consider using + * appendSwitch('switch', 'value') instead. Note: This will not affect + * process.argv. The intended usage of this function is to control Chromium's + * behavior. */ appendArgument: (value: string) => void; + hasSwitch: (the_switch: string) => boolean; + /** + * Note: When the switch is not present or has no value, it returns empty string. + */ + getSwitchValue: (the_switch: string) => string; } interface Config { @@ -7778,6 +8398,15 @@ declare namespace Electron { crashesDirectory?: string; } + interface CreateFromBitmapOptions { + width: number; + height: number; + /** + * Defaults to 1.0. + */ + scaleFactor?: number; + } + interface CreateFromBufferOptions { /** * Required for bitmap buffers. @@ -7850,8 +8479,7 @@ declare namespace Electron { */ value?: string; /** - * The domain of the cookie; this will be normalized with a preceding dot so that - * it's also valid for subdomains. Empty by default if omitted. + * The domain of the cookie. Empty by default if omitted. */ domain?: string; /** @@ -7935,15 +8563,13 @@ declare namespace Electron { * Hides the dock icon. */ hide: () => void; - /** - * Shows the dock icon. - */ - show: () => void; + show: () => Promise; isVisible: () => boolean; /** * Sets the application's dock menu. */ setMenu: (menu: Menu) => void; + getMenu: () => (Menu) | (null); /** * Sets the image associated with this dock icon. */ @@ -8094,6 +8720,21 @@ declare namespace Electron { password: string; } + interface Info { + /** + * Security origin for the isolated world. + */ + securityOrigin?: string; + /** + * Content Security Policy for the isolated world. + */ + csp?: string; + /** + * Name for isolated world. Useful in devtools. + */ + name?: string; + } + interface Input { /** * Either keyUp or keyDown. @@ -8288,12 +8929,18 @@ declare namespace Electron { * Will be called with click(menuItem, browserWindow, event) when the menu item is * clicked. */ - click?: (menuItem: MenuItem, browserWindow: BrowserWindow, event: Event) => void; + click?: (menuItem: MenuItem, browserWindow: BrowserWindow, event: KeyboardEvent) => void; /** - * Define the action of the menu item, when specified the click property will be - * ignored. See . + * Can be undo, redo, cut, copy, paste, pasteAndMatchStyle, delete, selectAll, + * reload, forceReload, toggleDevTools, resetZoom, zoomIn, zoomOut, + * togglefullscreen, window, minimize, close, help, about, services, hide, + * hideOthers, unhide, quit, startSpeaking, stopSpeaking, close, minimize, zoom, + * front, appMenu, fileMenu, editMenu, viewMenu, recentDocuments, toggleTabBar, + * selectNextTab, selectPreviousTab, mergeAllWindows, clearRecentDocuments, + * moveTabToNewWindow or windowMenu Define the action of the menu item, when + * specified the click property will be ignored. See . */ - role?: string; + role?: ('undo' | 'redo' | 'cut' | 'copy' | 'paste' | 'pasteAndMatchStyle' | 'delete' | 'selectAll' | 'reload' | 'forceReload' | 'toggleDevTools' | 'resetZoom' | 'zoomIn' | 'zoomOut' | 'togglefullscreen' | 'window' | 'minimize' | 'close' | 'help' | 'about' | 'services' | 'hide' | 'hideOthers' | 'unhide' | 'quit' | 'startSpeaking' | 'stopSpeaking' | 'close' | 'minimize' | 'zoom' | 'front' | 'appMenu' | 'fileMenu' | 'editMenu' | 'viewMenu' | 'recentDocuments' | 'toggleTabBar' | 'selectNextTab' | 'selectPreviousTab' | 'mergeAllWindows' | 'clearRecentDocuments' | 'moveTabToNewWindow' | 'windowMenu'); /** * Can be normal, separator, submenu, checkbox or radio. */ @@ -8306,6 +8953,11 @@ declare namespace Electron { * If false, the menu item will be greyed out and unclickable. */ enabled?: boolean; + /** + * default is true, and when false will prevent the accelerator from triggering the + * item if the item is not visible`. + */ + acceleratorWorksWhenHidden?: boolean; /** * If false, the menu item will be entirely hidden. */ @@ -8421,6 +9073,82 @@ declare namespace Electron { normalizeAccessKeys?: boolean; } + interface MessageBoxReturnValue { + /** + * The index of the clicked button. + */ + response: number; + /** + * The checked state of the checkbox if checkboxLabel was set. Otherwise false. + */ + checkboxChecked: boolean; + } + + interface MessageBoxSyncOptions { + /** + * Can be "none", "info", "error", "question" or "warning". On Windows, "question" + * displays the same icon as "info", unless you set an icon using the "icon" + * option. On macOS, both "warning" and "error" display the same warning icon. + */ + type?: string; + /** + * Array of texts for buttons. On Windows, an empty array will result in one button + * labeled "OK". + */ + buttons?: string[]; + /** + * Index of the button in the buttons array which will be selected by default when + * the message box opens. + */ + defaultId?: number; + /** + * Title of the message box, some platforms will not show it. + */ + title?: string; + /** + * Content of the message box. + */ + message: string; + /** + * Extra information of the message. + */ + detail?: string; + /** + * If provided, the message box will include a checkbox with the given label. The + * checkbox state can be inspected only when using callback. + */ + checkboxLabel?: string; + /** + * Initial checked state of the checkbox. false by default. + */ + checkboxChecked?: boolean; + icon?: (NativeImage) | (string); + /** + * The index of the button to be used to cancel the dialog, via the Esc key. By + * default this is assigned to the first button with "cancel" or "no" as the label. + * If no such labeled buttons exist and this option is not set, 0 will be used as + * the return value or callback response. + */ + cancelId?: number; + /** + * On Windows Electron will try to figure out which one of the buttons are common + * buttons (like "Cancel" or "Yes"), and show the others as command links in the + * dialog. This can make the dialog appear in the style of modern Windows apps. If + * you don't like this behavior, you can set noLink to true. + */ + noLink?: boolean; + /** + * Normalize the keyboard access keys across platforms. Default is false. Enabling + * this assumes & is used in the button labels for the placement of the keyboard + * shortcut access key and labels will be converted so they work correctly on each + * platform, & characters are removed on macOS, converted to _ on Linux, and left + * untouched on Windows. For example, a button label of Vie&w will be converted to + * Vie_w on Linux and View on macOS and can be selected via Alt-W on Windows and + * Linux. + */ + normalizeAccessKeys?: boolean; + } + interface NewWindowEvent extends Event { url: string; frameName: string; @@ -8488,6 +9216,7 @@ declare namespace Electron { method: string; webContentsId?: number; resourceType: string; + referrer: string; timestamp: number; redirectURL: string; statusCode: number; @@ -8513,6 +9242,7 @@ declare namespace Electron { method: string; webContentsId?: number; resourceType: string; + referrer: string; timestamp: number; uploadData: UploadData[]; } @@ -8531,6 +9261,7 @@ declare namespace Electron { method: string; webContentsId?: number; resourceType: string; + referrer: string; timestamp: number; requestHeaders: RequestHeaders; } @@ -8579,6 +9310,7 @@ declare namespace Electron { method: string; webContentsId?: number; resourceType: string; + referrer: string; timestamp: number; fromCache: boolean; /** @@ -8601,6 +9333,7 @@ declare namespace Electron { method: string; webContentsId?: number; resourceType: string; + referrer: string; timestamp: number; statusLine: string; statusCode: number; @@ -8634,6 +9367,7 @@ declare namespace Electron { method: string; webContentsId?: number; resourceType: string; + referrer: string; timestamp: number; responseHeaders: ResponseHeaders; /** @@ -8658,6 +9392,7 @@ declare namespace Electron { method: string; webContentsId?: number; resourceType: string; + referrer: string; timestamp: number; requestHeaders: RequestHeaders; } @@ -8677,6 +9412,11 @@ declare namespace Electron { * back. In detach mode it's not. */ mode: ('right' | 'bottom' | 'undocked' | 'detach'); + /** + * Whether to bring the opened devtools window to the foreground. The default is + * true. + */ + activate?: boolean; } interface OpenDialogOptions { @@ -8703,6 +9443,44 @@ declare namespace Electron { securityScopedBookmarks?: boolean; } + interface OpenDialogReturnValue { + /** + * An array of file paths chosen by the user. If the dialog is cancelled this will + * be an empty array. + */ + filePaths?: string[]; + /** + * An array matching the filePaths array of base64 encoded strings which contains + * security scoped bookmark data. securityScopedBookmarks must be enabled for this + * to be populated. + */ + bookmarks?: string[]; + } + + interface OpenDialogSyncOptions { + title?: string; + defaultPath?: string; + /** + * Custom label for the confirmation button, when left empty the default label will + * be used. + */ + buttonLabel?: string; + filters?: FileFilter[]; + /** + * Contains which features the dialog should use. The following values are + * supported: + */ + properties?: Array<'openFile' | 'openDirectory' | 'multiSelections' | 'showHiddenFiles' | 'createDirectory' | 'promptToCreate' | 'noResolveAliases' | 'treatPackageAsDirectory'>; + /** + * Message to display above input boxes. + */ + message?: string; + /** + * Create when packaged for the Mac App Store. + */ + securityScopedBookmarks?: boolean; + } + interface OpenExternalOptions { /** * true to bring the opened application to the foreground. The default is true. @@ -8714,6 +9492,17 @@ declare namespace Electron { workingDirectory?: string; } + interface OpenExternalSyncOptions { + /** + * true to bring the opened application to the foreground. The default is true. + */ + activate?: boolean; + /** + * The working directory. + */ + workingDirectory?: string; + } + interface PageFaviconUpdatedEvent extends Event { /** * Array of URLs. @@ -8874,21 +9663,31 @@ declare namespace Electron { landscape?: boolean; } - interface ProcessMemoryInfo { + interface Privileges { /** - * and The amount of memory currently pinned to actual physical RAM in Kilobytes. + * Default false. */ - residentSet: number; + standard?: boolean; /** - * The amount of memory not shared by other processes, such as JS heap or HTML - * content in Kilobytes. + * Default false. */ - private: number; + secure?: boolean; /** - * The amount of memory shared between processes, typically memory consumed by the - * Electron code itself in Kilobytes. + * Default false. */ - shared: number; + bypassCSP?: boolean; + /** + * Default false. + */ + allowServiceWorkers?: boolean; + /** + * Default false. + */ + supportFetchAPI?: boolean; + /** + * Default false. + */ + corsEnabled?: boolean; } interface ProgressBarOptions { @@ -8900,9 +9699,9 @@ declare namespace Electron { interface Provider { /** - * Returns Boolean. + * . */ - spellCheck: (text: string) => void; + spellCheck: (words: string[], callback: (misspeltWords: string[]) => void) => void; } interface ReadBookmark { @@ -8939,13 +9738,6 @@ declare namespace Electron { uploadData: UploadData[]; } - interface RegisterStandardSchemesOptions { - /** - * true to register the scheme as secure. Default false. - */ - secure?: boolean; - } - interface RegisterStreamProtocolRequest { url: string; headers: Headers; @@ -8961,29 +9753,6 @@ declare namespace Electron { uploadData: UploadData[]; } - interface RegisterURLSchemeAsPrivilegedOptions { - /** - * Default true. - */ - secure?: boolean; - /** - * Default true. - */ - bypassCSP?: boolean; - /** - * Default true. - */ - allowServiceWorkers?: boolean; - /** - * Default true. - */ - supportFetchAPI?: boolean; - /** - * Default true. - */ - corsEnabled?: boolean; - } - interface RelaunchOptions { args?: string[]; execPath?: string; @@ -9080,6 +9849,53 @@ declare namespace Electron { securityScopedBookmarks?: boolean; } + interface SaveDialogReturnValue { + /** + * whether or not the dialog was canceled. + */ + canceled: boolean; + /** + * If the dialog is canceled this will be undefined. + */ + filePath?: string; + /** + * Base64 encoded string which contains the security scoped bookmark data for the + * saved file. securityScopedBookmarks must be enabled for this to be present. + */ + bookmark?: string; + } + + interface SaveDialogSyncOptions { + title?: string; + /** + * Absolute directory path, absolute file path, or file name to use by default. + */ + defaultPath?: string; + /** + * Custom label for the confirmation button, when left empty the default label will + * be used. + */ + buttonLabel?: string; + filters?: FileFilter[]; + /** + * Message to display above text fields. + */ + message?: string; + /** + * Custom label for the text displayed in front of the filename text field. + */ + nameFieldLabel?: string; + /** + * Show the tags input box, defaults to true. + */ + showsTagField?: boolean; + /** + * Create a when packaged for the Mac App Store. If this option is enabled and the + * file doesn't already exist a blank file will be created at the chosen path. + */ + securityScopedBookmarks?: boolean; + } + interface Settings { /** * true to open the app at login, false to remove the app as a login item. Defaults @@ -9112,14 +9928,17 @@ declare namespace Electron { types: string[]; /** * The size that the media source thumbnail should be scaled to. Default is 150 x - * 150. + * 150. Set width or height to 0 when you do not need the thumbnails. This will + * save the processing time required for capturing the content of each window and + * screen. */ thumbnailSize?: Size; - } - - interface StartMonitoringOptions { - categoryFilter: string; - traceOptions: string; + /** + * Set to true to enable fetching window icons. The default value is false. When + * false the appIcon property of the sources return null. Same if a source has the + * type screen. + */ + fetchWindowIcons?: boolean; } interface SystemMemoryInfo { @@ -9487,7 +10306,7 @@ declare namespace Electron { */ devTools?: boolean; /** - * Whether node integration is enabled. Default is true. + * Whether node integration is enabled. Default is false. */ nodeIntegration?: boolean; /** @@ -9495,6 +10314,12 @@ declare namespace Electron { * this can be found in . */ nodeIntegrationInWorker?: boolean; + /** + * Experimental option for enabling Node.js support in sub-frames such as iframes + * and child windows. All your preloads will load for every iframe, you can use + * process.isMainFrame to determine if you are in the main frame or not. + */ + nodeIntegrationInSubFrames?: boolean; /** * Specifies a script that will be loaded before other scripts run in the page. * This script will always have access to node APIs no matter whether node @@ -9571,10 +10396,6 @@ declare namespace Electron { * Enables WebGL support. Default is true. */ webgl?: boolean; - /** - * Enables WebAudio support. Default is true. - */ - webaudio?: boolean; /** * Whether plugins should be enabled. Default is false. */ @@ -9642,19 +10463,17 @@ declare namespace Electron { */ contextIsolation?: boolean; /** - * Whether to use native window.open(). If set to true, the webPreferences of child - * window will always be the same with parent window, regardless of the parameters - * passed to window.open(). Defaults to false. This option is currently - * experimental. + * Whether to use native window.open(). Defaults to false. Child windows will + * always have node integration disabled unless nodeIntegrationInSubFrames is true. + * This option is currently experimental. */ nativeWindowOpen?: boolean; /** - * Whether to enable the . Defaults to the value of the nodeIntegration option. The - * preload script configured for the will have node integration enabled when it is - * executed so you should ensure remote/untrusted content is not able to create a - * tag with a possibly malicious preload script. You can use the - * will-attach-webview event on to strip away the preload script and to validate or - * alter the 's initial settings. + * Whether to enable the . Defaults to false. The preload script configured for the + * will have node integration enabled when it is executed so you should ensure + * remote/untrusted content is not able to create a tag with a possibly malicious + * preload script. You can use the will-attach-webview event on to strip away the + * preload script and to validate or alter the 's initial settings. */ webviewTag?: boolean; /** @@ -9678,6 +10497,17 @@ declare namespace Electron { * Default is false. */ navigateOnDragDrop?: boolean; + /** + * Autoplay policy to apply to content in the window, can be + * no-user-gesture-required, user-gesture-required, + * document-user-activation-required. Defaults to no-user-gesture-required. + */ + autoplayPolicy?: ('no-user-gesture-required' | 'user-gesture-required' | 'document-user-activation-required'); + /** + * Whether to prevent the window from resizing when entering HTML Fullscreen. + * Default is false. + */ + disableHtmlFullscreenWindowResize?: boolean; } interface DefaultFontFamily { @@ -9750,7 +10580,6 @@ declare namespace NodeJS { // addListener(event: 'loaded', listener: Function): this; // removeListener(event: 'loaded', listener: Function): this; // ### END VSCODE MODIFICATION ### - /** * Causes the main thread of the current process crash. */ @@ -9777,12 +10606,17 @@ declare namespace NodeJS { * private memory is more representative of the actual pre-compression memory usage * of the process on macOS. */ - getProcessMemoryInfo(): Electron.ProcessMemoryInfo; + getProcessMemoryInfo(): Promise; /** * Returns an object giving memory usage statistics about the entire system. Note * that all statistics are reported in Kilobytes. */ getSystemMemoryInfo(): Electron.SystemMemoryInfo; + /** + * Examples: Note: It returns the actual operating system version instead of kernel + * version on macOS unlike os.release(). + */ + getSystemVersion(): string; /** * Causes the main thread of the current process hang. */ @@ -9801,6 +10635,17 @@ declare namespace NodeJS { * this property is true in the main process, otherwise it is undefined. */ defaultApp?: boolean; + /** + * A Boolean that controls whether or not deprecation warnings are printed to + * stderr when formerly callback-based APIs converted to Promises are invoked using + * callbacks. Setting this to true will enable deprecation warnings. + */ + enablePromiseAPIs?: boolean; + /** + * A Boolean, true when the current renderer context is the "main" renderer frame. + * If you want the ID of the current frame you should use webFrame.routingId. + */ + isMainFrame?: boolean; /** * A Boolean. For Mac App Store build, this property is true, for other builds it * is undefined. @@ -9848,7 +10693,7 @@ declare namespace NodeJS { traceProcessWarnings?: boolean; /** * A String representing the current process's type, can be "browser" (i.e. main - * process) or "renderer". + * process), "renderer", or "worker" (i.e. web worker). */ type?: string; /** diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index 27d56d5263eb..b17d05c9b2c0 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -203,16 +203,16 @@ export const toggleClass: (node: HTMLElement | SVGElement, className: string, sh class DomListener implements IDisposable { private _handler: (e: any) => void; - private _node: Element | Window | Document; + private _node: EventTarget; private readonly _type: string; - private readonly _useCapture: boolean; + private readonly _options: boolean | AddEventListenerOptions; - constructor(node: Element | Window | Document, type: string, handler: (e: any) => void, useCapture?: boolean) { + constructor(node: EventTarget, type: string, handler: (e: any) => void, options?: boolean | AddEventListenerOptions) { this._node = node; this._type = type; this._handler = handler; - this._useCapture = (useCapture || false); - this._node.addEventListener(this._type, this._handler, this._useCapture); + this._options = (options || false); + this._node.addEventListener(this._type, this._handler, this._options); } public dispose(): void { @@ -221,7 +221,7 @@ class DomListener implements IDisposable { return; } - this._node.removeEventListener(this._type, this._handler, this._useCapture); + this._node.removeEventListener(this._type, this._handler, this._options); // Prevent leakers from holding on to the dom or handler func this._node = null!; @@ -229,9 +229,10 @@ class DomListener implements IDisposable { } } -export function addDisposableListener(node: Element | Window | Document, type: K, handler: (event: GlobalEventHandlersEventMap[K]) => void, useCapture?: boolean): IDisposable; -export function addDisposableListener(node: Element | Window | Document, type: string, handler: (event: any) => void, useCapture?: boolean): IDisposable; -export function addDisposableListener(node: Element | Window | Document, type: string, handler: (event: any) => void, useCapture?: boolean): IDisposable { +export function addDisposableListener(node: EventTarget, type: K, handler: (event: GlobalEventHandlersEventMap[K]) => void, useCapture?: boolean): IDisposable; +export function addDisposableListener(node: EventTarget, type: string, handler: (event: any) => void, useCapture?: boolean): IDisposable; +export function addDisposableListener(node: EventTarget, type: string, handler: (event: any) => void, useCapture: AddEventListenerOptions): IDisposable; +export function addDisposableListener(node: EventTarget, type: string, handler: (event: any) => void, useCapture?: boolean | AddEventListenerOptions): IDisposable { return new DomListener(node, type, handler, useCapture); } @@ -559,13 +560,11 @@ class SizeUtils { // Position & Dimension export class Dimension { - public width: number; - public height: number; - constructor(width: number, height: number) { - this.width = width; - this.height = height; - } + constructor( + public readonly width: number, + public readonly height: number, + ) { } static equals(a: Dimension | undefined, b: Dimension | undefined): boolean { if (a === b) { diff --git a/src/vs/base/browser/ui/actionbar/actionbar.css b/src/vs/base/browser/ui/actionbar/actionbar.css index 3dc9389da6c1..13c9cef6ad74 100644 --- a/src/vs/base/browser/ui/actionbar/actionbar.css +++ b/src/vs/base/browser/ui/actionbar/actionbar.css @@ -40,7 +40,7 @@ transform: scale(1.272019649, 1.272019649); /* 1.272019649 = √φ */ } -.monaco-action-bar .action-item .icon { +.monaco-action-bar .action-item .codicon { display: inline-block; } @@ -95,4 +95,4 @@ display: flex; align-items: center; justify-content: center; -} \ No newline at end of file +} diff --git a/src/vs/base/browser/ui/actionbar/actionbar.ts b/src/vs/base/browser/ui/actionbar/actionbar.ts index 9e29b0ed7b2f..e7274468a8e0 100644 --- a/src/vs/base/browser/ui/actionbar/actionbar.ts +++ b/src/vs/base/browser/ui/actionbar/actionbar.ts @@ -311,14 +311,14 @@ export class ActionViewItem extends BaseActionViewItem { if (this.options.icon) { this.cssClass = this.getAction().class; - DOM.addClass(this.label, 'icon'); + DOM.addClass(this.label, 'codicon'); if (this.cssClass) { DOM.addClasses(this.label, this.cssClass); } this.updateEnabled(); } else { - DOM.removeClass(this.label, 'icon'); + DOM.removeClass(this.label, 'codicon'); } } diff --git a/src/vs/base/browser/ui/codiconLabel/codicon/codicon-animations.css b/src/vs/base/browser/ui/codiconLabel/codicon/codicon-animations.css new file mode 100644 index 000000000000..b568eed82d33 --- /dev/null +++ b/src/vs/base/browser/ui/codiconLabel/codicon/codicon-animations.css @@ -0,0 +1,14 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +@keyframes codicon-spin { + 100% { + transform:rotate(360deg); + } +} + +.codicon-animation-spin { + animation: octicon-spin 1.5s linear infinite; +} diff --git a/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css b/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css new file mode 100644 index 000000000000..a2c2be8f4a00 --- /dev/null +++ b/src/vs/base/browser/ui/codiconLabel/codicon/codicon.css @@ -0,0 +1,354 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +@font-face { + font-family: "codicon"; + src: url("./codicon.ttf?e042d2dda15ef7b36b910e3edf539f26") format("truetype"); +} + +.codicon[class*='codicon-'] { + font: normal normal normal 16px/1 codicon; + display: inline-block; + text-decoration: none; + text-rendering: auto; + text-align: center; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + + +.codicon-add:before { content: "\ea60" } +.codicon-plus:before { content: "\ea60" } +.codicon-gist-new:before { content: "\ea60" } +.codicon-repo-create:before { content: "\ea60" } +.codicon-lightbulb:before { content: "\ea61" } +.codicon-light-bulb:before { content: "\ea61" } +.codicon-repo:before { content: "\ea62" } +.codicon-repo-delete:before { content: "\ea62" } +.codicon-gist-fork:before { content: "\ea63" } +.codicon-repo-forked:before { content: "\ea63" } +.codicon-git-pull-request:before { content: "\ea64" } +.codicon-git-pull-request-abandoned:before { content: "\ea64" } +.codicon-record-keys:before { content: "\ea65" } +.codicon-keyboard:before { content: "\ea65" } +.codicon-tag:before { content: "\ea66" } +.codicon-tag-add:before { content: "\ea66" } +.codicon-tag-remove:before { content: "\ea66" } +.codicon-person:before { content: "\ea67" } +.codicon-person-add:before { content: "\ea67" } +.codicon-person-follow:before { content: "\ea67" } +.codicon-person-outline:before { content: "\ea67" } +.codicon-person-filled:before { content: "\ea67" } +.codicon-git-branch:before { content: "\ea68" } +.codicon-git-branch-create:before { content: "\ea68" } +.codicon-git-branch-delete:before { content: "\ea68" } +.codicon-source-control:before { content: "\ea68" } +.codicon-mirror:before { content: "\ea69" } +.codicon-mirror-public:before { content: "\ea69" } +.codicon-star:before { content: "\ea6a" } +.codicon-star-add:before { content: "\ea6a" } +.codicon-star-delete:before { content: "\ea6a" } +.codicon-comment:before { content: "\ea6b" } +.codicon-comment-add:before { content: "\ea6b" } +.codicon-alert:before { content: "\ea6c" } +.codicon-warning:before { content: "\ea6c" } +.codicon-search:before { content: "\ea6d" } +.codicon-search-save:before { content: "\ea6d" } +.codicon-log-out:before { content: "\ea6e" } +.codicon-sign-out:before { content: "\ea6e" } +.codicon-log-in:before { content: "\ea6f" } +.codicon-sign-in:before { content: "\ea6f" } +.codicon-eye:before { content: "\ea70" } +.codicon-eye-unwatch:before { content: "\ea70" } +.codicon-eye-watch:before { content: "\ea70" } +.codicon-circle-filled:before { content: "\ea71" } +.codicon-primitive-dot:before { content: "\ea71" } +.codicon-stop:before { content: "\ea72" } +.codicon-primitive-square:before { content: "\ea72" } +.codicon-edit:before { content: "\ea73" } +.codicon-pencil:before { content: "\ea73" } +.codicon-info:before { content: "\ea74" } +.codicon-issue-opened:before { content: "\ea74" } +.codicon-gist-private:before { content: "\ea75" } +.codicon-git-fork-private:before { content: "\ea75" } +.codicon-lock:before { content: "\ea75" } +.codicon-mirror-private:before { content: "\ea75" } +.codicon-close:before { content: "\ea76" } +.codicon-remove-close:before { content: "\ea76" } +.codicon-x:before { content: "\ea76" } +.codicon-repo-sync:before { content: "\ea77" } +.codicon-sync:before { content: "\ea77" } +.codicon-clone:before { content: "\ea78" } +.codicon-desktop-download:before { content: "\ea78" } +.codicon-beaker:before { content: "\ea79" } +.codicon-microscope:before { content: "\ea79" } +.codicon-vm:before { content: "\ea7a" } +.codicon-device-desktop:before { content: "\ea7a" } +.codicon-file:before { content: "\ea7b" } +.codicon-file-text:before { content: "\ea7b" } +.codicon-more:before { content: "\ea7c" } +.codicon-kebab-horizontal:before { content: "\ea7c" } +.codicon-mail-reply:before { content: "\ea7d" } +.codicon-reply:before { content: "\ea7d" } +.codicon-organization:before { content: "\ea7e" } +.codicon-organization-filled:before { content: "\ea7e" } +.codicon-organization-outline:before { content: "\ea7e" } +.codicon-new-file:before { content: "\ea7f" } +.codicon-file-add:before { content: "\ea7f" } +.codicon-new-folder:before { content: "\ea80" } +.codicon-file-directory-create:before { content: "\ea80" } +.codicon-Vector:before { content: "\f101" } +.codicon-activate-breakpoints:before { content: "\f102" } +.codicon-archive:before { content: "\f103" } +.codicon-array:before { content: "\f104" } +.codicon-arrow-both:before { content: "\f105" } +.codicon-arrow-down:before { content: "\f106" } +.codicon-arrow-left:before { content: "\f107" } +.codicon-arrow-right:before { content: "\f108" } +.codicon-arrow-small-down:before { content: "\f109" } +.codicon-arrow-small-left:before { content: "\f10a" } +.codicon-arrow-small-right:before { content: "\f10b" } +.codicon-arrow-small-up:before { content: "\f10c" } +.codicon-arrow-up:before { content: "\f10d" } +.codicon-bell:before { content: "\f10e" } +.codicon-bold:before { content: "\f10f" } +.codicon-book:before { content: "\f110" } +.codicon-bookmark:before { content: "\f111" } +.codicon-boolean:before { content: "\f112" } +.codicon-breakpoint-conditional-unverified:before { content: "\f113" } +.codicon-breakpoint-conditional:before { content: "\f114" } +.codicon-breakpoint-data-unverified:before { content: "\f115" } +.codicon-breakpoint-data:before { content: "\f116" } +.codicon-breakpoint-log-unverified:before { content: "\f117" } +.codicon-breakpoint-log:before { content: "\f118" } +.codicon-briefcase:before { content: "\f119" } +.codicon-broadcast:before { content: "\f11a" } +.codicon-browser:before { content: "\f11b" } +.codicon-bug:before { content: "\f11c" } +.codicon-calendar:before { content: "\f11d" } +.codicon-case-sensitive:before { content: "\f11e" } +.codicon-check:before { content: "\f11f" } +.codicon-checklist:before { content: "\f120" } +.codicon-chevron-down:before { content: "\f121" } +.codicon-chevron-left:before { content: "\f122" } +.codicon-chevron-right:before { content: "\f123" } +.codicon-chevron-up:before { content: "\f124" } +.codicon-circle-outline:before { content: "\f125" } +.codicon-circle-slash:before { content: "\f126" } +.codicon-circuit-board:before { content: "\f127" } +.codicon-class:before { content: "\f128" } +.codicon-clear-all:before { content: "\f129" } +.codicon-clippy:before { content: "\f12a" } +.codicon-close-all:before { content: "\f12b" } +.codicon-cloud-download:before { content: "\f12c" } +.codicon-cloud-upload:before { content: "\f12d" } +.codicon-code:before { content: "\f12e" } +.codicon-collapse-all:before { content: "\f12f" } +.codicon-color-mode:before { content: "\f130" } +.codicon-color:before { content: "\f131" } +.codicon-comment-discussion:before { content: "\f132" } +.codicon-compare-changes:before { content: "\f133" } +.codicon-console:before { content: "\f134" } +.codicon-constant:before { content: "\f135" } +.codicon-continue:before { content: "\f136" } +.codicon-credit-card:before { content: "\f137" } +.codicon-current-and-breakpoint:before { content: "\f138" } +.codicon-current:before { content: "\f139" } +.codicon-dash:before { content: "\f13a" } +.codicon-dashboard:before { content: "\f13b" } +.codicon-database:before { content: "\f13c" } +.codicon-debug:before { content: "\f13d" } +.codicon-device-camera-video:before { content: "\f13e" } +.codicon-device-camera:before { content: "\f13f" } +.codicon-device-mobile:before { content: "\f140" } +.codicon-diff-added:before { content: "\f141" } +.codicon-diff-ignored:before { content: "\f142" } +.codicon-diff-modified:before { content: "\f143" } +.codicon-diff-removed:before { content: "\f144" } +.codicon-diff-renamed:before { content: "\f145" } +.codicon-diff:before { content: "\f146" } +.codicon-discard:before { content: "\f147" } +.codicon-disconnect-:before { content: "\f148" } +.codicon-editor-layout:before { content: "\f149" } +.codicon-ellipsis:before { content: "\f14a" } +.codicon-empty-window:before { content: "\f14b" } +.codicon-enumerator-member:before { content: "\f14c" } +.codicon-enumerator:before { content: "\f14d" } +.codicon-error:before { content: "\f14e" } +.codicon-event:before { content: "\f14f" } +.codicon-exclude:before { content: "\f150" } +.codicon-extensions:before { content: "\f151" } +.codicon-eye-closed:before { content: "\f152" } +.codicon-field:before { content: "\f153" } +.codicon-file-binary:before { content: "\f154" } +.codicon-file-code:before { content: "\f155" } +.codicon-file-media:before { content: "\f156" } +.codicon-file-pdf:before { content: "\f157" } +.codicon-file-submodule:before { content: "\f158" } +.codicon-file-symlink-directory:before { content: "\f159" } +.codicon-file-symlink-file:before { content: "\f15a" } +.codicon-file-zip:before { content: "\f15b" } +.codicon-files:before { content: "\f15c" } +.codicon-filter:before { content: "\f15d" } +.codicon-flame:before { content: "\f15e" } +.codicon-fold-down:before { content: "\f15f" } +.codicon-fold-up:before { content: "\f160" } +.codicon-fold:before { content: "\f161" } +.codicon-folder-active:before { content: "\f162" } +.codicon-folder-opened:before { content: "\f163" } +.codicon-folder:before { content: "\f164" } +.codicon-gift:before { content: "\f165" } +.codicon-gist-secret:before { content: "\f166" } +.codicon-gist:before { content: "\f167" } +.codicon-git-commit:before { content: "\f168" } +.codicon-git-compare:before { content: "\f169" } +.codicon-git-merge:before { content: "\f16a" } +.codicon-github-action:before { content: "\f16b" } +.codicon-github-alt:before { content: "\f16c" } +.codicon-github:before { content: "\f16d" } +.codicon-globe:before { content: "\f16e" } +.codicon-go-to-file:before { content: "\f16f" } +.codicon-grabber:before { content: "\f170" } +.codicon-graph:before { content: "\f171" } +.codicon-gripper:before { content: "\f172" } +.codicon-heart:before { content: "\f173" } +.codicon-history:before { content: "\f174" } +.codicon-home:before { content: "\f175" } +.codicon-horizontal-rule:before { content: "\f176" } +.codicon-hubot:before { content: "\f177" } +.codicon-inbox:before { content: "\f178" } +.codicon-interface:before { content: "\f179" } +.codicon-issue-closed:before { content: "\f17a" } +.codicon-issue-reopened:before { content: "\f17b" } +.codicon-issues:before { content: "\f17c" } +.codicon-italic:before { content: "\f17d" } +.codicon-jersey:before { content: "\f17e" } +.codicon-json:before { content: "\f17f" } +.codicon-kebab-vertical:before { content: "\f180" } +.codicon-key:before { content: "\f181" } +.codicon-keyword:before { content: "\f182" } +.codicon-law:before { content: "\f183" } +.codicon-lightbulb-autofix:before { content: "\f184" } +.codicon-link-external:before { content: "\f185" } +.codicon-link:before { content: "\f186" } +.codicon-list-ordered:before { content: "\f187" } +.codicon-list-unordered:before { content: "\f188" } +.codicon-live-share:before { content: "\f189" } +.codicon-loading:before { content: "\f18a" } +.codicon-location:before { content: "\f18b" } +.codicon-mail-read:before { content: "\f18c" } +.codicon-mail:before { content: "\f18d" } +.codicon-markdown:before { content: "\f18e" } +.codicon-megaphone:before { content: "\f18f" } +.codicon-mention:before { content: "\f190" } +.codicon-method:before { content: "\f191" } +.codicon-milestone:before { content: "\f192" } +.codicon-misc:before { content: "\f193" } +.codicon-mortar-board:before { content: "\f194" } +.codicon-move:before { content: "\f195" } +.codicon-multiple-windows:before { content: "\f196" } +.codicon-mute:before { content: "\f197" } +.codicon-namespace:before { content: "\f198" } +.codicon-no-newline:before { content: "\f199" } +.codicon-note:before { content: "\f19a" } +.codicon-numeric:before { content: "\f19b" } +.codicon-octoface:before { content: "\f19c" } +.codicon-open-preview:before { content: "\f19d" } +.codicon-operator:before { content: "\f19e" } +.codicon-package:before { content: "\f19f" } +.codicon-paintcan:before { content: "\f1a0" } +.codicon-parameter:before { content: "\f1a1" } +.codicon-pause:before { content: "\f1a2" } +.codicon-pin:before { content: "\f1a3" } +.codicon-play:before { content: "\f1a4" } +.codicon-plug:before { content: "\f1a5" } +.codicon-preserve-case:before { content: "\f1a6" } +.codicon-preview:before { content: "\f1a7" } +.codicon-project:before { content: "\f1a8" } +.codicon-property:before { content: "\f1a9" } +.codicon-pulse:before { content: "\f1aa" } +.codicon-question:before { content: "\f1ab" } +.codicon-quote:before { content: "\f1ac" } +.codicon-radio-tower:before { content: "\f1ad" } +.codicon-reactions:before { content: "\f1ae" } +.codicon-references:before { content: "\f1af" } +.codicon-refresh:before { content: "\f1b0" } +.codicon-regex:before { content: "\f1b1" } +.codicon-remote:before { content: "\f1b2" } +.codicon-remove:before { content: "\f1b3" } +.codicon-replace-all:before { content: "\f1b4" } +.codicon-replace:before { content: "\f1b5" } +.codicon-repo-clone:before { content: "\f1b6" } +.codicon-repo-force-push:before { content: "\f1b7" } +.codicon-repo-pull:before { content: "\f1b8" } +.codicon-repo-push:before { content: "\f1b9" } +.codicon-report:before { content: "\f1ba" } +.codicon-request-changes:before { content: "\f1bb" } +.codicon-restart:before { content: "\f1bc" } +.codicon-rocket:before { content: "\f1bd" } +.codicon-root-folder-opened:before { content: "\f1be" } +.codicon-root-folder:before { content: "\f1bf" } +.codicon-rss:before { content: "\f1c0" } +.codicon-ruby:before { content: "\f1c1" } +.codicon-ruler:before { content: "\f1c2" } +.codicon-save-all:before { content: "\f1c3" } +.codicon-save-as:before { content: "\f1c4" } +.codicon-save:before { content: "\f1c5" } +.codicon-screen-full:before { content: "\f1c6" } +.codicon-screen-normal:before { content: "\f1c7" } +.codicon-search-stop:before { content: "\f1c8" } +.codicon-selection:before { content: "\f1c9" } +.codicon-server:before { content: "\f1ca" } +.codicon-settings:before { content: "\f1cb" } +.codicon-shield:before { content: "\f1cc" } +.codicon-smiley:before { content: "\f1cd" } +.codicon-snippet:before { content: "\f1ce" } +.codicon-sort-precedence:before { content: "\f1cf" } +.codicon-split-horizontal:before { content: "\f1d0" } +.codicon-split-vertical:before { content: "\f1d1" } +.codicon-squirrel:before { content: "\f1d2" } +.codicon-star-empty:before { content: "\f1d3" } +.codicon-star-full:before { content: "\f1d4" } +.codicon-star-half:before { content: "\f1d5" } +.codicon-start:before { content: "\f1d6" } +.codicon-step-into:before { content: "\f1d7" } +.codicon-step-out:before { content: "\f1d8" } +.codicon-step-over:before { content: "\f1d9" } +.codicon-string:before { content: "\f1da" } +.codicon-structure:before { content: "\f1db" } +.codicon-tasklist:before { content: "\f1dc" } +.codicon-telescope:before { content: "\f1dd" } +.codicon-text-size:before { content: "\f1de" } +.codicon-three-bars:before { content: "\f1df" } +.codicon-thumbsdown:before { content: "\f1e0" } +.codicon-thumbsup:before { content: "\f1e1" } +.codicon-tools:before { content: "\f1e2" } +.codicon-trash:before { content: "\f1e3" } +.codicon-triangle-down:before { content: "\f1e4" } +.codicon-triangle-left:before { content: "\f1e5" } +.codicon-triangle-right:before { content: "\f1e6" } +.codicon-triangle-up:before { content: "\f1e7" } +.codicon-twitter:before { content: "\f1e8" } +.codicon-unfold:before { content: "\f1e9" } +.codicon-unlock:before { content: "\f1ea" } +.codicon-unmute:before { content: "\f1eb" } +.codicon-unverified:before { content: "\f1ec" } +.codicon-variable:before { content: "\f1ed" } +.codicon-verified:before { content: "\f1ee" } +.codicon-versions:before { content: "\f1ef" } +.codicon-vm-active:before { content: "\f1f0" } +.codicon-vm-outline:before { content: "\f1f1" } +.codicon-vm-running:before { content: "\f1f2" } +.codicon-watch:before { content: "\f1f3" } +.codicon-whitespace:before { content: "\f1f4" } +.codicon-whole-word:before { content: "\f1f5" } +.codicon-window:before { content: "\f1f6" } +.codicon-word-wrap:before { content: "\f1f7" } +.codicon-zoom-in:before { content: "\f1f8" } +.codicon-zoom-out:before { content: "\f1f9" } diff --git a/src/vs/base/browser/ui/codiconLabel/codicon/codicon.ttf b/src/vs/base/browser/ui/codiconLabel/codicon/codicon.ttf new file mode 100644 index 0000000000000000000000000000000000000000..688b9ffd0bd65ca22309fb809f1e91d3685faf52 GIT binary patch literal 52456 zcmeFad3;@Exi9{{Ywa<6o`)oR$j;t7$sUsIG2`B8lFsv7I;8VJ3zQDf76?UwLTd$N zC@KMQ01*`s1&$_i9Pm(7(1XfR>MZxDJ;GsW1&z((exJ2=O3`!g@AEmI``7Q@G*8y_ zu6KT)=lcxrD&veX6T5+ltYh}_m4lHF#IIqDcj4}e#mn1s11A=3!1YO7uiAR~<|{ql zZT$&jx;c#5zrXXcBRf9vh0pC|EU$;%a_{UxZh3pey(%|e_{fSPw* ze(>>69t(#XxkGdK2`56-mmv26NrBGnVpW~WEuGoC}_ULD3ypOT3 z-iqgSSMEJ<@S}$xY-jAB?`F)hl%eio;T@DM{_Y+8h`*;h7ys~patC+&R(&_P(bo58 zW)go#DSq;)=l4w?@#>)Xd$EXX0~6>je((<7evvtpE4o{Ue~i7OTr1CU-N7nM7@oCY z1+!kZ`QQ~S$TSx{zxbSAcInQ|$X|VvBON{ExWk!l`mXpIzk_LUWf!Q&PO3-#OV-Iz z1A%Ep-2E?pmh70hjSZaLB>h$CkF{vavq*MPZ-@_~f6(&(NB^%6{_mXw|EJc#|EYZb z|1%fCZHR9Xe@cVE+r+z7{v+ImyTpmoID&)XzDC%BYlF&d&t7`=2I;TwfUln2bas!# z=}h?{-WX)VI2YON?0f9H>;U^Z`wROb`#t*@D{}|?1^XZDHMW)|*_YX3_I`FRYiDn> zA!g%twvBz9y}%x053^6BbOANeg3s!arxCRYJDH2QnTL6qkNH^uW!AF> z*2qFE%pxqxnpliAvp7qz7S_tzSPCu4uq=3gC(E%sE3hIfu`brldRQ;(WBrf`RW`t8 zutC&*CY!}(vpH-oo5$v}1#BT(#Fnt7Y#Ce5R~eMm+lw?;vVCknJIJnLSF=OxFgwDoVb`+j*eJW69c4GL z_puw~rk%>~Z!4`vQBCeThBAo@URme_~%@+B8opX^O0u|Kjuu|Ko7*k9Qs`x`sYrkKp$;f!-GaFJ`c zmg~5l8@Q3zaT7Ol3%7C`cXAhZa}W1&AE5_MGWGxVA2W4;rBCe!pc$+%m1v!w-K-MU z7CVdeNQ70!&Tdr+>y4e=rV>^jJG)&atU-2mhe}wD>@4sf5!NL;yGtdkP|T|yy4l$)Rl+)FXMx#>u;SU-{VHMYv$Iq` z5V!z4dr&3t19tW*mB1O;*{f9ok6>pHsRVAp&K_0?e1n}mq7t*=2pmiVUc$~^s}i^h zJA0i<;4kd#s7l~8?CkX_f#{3C)iU=lq8K`tAV?u5 zQ92Oh5tAq#2$G3Olnw+L#iX)IkXB5hcR-L^Orm!{kYG%rcR-M3Orm!{Yyd}k2L$=X zBzgw~Nyj952SgHEqIW=$eoPur338B0^bQCTkxBFp2(pn$Ln=W^GKt;?L0&S6-UmT) zGKt;?L54Dk-UmUNGKt;?L9Q~1-UmU#GKt;?LDn*f-UmVIGKt;?LH;s{-UmSvGl|{@ zK_)Y4u}YB6OrpF%kkd?}yg-oHOrpF%kljq8yg-oROrpF%kmpRIyg-oJOrpF%knv2S zyg-olOrpF%Y!i-@7YGP|No!RC7GM(P3j!)&66Fg5K422%3j$JL66Fg5W?&NK3j%sz z(k7LFBbY?_f`BNPMEQb%Eto|4f`BrZv`r=84JJ_;ARrGWQ5hg$5GGL>Af;VY1_-!> zNmK?12!%;h1_)S%NmK?1sD(*X1_=0tNmK?1NQOyN1_+pjNmK?1=!Qv@F9y0Rl#15|sgBH{eKRfPkBrL}h@0pqNBu zfPkf#L}h@0s+dG&fPk-VsO2A=EqB1~0WK4R$O2B4J zqVhmMX-uN>K)`EEqVhmMZcL)`K)`TJqVhmMb4;T0K)`iOqVhmMcub=5K)`xTqVhmM zeN3YAK)`=YqVhmMf=r_FK){4dx?LrpLncw#AmBtMQQ06MMkZ0&AYex(QQ08&AdXZv z2zZi7R5l36l1WrH2pE$|_o@W6$t0==Na-u82M7q1NmLIIuqczL9w4AnCQ&^=z^6>2 zdVqjbnMCyf0kbmcV=4i?GU-8;fMc2Tag~5*nMCyi0oyW(>IVYKWfIj71iZ^6svii* zmq`z+1PsiiPpbqp%p|HS2)LL@R96rXGLxvTAhLZ)R96sCGn1&UAhOU%R96s?G?N}v ziG2-6sy7Jenn_e|5O6k=sNNtTZYEK^LBQTj`l3oe;Y^}7fPlxDL~Q^8nKOyn00Ks5 z614#Yw9X`I0|>aCNnch82%bsQ9uTlRlc+r)pn4`zdqBYVOrrLHfb^L}?EwMvGl|** z0{Umt*HnTYz@)FM1dV`6)HV=w113@1KxF%qsBIwV3rwQ6fuK1siP{E&4#6a98wlD2 zlc;SV=oL(QK_&J}9I2fk=o(C-c7mXFFzLH0LH}S9wHX9Wgh|w95OfkIeNQE5CrqOD zgP^A{iP{f>#=<0OKM1-Dlc@b5XfaHp_Jg3$Fp1g^f@Z@cYCi}%4wI<;AZR;GqV|KJ z_b`dt4}u27Bx*khx)777{UB&XOrrLKpdT@b+7E)J#3X7z2s#s!exVYyCno(;CFoI1 zqP_q@qhb>E1&Az~67>a$<5GG}C5{W}f2c&(M~V6f#6=vbk3d|5BlQu8YjOO&N~C>B z)L$U3$C3IA#0@x7e}T9WN9r#Sufvi03&c%0Qh$NC8As|b5Vzn+{RQGy9I3xR+=e6d z7l=D?Jf{+O;YfW4;%*$N??BvxMDq#l8tpaObGj>ZFYA7%FX*tQ8h9!2W^hjMrr=lVchyfc z7#cp@SZMr4NEez2Umt0R?1)?wc`ovY=(6ZXqu*_EHXUwytVxblVxMYe&5N4f-~7|~ zSp3z*rxL$TE=fL_e5<9Y<$=}(tv9v)vF+}*zoZJOO{rt4CsKb%kEHKTKbNV?tj(Ot zyqayuuF9UwKASz)UTMFg{i7W%9XE6~Q3!(CtN`gPZv-Cye|^^Ekq);rky?Y^GA5BHtxZ|UFG|7gWs zxvuhBHCP?4URiyjDi5>_Y#MlAM%|3T86z`Z8eBH`%8++x)zEi`>xNei|8AyvW_H%9 zS$EHRV%E=R{c(1~>^ZYf%zkiAWX`5JUz+Qj+c)>*+-K*$Iaa0T=?L^vy1u`?O*gyiw%pHEk3;XfyJ*aX;^aSk}oYix%9`&LQI4w zVTpJntnbaxGUr2`NEUf9D_X@yuD5cx*T+LVS1NP8E+ke2o~Q`E5XUo@l{Y1lm}dn% z^jNt^7ZNH$0&j~qER#RM?cro98M41>v)gT4w6(M)ZG3ZwsP{C+qCWl9H@L=ZHCUXp z=W8uit42Pr(djh2Y%p@OuQBKl%x3QD_=SdVT;oM9+#X7Le*cOMS=!|DN|u}McSM7B z!B!s?9@SgxG@QGwYiCa#)@ijmE@&+_ie|aDxNt1!!Uk?C&J;# z$kdXs@W#|qVc#faD7+OJ4^RC858dJMvC+|~m-so^jDo;Tl|G_!o`HT2j^xrieEDL` zkuUPHBN_AL9Wl>HIE=T1J0cM}JQGIlRbgLc%1NbFrfygAA=b>q+xef!HsSK=lf|-6 zA5E5h9(}SraV#@Fo;fD$yM>OoaDFU3ksePUJBB|K>0|UN_0LDdEod+JZkgwKlB0|G zw|G~Ycc_JF7dne{3whgRhrAwX>G?%ltg0#rmRN@I-z5)Yw>Nr;_B}`&0L)@z!*{ zwX|I6DbznHG*EW(r}+Av{QPv{_tv4DM-QWa^t>B&pxpQaRDkl5(#B)x%h%L?iNWNE z`f_m#T8O?-I)ZVlri8AIE|xqv4DI}z@jm@j7_fA_CSN9IlFv@-4+Nu zFt}p0QtWEYmbF?zuo!Fxt25xM59j6|Z)y$X_N4q-r_1S#XZ@+w#=1JE$7=O#p4rvV zo;Y4?_oc0^MvF$!X69wn9it=jXHxsI`2G{~&XSa3?1d{G;32TQ?t`toJ)6uNR?rsBS%9Mxj&dq7G2 zjpL|St~@dEg!~zAYpX|oBpl}RO6d}x7m-sYH`ZpVo!>2=_8QYXC7Z|9A7S5w`XfAJ zY-urur*03IN?~E&)JIK5A>TfA-e?f~>Cx(Vbprn?_($D>dfzQ(Fc%GE`BN%s#M~2; zF}))yTBcqKmtbrc%GKXwe*?7hR^TXlaz=>(sD9d^wk(t%oDAK#dHK2;UFIm|MJH4m%p68iL-Mz(aDW}0vo*~5<$sf z!SyO0;@?dbezS6NBVaK2wKASPsBt4ojHBMAd@dw-+*Y0yN-l4nrfwn2t%A2)>s)?m zbwO_Zti+0w_v{Jv>`4`xs-3}}O*`j$T7FO6CfM5&v)1PpT;CJgbI(cnJB`84YExl# zi)Zf6P2X4hN}WeM7#ccoEB)!7oSYPTY8SX8 zpRQKND;27~(gqoMX%#6I)kD@0yUY88%jI9(aR-my@z3AfvgMnbg&t(`8T{1v-IVeU zcq2G|RG7L$P0R<7>O#J63jLV%K-(_x#3F{^uYdd7-#+-k5Aqr3&;RWToYeB7wxCrU z9FCt;>Poo)y({DS4CJ8$(lAVRP97^JArvtyNGQfJMYSG%5wpaT1mgnpa`+%I%SB9` z6R$?()8R8=8s(`MCnu40csw%6pB|N;pA1ig(It>`BVpN$d3|oATIo0$jzq$@XC^9@ zNj~FtZ#e9}P5y==msLJlg2n$DvcB*NuMM?FR2^u0XiRoIr^<<^6Jfsqj;Nn$v?VdPdu`ic5`K|BgJR)pq z-X))k_08^!Z4?^3p1JEbubb=fHV8s}b8dMj&n?e2BNK7?h|?(+gf+88%BQ`M%W4kC zuNUX66$+x$xp9TKCBCyc))&KXb5WePV(!qu#$0aWz|hS zYxM-TYy4NdPvQ4=NVs80R9{|YYZNOTJ66PW|yN9x?vn7e=E~zP#s8!*lf}i?-k6uk$qr?HW(GwWFoc z5sO}~6DVZyfbPg^7OMpBP?uRQhDuFj@sq|lLklG=q!i?etzeN?)!fxlg?k8U|BsYtqi@C`N6x!q*^8Dl;`BlUkFOe?%8)m6wZc z{Cm`nI1p$I==@LIo|J!@lz*1^XFB^C)*!KU72Dp3u~XWt2P*gR1Jfz^4EfC@Z>C>0 z?K@|H#g(+$Bwzt>|F#rK{}k_TPGfl{2<>0US6suVYJCZ=Z=BKb*5 ztk&T_A$$C|$b~d+tEMTdDO5E4T+={TChBxXGhGA2@16=he}6}PsPG&JQ&lDh$gdYy zrLiiR%K!x69V$CXf~9ymA*nBx(7#PXy-zEDKH0uFn_WEXU1>d;u02W5zahK09gkHW zcT9W?>)nfW-hujuv{p@4Q-p@AX!T%t8f^g@JQQ8DXU27xEz4Pg{p; z+K;c6k7rYYy>-j(-CLIQgoHP;si`+FT)oBPbG9FT-_iZ+`-7I;vdgZUvHIiJ4$AWf zNArR`ma^G1v)Aq{;cDv5=_~XpMiUsH_>dmxqyb*XKJt#KptI}%mIIp0m9QwS(5gu5 z4DooA&JCzkD(9iH#IjrjXUdbw!R;kHoWOHEsi#QlqgPeMcbe6VLUsi04TiauV}=HK zZLk@Ot-(02a*MISHaTtsM2Oq@ryUK3W0kpv20Ky9P3p0R{GN`Qv2oS500iMN7J*xua;*XUYbHNxSS;fsYSW zhcZVphvhBlBkAGFaJnSqM#mzRKsJT|n^-nbi9o{;{^qLJTkXzFL}NBJxu%{N=8NSg zhtfxm@Q%ZoVfjhEXc+dOnvG=-_@@b;ei}Q*74}F-pbXYaa3kgcsCATk1s{wdQ6HwP zP@leJO?nM}FG*i=yuzO*;U&+nf)!3yN9B2w@_haj?@eEV$7(u$FLdR3B;wF{Re;V3 z8P>NNkJJiF6|{X}bo4%c?zQx5)4$w3eTG|#ZuD)8?X~FXS;zv&6I~oDLb(90NpYY| zD9cGj&Ox^Iau12Lis-9|A(1O~3m+i8@E>AgxY)oOi^BFx?Ls3vAzGP0!PFhz?^*1B+RK)!z&Um-uX1cslB+EF17yMmx0e^b0jdeZ-Q zQ$)r5p{OQmQyS2c>#qCXv}J7UAKJ1NO-Ud7Z#Cr)%jD1VrAyMc+@kW||G@eunMB$N z^dQXXVj21qW)carBu_l_P_sj)8J)y4nqp@%XP>4}VTkiXU->}$AaPKwCM zPtheL#Q&FbF;7M@SX%z;Nhnn5!GAIJl>R@a@MaHLAD}gALYj)ESb`PQ0ksul@ehI` z&W9i5===|aH}LQj(lFBX-Z?3LPWTI?(gMUSjKKGQCHcuwn$XC> z3`kK5X>>3?Lrtm;d`yhN*qoq8mfMS~W2jDL}yDUgd-|Az=1Gg5*Yg zS~v1dpYU@KR6}Akye8^sOSeXx+-7Ss`TQO6H4k1pG<5BQYlNuT$wiaZ9}MOOXAcHU zA-lmGt*qLy_wp@^3tm&(?Dc!P`r{A_5Dnv`ndIa0{D7jP@b9VD)taXCXA-3K8K2h@ zwISp$+?r`~#MVcfHR!j;ATZCrY&W@@ErA)cX5{MY17@?xt#!Vx-5X}5Oy0s`C{8<9 z^+$z+kTQjMT<%bhJWN-G=8AgxBlQ6O0j&SdUCJ0bVch{b0*1xBi-BE%bHP09$osUI zfDk@VlO4V?S&T_Jo~+I(0-y_Tz%!&faAk5boWq(`<`9)n{F5vXR7M_#73kt3tVoamknf6NdaU?G{z#-M z@Ae*xi~$+Ji2P!B%==_m$WOgqckIdthgVFQ`zOT%!rN#~7V$N!k*A|Gg!oC|rBd0U za0O*$`Shf5i{7#a%p$28RpQgO9Lyd^7D_pwl}_&iq!Gct(j%^#>CVQOq|y_eHM4bs&7cdhF4^c zgV`+YP4Uaki$|)}k;P{DE`!ai6AE^Bu$hW&&_l~r##O~lWG{hV(FLhf20<*rnqDQz ztf;)B>7V^9{RQDIC{Gna$M;*PPAkuyU65%~wgGf?+_Gu6lsd`*Vx zeW4y?&(YU>7=Rscenfe+N#}83P@)x{bO6SY=D6pAw&=th4+?JtvFLs@I?B&Owt~-{ ziwER+=qTPChVQE-uh=@sZ3sQvQ4Vx z8Ob&Y4NWnj)=GeN^uNmUs(;K!)i<8t12o#i;dwXtefR*{M7a4|iY?v?>v%Wh{1S@n zbfHmjSV2t%wyy{-AmEA!hOL;HU@r5Lp|L_Iip^NDG{*H_pHA7WSQ{Fz z?@ze{{Y&rJUx^KE*n9ZU?v>p>Q+DCXwk`SL>o*s8sC)6q_HAqDbhzu11N_68TWpT* znH{>8p>U6(&f+w|jIJ?oSG$e#a5iO(eLmijUQ&sMvq8VExqs=p^=-lznwog!z&%U* z4s2hLHT%0(?Y{c(-jS&%WSzTX&f0C;M;7y+rf&%kwdgu$b~$Z( zwN6W&&7iTk9Cx)RTl@0afe#Nav_&DUah_bee$}jYrx}iNb^K2#w&EbH8^h>J=s8(( zX97h#V9)V+bC4uec$3QU9HflbO^$^ocp4IgGK_3+c3!yA&K)6MhEx(WD`(ApbndK` zi0T%Ns|II2FmrH~Q6q5q^z2HZDI9JpRBoF-;WGqHD4OrRtv4SHX@u^hgM&xAg-cSi zJqY*q%uea2e&mf;98RaB688!z{d$Mh=V|hI$+hWoH+g(k$JBmv@~TQwb5z3))F=2+WFA?Ld9F5b)!v>oc}+PHsXxBOAw)-rR+%ohF; zVd}gP_WM(!*^;lD>kiIIHz3d?k<(*EL^dP`ij;j z@y%!8!vd!96>~Je@Te*BFiGkrX&lL#?lIXD@*A}BZ>KF)d`Ecf=KC`{D?8I;S5-^~ zyU_(tML0b5Y?Z%8@@g!-3m%Tk@mQI6`12_21%&u86*&ygDk~`Z;7mbTE+V7}N zgIwf0?eg=)Zzspc1@qN6hr{x`aCr2p5Z^A}gQfrc?&w~1yp?=ae%1_)Y5;lX73YPc zoQF{o9vVlioyTlE=EK;jzUzcu3vp)^vM_H}gmUdZB%>hQ|GMJV2)|zRPIzUH@WvT$ z@e@VwnUUHGGdV3ha1qlBbdRLHOZQ$nz#ED)g(K6|FB*+gALi$brIN8?g2h|% zuUyPhs{@zrUFuDbbjN2F!HNl=sB5!kzvVUiEy**oDe{1VFw9}-Me=;|E|V*V#^d~- zV9ve0#@|)HIpu*Em}wg@aA zj*pXnNv)S^moO1te@~uy58Mjxu`Kg*@T0%CZliPuS^ZsJfp_yF8#Q?=X@S9P@4F~R z`FMU+j^|hHQ2ypGG#b@Mt6$2|1LY5d1l3h-zlin&9dN8#@uGed_Q|Kuyr=C)p=KVs zsNH{8N5mjq++K`B21?>Z)thpTPLOk$dhgxQkmpZKPL7joLZwu{8r6p)6R_5%{cAAW z5UYvBr)x+)WYB1scVossAFjfgGs#cA3nk%%ok7s)p;lpagV(G6;^LX`nFo4P`}ePEY)r}H89uY@!p{}+=ggU3P7BYR z30Hekq0q_$`&05nh7XtL&zU>#3b)JUE~k#F*zN$?{keI4Vb} z#R55^lQ<&rjk*XJh8Vf@*#7kBv-~|2!akld!apk%?<6imnhUA8WpadSc)Rf`G?D|b#40Pq<9*>ez7;X%#Os;gf8tXU9f810* zI}u(HP@Z_a@y;&!@lDCiS8YygPHwKBl?e9+Rwh=u+@av+HFe&2uAFoAexnBv@Zz~@ zRJ?@@Mp?b+iU`Z@YdBbm=KSj;>q>k1xxK<$Q|GPnJ8SN$6Ey3l z){n0Z)7^{nBOXY0(_~cDG?XY6CFmdu?V}hK9}UTEA^Ew+#s?aOt3v000PH#S(#8<% zBoBmneJCUw8s-1d*vQus2!cnhaidya6ml6qika^x>x`?0?R+J#cKS5u)kohwpM zwx5VAfDjHZo!i1Lz)0Rr4-B@56Bh%nuuWedk9j!|A;Y9D!isL4(v?vHlS0)a)lm+37!8EUjy*^G(dG^YAM!=g6~u;ZHD9s zV!n}ghN;*bMZkO(^G!<=#AdKf`1EdpJKhD1wpv5(c z6?KGSGjP9qV!VP!@bJP(GReEDfjFv(M~ZGfT?Ys1FdZGhpv8IeP#sZF*`Y;|@O`QN zRYic(9YPtLDnp@5D134frBD7U0E~X2E{MTVRyrOE&!8V_)u{vmwdzoX6@L!=bhWwp ze%KNJ27Xeey-s8vP%up`3xrahIT3jA&IluXaYTt~`%bMMyP~UxJ*Y%cD>3i=@ zpE^aq!du!{qcCp?{&RY zIKDR{4{f_~LDwI?#J~ExmtOjj@PpXaS9Z%?bKYEAFMo-@0LDzFLq%7h)s$Xg#}N9Zx~&5@4c%=ZwS9X>i0SYLEkZ2Snj&}ZrAd{Xh-9` zM&&YIXIdMpr(}m(1}veVw#gf8uJ<_2&6%d|{f%=HsimR%sNUJuKX<{bu4a=hY2w|& zTU^OlEu%iR)>IdNPp-8>^n&2@`lIiss(d`tY<7AY;z4hdHW;!Dv@~@$J?;-i!}T-g z&#PvRjcZnTOj^UEMVpnjv%QOqj69L73sqi_5!J`KrN1I(i!t0cp!QA z^nyCS3QLK8t5ZuUQV)@4M7SfL7T%gpMMk-)7nNmsTAlBQC;?7+mpXRp+H+vt(>EFz zZ_@h^E}~5NJcU6ev_2Tc@(8%}>3oRiAeV@*Y~(&2?70{sEd*aX2|yS^-~l`L!JWC4 zmv3LQGMBIRb#L7<^@D94MPA9Ijwg8MszQ9gKarYp^2k&11UiX#SL^y)s}{4tQCO7| zo|&KSF636O-hODu)}G$#hVDBT&pOgR+CErV)fx0Zo)#XLKk-N)+7oS^)1Bznb|L-} zc`E!Le+Ucsz@0l0x2I@SYAa$&E1GC^dh)8m0XB`gmoG%80Hs!YQQ0?lZ0+u==5s68 ze483D(Yb0D0ia#0Ix9`x!`)5FpKl(fRMfV^N;4_>cGO-;hw7kNwZzKWcv{f|RR0H1 zBag?l;9}Z*9N0Ww)Rr?`>(UnE;w|#&Zh1ab)vj*-RBY<^@^5({4A10)yxAk~=BI@E zu`^})-@C@nbirWIaPs$~g`2*Ar~I?;{CPt0zfuev#VlGOp&Buoo$#&?pu8}ra3cz& z!c0nP-HJ*MB;hOM+)adHih2(sb3~FTh=u{PeQy%so)-}bvmczXXu6`35zoI4jjBe(8{?d#U`YoJ@x`-y*%ZCJH&Ibb_% zK)h5dY#oS8gQW^a#C)+`P|S%%L@&m)H3f-5oj)_i5y^c2hQC_fZFQ>}Z^ zD~I4Tn&3~v*1lSe<%v{xr03q8ZsofCrtR`~D-T?2fv+g&bP^`49QVCsB$Nx;Jw6de z{hC; zFZyW0!4!&6K6E3gDsXKm5q>%ftj!g`VH58Zc<*Nr4dKL`B2TzlE#mCF2%{~q6o)AlW95mKFGT-Y zMPH26X8vnfsl(@gfEsms`B%Aob?aIIyr`C?}H6?r6#vn~@K}M4{Sr)On=N{Rr*T zRn{GWKdd+4KhzlOPpnOR3d%v8TM^3-Nt6|MW&&}m$I+9&!73b)=cCR~p$93Dy!ue) z;VZseTZgdVhR;m$KxA@UJ{?ihfw3JL8Nc%3%tMfj^nE27SK?z7Me_HNLUU}K)}DSl)*8oKw=n|$RA(CnoXjLWlY@SMaN!VjA_g8_JSkk9b=fj#v=9CB;x9d z#?eT)g5Ob~)XGJ~ZzxR^>ncz{5l~u^*AQC^k51jrKTz~>-&hzkXl%-fSMU!0VoIrh z=9IWu{xb$`7eA%Qr&_y6-+>CGu2EW)2shx$Qgzgpm=enlZbmCjSuUjc3L|E4Fj!15 z_0Uf={}+h&07F<1Yw076l+dyoeh59sk@UqdQc!*nP)j~=;U@sd2q5Hd_kFWGv32XH zY@UGk1XiH#aj}3WQz!fSp6%mrZ{3Pw_Q}Wks$lh`ucPvNYW91KP8{4ou@M7?0dCXu zlFbOBbW!f zXYd}VgF3>|dJ3jaB(3N%B@^hql!*rq6L?MNjatlFkE1c6&l>bjQzX{8vI74y*XpeH zU^wmcxPlIkR_jRT7o_!8uNjf~u4FuNc;0A96AHIAS(5!uo29{7ZSs07wzjDLn0|4i zm1|0!VY_?est!jgleGG0G!JxTGYy$`V}9{S&gS-nLWx*w-0AZ-MO&KNnp&H;9K5d2 z+n|Leam~7+HFf-p{`9g?46OVPd5 zSKks3qp!$k3(eJt*r0q|J&1=911uj;^AFPR^r`SdhL%@GKH!}`QSKJh=8e-*|Tg|~$e z3ApnG4oL9vLWnXMG!T41Ot7&@zPd!bZ$M|2Z&UbT@;zMe;=twS?#aDxSrYRV&O)zs zE%Co-h!&LbNremk^gU6`e|$dv`57IE_^;WvzOVRhDE@+BWx5bHk|h4Dcetpg9@sfi zdu`Meg%E+uGW1aB=7@X>YaGTT@MAAu1B0gg!^W!c^wf$=d6T!ucb9$q>Eu|XNDxZ+ zP{}xIEPB88ao%MFz?d%gT{(eXQVJx-pa^S}l%y^zQVrnKS)-H|7Bk@i0Gfce#7{sV zpi~kF*xMl-QAz^C*e6>?_#tDdQb!C7Ws&_}wckgs)vioZg-fR^(N(xQHz4S zI)@%?N7T{|rdr>s7}ABvw>BlVm5~Qpo#p~!u7O7o=9Kgxcn9D$3X42>KZKB&)HNc$ z7hwZ>@YIP(t^6xY^51x0GzQ`%SHXtQRN(Cf9-IzG`iyI|x$u78RqobOXshuh-^b$K zK==$cI!so>lklB~tNv`vO>szWSFi$G%lO0u%{SsT9}-@N+^8oliI{RR=23#f3F)fQ z;)$eag{cu^3?NeSl`w<|d(#V#1qPZciQ#;M;O`iWn=AFf{&~?*+S?uOSmd&Z(e5Uj zJrYV6g`k(84@9(+le&2KoP~4yTMUzv8dKnkp|-^#`7d67AswAnY10V;w>THKhr2xr zznF#ijJFY^*nw|PY=oqNoq#~T5(n!bcOtnQX&#X)-J2KTTLk3B%*8eam}qFTin6}} z^AB#^6nUO#&y&Jdu!m4h==1WImbBe`b9(UsL>BooL$2n<9g*I&pBK_rTRHg!e?u_i z7i{)ISBuFj+@Fnl{l(QAhMn5gttPL(!GH(pn=^|M za1`e2>IOHgDR6(fH-etgit;1SRW$bcOu_5-m|D6D@(yE@(rt0q(1z9Z4jo^YEH6=W z@MXHgQ;&=7Lk((LI~hz#-rpo%0pDA{5{iX= zdZX=&o1H`1P#_t14vnlX`qPb8Yv+RgnfkWMtVp`Z15>{bI-I(Wp?bd;&8%x^r$fPE z75o|gb01pdHMN$Dc9)(n_4*2Fi&fh;yt+fsVHQMt(gBX19x}>b*E#ByE?-0=gH?8F zW-_dcbz#2^O_#1`&Ax`AcD+j(BmQJW87iN*kg1n<_}?9p!3+KNIrLi-e6mXki^P4H z+C|6TcbO8QMln*!_w)(^IFwiqMeQtO3zXM$QSTL{nFp zaQsMEoq(P~HYl%)8u_{l&oYb4N%y<0+pL*#heAk|Jo8t26HT=+oeAk;$_sHOcdR!$w zESl*1B+8Bk9ql@yxGC&k!S)CGM|&Tjq;@LuD4~a_0{%nqE3RS~5n-XiZWek&M}iD7 z26RX!Fy~1u!O%#DaxqcHP1?c&pjpb(O)xkr04c=Fx)=8Db|Lm%+w7X1?g=#)^(Jog zxtrU3w;uPdP8zsL-_Y0;>dwryIqae%=1hnzvtVX#;<8m%C@O+Jssqx0B&jZGa| z|L(pe-A0|&rMCydZMhRCI@%%uhu+mFTr#}w)WTk8v~-`<;jrG9Z_Z&?O0IX)+}?0~ zUr=MThQy{OvDnxf2-nYwZS2Vf-NsI5%J(s|({5dDHrLhLn?#S^ENTs=IlU?K{Yd98*+7JMwu`N^h|K;L0MB3?h61_bgYlk@w!>=wY zAh<1|uoKm&O`axnSc*J{`h*_b7IBdT49G%@KG%_Wr!(9lt9T3?NN}UOLn5B!bLv)x z6witkQ3(%4)cf#_`&Sn&^#hxa9J}@C?gcHg-~XBQ*Jv~bt!T2fBqALqGk3c+8a=vG zW2q1NOk1~FjFw=~g58cY@CTZ*gq-#EPfGw+{${wrM6 z={1+TlU9w@(iv@OYwYrvd}dq7$_>~u-q2vw8q5Z64cW{-lc%e(wI!OfST%JGwmKeg z8LTD)K#G7lpx0Tfwz>wygbh)fysHm?;N-RIJ3H53d-4N^S9|H}xPAnPBDA&A z=6r-(?jo2Y>c@<)iW&*$)}QGOMvFIw4L>Gxmv@GlgMN?T>sp zawgorXtY^)D>gJzUb}hsn*2qPHbb$=p4HwZt0(NXj%;&%z+rESh9g{T?1uqqpg(*j z9_uR#oUhutX0dm5Pc^$K(_gJ`o}9JOzI;*7x@xL6)EFDjl%Sn`5iz^;qnmP8*-C6`g^k%;3 z`k|hjzWmTr!Vu!)%8N=>HG{0$zXc4d_ zl6Fl0#7EV@b#adp#irUBVQUBW(kVM+$kRo;V4-V}lMDNq!F*v3APWU2#T~oe5j#oF zfD89W#0BSnP#F!6B3@$z5w*1wghHx!5RsQY|E1B3V;O{{ib-f6c0j!(!NK7%ajjqsmAuiI)6uAD zZPhe7oUy34)K}?&CV4yG=PmX0&-Nsc0iS74wq;UDJA!U01N@~Mu+uBN@@BN zDw3l*4h1f+f9f^0{2^`R5DAGoZaf3O&f94jXv^M`;W>WG8{izcVBZIH~2%D zNGuk46jnj%QmhN|vyp$sg`Oq*0ydFH^&;&ag)O~i`qW?B>%Z_R{zy5@gZ$@SkK8H068Xv^n|)!$zHpIE;7`?bg$IT6 zu)UBcgY-N|0SZ(>RF-TKre zqaWH;y`m+Z(sy-c4jjF%xOBB~*Q&1TLugu6IA@qq>bd)qAFcK-3VG-3J9+Piu2~sB z|B87iSGxHFH{>#1=5TY@6W{uDd)UdxC*g;rd5jME0_IqNz80xKix{*x+AEIDR?u4n zM7lT>-9%C9gqZNXmU+Di-Pg^FMks6+FG0X+4ScF0nF=cOkzoU{ zr)C+UxJ0iKZ9~y}on?HI0t}i=qCZ-{cwx33QG(^t`i(yuSzqdc)pGsF-mR-wcBMP! zE?70PWKpKQJ+o*D|JAb0k5*+aPw&jjXideyv*`tk*7XH~S8QBgD!==FCcQ8{oNT3c z#+NM0X8Fs@(yy$}T(&d6Zj^lQv{9b)!e_ zPd>Qr@U6EV*-+9{+7@1V=;})sc|Pd>4nMl0r&3;D4Oj~+_Vf&2zkP-$FuZHv8vnOH z3tvv<;TIZ42X+nnJ%c-r&J6eM+tBB6_igCv=~+R;PxU^AxZ-DkLurN3q+ zGirgqI}rH$GlYgz6@BC-;XhC}tW&LC`KSk`hYJu4^AgSw#V|&J{2?ynb@Gdbtj%w7 zx!&inhT8NzqHA{egC4pHx!r=!=m-w-Z*(%@lY)FKw6q4_zn#E3B4hr2<$rJhjfkQwmPFI!glA6I(4uEILv0f0XK|w zZRJMXi2A`F(WW}Pz0P98=bCIti2qaKzxSeWO}{S{4Ack9^)81!V5{g2W(z(U<*F~& z2Lr*BuiqAEa(UWXlg$o`*=%t%CtKS*uBL!(>g0d-O+}8J7V^SZptA?)%N|--!Ai-J zVgz$tV41Lxy?f68)vm;ysWE7|=_s>zROxzjOEW9Rkx zp;)vExvs>0sPQ|bdrZfeC~>egKQ|&LJn&d}qVXDWSea=PzW6BF$g7BLCB0x2-{^W= zK22UFc=YfKnAYF2b5^)Lh?u1zmuwD1uX=$>Fju#42pCkQOU4nb_(2Se0h6XHcd?3rat z!}Hfio7TNT+~RF?Y|AVRw`}c6(n~H^JYSuY?CFF*xW=p7fjQ%frc7TNQTDaI zTTk#`EmaeC1Em?cQc?IxxXI!TIjBzNrbuhrO;C)Pow1q?oS`@FHji5Dc{z7703(>GLXz%OY>|yY zFp~{W18<&{-yTS1e2>EK4{h!O4G0=h123D&60(|XFc*tjtybhZ&A@1PQX|5H$8~m- z&1Q00jXKDEQKQvpVB`~QCP9nunjpDGWIh!L5RBCLy|sZMXH(`gKKW|OwAP7lKzzGI@uICa0`d-y47NQ6M?%QMQCXwbpb$?pMZFze`Lc3nD=?gh-pdC z9Yr(A>o0bg`^Xt4zy(CkFrlvxUm5s9pMOzHZ+j_U>K%Z;b>)qpSecst`hOf=a&OP9 z(BRD8xw-W}&6Hz<`TD-?dlq`yR@O&7`1%Wv_YJgL0>bN!@?U$V8V_)d&gDsE7kWCf z%kR9bB>d#ylh5weba&6&T#Ymu^>2mh^MkSS+BVO^J=_1)iO+9(Bjtc4JJ7GlrjLpr z5Os)9iNYf~%ogJ7dK(q5GnOh?5f~8`q!x7HTCZ4%n_w>%@~(>?Xmeh5Cv};Wd^&Ya z-@&YWH#dIH-rzNxy$uodAb-8i6$}MjCX*`=3cBiAEItSRKNN>ASUXzEMw{E~wi#;& zKH#peZ>g_;%NO-}qrQ7A6msX>>-G@6Q}C%qs@!oMMkjN)1cz~{1DVQL;7d@?R2 z^xTKY8Q>-pSEdSdRYfvBSF-K?b0@?6t%Ekg1qT5WuY_*dw`2Z5s+ z5Tk4ZRKzq38f=Fn%HC9UQls#x)dKS zhK|v8UOvEZ4`i=wb7SPnlr+PQufYPG&|dh5YN9N&Pzryvs7$@BOj>OU%% z@X^aaM`mSbcnn`+gY6{_#SU@@p(9X*mwSEo9-- zr$BrtPxz1x&f{8#c2pGG+Taf)}8=a}%u*npSUh`eOA(Zl19CuFeZ+{y_l72WP`h;s>ty>7~z>0`36o^2AT0}Vs zk60p!`*6Y{q#W$Y>nrE=I$~htY^sHwgh(q$QXq1^0LPxJi|el#Jp{`4iI)(fTl_1lVCCY! z!-IoYLfgzt6C702(?HYB{q#xJdC%$RpMUr#PqHod@LR~|4epPLe{1_ED737GQF0#UA;YXtW z$Bc!;$0FFd4-dz}gE(0QKUeCD8gtbG6c)VCuw0y!zu4>o0yP*1G!=%WlzRSmT$ae> z@&k_?yKXh{2^<_QEg8PGxifs>(!knFuj+Q!?+S4bpyUk{PRz=5Z1vplMsJ;hFAdPh z8TM&pXB98KFx)v-@9w_pQt`KLVxHlshdn!Tb87hSbHD%!n9l)2b0G%|?f*3gjFQ6^ za=;c@O4xFz#*c@lR>%FaRt0YN*JSOOeA21qmTSj{Tjp#}nF!82LJLdYtBg}2oy zZj0Sk3&Zh!_gO2e`QAHkpJDmVhg=Uj^WPSqV%vZy{_f+Ci&@s%x@s$Qpn}x?0?*b} z7yeq&+b0O6E4;CocSZMg*Ibudi@lV^h-~pYi?So8NREKQ8Qxj*do0Mk4gbKKz3d{IGw7KoH;g zXW_H2YG%*kjrl!re$sk1j2I#1`oZO9%VqkJ?iut+z-LT-9@(VyxLGN?nBDmUB{AAU z&LXFErQt<(IZrWu@$CYl+eX^oN%kVFc+eel8I3iOe@8J%6k1Ad;tDueqFoTN?&|AtTymMBP+bCkGspfCMQH6?HtN_OyDg)7AU4MZP!Aev-|zOi@w3=~o;gE?#4Vhyx+7Do8v)Rf-VAGyEufk?8sY>MOz^$9-A za~>0pD~CqF`8?+}dP*={oQ!;wGmubHCH^Uykghm@mVgin^;2=8f}}&!8YtPjk{?Dg z8*qvFg+@dgW*7eHg3ItPJWJV$@U74jJwRwKyrLXsWxZL`@i>RUy z;w4Q8timQxQjmhYZ+Hro!5d57Q3)L5Cx2UGgXA{mYc8c~5g7%4n$K*MQ53xn^hNda z4OtJ&t9+fw=JAzBgViO2CDp;U+7gdJnJ*V~t6LiSw=?ICf%-OaU8-qgS+vk&FLal> zYTK5!);UYvh4z+c*~X^#Js~&;%l_&qD+5%4eaBPMzC>UOg~{sE!E%%9%54VV4VJW* zc)EtK-?-N2@%Ywmyna|rs|~@*ExxvVn+m88076u@y7=aB5WKC{)(cw~{^qaEtuo>r zB)Jfzd=SbM0D3FH$1$bo12$d)>qyFurWg~hlX4!tWn?hgVnu%OwN>Jq2zvlPD%@ON zB6!M-dduyR+kNX+H#iKD;?n$LuZyGFfMSDhB`&^blq17#$EvvSMeakpQLvjE;&IPs zqIWnn1x>5hF0oiXlu2MUCjr-}zpyj;7@6biTq!&lOTeeGk8`WQ9_Lz8G5Xfb0Zjb< zk!eR@8@R|cFw@Bv<+5S1?6Dz0L4!aEPX=*~AWb~S4vRyOl|=m6Ujhk`N*gY)nqA;O zQ5lyR4GNjoY%^;Fz1k>>FNE0!W{ur?3`&*OOeUAjBv)qn;l&soJa*aqk#T%B zfzRNx#lCzaKI^vllp5IjN~0Ud9*y4kRX(?1qW|p{%z+OfPdtbo)&eU!fm%?XM}48M zV?HRQ5Cc!kr=XqELE%Jrp0OC`;Gr1!O>l20?F4clz!-87C=StFPvZkVvU*A_aB?$? zoI)JpL~tiyZQ$nylO?%_ko`>FYapAz2L@XPflCV8!0Lh5g#6){;XqD7)vbqZMjkf& zBh;CLWtORkIk^ZRH=)(H$j<8Eu6oG@e@DJI)Xc$+jwTIH{F-||oi2*NfYpK>rox%Y z*KM+UVZI15_J+B_am-m!WMMY7%YZ<8{*K9)t-rfXaGnfaYvz z+^T{3iY&IdAI2lT{Y+=gH{9T?@SB;pgn8{ZKZmcDvAfI`X7*P&zvZ!Mwd~U-ug(mM z!~1EbwdEVW<*X3j{|@hwO7Wdjb{M2arBy3V#;M=W-HZ(-UjFeLJY^8K5#c*NBu(p_ zHMl@iR=17uq&Tvb-@w?Igf2_`N`4YME4H1Yz_L!WzL0sg{XL zcFSt<+sx=G2^PDJU-Ni=hUgKZNUWg(P*Ate>rjdyK)|%qnPbc4I)hIBGq~Cm%&b;3 zLFXuN83mp851U_=v$_jxIE2w%VtswuyTSW2gUV!5;XpST3$c?Szfjn!G^jx*WYJ#` zpF2fI`472Xua`6P^D3*x;4;eSc&g|Al+#0>0MCpEXj+N$yrB#fs1o=JON^8}g2SMgJo(W<#uV>POfu+XPa2`Bgj=hP7K z@Tzr9M(}_UUSIqL_I5tzDQBPYMc`mE-Nwp2kKvdW@#jx#tp-30QM*R_^qUHmO7SMY z95~NYvO92|Hl9O{aSa!t!^J2*fwQ2Xv5BbKk)VyN*+!Qz(E3*GNceeicJJxq)g}7; z2X1mb>v=(LP^+fCv7|ZhN8_|`)^**zE`4$I3*v{T@2(iCd9zt-_;0_v%{%q7!6Sdh zz2*ehah{TuLx&PbpCa}(W|PW+=VAA`6FwI$ASX$@VINdF*9v5-Oa z!JUXfe8SU=?Nr$Zt^%m+5R@;)yM1LPMe8eK73+&i%6#3isaHMZ|G|oN5Yz=TzB6^Z zOA3REmtR%{3rlv_o$(<7_)51IUZUU({6M%k^Lkzz^gI)E=@3UV0l};Srp*R5sRTZ7 z7*bLutOH$w_6dy)!?*#xl%560ik5d9R{qQKwk>iwlPfl}yY5`MsV3aGjP34MnF2lR83zp8NXa`W3E1Ona#D4g6ZFIxW{;44fg^kvIDPDI@ z_sr%0FJ{6a4HPvY0y6T@W0?wJ*(S4U>Krv5vh<#%5f5rS_TDr!$Ehhi;zu|^g>c|A zXuMokGS|2>&#&0Mnbd%6I{&UgFYfdi8N_SXXNAQQ^=S75RwHh78fFbP=1;K z)5ZwF-#jHgDZ20-;fZU#M6E{Z4%|l=QuR=BD18u5_(-TB1cR*Xi_QvDk=i|R+9DuX zqonn^Sm;X|zie@^@-jz3Qb2ASjx9I_G}<9sS{W~=vrYoSs2 zkNvSMs%$-f%2%~|*~G;~KSh<1gJOa;ihqu|o)o{qzAV1uigP(9@$)%g6nrkKSEmsqh)ydXLdxR#`5-ww8~VFUbxF--cBgA-zwf zbdZim#td{oLXk8zD?V|ni@kE7p+#${SmSVQU$rx_>4IZyj<#4URy$nVq%CWNo$D{$ zaadOBFRfm;^QJ4Fz2=HtnZMt{=kwj*y(hba@EEm-a$E>3_fE(}3|#m*A?%tllrcc@ zgoPfPXbFOD2rrFYX#+43%At$404o9Lq6(>xYxpLr4R9!te!$HB2!#UZZ18$@Vj5=Mo zW03f;VJKU>1PB1!K8~%!6vkzP8m_`!Bukc}j;vXgQ`y0s+SH6}1BeG)y=Jmj$ygPB zCwW1{8@C@VudJ%NVAGx*8`ket$JG}siLdVJh?kWW`_rqsfz(7Dk-Gxzj;Qa#-CK~l zw&Z?k8&(uL1XJ%_zhTE7pzW$b9C&p5sc)4W-1hejnU|{*ux_ zB;RwN#D@!OWPUzN2x>{Df4t5`9?Nl!qJ&jb$kJ5Clj%veVKV3z z*W<$l>?L=pXppK_%n-J9q3981a7&I1Eo$_2$=-#Zs1PH&0(Y(rnC=enk*$NRnnT_r z+k>E8j*=>5k;8_9+GpcAk1oQj0eAvG5=TM_O zL9fZ=4cdc2%EzM=s*~HQkN#X72PMjF0dbsd5MTHeT7k!}6JO};Jem9Hz;kEk%{Q+V z4&W#_Kq%-ncQ8-YlI88yZUKk*=<$Hy2n6PSUCh3V&(Hlj(8<1g@>k-yBnFkHzQJS!_pOO~p@l@Jr|Dje)sxxSzFqTTy9uUj*e-|pDHB(`Gf+EtbK zS+#cS1uHtjZ2*ze&?2AGDcKnGEs5c=-a#o2Dq=a1ypCkd3hAW}QY7!er4RZZddT;n zs3UFQ)t;6_qNV56+_U&5Ig{vk&&fiNTRi6+xt7R}0HqIdu2D%b^uY%qp3glaie;3~ z{bMA3i|^64A|!n?D=Ok)08q!^+q;}B19BQl84qk36b*^TfqeNUXns|g^BcexI*Q8C zH^N%K1N$S03#2xI{BRES4#c9kn8SQ_Ky7tN7CB%UFvyz`Ac9I(48^vo4yZ9Gkg%~q z^Dr;!L0tm`B7_1;;4~DgpjXz{QQ@WBu0WzSH`1zr$g!)jjJyIM=w1}CliC*H2gVvV zE6jK4f)(MGS)Iu2JDJ$4tX z3D!C_K0`YzFR*LXdeciq09D#u`DG@(CsZ8~_r!%dP&K^))vOL|8AQJ?+)@|`Rn)m` zXNOjr9i9Ta%Via3udRr6TmAV4(C}LI-R%|O;VzA`(pIQcHxv{WwHwS1y~ZJ%n{gMh zqJjpsvd~tk)N~E6ffvW2(L0uCqXtin*HEo)jF#5|vL*4I5t0l3Y`mbzC}FoGz1#}B zf!J!hsQSAN4ty1y83exs6Nw|FsL$ONxRs!#TQFZK{4QO28C_GnhBMJYSC!wE1>ZaI zRh_!^rZCLDy?d=CZX;X4CYU7L~J$nEAG z=4A0;n}KtF-7PnV<9!DEQf71)+xj|=7seZEnuFJfuV3tN6&AXE#ct7P>TF!HN*A*P zmz7#r71&4M#N%9a9MAhS=Jhj(7eHz`aE|CP*>o6HG^df?*354D@WaT-16S_cHdq_? z79Q#Ay=nie!ZRO6Ka8C6dM^3gA*b_--Mv@%i~c#sy+dPLLf1FKch|*ruic1iFa207)0PcqSOH39#^T_xZuR@& z(;1$cicL?yisBZ`;&<5WbI*|TC(h(*i$>$uXe?^^RGd-^i2o9%!}{szZkd7dg<`Rw zo3&uoxG6>+mlzKva5*By^{euhVf^I8eIpn7v}$pzG+Y~~(+a`K%QeBu!|8!F8X+S} z(?TQ*$ukr>`fs6!D3fMp=Dt1iI@0)5GH8KJWzZnE@;k3P%Y9~?_uaAN|!*aq730JGF@ZP2guoo zpmuTDV!rQ1SX|j~+49vb1*ZBEjrt2;fI&bM?mR=mlmFC0`^f@NdBfb%oM(WzA-7Rm z4bn*R;@!Uh+ZEbMm-5gYp3(Wy!tzlTtD=h%1LzSVFYAUIN&9W$^pzS9f|GAe@jS3aq z;9}C4&D;U0R{3zZdgjKVtMDK8KJM2+cJxU2xw#j+tTR^OtvxYOe+G9O_}Yc>8k6p_ z-RAIH7z+IUn~4uy=8qhTUyczXwslWa*ee%bCT<~V-cs$cJZNMn3o}1exdQ@V1(JUh zWRp$?ux%sPB1Hf`DV=i^yYq!#{o<6UEoKTZ7N##ovSROuQ}d zow%AQ(+U(SQVj?UL=gPb<>0}SPpk<6C(3cSQ}8E!E3XcLZ>8!GSiAMh*Z~w9<6jIN z2>B3~LN7xVAIACIAqXTb5jqO)0_Ps&L77G=ZxGu!TZ*hO6oh55_2$6#!hz0}U7!-I zZD^$s;Q9t^Q%T^-AUWxLmvbo6`A>6hhYD>Ady-OGZZGVD1& zMrd7f-Og{Xu={?4B_Rf1zMolf*v$7L_1LN3AlSs-H4v@d5w5Jx`}M#DjDaJ z5SnB+9Xoc}0r4ZB29|_#+CLiK@X_}l+YocJKKG8zr{8K?ot}K~er;dZrCqcjksW~P0aThB;$yyPXN=9pG(ILfc-gUI;x8`T;TDg%V;de@^Mj2W4tMow?|*P| z|LPfdsn}0kh0qmD3=V$qZ{*2ggPf9m7uT1niZt-^LH?hN@|^2!fwBhp`*PcP;sB(8v757v%q+&*p^h*ZG!5nly2ZlYH>& z0);-1p|kCvESmf6|8_pm{9yw`;geUu108_=gZfXn#0~@-K?TIME0u$fcX|m$2wEuk zF{e^*-oGtgSN(5jUy&XELUrA2JY2DQ?L~ZQ@Hor& zczlOAF2RCmLlr$jK$#fWsepk3iEW%c5M>Yin^R1<-UN~RtVn47@8Fx7XG10Lv7;jR#A_v3FJT{ zRG%wU0tJLf;HU;6mkV^CB@BRxOT9A8L-cepLmSbv0jq+#IF0oV6er`|UA5>CYjJjK zrn>lAcQ>xppSjd6<7)=-`oaAUIf$=?Do0PH1N|{3X#Ndu+&jAk@d>p{IbFF&=~4sq^@M-9wvH{Y72X|;I(!X-v$J!z!KxXL z{}pOunD$k4S5B+2?+&p&R>Q3vU#Pjaw@2)gtArIV54CJfj^sUHNUO;ZA(SP_ybSe zyiw-$g*OhS2Jeb@5zdxTW$41TmF|^g?X}`;abX=(76WS=Tp}dP2U?b8%6)#VQT1OH zzlcBf=;ccs`|iA`pdjC>ZW-7Z4vQy=cdZuHqYJ5e^jmI7EM;wb*-9oCXKR-bY*+G0 zTqt`7Ts?&S%agQjj79)6(eIBX1YeqbdW)}9e71^hTK?#Rhnn*fcU-jf*5!f8$M4^Z zZnwx9#=df@OGIIh%j$jAnfo8Re#6|+z4twFsJP>nZPX_|4#|I!izmWkL9r*2!VCit zM*7?vRNaVB9p`f!O1c>7kt8!pkV71&YQ9us16-rPh0!Qm_@`8;5`+`^dz?;{Prrrt zQH&HUb7YnBLC;;HG;a9+aSVQtte(kLnFY~8TntOw#4t9}26~VE07R@{==JAzgogYP zYDy#bAL=LlK$}60j~O5Spk70Fp&uaL_&(sg%>7_=N#jf6r*`4oR>7N{2m6f!+`zyN zPZ26+lS{_c>;iIvmn25mgEM|3r-VmpI4BVoT~Hc#Scx(4aybgz#B$2ozwm{gqqi6T zOY!YTd(Jf<`P}DR`<5JSD&A7ubacr+*ZD8SZMYTru--rIikgM4Rsqc^;!*b>{i>0Z6o;Wj!;&1zp|IW23DDaR3$ z`Uor?U8t~~r>K%sp=>by5`?-5E(DNOKzjvRN3KHdAAlZC7aZ8g0jF}>CCGE7R9EDv zs$eJ*!y8RHFp>$f>h5jpTVTxwTACX2OG2@@9h_SE4NWZp*tjk0x9zPK?_u8B6`LYu z!Lndv(+Y6SXl)9A9Dw@5B2!(pzcseZ&vv$dhaw=X^EQ|O!~m%*Zz z>8(2Wisb&2OWT|Kwys;|6~6pbL+_3aD~bwSPIu9Y4LfS;8=hh^V}Vs`)Ch>(1l=qEQu(>JkNKwppOB{Rz-i>qd zEyA)b$6_29jM&a6e7{4QG7H}iH)!trU4(|l=)^Sq2j9=`5>K&49Gp6kh-DUaV1N9! za~*hVp#v1Zkm?gst~%B3Chs@-7$H)f3l*?<0#2^d`a(TG$3h)I_$|3zAVq~r7;CzW zSFQ!6@uG6JJI~coA3)W=3wXWshom3_Yn5vIooU!v&=fd)%K=|Y{>4qg=Szbfmp-)p z{7SlWSD;e*B{!}=zn~x60pC9rG-tWz4_H&QD7H%075CJ%(Y>C>r4hzgswO1y3gE8W zRD?Ole~3XkQ8a=g7W2;}=Ex>K?gI`ES$uzvL&hL=1JVYMlxGUX05%+o-ymS|h97m} z0IogvgYPE)gS3WA6{Mv~5ER|xWgy`3ZiGJ)8VtTVgnLhl8bgmDaO>et<1pk9u&M;{ zHPpcah=};q6oB1Q;Yqv!9i}zDE^>L~is)K|$!BKn)z*HU0_RgG8^J465Nk02K+|B* z`QzHi<(Eg`{vcg$oooc_QXII;?Ns%O@kA?lv?u`-erZb#vfs3cPB-p{BtHSFU$f7nU`%?S+yR=4)m@ zimoqLHE!PCVeki8fr~|zzN(hC`j7)Rt*xcXrxdSI7DZcH8!B8fOKELuYi)_fSGKBE z=BaEvU04lNSaYMVd2TXCV&`vWV-oQkeNk?X(!HZKk+LH4xb=-YL>4~%Q`8D0;EM=P zQRM-sTX-b(nQx^S7X9R3&+7HrOzxn2+yz7zw7 z2Mq$wJC%cQe6>!53-IegMMv0?g!_|$%|WOSID!6=$m2-D!?+||0p(D_bg7b{2GFUH zsLm)>3$QW){}8JntU?IKDF*Tu$?NnQH=mAxS&s$5#Z4gMp+oUA9GQNpC-f^Zt@Gnk z$O-#kD~=9hDa6PDOkqwHyBLX*g342DUDIc>HhGI>D@qDf*i>4Ve}!3T_abFjP-&Iy zp@zMy0}5FL3$|7P7E=ZC8+G=oNU^mlxTI`r<)i`%%_d#Ecv;L@p>I_M7C3Z{>wD_gwrL7YcC${2v`v5jG`gaYvvuu~klmoxMT0e1^Mg%n?(Vw6 z8n-sTRPebf{2DcDQK^B@QM%k}twyQcsPYvTFNwNw1esD_?kz8uJ2%)Wd_c!)OrD6# z>ToIZwMrFOVYNV01)OE;J(`LpARE`x7_kie0^kepuWV$h{%i2#b!1+`)N| zEJ@Y{ch9+F?l^Z$;p|^J9ubeQs~+k2%baELQKjRaf*y0!C0*Lx^uVs3ryHBqtedGeEnB{R)5?yrXPJD7IC-uP2>bTz1IyO! z=-Rl~Y+NxO2n2&myO)ZE=Sqs;mhtOF{LCjH!LSx`$%*AnRyg;Juz7ADYSj**^&qjp z)BD_Zk#ZP_wZ9FTI&@U5(hCb?@PCe)x}?Z!trX9LxZ!bCScK41KPh4gD?x&vp)%Zm zenK4dv0xDK)1rKrJ{LRv`S}r00;YW%$EYXlSN+_#U-`ti=28Shc~q3KJ(gThG<9A` zlTQ7dy)Wf;7QpvZMrV)II4Fk-#MuHWm?&Tg!%&AU6dV{iN@P{0i+%tVY#m}FV{g-ez(53bCy;S~N72+{2a{Ibyao_-fAdPp7 ze3IroLBj>nPt@2`UA0$NUtP0SxozdD3wNwvTMi_^&Vk--Tk0Bwr#4hyRdZGKI(b9g zmTf1uZKfa?y7PUqw=kxNmAaKi6)Y7DX)G}q)wOiD(XmEVnG|Y7Bp0wnlb~S%bP-Nf z5c=T#IFq%CDA~A=bw+P$Y+rlLMQT|%WOv1?Y9QVXwb8T4!e>XsU$QG%eyk|K1a)VB zONsv%8sUUS+#3;RZuZI*@q=q%+zgc4Ot+4iNw_+%Jqt*s>v(WM+S^g>6AY_t_TQgF<3zy9V@0^PNp*|IrsA8~Qo9W-y4C!twArnJZ z(ky(f^cv$ELG4dUwGWm?Eys1HpXe-bAK&Z>teRd~JaKgFUgOJa55x|vyhJ!_692Vy zr^&rrm)~I;40gCn_aS`x)uz_Qu6s7!y;1yIoaM#DtHd{6o@E{4heRVza+}X@w)kHi(RcXC3-*4F_-d?a-We}{9@`y}jP`wdYDU%y5N_J_{Rs}Q?qc&!? zwtH`|2P5{sb`?OSDm`NXUP64@C7fVtRZZ`%ixpsWV3mb;tIb*k+oM5zTcdbKW1{7p z&)sjpuK%)h9*iaxi0 zJkDrMT>f3paffBFNV|ah+=DzAaPN?R2KWj(5jzPXD7kP;NGE|LJ5WTxR|w1lN7ptr zLtrV16b3OXY@xs>BxpS{H)-fsGn3#7!C@H!1jk7^Nu(R2i6kTfi-u&!YOYy-rM@Cu zz0F@5v~E6_UEz>1Q-RM~Rh%)?1sQ1(@U$wlT z%waQtJ;IzmP??$Ay)P23lp9|NHpnx9PYWydus6crC@8Qs7MOM448JN@>U2tIcyeKv z{_3vlYmF>eT)Z<8*)%z^#uLE(M`29+^26ght-%#LY9e#@$-ow@03LqXf#IEcm-Ykp zmC}xSx4R%TTvy!SnFt@)e^s&Qe04tet3)2PSgZ1o( zCB@>?(mvpecouUM`!HbdSfg+Yy(gS-&#f$A`>G2wxqhp&s`pH5)8njsiMLrt=$tW|DcL;QOW-MV4jrJMr?e_+59!m%a2)%VKxm9pkb|>W@3mPrZR@NwDU6TuY!(7RWL}s&)q5 zkhRurt6!3^FE_4Awywx@!lMGN460#-0!5ck)org^)!ekThT*Mwjtsbcfx5@-<95yc zxPRp9A&+~Qs8iYf5;@S{v;P6H*7+RhsSDPuYK_z$yz*d8v~A^@4GsIsqs#7F z7A@ab-x#gRZz`z5jgf4cs5RAXs&nd-sikVw>P1=Cck@kr$PxIhE8E}7TP>zE}h=uu;`ohR=Y(f z6Le~|%Tm|e+&AL8YdAW5Pthe8X{m4OZW7<=*}jju1G#eU{xtpn z-yPgbtY51CqS|cxeK_ciJ%+` znh_}gVso>xZlA9^HWR0(IEcilyultQNVB+7>wNKP)Yb(ZHyFz3dd$~ADuMQWE+aWW z;3--$5D`Qb0`enEArc$E0K7nVQ2@$UtI`mjodK(dQ2I$#j!yWXdLs0P5;jGvP{9`1 z81cv_RybPR7m6DGgT5@}ry}=FIq#k{3LKX6u?~F^!Mq{_^DaVQuWp{=juP;Z7XSCX zdV->dMQumA>cHp&4w6O0c5tRh*J^#PUQ`s=vwzS((2rr#BB=#)))jJ4qS42@RlKYv z8n9Xe(U$nibIk`~(0x+3KE9vykq<7L$4FD0`UHGFHppVa6G8A(@EqnOFh%f2-bC?f zwqphU_02cWVZ)HC6qRihEYf5z*lg3jW5BfV)Zkxrf_Eq^zkn2XPGvtE1(_j%_ zh|gZmz9ufe{HLFn=7bav`It{#@`VF$&qp8T03UbrV-I0qWCo4{Uk-bh{3L_}ISZ^n zJ+9Nn2rpKJpnV~s&)MI{v&Ke$>@+u$sLyO{)%N{Oo}`L=^qs4av~@T&?f zfXJGFB5P3DK`2dpiJZa>K{4X-2RCcZpH3y{8{tr(Bf)q8bsO+Ogb~ig2vvbU>8;)} zmqV=*w8pYhD_i0ybjUSYxudYuuaD~*lzQMp6&lbADm7Y`R5(hU`8JKZwN-D`=<`bp zl*)FM-eNQBmGoo*kNj(Dhu5ZaC_U}f#TH9Z*yHpY706rF*ipz!u3%c>M^uB-FZNs& zdGW=_6gw>JHmYftD1Zj#L=Ln5C*0VNg1k|msS zGoSb*K9Dl_2YE%9=_2CUh9lP=+Ep!U*n9Zesh&3RS-4ze6_M`yAD;{?zx8(e|D58)VhH4vC)%mkZ^CE!%QXidn)6Ad8D+&bfY(YkUJb$0n*k_FIm&nWFxexpG>{?6GjZ);JJ~LFG26}d zAeyk3?PCen!+KdC6oP&>zy{e6yMzt15jM)kSc;9K7)qLDSQd#j2S8eJh)uG?>{50a zn_@@U^gQmyMf(^a*;Q)PqACrt?VefjeVMZhTYEYV4nrr`Yv`i zyNBJ&j;(HV`wIIin`Sd8So}}yYwQ&`V86j$W&g~+$-c#2V<*`u_Al&T+3W1v><#uN z`#1I-WaNF1eV_e+y~R$me`h~rZ?iM(9rh#kWA+pFQ}!-Ofc}jAC;Kn_heun`0vTn~*1f8%01hNzjNW z1r@ktHG)>q2}n{EjDkrp3rGqVY=T{IfHl=6CYJ5RX{^C*?JCwj9(2QcZPJ?Ug( zcsw;Umd(f#eSIp-*TJC!Nkt-^PE2a?m^v8kNo5Cf&A!yZv0QT`*`L)(JEVsO2D4_V zof%Dl^^5j9Zv*XZS-c1TjB)XniE*{`a$;QBgI*=m@*Yfoc~5Gj4-ctfH9d?b(!(k= zMv{rKz`5Q>A#nPJvJfqak?6$Ofn<89e<<1K{Nz2{i#GQqvWZ2XFn#Q;f<>=KQUiXdTQX)3Tn40d;R;BstcXNE>ONXeK$9L4R?ndk2%f z!y5iLGL*^c(K?V$jqz)^@Pe+NVg4O{?X>f+CdLiDL+Rd;WVC;1WF*;V%)LrYWJiX^ zlKR}s%t#_L$oHHW%Hp0T(tV0vY|Ciy(L_3m+otRt85$p-#D)}lhz-~QEh8Q$`gngv zaAJDt<-|C>kVA^&D^epPiE-&OTD(Z5qodfa;7u#U^k{M{8|@p)^iE_*{Ht=COn4qo zq?6I!!Nk}=GK1}78QfYmJ!cbRS!!g5#wL=w-gFZ8H`L! z`*5Nfdf+FirW@RYORnfk;x?H3l9}ObYJA~zY<E(!;8b+< zoEjU$tc=R(1P%0n%Qupk#Qjra{11(1hBEr(=y-NAdT?kAGs}`3o1nhZexu3Jo@82k zZi^y`Ih$4_58wi+l81UnCi;@vq)&%|15q$EmPk)( zc$@C3mbcL3p@f<@$NTz?ypfsc!Ff)MB%RXB$x#fnVcg#|moAf*^ET59=>Ug^#^v-1 z-#U`aDDjw0rWO4oxGb7}%yNEDRJ=8TJ+Z|=4@nG8l2^(3SE+I641G%JJ^8>81knIy z4(1d_Nme^R;{;=Fh&J;kjWG?iaoGlvW@?R3Kuo2R`zMl_tN|MaCwlm|q{is;{MATS zDK!-XBdMOGb|4karf8BSRRifn53aic&*Ou5g=vFVgP2EI)gZc(N>9oMQ=>`KU@ARy z7-JYxDUA!Jz=2a)#n4z!>X3YBtUslJapZOen+&t!3T?W46JU+gCzUp zW2tOX#b?V=6&*@8ZY=%(x=h`iLs%>{Df5FiQeJFKvF%Pzzyn6 zjA<~0FidH@jwdEENhLmo%P$)r8k3J>vf^Q4z<^^y+T${jh*#yl!91QG!dT>2Z~p{c zzMMX*#xo9+om5~jW|C??7IAqL`zO$CT^iSzM%=+K8G!k#!xX~}iQ*n-wCQ9& zZq!&W1SgvK?m^DA<8Ke(ktf$h$$v%8L+B9M2TbV&QZNnFg<38cDyz2{696iC!%PP|t70IkMol0eK zC_a&;@vK|4NtVuJB6-@T-K^$#HZhn^N-@x+87Epij%C)5@mNdu&Zl^kyg0NqJ^+tXGyz45+h- z%rH$;4Gg6uR+8}~T8AJBhYlyT*+E>hXip-YL33iXCquJQEj1^`6y(mE&`>*i5JFjXIF%ZuTg;zvTy8!6KTI&Tu>b%7 literal 0 HcmV?d00001 diff --git a/src/vs/base/browser/ui/codiconLabel/codiconLabel.mock.ts b/src/vs/base/browser/ui/codiconLabel/codiconLabel.mock.ts new file mode 100644 index 000000000000..ef04a2056903 --- /dev/null +++ b/src/vs/base/browser/ui/codiconLabel/codiconLabel.mock.ts @@ -0,0 +1,24 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { escape } from 'vs/base/common/strings'; + +export function renderCodicons(text: string): string { + return escape(text); +} + +export class CodiconLabel { + + private _container: HTMLElement; + + constructor(container: HTMLElement) { + this._container = container; + } + + set text(text: string) { + this._container.innerHTML = renderCodicons(text || ''); + } + +} diff --git a/src/vs/base/browser/ui/codiconLabel/codiconLabel.ts b/src/vs/base/browser/ui/codiconLabel/codiconLabel.ts new file mode 100644 index 000000000000..f97348a3a948 --- /dev/null +++ b/src/vs/base/browser/ui/codiconLabel/codiconLabel.ts @@ -0,0 +1,33 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'vs/css!./codicon/codicon'; +import 'vs/css!./codicon/codicon-animations'; +import { escape } from 'vs/base/common/strings'; + +function expand(text: string): string { + return text.replace(/\$\(((.+?)(~(.*?))?)\)/g, (_match, _g1, name, _g3, animation) => { + return ``; + }); +} + +export function renderCodicons(label: string): string { + return expand(escape(label)); +} + +export class CodiconLabel { + + constructor( + private readonly _container: HTMLElement + ) { } + + set text(text: string) { + this._container.innerHTML = renderCodicons(text || ''); + } + + set title(title: string) { + this._container.title = title; + } +} diff --git a/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts b/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts index 950001d01352..9732e00fa309 100644 --- a/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts +++ b/src/vs/base/browser/ui/highlightedlabel/highlightedLabel.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as objects from 'vs/base/common/objects'; -import { renderOcticons } from 'vs/base/browser/ui/octiconLabel/octiconLabel'; +import { renderCodicons } from 'vs/base/browser/ui/codiconLabel/codiconLabel'; import { escape } from 'vs/base/common/strings'; export interface IHighlight { @@ -65,13 +65,13 @@ export class HighlightedLabel { if (pos < highlight.start) { htmlContent += ''; const substring = this.text.substring(pos, highlight.start); - htmlContent += this.supportOcticons ? renderOcticons(substring) : escape(substring); + htmlContent += this.supportOcticons ? renderCodicons(substring) : escape(substring); htmlContent += ''; pos = highlight.end; } htmlContent += ''; const substring = this.text.substring(highlight.start, highlight.end); - htmlContent += this.supportOcticons ? renderOcticons(substring) : escape(substring); + htmlContent += this.supportOcticons ? renderCodicons(substring) : escape(substring); htmlContent += ''; pos = highlight.end; } @@ -79,7 +79,7 @@ export class HighlightedLabel { if (pos < this.text.length) { htmlContent += ''; const substring = this.text.substring(pos); - htmlContent += this.supportOcticons ? renderOcticons(substring) : escape(substring); + htmlContent += this.supportOcticons ? renderCodicons(substring) : escape(substring); htmlContent += ''; } diff --git a/src/vs/base/browser/ui/inputbox/inputBox.css b/src/vs/base/browser/ui/inputbox/inputBox.css index 9415d36a1b2a..a56bf03fbe50 100644 --- a/src/vs/base/browser/ui/inputbox/inputBox.css +++ b/src/vs/base/browser/ui/inputbox/inputBox.css @@ -123,7 +123,7 @@ margin-left: 2px; } -.monaco-inputbox .monaco-action-bar .action-item .icon { +.monaco-inputbox .monaco-action-bar .action-item .codicon { background-repeat: no-repeat; width: 16px; height: 16px; diff --git a/src/vs/base/common/network.ts b/src/vs/base/common/network.ts index 6a579f0bbf07..26bd2964d619 100644 --- a/src/vs/base/common/network.ts +++ b/src/vs/base/common/network.ts @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { URI, UriComponents } from 'vs/base/common/uri'; +import { URI } from 'vs/base/common/uri'; import * as platform from 'vs/base/common/platform'; export namespace Schemas { @@ -60,7 +60,7 @@ class RemoteAuthoritiesImpl { private readonly _ports: { [authority: string]: number; }; private readonly _connectionTokens: { [authority: string]: string; }; private _preferredWebSchema: 'http' | 'https'; - private _delegate: ((uri: URI) => UriComponents) | null; + private _delegate: ((uri: URI) => URI) | null; constructor() { this._hosts = Object.create(null); @@ -74,7 +74,7 @@ class RemoteAuthoritiesImpl { this._preferredWebSchema = schema; } - public setDelegate(delegate: (uri: URI) => UriComponents): void { + public setDelegate(delegate: (uri: URI) => URI): void { this._delegate = delegate; } @@ -89,8 +89,7 @@ class RemoteAuthoritiesImpl { public rewrite(uri: URI): URI { if (this._delegate) { - const result = this._delegate(uri); - return URI.revive(result); + return this._delegate(uri); } const authority = uri.authority; const host = this._hosts[authority]; diff --git a/src/vs/base/parts/contextmenu/electron-main/contextmenu.ts b/src/vs/base/parts/contextmenu/electron-main/contextmenu.ts index af464fa42b19..388185df189e 100644 --- a/src/vs/base/parts/contextmenu/electron-main/contextmenu.ts +++ b/src/vs/base/parts/contextmenu/electron-main/contextmenu.ts @@ -3,11 +3,11 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Menu, MenuItem, BrowserWindow, Event, ipcMain } from 'electron'; +import { Menu, MenuItem, BrowserWindow, ipcMain } from 'electron'; import { ISerializableContextMenuItem, CONTEXT_MENU_CLOSE_CHANNEL, CONTEXT_MENU_CHANNEL, IPopupOptions } from 'vs/base/parts/contextmenu/common/contextmenu'; export function registerContextMenuListener(): void { - ipcMain.on(CONTEXT_MENU_CHANNEL, (event: Event, contextMenuId: number, items: ISerializableContextMenuItem[], onClickChannel: string, options?: IPopupOptions) => { + ipcMain.on(CONTEXT_MENU_CHANNEL, (event: Electron.IpcMainEvent, contextMenuId: number, items: ISerializableContextMenuItem[], onClickChannel: string, options?: IPopupOptions) => { const menu = createMenu(event, onClickChannel, items); menu.popup({ @@ -27,7 +27,7 @@ export function registerContextMenuListener(): void { }); } -function createMenu(event: Event, onClickChannel: string, items: ISerializableContextMenuItem[]): Menu { +function createMenu(event: Electron.IpcMainEvent, onClickChannel: string, items: ISerializableContextMenuItem[]): Menu { const menu = new Menu(); items.forEach(item => { diff --git a/src/vs/base/parts/tree/browser/collapse-all-dark.svg b/src/vs/base/parts/tree/browser/collapse-all-dark.svg deleted file mode 100644 index 4862c55dbeba..000000000000 --- a/src/vs/base/parts/tree/browser/collapse-all-dark.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/vs/base/parts/tree/browser/collapse-all-hc.svg b/src/vs/base/parts/tree/browser/collapse-all-hc.svg deleted file mode 100644 index 05f920b29b68..000000000000 --- a/src/vs/base/parts/tree/browser/collapse-all-hc.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/vs/base/parts/tree/browser/collapse-all-light.svg b/src/vs/base/parts/tree/browser/collapse-all-light.svg deleted file mode 100644 index 6359b42e623f..000000000000 --- a/src/vs/base/parts/tree/browser/collapse-all-light.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/vs/base/parts/tree/browser/tree.css b/src/vs/base/parts/tree/browser/tree.css index a74ae446e029..c0289b6fcb4e 100644 --- a/src/vs/base/parts/tree/browser/tree.css +++ b/src/vs/base/parts/tree/browser/tree.css @@ -120,15 +120,3 @@ .hc-black .monaco-tree .monaco-tree-rows > .monaco-tree-row.has-children.loading > .content:before { background-image: url('loading-hc.svg'); } - -.monaco-tree-action.collapse-all { - background: url('collapse-all-light.svg') center center no-repeat; -} - -.vs-dark .monaco-tree-action.collapse-all { - background: url('collapse-all-dark.svg') center center no-repeat; -} - -.hc-black .monaco-tree-action.collapse-all { - background: url('collapse-all-hc.svg') center center no-repeat; -} diff --git a/src/vs/code/browser/workbench/workbench-dev.html b/src/vs/code/browser/workbench/workbench-dev.html index 9fb0fab5a1ae..910666edf088 100644 --- a/src/vs/code/browser/workbench/workbench-dev.html +++ b/src/vs/code/browser/workbench/workbench-dev.html @@ -14,7 +14,7 @@ default-src 'self'; img-src 'self' https: data: blob:; media-src 'none'; - script-src 'self' https://az416426.vo.msecnd.net 'unsafe-eval' https: 'sha256-4DqvCTjCHj2KW4QxC/Yt6uBwMRyYiEg7kOoykSEkonQ=' 'sha256-meDZW3XhN5JmdjFUrWGhTouRKBiWYtXHltaKnqn/WMo='; + script-src 'self' https://az416426.vo.msecnd.net 'unsafe-eval' https: 'sha256-AMRGFXNZ7mBnD/6F4lTV00XAjE5CBSM7ZeIv3DIp5YM=' 'sha256-meDZW3XhN5JmdjFUrWGhTouRKBiWYtXHltaKnqn/WMo='; child-src 'self'; frame-src 'self' https://*.vscode-webview-test.com; worker-src 'self'; @@ -44,13 +44,13 @@ self.require = { baseUrl: `${window.location.origin}/static/out`, paths: { - 'vscode-textmate': `${window.location.origin}/static/node_modules/vscode-textmate/release/main`, - 'onigasm-umd': `${window.location.origin}/static/node_modules/onigasm-umd/release/main`, - 'xterm': `${window.location.origin}/static/node_modules/xterm/lib/xterm.js`, - 'xterm-addon-search': `${window.location.origin}/static/node_modules/xterm-addon-search/lib/xterm-addon-search.js`, - 'xterm-addon-web-links': `${window.location.origin}/static/node_modules/xterm-addon-web-links/lib/xterm-addon-web-links.js`, - 'semver-umd': `${window.location.origin}/static/node_modules/semver-umd/lib/semver-umd.js`, - '@microsoft/applicationinsights-web': `${window.location.origin}/static/node_modules/@microsoft/applicationinsights-web/dist/applicationinsights-web.js`, + 'vscode-textmate': `${window.location.origin}/static/remote/web/node_modules/vscode-textmate/release/main`, + 'onigasm-umd': `${window.location.origin}/static/remote/web/node_modules/onigasm-umd/release/main`, + 'xterm': `${window.location.origin}/static/remote/web/node_modules/xterm/lib/xterm.js`, + 'xterm-addon-search': `${window.location.origin}/static/remote/web/node_modules/xterm-addon-search/lib/xterm-addon-search.js`, + 'xterm-addon-web-links': `${window.location.origin}/static/remote/web/node_modules/xterm-addon-web-links/lib/xterm-addon-web-links.js`, + 'semver-umd': `${window.location.origin}/static/remote/web/node_modules/semver-umd/lib/semver-umd.js`, + '@microsoft/applicationinsights-web': `${window.location.origin}/static/remote/web/node_modules/@microsoft/applicationinsights-web/dist/applicationinsights-web.js`, } }; diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts index 41fc2d055cbb..3593c528ae68 100644 --- a/src/vs/code/browser/workbench/workbench.ts +++ b/src/vs/code/browser/workbench/workbench.ts @@ -3,16 +3,12 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IWorkbenchConstructionOptions, create } from 'vs/workbench/workbench.web.api'; -import { IURLCallbackProvider } from 'vs/workbench/services/url/browser/urlService'; -import { Event, Emitter } from 'vs/base/common/event'; -import { URI, UriComponents } from 'vs/base/common/uri'; +import { IWorkbenchConstructionOptions, create, URI, Event, Emitter, UriComponents, ICredentialsProvider, IURLCallbackProvider } from 'vs/workbench/workbench.web.api'; import { generateUuid } from 'vs/base/common/uuid'; import { CancellationToken } from 'vs/base/common/cancellation'; import { streamToBuffer } from 'vs/base/common/buffer'; import { Disposable } from 'vs/base/common/lifecycle'; import { request } from 'vs/base/parts/request/browser/request'; -import { ICredentialsProvider } from 'vs/workbench/services/credentials/browser/credentialsService'; interface ICredential { service: string; @@ -24,7 +20,7 @@ class LocalStorageCredentialsProvider implements ICredentialsProvider { static readonly CREDENTIALS_OPENED_KEY = 'credentials.provider'; - private _credentials: ICredential[]; + private _credentials!: ICredential[]; private get credentials(): ICredential[] { if (!this._credentials) { try { @@ -205,4 +201,12 @@ const options: IWorkbenchConstructionOptions = JSON.parse(document.getElementByI options.urlCallbackProvider = new PollingURLCallbackProvider(); options.credentialsProvider = new LocalStorageCredentialsProvider(); +if (options.folderUri) { + options.folderUri = URI.revive(options.folderUri); +} + +if (options.workspaceUri) { + options.workspaceUri = URI.revive(options.workspaceUri); +} + create(document.body, options); diff --git a/src/vs/code/electron-browser/issue/issueReporterMain.ts b/src/vs/code/electron-browser/issue/issueReporterMain.ts index 1f9b21d0aa62..463731066888 100644 --- a/src/vs/code/electron-browser/issue/issueReporterMain.ts +++ b/src/vs/code/electron-browser/issue/issueReporterMain.ts @@ -10,8 +10,7 @@ import { $ } from 'vs/base/browser/dom'; import * as collections from 'vs/base/common/collections'; import * as browser from 'vs/base/browser/browser'; import { escape } from 'vs/base/common/strings'; -import product from 'vs/platform/product/node/product'; -import pkg from 'vs/platform/product/node/package'; +import product from 'vs/platform/product/common/product'; import * as os from 'os'; import { debounce } from 'vs/base/common/decorators'; import * as platform from 'vs/base/common/platform'; @@ -33,7 +32,7 @@ import { EnvironmentService } from 'vs/platform/environment/node/environmentServ import { IssueReporterModel, IssueReporterData as IssueReporterModelData } from 'vs/code/electron-browser/issue/issueReporterModel'; import { IssueReporterData, IssueReporterStyles, IssueType, ISettingsSearchIssueReporterData, IssueReporterFeatures, IssueReporterExtensionData } from 'vs/platform/issue/node/issue'; import BaseHtml from 'vs/code/electron-browser/issue/issueReporterPage'; -import { LogLevelSetterChannelClient, FollowerLogService } from 'vs/platform/log/common/logIpc'; +import { LoggerChannelClient, FollowerLogService } from 'vs/platform/log/common/logIpc'; import { ILogService, getLogLevel } from 'vs/platform/log/common/log'; import { OcticonLabel } from 'vs/base/browser/ui/octiconLabel/octiconLabel'; import { normalizeGitHubUrl } from 'vs/code/electron-browser/issue/issueReporterUtil'; @@ -85,7 +84,7 @@ export class IssueReporter extends Disposable { this.issueReporterModel = new IssueReporterModel({ issueType: configuration.data.issueType || IssueType.Bug, versionInfo: { - vscodeVersion: `${pkg.name} ${pkg.version} (${product.commit || 'Commit unknown'}, ${product.date || 'Date unknown'})`, + vscodeVersion: `${product.nameShort} ${product.version} (${product.commit || 'Commit unknown'}, ${product.date || 'Date unknown'})`, os: `${os.type()} ${os.arch()} ${os.release()}${isSnap ? ' snap' : ''}` }, extensionsDisabled: !!this.environmentService.disableExtensions, @@ -301,8 +300,8 @@ export class IssueReporter extends Disposable { this.environmentService = new EnvironmentService(configuration, configuration.execPath); const logService = new SpdLogService(`issuereporter${configuration.windowId}`, this.environmentService.logsPath, getLogLevel(this.environmentService)); - const logLevelClient = new LogLevelSetterChannelClient(mainProcessService.getChannel('loglevel')); - this.logService = new FollowerLogService(logLevelClient, logService); + const loggerClient = new LoggerChannelClient(mainProcessService.getChannel('logger')); + this.logService = new FollowerLogService(loggerClient, logService); const sharedProcess = (serviceCollection.get(IWindowsService)).whenSharedProcessReady() .then(() => connectNet(this.environmentService.sharedIPCHandle, `window:${configuration.windowId}`)); @@ -311,7 +310,7 @@ export class IssueReporter extends Disposable { if (!this.environmentService.isExtensionDevelopment && !this.environmentService.args['disable-telemetry'] && !!product.enableTelemetry) { const channel = getDelayedChannel(sharedProcess.then(c => c.getChannel('telemetryAppender'))); const appender = combinedAppender(new TelemetryAppenderClient(channel), new LogAppender(logService)); - const commonProperties = resolveCommonProperties(product.commit || 'Commit unknown', pkg.version, configuration.machineId, product.msftInternalDomains, this.environmentService.installSourcePath); + const commonProperties = resolveCommonProperties(product.commit || 'Commit unknown', product.version, configuration.machineId, product.msftInternalDomains, this.environmentService.installSourcePath); const piiPaths = this.environmentService.extensionsPath ? [this.environmentService.appRoot, this.environmentService.extensionsPath] : [this.environmentService.appRoot]; const config: ITelemetryServiceConfig = { appender, commonProperties, piiPaths }; diff --git a/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts b/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts index a2ac7f68751f..d69a40090cca 100644 --- a/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts +++ b/src/vs/code/electron-browser/processExplorer/processExplorerMain.ts @@ -7,7 +7,7 @@ import 'vs/css!./media/processExplorer'; import { webFrame, ipcRenderer, clipboard } from 'electron'; import { repeat } from 'vs/base/common/strings'; import { totalmem } from 'os'; -import product from 'vs/platform/product/node/product'; +import product from 'vs/platform/product/common/product'; import { localize } from 'vs/nls'; import { ProcessExplorerStyles, ProcessExplorerData } from 'vs/platform/issue/node/issue'; import * as browser from 'vs/base/browser/browser'; diff --git a/src/vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner.ts b/src/vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner.ts index 5c59f89bf5cf..c4d8930fc6de 100644 --- a/src/vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner.ts +++ b/src/vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner.ts @@ -7,7 +7,7 @@ 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/node/product'; +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'; diff --git a/src/vs/code/electron-browser/sharedProcess/contrib/nodeCachedDataCleaner.ts b/src/vs/code/electron-browser/sharedProcess/contrib/nodeCachedDataCleaner.ts index 87488502d813..91ebed4dfd8c 100644 --- a/src/vs/code/electron-browser/sharedProcess/contrib/nodeCachedDataCleaner.ts +++ b/src/vs/code/electron-browser/sharedProcess/contrib/nodeCachedDataCleaner.ts @@ -8,7 +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 product from 'vs/platform/product/node/product'; +import product from 'vs/platform/product/common/product'; export class NodeCachedDataCleaner { diff --git a/src/vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner.ts b/src/vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner.ts index db03b8f82c09..5e856bff2ac9 100644 --- a/src/vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner.ts +++ b/src/vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner.ts @@ -8,7 +8,7 @@ import { join } from 'vs/base/common/path'; import { readdir, readFile, rimraf } from 'vs/base/node/pfs'; import { onUnexpectedError } from 'vs/base/common/errors'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; -import { IBackupWorkspacesFormat } from 'vs/platform/backup/common/backup'; +import { IBackupWorkspacesFormat } from 'vs/platform/backup/node/backup'; export class StorageDataCleaner extends Disposable { diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index 001458bd1611..f99f4f2c9afd 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -5,8 +5,7 @@ import * as fs from 'fs'; import * as platform from 'vs/base/common/platform'; -import product from 'vs/platform/product/node/product'; -import pkg from 'vs/platform/product/node/package'; +import product from 'vs/platform/product/common/product'; 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'; @@ -31,7 +30,7 @@ import { IWindowsService } from 'vs/platform/windows/common/windows'; import { WindowsService } from 'vs/platform/windows/electron-browser/windowsService'; import { ipcRenderer } from 'electron'; import { ILogService, LogLevel } from 'vs/platform/log/common/log'; -import { LogLevelSetterChannelClient, FollowerLogService } from 'vs/platform/log/common/logIpc'; +import { LoggerChannelClient, FollowerLogService } from 'vs/platform/log/common/logIpc'; import { LocalizationsService } from 'vs/platform/localizations/node/localizations'; import { ILocalizationsService } from 'vs/platform/localizations/common/localizations'; import { LocalizationsChannel } from 'vs/platform/localizations/node/localizationsIpc'; @@ -51,7 +50,7 @@ import { FileService } from 'vs/platform/files/common/fileService'; import { IFileService } from 'vs/platform/files/common/files'; import { DiskFileSystemProvider } from 'vs/platform/files/electron-browser/diskFileSystemProvider'; import { Schemas } from 'vs/base/common/network'; -import { IProductService } from 'vs/platform/product/common/product'; +import { IProductService } from 'vs/platform/product/common/productService'; export interface ISharedProcessConfiguration { readonly machineId: string; @@ -97,8 +96,8 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat const environmentService = new EnvironmentService(initData.args, process.execPath); const mainRouter = new StaticRouter(ctx => ctx === 'main'); - const logLevelClient = new LogLevelSetterChannelClient(server.getChannel('loglevel', mainRouter)); - const logService = new FollowerLogService(logLevelClient, new SpdLogService('sharedprocess', environmentService.logsPath, initData.logLevel)); + const loggerClient = new LoggerChannelClient(server.getChannel('logger', mainRouter)); + const logService = new FollowerLogService(loggerClient, new SpdLogService('sharedprocess', environmentService.logsPath, initData.logLevel)); disposables.add(logService); logService.info('main', JSON.stringify(configuration)); @@ -136,7 +135,7 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat const services = new ServiceCollection(); const environmentService = accessor.get(IEnvironmentService); const { appRoot, extensionsPath, extensionDevelopmentLocationURI: extensionDevelopmentLocationURI, isBuilt, installSourcePath } = environmentService; - const telemetryLogService = new FollowerLogService(logLevelClient, new SpdLogService('telemetry', environmentService.logsPath, initData.logLevel)); + 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('==========================================================='); @@ -148,7 +147,7 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat } const config: ITelemetryServiceConfig = { appender: combinedAppender(appInsightsAppender, new LogAppender(logService)), - commonProperties: resolveCommonProperties(product.commit, pkg.version, configuration.machineId, product.msftInternalDomains, installSourcePath), + commonProperties: resolveCommonProperties(product.commit, product.version, configuration.machineId, product.msftInternalDomains, installSourcePath), piiPaths: extensionsPath ? [appRoot, extensionsPath] : [appRoot] }; diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 7ae330744cdf..0ba485835759 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -9,7 +9,7 @@ import { WindowsManager } from 'vs/code/electron-main/windows'; import { IWindowsService, OpenContext, ActiveWindowManager, IURIToOpen } from 'vs/platform/windows/common/windows'; import { WindowsChannel } from 'vs/platform/windows/common/windowsIpc'; import { WindowsService } from 'vs/platform/windows/electron-main/windowsService'; -import { ILifecycleService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMain'; +import { ILifecycleMainService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; import { getShellEnvironment } from 'vs/code/node/shellEnv'; import { IUpdateService } from 'vs/platform/update/common/update'; import { UpdateChannel } from 'vs/platform/update/electron-main/updateIpc'; @@ -17,7 +17,7 @@ import { Server as ElectronIPCServer } from 'vs/base/parts/ipc/electron-main/ipc import { Client } from 'vs/base/parts/ipc/common/ipc.net'; import { Server, connect } from 'vs/base/parts/ipc/node/ipc.net'; import { SharedProcess } from 'vs/code/electron-main/sharedProcess'; -import { LaunchService, LaunchChannel, ILaunchService } from 'vs/platform/launch/electron-main/launchService'; +import { LaunchMainService, LaunchChannel, ILaunchMainService } from 'vs/platform/launch/electron-main/launchService'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; @@ -33,28 +33,26 @@ import { TelemetryAppenderClient } from 'vs/platform/telemetry/node/telemetryIpc import { TelemetryService, ITelemetryServiceConfig } from 'vs/platform/telemetry/common/telemetryService'; import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProperties'; import { getDelayedChannel, StaticRouter } from 'vs/base/parts/ipc/common/ipc'; -import product from 'vs/platform/product/node/product'; -import pkg from 'vs/platform/product/node/package'; +import product from 'vs/platform/product/common/product'; import { ProxyAuthHandler } from 'vs/code/electron-main/auth'; import { Disposable } from 'vs/base/common/lifecycle'; import { IWindowsMainService, ICodeWindow } from 'vs/platform/windows/electron-main/windows'; -import { IHistoryMainService } from 'vs/platform/history/common/history'; import { URI } from 'vs/base/common/uri'; -import { WorkspacesChannel } from 'vs/platform/workspaces/node/workspacesIpc'; -import { IWorkspacesMainService, hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces'; +import { WorkspacesChannel } from 'vs/platform/workspaces/electron-main/workspacesIpc'; +import { hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces'; import { getMachineId } from 'vs/base/node/id'; import { Win32UpdateService } from 'vs/platform/update/electron-main/updateService.win32'; import { LinuxUpdateService } from 'vs/platform/update/electron-main/updateService.linux'; import { DarwinUpdateService } from 'vs/platform/update/electron-main/updateService.darwin'; import { IIssueService } from 'vs/platform/issue/node/issue'; -import { IssueChannel } from 'vs/platform/issue/node/issueIpc'; -import { IssueService } from 'vs/platform/issue/electron-main/issueService'; -import { LogLevelSetterChannel } from 'vs/platform/log/common/logIpc'; +import { IssueChannel } from 'vs/platform/issue/electron-main/issueIpc'; +import { IssueMainService } from 'vs/platform/issue/electron-main/issueMainService'; +import { LoggerChannel } from 'vs/platform/log/common/logIpc'; import { setUnexpectedErrorHandler, onUnexpectedError } from 'vs/base/common/errors'; import { ElectronURLListener } from 'vs/platform/url/electron-main/electronUrlListener'; import { serve as serveDriver } from 'vs/platform/driver/electron-main/driver'; import { IMenubarService } from 'vs/platform/menubar/node/menubar'; -import { MenubarService } from 'vs/platform/menubar/electron-main/menubarService'; +import { MenubarMainService } from 'vs/platform/menubar/electron-main/menubarMainService'; import { MenubarChannel } from 'vs/platform/menubar/node/menubarIpc'; import { RunOnceScheduler } from 'vs/base/common/async'; import { registerContextMenuListener } from 'vs/base/parts/contextmenu/electron-main/contextmenu'; @@ -67,10 +65,10 @@ import { IStorageMainService, StorageMainService } from 'vs/platform/storage/nod import { GlobalStorageDatabaseChannel } from 'vs/platform/storage/node/storageIpc'; import { startsWith } from 'vs/base/common/strings'; import { BackupMainService } from 'vs/platform/backup/electron-main/backupMainService'; -import { IBackupMainService } from 'vs/platform/backup/common/backup'; -import { HistoryMainService } from 'vs/platform/history/electron-main/historyMainService'; +import { IBackupMainService } from 'vs/platform/backup/electron-main/backup'; +import { HistoryMainService, IHistoryMainService } from 'vs/platform/history/electron-main/historyMainService'; import { URLService } from 'vs/platform/url/node/urlService'; -import { WorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService'; +import { WorkspacesMainService, IWorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService'; import { statSync } from 'fs'; import { DiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsIpc'; import { IDiagnosticsService } from 'vs/platform/diagnostics/node/diagnosticsService'; @@ -78,6 +76,8 @@ import { FileService } from 'vs/platform/files/common/fileService'; import { IFileService } from 'vs/platform/files/common/files'; import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider'; import { ExtensionHostDebugBroadcastChannel } from 'vs/platform/debug/common/extensionHostDebugIpc'; +import { IElectronService } from 'vs/platform/electron/node/electron'; +import { ElectronMainService, ElectronChannel } from 'vs/platform/electron/electron-main/electronMainService'; export class CodeApplication extends Disposable { @@ -92,7 +92,7 @@ export class CodeApplication extends Disposable { @IInstantiationService private readonly instantiationService: IInstantiationService, @ILogService private readonly logService: ILogService, @IEnvironmentService private readonly environmentService: IEnvironmentService, - @ILifecycleService private readonly lifecycleService: ILifecycleService, + @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, @IConfigurationService private readonly configurationService: IConfigurationService, @IStateService private readonly stateService: IStateService ) { @@ -109,7 +109,7 @@ export class CodeApplication extends Disposable { process.on('unhandledRejection', (reason: unknown) => onUnexpectedError(reason)); // Dispose on shutdown - this.lifecycleService.onWillShutdown(() => this.dispose()); + this.lifecycleMainService.onWillShutdown(() => this.dispose()); // Contextmenu via IPC support registerContextMenuListener(); @@ -255,10 +255,10 @@ export class CodeApplication extends Disposable { this.logService.trace('IPC#vscode:exit', code); this.dispose(); - this.lifecycleService.kill(code); + this.lifecycleMainService.kill(code); }); - ipc.on('vscode:fetchShellEnv', async (event: Event) => { + ipc.on('vscode:fetchShellEnv', async (event: Electron.IpcMainEvent) => { const webContents = event.sender; try { @@ -275,14 +275,14 @@ export class CodeApplication extends Disposable { } }); - ipc.on('vscode:toggleDevTools', (event: Event) => event.sender.toggleDevTools()); - ipc.on('vscode:openDevTools', (event: Event) => event.sender.openDevTools()); + ipc.on('vscode:toggleDevTools', (event: Electron.IpcMainEvent) => event.sender.toggleDevTools()); + ipc.on('vscode:openDevTools', (event: Electron.IpcMainEvent) => event.sender.openDevTools()); - ipc.on('vscode:reloadWindow', (event: Event) => event.sender.reload()); + ipc.on('vscode:reloadWindow', (event: Electron.IpcMainEvent) => event.sender.reload()); // Some listeners after window opened (async () => { - await this.lifecycleService.when(LifecycleMainPhase.AfterWindowOpen); + await this.lifecycleMainService.when(LifecycleMainPhase.AfterWindowOpen); // After waking up from sleep (after window opened) powerMonitor.on('resume', () => { @@ -361,7 +361,7 @@ export class CodeApplication extends Disposable { // Spawn shared process after the first window has opened and 3s have passed const sharedProcess = this.instantiationService.createInstance(SharedProcess, machineId, this.userEnv); const sharedProcessClient = sharedProcess.whenReady().then(() => connect(this.environmentService.sharedIPCHandle, 'main')); - this.lifecycleService.when(LifecycleMainPhase.AfterWindowOpen).then(() => { + this.lifecycleMainService.when(LifecycleMainPhase.AfterWindowOpen).then(() => { this._register(new RunOnceScheduler(async () => { const userEnv = await getShellEnvironment(this.logService, this.environmentService); @@ -424,7 +424,6 @@ export class CodeApplication extends Disposable { private async createServices(machineId: string, trueMachineId: string | undefined, sharedProcess: SharedProcess, sharedProcessClient: Promise>): Promise { const services = new ServiceCollection(); - // Files const fileService = this._register(new FileService(this.logService)); services.set(IFileService, fileService); @@ -451,17 +450,18 @@ export class CodeApplication extends Disposable { services.set(IWindowsMainService, new SyncDescriptor(WindowsManager, [machineId, this.userEnv])); services.set(IWindowsService, new SyncDescriptor(WindowsService, [sharedProcess])); - services.set(ILaunchService, new SyncDescriptor(LaunchService)); + services.set(ILaunchMainService, new SyncDescriptor(LaunchMainService)); const diagnosticsChannel = getDelayedChannel(sharedProcessClient.then(client => client.getChannel('diagnostics'))); services.set(IDiagnosticsService, new SyncDescriptor(DiagnosticsService, [diagnosticsChannel])); - services.set(IIssueService, new SyncDescriptor(IssueService, [machineId, this.userEnv])); - services.set(IMenubarService, new SyncDescriptor(MenubarService)); + services.set(IIssueService, new SyncDescriptor(IssueMainService, [machineId, this.userEnv])); + services.set(IElectronService, new SyncDescriptor(ElectronMainService)); + services.set(IMenubarService, new SyncDescriptor(MenubarMainService)); const storageMainService = new StorageMainService(this.logService, this.environmentService); services.set(IStorageMainService, storageMainService); - this.lifecycleService.onWillShutdown(e => e.join(storageMainService.close())); + this.lifecycleMainService.onWillShutdown(e => e.join(storageMainService.close())); const backupMainService = new BackupMainService(this.environmentService, this.configurationService, this.logService); services.set(IBackupMainService, backupMainService); @@ -474,7 +474,7 @@ export class CodeApplication extends Disposable { if (!this.environmentService.isExtensionDevelopment && !this.environmentService.args['disable-telemetry'] && !!product.enableTelemetry) { const channel = getDelayedChannel(sharedProcessClient.then(client => client.getChannel('telemetryAppender'))); const appender = combinedAppender(new TelemetryAppenderClient(channel), new LogAppender(this.logService)); - const commonProperties = resolveCommonProperties(product.commit, pkg.version, machineId, product.msftInternalDomains, this.environmentService.installSourcePath); + const commonProperties = resolveCommonProperties(product.commit, product.version, machineId, product.msftInternalDomains, this.environmentService.installSourcePath); const piiPaths = this.environmentService.extensionsPath ? [this.environmentService.appRoot, this.environmentService.extensionsPath] : [this.environmentService.appRoot]; const config: ITelemetryServiceConfig = { appender, commonProperties, piiPaths, trueMachineId }; @@ -529,8 +529,8 @@ export class CodeApplication extends Disposable { private openFirstWindow(accessor: ServicesAccessor, electronIpcServer: ElectronIPCServer, sharedProcessClient: Promise>): ICodeWindow[] { // Register more Main IPC services - const launchService = accessor.get(ILaunchService); - const launchChannel = new LaunchChannel(launchService); + const launchMainService = accessor.get(ILaunchMainService); + const launchChannel = new LaunchChannel(launchMainService); this.mainIpcServer.registerChannel('launch', launchChannel); // Register more Electron IPC services @@ -542,8 +542,12 @@ export class CodeApplication extends Disposable { const issueChannel = new IssueChannel(issueService); electronIpcServer.registerChannel('issue', issueChannel); - const workspacesService = accessor.get(IWorkspacesMainService); - const workspacesChannel = new WorkspacesChannel(workspacesService); + const electronService = accessor.get(IElectronService); + const electronChannel = new ElectronChannel(electronService); + electronIpcServer.registerChannel('electron', electronChannel); + + const workspacesMainService = accessor.get(IWorkspacesMainService); + const workspacesChannel = new WorkspacesChannel(workspacesMainService); electronIpcServer.registerChannel('workspaces', workspacesChannel); const windowsService = accessor.get(IWindowsService); @@ -563,16 +567,15 @@ export class CodeApplication extends Disposable { const storageChannel = this._register(new GlobalStorageDatabaseChannel(this.logService, storageMainService)); electronIpcServer.registerChannel('storage', storageChannel); - // Log level management - const logLevelChannel = new LogLevelSetterChannel(accessor.get(ILogService)); - electronIpcServer.registerChannel('loglevel', logLevelChannel); - sharedProcessClient.then(client => client.registerChannel('loglevel', logLevelChannel)); + const loggerChannel = new LoggerChannel(accessor.get(ILogService)); + electronIpcServer.registerChannel('logger', loggerChannel); + sharedProcessClient.then(client => client.registerChannel('logger', loggerChannel)); // ExtensionHost Debug broadcast service electronIpcServer.registerChannel(ExtensionHostDebugBroadcastChannel.ChannelName, new ExtensionHostDebugBroadcastChannel()); // Signal phase: ready (services set) - this.lifecycleService.phase = LifecycleMainPhase.Ready; + this.lifecycleMainService.phase = LifecycleMainPhase.Ready; // Propagate to clients const windowsMainService = this.windowsMainService = accessor.get(IWindowsMainService); @@ -682,7 +685,7 @@ export class CodeApplication extends Disposable { private afterWindowOpen(): void { // Signal phase: after window open - this.lifecycleService.phase = LifecycleMainPhase.AfterWindowOpen; + this.lifecycleMainService.phase = LifecycleMainPhase.AfterWindowOpen; // Remote Authorities this.handleRemoteAuthorities(); diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index 06549d2ce185..d8f8a2b9858f 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -7,12 +7,12 @@ import 'vs/platform/update/common/update.config.contribution'; import { app, dialog } from 'electron'; import { assign } from 'vs/base/common/objects'; import * as platform from 'vs/base/common/platform'; -import product from 'vs/platform/product/node/product'; +import product from 'vs/platform/product/common/product'; import { parseMainProcessArgv } from 'vs/platform/environment/node/argvHelper'; import { addArg, createWaitMarkerFile } from 'vs/platform/environment/node/argv'; import { mkdirp } from 'vs/base/node/pfs'; import { validatePaths } from 'vs/code/node/paths'; -import { LifecycleService, ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain'; +import { LifecycleMainService, ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; import { Server, serve, connect } from 'vs/base/parts/ipc/node/ipc.net'; import { LaunchChannelClient } from 'vs/platform/launch/electron-main/launchService'; import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -27,7 +27,7 @@ import { EnvironmentService, xdgRuntimeDir } from 'vs/platform/environment/node/ import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ConfigurationService } from 'vs/platform/configuration/node/configurationService'; import { IRequestService } from 'vs/platform/request/common/request'; -import { RequestService } from 'vs/platform/request/electron-main/requestService'; +import { RequestMainService } from 'vs/platform/request/electron-main/requestMainService'; import * as fs from 'fs'; import { CodeApplication } from 'vs/code/electron-main/app'; import { localize } from 'vs/nls'; @@ -116,13 +116,13 @@ class CodeMain { await instantiationService.invokeFunction(async accessor => { const environmentService = accessor.get(IEnvironmentService); const logService = accessor.get(ILogService); - const lifecycleService = accessor.get(ILifecycleService); + const lifecycleMainService = accessor.get(ILifecycleMainService); const configurationService = accessor.get(IConfigurationService); - const mainIpcServer = await this.doStartup(logService, environmentService, lifecycleService, instantiationService, true); + const mainIpcServer = await this.doStartup(logService, environmentService, lifecycleMainService, instantiationService, true); bufferLogService.logger = new SpdLogService('main', environmentService.logsPath, bufferLogService.getLevel()); - once(lifecycleService.onWillShutdown)(() => (configurationService as ConfigurationService).dispose()); + once(lifecycleMainService.onWillShutdown)(() => (configurationService as ConfigurationService).dispose()); return instantiationService.createInstance(CodeApplication, mainIpcServer, instanceEnvironment).startup(); }); @@ -143,9 +143,9 @@ class CodeMain { services.set(ILogService, logService); services.set(IConfigurationService, new ConfigurationService(environmentService.settingsResource)); - services.set(ILifecycleService, new SyncDescriptor(LifecycleService)); + services.set(ILifecycleMainService, new SyncDescriptor(LifecycleMainService)); services.set(IStateService, new SyncDescriptor(StateService)); - services.set(IRequestService, new SyncDescriptor(RequestService)); + services.set(IRequestService, new SyncDescriptor(RequestMainService)); services.set(IThemeMainService, new SyncDescriptor(ThemeMainService)); services.set(ISignService, new SyncDescriptor(SignService)); @@ -191,7 +191,7 @@ class CodeMain { return instanceEnvironment; } - private async doStartup(logService: ILogService, environmentService: IEnvironmentService, lifecycleService: ILifecycleService, instantiationService: IInstantiationService, retry: boolean): Promise { + private async doStartup(logService: ILogService, environmentService: IEnvironmentService, 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 @@ -199,7 +199,7 @@ class CodeMain { let server: Server; try { server = await serve(environmentService.mainIPCHandle); - once(lifecycleService.onWillShutdown)(() => server.dispose()); + once(lifecycleMainService.onWillShutdown)(() => server.dispose()); } catch (error) { // Handle unexpected errors (the only expected error is EADDRINUSE that @@ -247,7 +247,7 @@ class CodeMain { throw error; } - return this.doStartup(logService, environmentService, lifecycleService, instantiationService, false); + return this.doStartup(logService, environmentService, lifecycleMainService, instantiationService, false); } // Tests from CLI require to be the only instance currently @@ -376,7 +376,7 @@ class CodeMain { private quit(accessor: ServicesAccessor, reason?: ExpectedError | Error): void { const logService = accessor.get(ILogService); - const lifecycleService = accessor.get(ILifecycleService); + const lifecycleMainService = accessor.get(ILifecycleMainService); let exitCode = 0; @@ -396,7 +396,7 @@ class CodeMain { } } - lifecycleService.kill(exitCode); + lifecycleMainService.kill(exitCode); } } diff --git a/src/vs/code/electron-main/sharedProcess.ts b/src/vs/code/electron-main/sharedProcess.ts index a47fce312ded..a3d0a427059c 100644 --- a/src/vs/code/electron-main/sharedProcess.ts +++ b/src/vs/code/electron-main/sharedProcess.ts @@ -10,7 +10,7 @@ import { BrowserWindow, ipcMain } from 'electron'; import { ISharedProcess } from 'vs/platform/windows/electron-main/windows'; import { Barrier } from 'vs/base/common/async'; import { ILogService } from 'vs/platform/log/common/log'; -import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain'; +import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; import { IThemeMainService } from 'vs/platform/theme/electron-main/themeMainService'; import { toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; @@ -24,7 +24,7 @@ export class SharedProcess implements ISharedProcess { private readonly machineId: string, private userEnv: NodeJS.ProcessEnv, @IEnvironmentService private readonly environmentService: IEnvironmentService, - @ILifecycleService private readonly lifecycleService: ILifecycleService, + @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, @ILogService private readonly logService: ILogService, @IThemeMainService private readonly themeMainService: IThemeMainService ) { } @@ -36,9 +36,8 @@ export class SharedProcess implements ISharedProcess { backgroundColor: this.themeMainService.getBackgroundColor(), webPreferences: { images: false, - webaudio: false, - webgl: false, nodeIntegration: true, + webgl: false, disableBlinkFeatures: 'Auxclick' // do NOT change, allows us to identify this window as shared-process in the process explorer } }); @@ -69,7 +68,7 @@ export class SharedProcess implements ISharedProcess { const disposables = new DisposableStore(); - this.lifecycleService.onWillShutdown(() => { + this.lifecycleMainService.onWillShutdown(() => { disposables.dispose(); // Shut the shared process down when we are quitting diff --git a/src/vs/code/electron-main/window.ts b/src/vs/code/electron-main/window.ts index 67cfad175989..9a8993a6dca7 100644 --- a/src/vs/code/electron-main/window.ts +++ b/src/vs/code/electron-main/window.ts @@ -12,13 +12,14 @@ import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/ 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 product from 'vs/platform/product/node/product'; +import product from 'vs/platform/product/common/product'; import { IWindowSettings, MenuBarVisibility, IWindowConfiguration, ReadyState, getTitleBarStyle } from 'vs/platform/windows/common/windows'; import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; import { isLinux, isMacintosh, isWindows } from 'vs/base/common/platform'; import { ICodeWindow, IWindowState, WindowMode } from 'vs/platform/windows/electron-main/windows'; -import { IWorkspaceIdentifier, IWorkspacesMainService } from 'vs/platform/workspaces/common/workspaces'; -import { IBackupMainService } from 'vs/platform/backup/common/backup'; +import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService'; +import { IBackupMainService } from 'vs/platform/backup/electron-main/backup'; import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; import * as perf from 'vs/base/common/performance'; import { resolveMarketplaceHeaders } from 'vs/platform/extensionManagement/common/extensionGalleryService'; @@ -26,7 +27,6 @@ import { IThemeMainService } from 'vs/platform/theme/electron-main/themeMainServ import { endsWith } from 'vs/base/common/strings'; import { RunOnceScheduler } from 'vs/base/common/async'; import { IFileService } from 'vs/platform/files/common/files'; -import pkg from 'vs/platform/product/node/package'; const RUN_TEXTMATE_IN_WORKER = false; @@ -310,7 +310,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { private handleMarketplaceRequests(): void { // Resolve marketplace headers - this.marketplaceHeadersPromise = resolveMarketplaceHeaders(pkg.version, this.environmentService, this.fileService); + this.marketplaceHeadersPromise = resolveMarketplaceHeaders(product.version, this.environmentService, this.fileService); // Inject headers when requests are incoming const urls = ['https://marketplace.visualstudio.com/*', 'https://*.vsassets.io/*']; @@ -561,7 +561,7 @@ export class CodeWindow extends Disposable implements ICodeWindow { autoDetectHighContrast = false; } windowConfiguration.highContrast = isWindows && autoDetectHighContrast && systemPreferences.isInvertedColorScheme(); - windowConfiguration.accessibilitySupport = app.isAccessibilitySupportEnabled(); + windowConfiguration.accessibilitySupport = app.accessibilitySupportEnabled; // Title style related windowConfiguration.maximized = this._win.isMaximized(); diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 637ced15d8cc..e720e9c70ead 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -8,24 +8,26 @@ import { basename, normalize, join, dirname } 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 { IBackupMainService, IEmptyWindowBackupInfo } from 'vs/platform/backup/common/backup'; +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 { IStateService } from 'vs/platform/state/common/state'; import { CodeWindow, defaultWindowState } from 'vs/code/electron-main/window'; import { ipcMain as ipc, screen, BrowserWindow, dialog, systemPreferences, FileFilter } from 'electron'; import { parseLineAndColumnAware } from 'vs/code/node/paths'; -import { ILifecycleService, UnloadReason, LifecycleService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMain'; +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, IWindowConfiguration, INativeOpenDialogOptions, IPathsToWaitFor, IEnterWorkspaceResult, IMessageBoxResult, INewWindowOptions, IURIToOpen, isFileToOpen, isWorkspaceToOpen, isFolderToOpen } from 'vs/platform/windows/common/windows'; import { getLastActiveWindow, findBestWindowOrFolderForFile, findWindowOnWorkspace, findWindowOnExtensionDevelopmentPath, findWindowOnWorkspaceOrFolderUri } from 'vs/code/node/windowsFinder'; import { Event as CommonEvent, Emitter } from 'vs/base/common/event'; -import product from 'vs/platform/product/node/product'; +import product from 'vs/platform/product/common/product'; import { ITelemetryService, ITelemetryData } from 'vs/platform/telemetry/common/telemetry'; import { IWindowsMainService, IOpenConfiguration, IWindowsCountChangedEvent, ICodeWindow, IWindowState as ISingleWindowState, WindowMode } from 'vs/platform/windows/electron-main/windows'; -import { IHistoryMainService, IRecent } from 'vs/platform/history/common/history'; +import { IRecent } from 'vs/platform/history/common/history'; +import { IHistoryMainService } from 'vs/platform/history/electron-main/historyMainService'; import { IProcessEnvironment, isMacintosh, isWindows } from 'vs/base/common/platform'; -import { IWorkspacesMainService, IWorkspaceIdentifier, WORKSPACE_FILTER, isSingleFolderWorkspaceIdentifier, hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspaceIdentifier, WORKSPACE_FILTER, isSingleFolderWorkspaceIdentifier, hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { mnemonicButtonLabel } from 'vs/base/common/labels'; import { Schemas } from 'vs/base/common/network'; @@ -36,7 +38,7 @@ import { exists, dirExists } from 'vs/base/node/pfs'; import { getComparisonKey, isEqual, normalizePath, basename as resourcesBasename, originalFSPath, hasTrailingPathSeparator, removeTrailingPathSeparator } from 'vs/base/common/resources'; import { getRemoteAuthority } from 'vs/platform/remote/common/remoteHosts'; import { restoreWindowsState, WindowsStateStorageData, getWindowsStateStoreData } from 'vs/code/electron-main/windowsStateStorage'; -import { getWorkspaceIdentifier } from 'vs/platform/workspaces/electron-main/workspacesMainService'; +import { getWorkspaceIdentifier, IWorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService'; import { once } from 'vs/base/common/functional'; import { Disposable } from 'vs/base/common/lifecycle'; @@ -186,7 +188,7 @@ export class WindowsManager extends Disposable implements IWindowsMainService { @ILogService private readonly logService: ILogService, @IStateService private readonly stateService: IStateService, @IEnvironmentService private readonly environmentService: IEnvironmentService, - @ILifecycleService private readonly lifecycleService: ILifecycleService, + @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, @IBackupMainService private readonly backupMainService: IBackupMainService, @ITelemetryService private readonly telemetryService: ITelemetryService, @IConfigurationService private readonly configurationService: IConfigurationService, @@ -205,8 +207,8 @@ export class WindowsManager extends Disposable implements IWindowsMainService { this.dialogs = new Dialogs(stateService, this); this.workspacesManager = new WorkspacesManager(workspacesMainService, backupMainService, this); - this.lifecycleService.when(LifecycleMainPhase.Ready).then(() => this.registerListeners()); - this.lifecycleService.when(LifecycleMainPhase.AfterWindowOpen).then(() => this.installWindowsMutex()); + this.lifecycleMainService.when(LifecycleMainPhase.Ready).then(() => this.registerListeners()); + this.lifecycleMainService.when(LifecycleMainPhase.AfterWindowOpen).then(() => this.installWindowsMutex()); } private installWindowsMutex(): void { @@ -215,7 +217,7 @@ export class WindowsManager extends Disposable implements IWindowsMainService { try { const WindowsMutex = (require.__$__nodeRequire('windows-mutex') as typeof import('windows-mutex')).Mutex; const mutex = new WindowsMutex(win32MutexName); - once(this.lifecycleService.onWillShutdown)(() => mutex.release()); + once(this.lifecycleMainService.onWillShutdown)(() => mutex.release()); } catch (e) { this.logService.error(e); } @@ -249,8 +251,8 @@ export class WindowsManager extends Disposable implements IWindowsMainService { } // Handle various lifecycle events around windows - this.lifecycleService.onBeforeWindowClose(window => this.onBeforeWindowClose(window)); - this.lifecycleService.onBeforeShutdown(() => this.onBeforeShutdown()); + this.lifecycleMainService.onBeforeWindowClose(window => this.onBeforeWindowClose(window)); + this.lifecycleMainService.onBeforeShutdown(() => this.onBeforeShutdown()); this.onWindowsCountChanged(e => { if (e.newCount - e.oldCount > 0) { // clear last closed window state when a new window opens. this helps on macOS where @@ -338,7 +340,7 @@ export class WindowsManager extends Disposable implements IWindowsMainService { // See note on #onBeforeShutdown() for details how these events are flowing private onBeforeWindowClose(win: ICodeWindow): void { - if (this.lifecycleService.quitRequested) { + if (this.lifecycleMainService.quitRequested) { return; // during quit, many windows close in parallel so let it be handled in the before-quit handler } @@ -983,7 +985,7 @@ export class WindowsManager extends Disposable implements IWindowsMainService { private getRestoreWindowsSetting(): RestoreWindowsSetting { let restoreWindows: RestoreWindowsSetting; - if (this.lifecycleService.wasRestarted) { + if (this.lifecycleMainService.wasRestarted) { restoreWindows = 'all'; // always reopen all windows when an update was applied } else { const windowConfig = this.configurationService.getValue('window'); @@ -1326,7 +1328,7 @@ export class WindowsManager extends Disposable implements IWindowsMainService { // Window state is from a previous session: only allow fullscreen when we got updated or user wants to restore else { - allowFullscreen = this.lifecycleService.wasRestarted || (windowConfig && windowConfig.restoreFullscreen); + allowFullscreen = this.lifecycleMainService.wasRestarted || (windowConfig && windowConfig.restoreFullscreen); } if (state.mode === WindowMode.Fullscreen && !allowFullscreen) { @@ -1362,7 +1364,7 @@ export class WindowsManager extends Disposable implements IWindowsMainService { window.win.on('closed', () => this.onWindowClosed(window!)); // Lifecycle - (this.lifecycleService as LifecycleService).registerWindow(window); + (this.lifecycleMainService as LifecycleMainService).registerWindow(window); } // Existing window @@ -1385,7 +1387,7 @@ export class WindowsManager extends Disposable implements IWindowsMainService { // first and only load the new configuration if that was // not vetoed if (window.isReady) { - this.lifecycleService.unload(window, UnloadReason.LOAD).then(veto => { + this.lifecycleMainService.unload(window, UnloadReason.LOAD).then(veto => { if (!veto) { this.doOpenInBrowserWindow(window!, configuration, options); } @@ -1552,7 +1554,7 @@ export class WindowsManager extends Disposable implements IWindowsMainService { async reload(win: ICodeWindow, cli?: ParsedArgs): Promise { // Only reload when the window has not vetoed this - const veto = await this.lifecycleService.unload(win, UnloadReason.RELOAD); + const veto = await this.lifecycleMainService.unload(win, UnloadReason.RELOAD); if (!veto) { win.reload(undefined, cli); } @@ -1864,7 +1866,7 @@ export class WindowsManager extends Disposable implements IWindowsMainService { // Otherwise: normal quit else { setTimeout(() => { - this.lifecycleService.quit(); + this.lifecycleMainService.quit(); }, 10 /* delay to unwind callback stack (IPC) */); } } @@ -1955,24 +1957,21 @@ class Dialogs { } showMessageBox(options: Electron.MessageBoxOptions, window?: ICodeWindow): Promise { - return this.getDialogQueue(window).queue(() => { - return new Promise(resolve => { - const callback = (response: number, checkboxChecked: boolean) => { - resolve({ button: response, checkboxChecked }); - }; + return this.getDialogQueue(window).queue(async () => { + let result: Electron.MessageBoxReturnValue; + if (window) { + result = await dialog.showMessageBox(window.win, options); + } else { + result = await dialog.showMessageBox(options); + } - if (window) { - dialog.showMessageBox(window.win, options, callback); - } else { - dialog.showMessageBox(options, callback); - } - }); + return { button: result.response, checkboxChecked: result.checkboxChecked }; }); } showSaveDialog(options: Electron.SaveDialogOptions, window?: ICodeWindow): Promise { - function normalizePath(path: string): string { + function normalizePath(path: string | undefined): string | undefined { if (path && isMacintosh) { path = normalizeNFC(path); // normalize paths returned from the OS } @@ -1980,24 +1979,21 @@ class Dialogs { return path; } - return this.getDialogQueue(window).queue(() => { - return new Promise(resolve => { - const callback = (path: string) => { - resolve(normalizePath(path)); - }; + return this.getDialogQueue(window).queue(async () => { + let result: Electron.SaveDialogReturnValue; + if (window) { + result = await dialog.showSaveDialog(window.win, options); + } else { + result = await dialog.showSaveDialog(options); + } - if (window) { - dialog.showSaveDialog(window.win, options, callback); - } else { - dialog.showSaveDialog(options, callback); - } - }); + return normalizePath(result.filePath); }); } showOpenDialog(options: Electron.OpenDialogOptions, window?: ICodeWindow): Promise { - function normalizePaths(paths: string[]): string[] { + function normalizePaths(paths: string[] | undefined): string[] | undefined { if (paths && paths.length > 0 && isMacintosh) { paths = paths.map(path => normalizeNFC(path)); // normalize paths returned from the OS } @@ -2005,32 +2001,25 @@ class Dialogs { return paths; } - return this.getDialogQueue(window).queue(() => { - return new Promise(resolve => { + return this.getDialogQueue(window).queue(async () => { - // Ensure the path exists (if provided) - let validatePathPromise: Promise = Promise.resolve(); - if (options.defaultPath) { - validatePathPromise = exists(options.defaultPath).then(exists => { - if (!exists) { - options.defaultPath = undefined; - } - }); + // Ensure the path exists (if provided) + if (options.defaultPath) { + const pathExists = await exists(options.defaultPath); + if (!pathExists) { + options.defaultPath = undefined; } + } - // Show dialog and wrap as promise - validatePathPromise.then(() => { - const callback = (paths: string[]) => { - resolve(normalizePaths(paths)); - }; + // Show dialog + let result: Electron.OpenDialogReturnValue; + if (window) { + result = await dialog.showOpenDialog(window.win, options); + } else { + result = await dialog.showOpenDialog(options); + } - if (window) { - dialog.showOpenDialog(window.win, options, callback); - } else { - dialog.showOpenDialog(options, callback); - } - }); - }); + return normalizePaths(result.filePaths); }); } } diff --git a/src/vs/code/node/cli.ts b/src/vs/code/node/cli.ts index 20a1ad506307..fff452e2aac2 100644 --- a/src/vs/code/node/cli.ts +++ b/src/vs/code/node/cli.ts @@ -7,8 +7,7 @@ import { spawn, ChildProcess, SpawnOptions } from 'child_process'; import { buildHelpMessage, buildVersionMessage, addArg, createWaitMarkerFile, OPTIONS } from 'vs/platform/environment/node/argv'; import { parseCLIProcessArgv } from 'vs/platform/environment/node/argvHelper'; import { ParsedArgs } from 'vs/platform/environment/common/environment'; -import product from 'vs/platform/product/node/product'; -import pkg from 'vs/platform/product/node/package'; +import product from 'vs/platform/product/common/product'; import * as paths from 'vs/base/common/path'; import * as os from 'os'; import * as fs from 'fs'; @@ -46,12 +45,12 @@ export async function main(argv: string[]): Promise { // Help if (args.help) { const executable = `${product.applicationName}${os.platform() === 'win32' ? '.exe' : ''}`; - console.log(buildHelpMessage(product.nameLong, executable, pkg.version, OPTIONS)); + console.log(buildHelpMessage(product.nameLong, executable, product.version, OPTIONS)); } // Version Info else if (args.version) { - console.log(buildVersionMessage(pkg.version, product.commit)); + console.log(buildVersionMessage(product.version, product.commit)); } // Extensions Management diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index 43ecdb988fd2..1512375fa597 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -4,8 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { localize } from 'vs/nls'; -import product from 'vs/platform/product/node/product'; -import pkg from 'vs/platform/product/node/package'; +import product from 'vs/platform/product/common/product'; import * as path from 'vs/base/common/path'; import * as semver from 'semver-umd'; @@ -46,7 +45,7 @@ import { FileService } from 'vs/platform/files/common/fileService'; import { IFileService } from 'vs/platform/files/common/files'; import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider'; import { DisposableStore } from 'vs/base/common/lifecycle'; -import { IProductService } from 'vs/platform/product/common/product'; +import { 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); @@ -350,7 +349,7 @@ export async function main(argv: ParsedArgs): Promise { const config: ITelemetryServiceConfig = { appender: combinedAppender(...appenders), - commonProperties: resolveCommonProperties(product.commit, pkg.version, stateService.getItem('telemetry.machineId'), product.msftInternalDomains, installSourcePath), + commonProperties: resolveCommonProperties(product.commit, product.version, stateService.getItem('telemetry.machineId'), product.msftInternalDomains, installSourcePath), piiPaths: extensionsPath ? [appRoot, extensionsPath] : [appRoot] }; diff --git a/src/vs/editor/browser/services/openerService.ts b/src/vs/editor/browser/services/openerService.ts index df7b412c69eb..f71d0a604a8b 100644 --- a/src/vs/editor/browser/services/openerService.ts +++ b/src/vs/editor/browser/services/openerService.ts @@ -13,7 +13,7 @@ import { equalsIgnoreCase } from 'vs/base/common/strings'; import { URI } from 'vs/base/common/uri'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands'; -import { IOpener, IOpenerService, IValidator } from 'vs/platform/opener/common/opener'; +import { IOpener, IOpenerService, IValidator, IExternalUriResolver } from 'vs/platform/opener/common/opener'; export class OpenerService extends Disposable implements IOpenerService { @@ -21,6 +21,7 @@ export class OpenerService extends Disposable implements IOpenerService { private readonly _openers = new LinkedList(); private readonly _validators = new LinkedList(); + private readonly _resolvers = new LinkedList(); constructor( @ICodeEditorService private readonly _editorService: ICodeEditorService, @@ -39,6 +40,11 @@ export class OpenerService extends Disposable implements IOpenerService { return { dispose: remove }; } + registerExternalUriResolver(resolver: IExternalUriResolver): IDisposable { + const remove = this._resolvers.push(resolver); + return { dispose: remove }; + } + async open(resource: URI, options?: { openToSide?: boolean, openExternal?: boolean }): Promise { // no scheme ?!? if (!resource.scheme) { @@ -118,7 +124,11 @@ export class OpenerService extends Disposable implements IOpenerService { } } - private _doOpenExternal(resource: URI): Promise { + private async _doOpenExternal(resource: URI): Promise { + for (const resolver of this._resolvers.toArray()) { + resource = await resolver.resolveExternalUri(resource); + } + dom.windowOpenNoOpener(encodeURI(resource.toString(true))); return Promise.resolve(true); diff --git a/src/vs/editor/browser/viewParts/lineNumbers/lineNumbers.ts b/src/vs/editor/browser/viewParts/lineNumbers/lineNumbers.ts index b3dd48afd517..bd46aff4f28f 100644 --- a/src/vs/editor/browser/viewParts/lineNumbers/lineNumbers.ts +++ b/src/vs/editor/browser/viewParts/lineNumbers/lineNumbers.ts @@ -101,15 +101,6 @@ export class LineNumbersOverlay extends DynamicViewOverlay { } const modelLineNumber = modelPosition.lineNumber; - if (!this._renderFinalNewline) { - const lineCount = this._context.model.getLineCount(); - const lineContent = this._context.model.getLineContent(modelLineNumber); - - if (modelLineNumber === lineCount && lineContent === '') { - return ''; - } - } - if (this._renderCustomLineNumbers) { return this._renderCustomLineNumbers(modelLineNumber); } @@ -146,10 +137,19 @@ export class LineNumbersOverlay extends DynamicViewOverlay { const visibleEndLineNumber = ctx.visibleRange.endLineNumber; const common = '
'; + const lineCount = this._context.model.getLineCount(); const output: string[] = []; for (let lineNumber = visibleStartLineNumber; lineNumber <= visibleEndLineNumber; lineNumber++) { const lineIndex = lineNumber - visibleStartLineNumber; + if (!this._renderFinalNewline) { + if (lineNumber === lineCount && this._context.model.getLineLength(lineNumber) === 0) { + // Do not render last (empty) line + output[lineIndex] = ''; + continue; + } + } + const renderLineNumber = this._getLineRenderLineNumber(lineNumber); if (renderLineNumber) { diff --git a/src/vs/editor/common/commands/shiftCommand.ts b/src/vs/editor/common/commands/shiftCommand.ts index 6589c265a379..da4e75c846d6 100644 --- a/src/vs/editor/common/commands/shiftCommand.ts +++ b/src/vs/editor/common/commands/shiftCommand.ts @@ -179,7 +179,7 @@ export class ShiftCommand implements ICommand { } this._addEditOperation(builder, new Range(lineNumber, 1, lineNumber, indentationEndIndex + 1), desiredIndent); - if (lineNumber === startLine) { + if (lineNumber === startLine && !this._selection.isEmpty()) { // Force the startColumn to stay put because we're inserting after it this._selectionStartColumnStaysPut = (this._selection.startColumn <= indentationEndIndex + 1); } @@ -226,7 +226,7 @@ export class ShiftCommand implements ICommand { this._addEditOperation(builder, new Range(lineNumber, 1, lineNumber, indentationEndIndex + 1), ''); } else { this._addEditOperation(builder, new Range(lineNumber, 1, lineNumber, 1), oneIndent); - if (lineNumber === startLine) { + if (lineNumber === startLine && !this._selection.isEmpty()) { // Force the startColumn to stay put because we're inserting after it this._selectionStartColumnStaysPut = (this._selection.startColumn === 1); } diff --git a/src/vs/editor/contrib/find/findWidget.ts b/src/vs/editor/contrib/find/findWidget.ts index a6b76b13d640..10d08dee841f 100644 --- a/src/vs/editor/contrib/find/findWidget.ts +++ b/src/vs/editor/contrib/find/findWidget.ts @@ -642,6 +642,10 @@ export class FindWidget extends Widget implements IOverlayWidget, IHorizontalSas if (!this._isVisible) { return; } + if (!dom.isInDOM(this._domNode)) { + // the widget is not in the DOM + return; + } const layoutInfo = this._codeEditor.getLayoutInfo(); const editorContentWidth = layoutInfo.contentWidth; diff --git a/src/vs/editor/contrib/linesOperations/test/linesOperations.test.ts b/src/vs/editor/contrib/linesOperations/test/linesOperations.test.ts index 036e51d29667..30904395a7ef 100644 --- a/src/vs/editor/contrib/linesOperations/test/linesOperations.test.ts +++ b/src/vs/editor/contrib/linesOperations/test/linesOperations.test.ts @@ -928,6 +928,28 @@ suite('Editor Contrib - Line Operations', () => { model.dispose(); }); + test('issue #80736: Indenting while the cursor is at the start of a line of text causes the added spaces or tab to be selected', () => { + const model = createTextModel( + [ + 'Some text' + ].join('\n'), + { + insertSpaces: false, + } + ); + + withTestCodeEditor(null, { model: model }, (editor) => { + const indentLinesAction = new IndentLinesAction(); + editor.setPosition(new Position(1, 1)); + + indentLinesAction.run(null!, editor); + assert.equal(model.getLineContent(1), '\tSome text'); + assert.deepEqual(editor.getSelection(), new Selection(1, 2, 1, 2)); + }); + + model.dispose(); + }); + test('issue #62112: Delete line does not work properly when multiple cursors are on line', () => { const TEXT = [ 'a', diff --git a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts index f0a4d60dc7d4..15c44fad54df 100644 --- a/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts +++ b/src/vs/editor/contrib/parameterHints/parameterHintsWidget.ts @@ -332,7 +332,12 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget, private updateMaxHeight(): void { const height = Math.max(this.editor.getLayoutInfo().height / 4, 250); - this.element.style.maxHeight = `${height}px`; + const maxHeight = `${height}px`; + this.element.style.maxHeight = maxHeight; + const wrapper = this.element.getElementsByClassName('wrapper') as HTMLCollectionOf; + if (wrapper.length) { + wrapper[0].style.maxHeight = maxHeight; + } } } diff --git a/src/vs/editor/standalone/browser/simpleServices.ts b/src/vs/editor/standalone/browser/simpleServices.ts index a3489b4e3cc5..340215e251c0 100644 --- a/src/vs/editor/standalone/browser/simpleServices.ts +++ b/src/vs/editor/standalone/browser/simpleServices.ts @@ -186,6 +186,10 @@ export class SimpleDialogService implements IDialogService { public show(severity: Severity, message: string, buttons: string[], options?: IDialogOptions): Promise { return Promise.resolve({ choice: 0 }); } + + public about(): Promise { + return Promise.resolve(undefined); + } } export class SimpleNotificationService implements INotificationService { diff --git a/src/vs/editor/test/browser/commands/shiftCommand.test.ts b/src/vs/editor/test/browser/commands/shiftCommand.test.ts index 909db815f26f..265786c631bc 100644 --- a/src/vs/editor/test/browser/commands/shiftCommand.test.ts +++ b/src/vs/editor/test/browser/commands/shiftCommand.test.ts @@ -92,7 +92,7 @@ suite('Editor Commands - ShiftCommand', () => { '', '123' ], - new Selection(1, 1, 1, 2) + new Selection(1, 2, 1, 2) ); }); diff --git a/src/vs/nls.d.ts b/src/vs/nls.d.ts index 14b2baed54b6..9b5dbc8f9840 100644 --- a/src/vs/nls.d.ts +++ b/src/vs/nls.d.ts @@ -9,11 +9,17 @@ export interface ILocalizeInfo { } /** - * Localize a message. `message` can contain `{n}` notation where it is replaced by the nth value in `...args`. + * Localize a message. + * + * `message` can contain `{n}` notation where it is replaced by the nth value in `...args` + * For example, `localize('hello {0}', name)` */ export declare function localize(info: ILocalizeInfo, message: string, ...args: (string | number | boolean | undefined | null)[]): string; /** - * Localize a message. `message` can contain `{n}` notation where it is replaced by the nth value in `...args`. + * Localize a message. + * + * `message` can contain `{n}` notation where it is replaced by the nth value in `...args` + * For example, `localize('hello {0}', name)` */ export declare function localize(key: string, message: string, ...args: (string | number | boolean | undefined | null)[]): string; diff --git a/src/vs/platform/backup/common/backup.ts b/src/vs/platform/backup/electron-main/backup.ts similarity index 71% rename from src/vs/platform/backup/common/backup.ts rename to src/vs/platform/backup/electron-main/backup.ts index 145f9af1f909..59effce98740 100644 --- a/src/vs/platform/backup/common/backup.ts +++ b/src/vs/platform/backup/electron-main/backup.ts @@ -6,27 +6,10 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { URI } from 'vs/base/common/uri'; - -export interface ISerializedWorkspace { id: string; configURIPath: string; remoteAuthority?: string; } - -export interface IBackupWorkspacesFormat { - rootURIWorkspaces: ISerializedWorkspace[]; - folderURIWorkspaces: string[]; - emptyWorkspaceInfos: IEmptyWindowBackupInfo[]; - - // deprecated - folderWorkspaces?: string[]; // use folderURIWorkspaces instead - emptyWorkspaces?: string[]; - rootWorkspaces?: { id: string, configPath: string }[]; // use rootURIWorkspaces instead -} +import { IEmptyWindowBackupInfo } from 'vs/platform/backup/node/backup'; export const IBackupMainService = createDecorator('backupMainService'); -export interface IEmptyWindowBackupInfo { - backupFolder: string; - remoteAuthority?: string; -} - export interface IWorkspaceBackupInfo { workspace: IWorkspaceIdentifier; remoteAuthority?: string; @@ -48,4 +31,4 @@ export interface IBackupMainService { unregisterWorkspaceBackupSync(workspace: IWorkspaceIdentifier): void; unregisterFolderBackupSync(folderUri: URI): void; unregisterEmptyWindowBackupSync(backupFolder: string): void; -} \ No newline at end of file +} diff --git a/src/vs/platform/backup/electron-main/backupMainService.ts b/src/vs/platform/backup/electron-main/backupMainService.ts index d5b98fd27987..62ab01884ad6 100644 --- a/src/vs/platform/backup/electron-main/backupMainService.ts +++ b/src/vs/platform/backup/electron-main/backupMainService.ts @@ -9,7 +9,8 @@ import * as path from 'vs/base/common/path'; import * as platform from 'vs/base/common/platform'; import { writeFileSync, writeFile, readFile, readdir, exists, rimraf, rename, RimRafMode } from 'vs/base/node/pfs'; import * as arrays from 'vs/base/common/arrays'; -import { IBackupMainService, IBackupWorkspacesFormat, IEmptyWindowBackupInfo, IWorkspaceBackupInfo } from 'vs/platform/backup/common/backup'; +import { IBackupMainService, IWorkspaceBackupInfo } 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 { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IFilesConfiguration, HotExitConfiguration } from 'vs/platform/files/common/files'; diff --git a/src/vs/platform/backup/node/backup.ts b/src/vs/platform/backup/node/backup.ts new file mode 100644 index 000000000000..f2e5cd022f53 --- /dev/null +++ b/src/vs/platform/backup/node/backup.ts @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export interface ISerializedWorkspace { id: string; configURIPath: string; remoteAuthority?: string; } + +export interface IBackupWorkspacesFormat { + rootURIWorkspaces: ISerializedWorkspace[]; + folderURIWorkspaces: string[]; + emptyWorkspaceInfos: IEmptyWindowBackupInfo[]; + + // deprecated + folderWorkspaces?: string[]; // use folderURIWorkspaces instead + emptyWorkspaces?: string[]; + rootWorkspaces?: { id: string, configPath: string }[]; // use rootURIWorkspaces instead +} + +export interface IEmptyWindowBackupInfo { + backupFolder: string; + remoteAuthority?: string; +} diff --git a/src/vs/platform/backup/test/electron-main/backupMainService.test.ts b/src/vs/platform/backup/test/electron-main/backupMainService.test.ts index dd9ccd4d96f6..d888c87bdae2 100644 --- a/src/vs/platform/backup/test/electron-main/backupMainService.test.ts +++ b/src/vs/platform/backup/test/electron-main/backupMainService.test.ts @@ -9,11 +9,12 @@ import * as fs from 'fs'; import * as os from 'os'; import * as path from 'vs/base/common/path'; import * as pfs from 'vs/base/node/pfs'; -import { URI as Uri, URI } from 'vs/base/common/uri'; +import { URI } from 'vs/base/common/uri'; import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv'; import { BackupMainService } from 'vs/platform/backup/electron-main/backupMainService'; -import { IBackupWorkspacesFormat, ISerializedWorkspace, IWorkspaceBackupInfo } from 'vs/platform/backup/common/backup'; +import { IWorkspaceBackupInfo } from 'vs/platform/backup/electron-main/backup'; +import { IBackupWorkspacesFormat, ISerializedWorkspace } from 'vs/platform/backup/node/backup'; import { HotExitConfiguration } from 'vs/platform/files/common/files'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; import { ConsoleLogMainService } from 'vs/platform/log/common/log'; @@ -24,7 +25,7 @@ import { Schemas } from 'vs/base/common/network'; suite('BackupMainService', () => { - function assertEqualUris(actual: Uri[], expected: Uri[]) { + function assertEqualUris(actual: URI[], expected: URI[]) { assert.deepEqual(actual.map(a => a.toString()), expected.map(a => a.toString())); } @@ -43,12 +44,12 @@ suite('BackupMainService', () => { this.workspacesJsonPath = backupWorkspacesPath; } - public toBackupPath(arg: Uri | string): string { - const id = arg instanceof Uri ? super.getFolderHash(arg) : arg; + public toBackupPath(arg: URI | string): string { + const id = arg instanceof URI ? super.getFolderHash(arg) : arg; return path.join(this.backupHome, id); } - public getFolderHash(folderUri: Uri): string { + public getFolderHash(folderUri: URI): string { return super.getFolderHash(folderUri); } @@ -81,7 +82,7 @@ suite('BackupMainService', () => { }; } - async function ensureFolderExists(uri: Uri): Promise { + async function ensureFolderExists(uri: URI): Promise { if (!fs.existsSync(uri.fsPath)) { fs.mkdirSync(uri.fsPath); } @@ -110,10 +111,10 @@ suite('BackupMainService', () => { return platform.isLinux ? p : p.toLowerCase(); } - const fooFile = Uri.file(platform.isWindows ? 'C:\\foo' : '/foo'); - const barFile = Uri.file(platform.isWindows ? 'C:\\bar' : '/bar'); + const fooFile = URI.file(platform.isWindows ? 'C:\\foo' : '/foo'); + const barFile = URI.file(platform.isWindows ? 'C:\\bar' : '/bar'); - const existingTestFolder1 = Uri.file(path.join(parentDir, 'folder1')); + const existingTestFolder1 = URI.file(path.join(parentDir, 'folder1')); let service: TestBackupMainService; let configService: TestConfigurationService; @@ -231,7 +232,7 @@ suite('BackupMainService', () => { const backupPathToMigrate = service.toBackupPath(fooFile); fs.mkdirSync(backupPathToMigrate); fs.writeFileSync(path.join(backupPathToMigrate, 'backup.txt'), 'Some Data'); - service.registerFolderBackupSync(Uri.file(backupPathToMigrate)); + service.registerFolderBackupSync(URI.file(backupPathToMigrate)); const workspaceBackupPath = service.registerWorkspaceBackupSync(toWorkspaceBackupInfo(barFile.fsPath), backupPathToMigrate); @@ -247,12 +248,12 @@ suite('BackupMainService', () => { const backupPathToMigrate = service.toBackupPath(fooFile); fs.mkdirSync(backupPathToMigrate); fs.writeFileSync(path.join(backupPathToMigrate, 'backup.txt'), 'Some Data'); - service.registerFolderBackupSync(Uri.file(backupPathToMigrate)); + service.registerFolderBackupSync(URI.file(backupPathToMigrate)); const backupPathToPreserve = service.toBackupPath(barFile); fs.mkdirSync(backupPathToPreserve); fs.writeFileSync(path.join(backupPathToPreserve, 'backup.txt'), 'Some Data'); - service.registerFolderBackupSync(Uri.file(backupPathToPreserve)); + service.registerFolderBackupSync(URI.file(backupPathToPreserve)); const workspaceBackupPath = service.registerWorkspaceBackupSync(toWorkspaceBackupInfo(barFile.fsPath), backupPathToMigrate); @@ -270,8 +271,8 @@ suite('BackupMainService', () => { test('migration folder path to URI makes sure to preserve existing backups', async () => { let path1 = path.join(parentDir, 'folder1'); let path2 = path.join(parentDir, 'FOLDER2'); - let uri1 = Uri.file(path1); - let uri2 = Uri.file(path2); + let uri1 = URI.file(path1); + let uri2 = URI.file(path2); if (!fs.existsSync(path1)) { fs.mkdirSync(path1); @@ -372,8 +373,8 @@ suite('BackupMainService', () => { }); test('getFolderBackupPaths() should return [] when files.hotExit = "onExitAndWindowClose"', async () => { - service.registerFolderBackupSync(Uri.file(fooFile.fsPath.toUpperCase())); - assertEqualUris(service.getFolderBackupPaths(), [Uri.file(fooFile.fsPath.toUpperCase())]); + service.registerFolderBackupSync(URI.file(fooFile.fsPath.toUpperCase())); + assertEqualUris(service.getFolderBackupPaths(), [URI.file(fooFile.fsPath.toUpperCase())]); configService.setUserConfiguration('files.hotExit', HotExitConfiguration.ON_EXIT_AND_WINDOW_CLOSE); await service.initialize(); assertEqualUris(service.getFolderBackupPaths(), []); @@ -591,11 +592,11 @@ suite('BackupMainService', () => { }); test('should always store the workspace path in workspaces.json using the case given, regardless of whether the file system is case-sensitive (folder workspace)', () => { - service.registerFolderBackupSync(Uri.file(fooFile.fsPath.toUpperCase())); - assertEqualUris(service.getFolderBackupPaths(), [Uri.file(fooFile.fsPath.toUpperCase())]); + service.registerFolderBackupSync(URI.file(fooFile.fsPath.toUpperCase())); + assertEqualUris(service.getFolderBackupPaths(), [URI.file(fooFile.fsPath.toUpperCase())]); return pfs.readFile(backupWorkspacesPath, 'utf-8').then(buffer => { const json = JSON.parse(buffer); - assert.deepEqual(json.folderURIWorkspaces, [Uri.file(fooFile.fsPath.toUpperCase()).toString()]); + assert.deepEqual(json.folderURIWorkspaces, [URI.file(fooFile.fsPath.toUpperCase()).toString()]); }); }); @@ -681,11 +682,11 @@ suite('BackupMainService', () => { } if (platform.isMacintosh) { - assert.equal(service.getFolderHash(Uri.file('/foo')), service.getFolderHash(Uri.file('/FOO'))); + assert.equal(service.getFolderHash(URI.file('/foo')), service.getFolderHash(URI.file('/FOO'))); } if (platform.isWindows) { - assert.equal(service.getFolderHash(Uri.file('c:\\foo')), service.getFolderHash(Uri.file('C:\\FOO'))); + assert.equal(service.getFolderHash(URI.file('c:\\foo')), service.getFolderHash(URI.file('C:\\FOO'))); } }); }); @@ -693,7 +694,7 @@ suite('BackupMainService', () => { suite('mixed path casing', () => { test('should handle case insensitive paths properly (registerWindowForBackupsSync) (folder workspace)', () => { service.registerFolderBackupSync(fooFile); - service.registerFolderBackupSync(Uri.file(fooFile.fsPath.toUpperCase())); + service.registerFolderBackupSync(URI.file(fooFile.fsPath.toUpperCase())); if (platform.isLinux) { assert.equal(service.getFolderBackupPaths().length, 2); @@ -722,7 +723,7 @@ suite('BackupMainService', () => { // mixed case service.registerFolderBackupSync(fooFile); - service.unregisterFolderBackupSync(Uri.file(fooFile.fsPath.toUpperCase())); + service.unregisterFolderBackupSync(URI.file(fooFile.fsPath.toUpperCase())); if (platform.isLinux) { assert.equal(service.getFolderBackupPaths().length, 1); diff --git a/src/vs/platform/debug/common/extensionHostDebug.ts b/src/vs/platform/debug/common/extensionHostDebug.ts index 4593debff50a..2c7cb8c52b23 100644 --- a/src/vs/platform/debug/common/extensionHostDebug.ts +++ b/src/vs/platform/debug/common/extensionHostDebug.ts @@ -6,6 +6,8 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { Event } from 'vs/base/common/event'; import { IRemoteConsoleLog } from 'vs/base/common/console'; +import { ParsedArgs } from 'vs/platform/environment/common/environment'; +import { IProcessEnvironment } from 'vs/base/common/platform'; export const IExtensionHostDebugService = createDecorator('extensionHostDebugService'); @@ -37,17 +39,19 @@ export interface IExtensionHostDebugService { _serviceBrand: undefined; reload(sessionId: string): void; - onReload: Event; + readonly onReload: Event; close(sessionId: string): void; - onClose: Event; + readonly onClose: Event; attachSession(sessionId: string, port: number, subId?: string): void; - onAttachSession: Event; + readonly onAttachSession: Event; logToSession(sessionId: string, log: IRemoteConsoleLog): void; - onLogToSession: Event; + readonly onLogToSession: Event; terminateSession(sessionId: string, subId?: string): void; - onTerminateSession: Event; + readonly onTerminateSession: Event; + + openExtensionDevelopmentHostWindow(args: ParsedArgs, env: IProcessEnvironment): Promise; } diff --git a/src/vs/platform/debug/common/extensionHostDebugIpc.ts b/src/vs/platform/debug/common/extensionHostDebugIpc.ts index 35870be158dd..11df86a82e1e 100644 --- a/src/vs/platform/debug/common/extensionHostDebugIpc.ts +++ b/src/vs/platform/debug/common/extensionHostDebugIpc.ts @@ -8,6 +8,8 @@ import { IReloadSessionEvent, ICloseSessionEvent, IAttachSessionEvent, ILogToSes import { Event, Emitter } from 'vs/base/common/event'; import { IRemoteConsoleLog } from 'vs/base/common/console'; import { Disposable } from 'vs/base/common/lifecycle'; +import { ParsedArgs } from 'vs/platform/environment/common/environment'; +import { IProcessEnvironment } from 'vs/base/common/platform'; export class ExtensionHostDebugBroadcastChannel implements IServerChannel { @@ -99,4 +101,10 @@ export class ExtensionHostDebugChannelClient extends Disposable implements IExte get onTerminateSession(): Event { return this.channel.listen('terminate'); } -} \ No newline at end of file + + openExtensionDevelopmentHostWindow(args: ParsedArgs, env: IProcessEnvironment): Promise { + // TODO@Isidor + //return this.channel.call('openExtensionDevelopmentHostWindow', [args, env]); + return Promise.resolve(); + } +} diff --git a/src/vs/platform/diagnostics/node/diagnosticsIpc.ts b/src/vs/platform/diagnostics/node/diagnosticsIpc.ts index 365e25431eac..b023a180215b 100644 --- a/src/vs/platform/diagnostics/node/diagnosticsIpc.ts +++ b/src/vs/platform/diagnostics/node/diagnosticsIpc.ts @@ -7,7 +7,7 @@ import { IServerChannel, IChannel } from 'vs/base/parts/ipc/common/ipc'; import { IRemoteDiagnosticInfo, IRemoteDiagnosticError, SystemInfo, PerformanceInfo } from 'vs/platform/diagnostics/common/diagnostics'; import { IDiagnosticsService } from './diagnosticsService'; import { Event } from 'vs/base/common/event'; -import { IMainProcessInfo } from 'vs/platform/launch/common/launchService'; +import { IMainProcessInfo } from 'vs/platform/launch/common/launch'; import { IWorkspace } from 'vs/platform/workspace/common/workspace'; export class DiagnosticsChannel implements IServerChannel { diff --git a/src/vs/platform/diagnostics/node/diagnosticsService.ts b/src/vs/platform/diagnostics/node/diagnosticsService.ts index 169bc3c60cfd..f1cf312dc1b7 100644 --- a/src/vs/platform/diagnostics/node/diagnosticsService.ts +++ b/src/vs/platform/diagnostics/node/diagnosticsService.ts @@ -9,13 +9,12 @@ import { readdir, stat, exists, readFile } from 'fs'; import { join, basename } from 'vs/base/common/path'; import { parse, ParseError } from 'vs/base/common/json'; import { listProcesses } from 'vs/base/node/ps'; -import product from 'vs/platform/product/node/product'; -import pkg from 'vs/platform/product/node/package'; +import product from 'vs/platform/product/common/product'; import { repeat, pad } from 'vs/base/common/strings'; import { isWindows } 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/launchService'; +import { IMainProcessInfo } from 'vs/platform/launch/common/launch'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; @@ -267,7 +266,7 @@ export class DiagnosticsService implements IDiagnosticsService { const GB = 1024 * MB; const output: string[] = []; - output.push(`Version: ${pkg.name} ${pkg.version} (${product.commit || 'Commit unknown'}, ${product.date || 'Date unknown'})`); + output.push(`Version: ${product.nameShort} ${product.version} (${product.commit || 'Commit unknown'}, ${product.date || 'Date unknown'})`); output.push(`OS Version: ${osLib.type()} ${osLib.arch()} ${osLib.release()}`); const cpus = osLib.cpus(); if (cpus && cpus.length > 0) { diff --git a/src/vs/platform/dialogs/common/dialogs.ts b/src/vs/platform/dialogs/common/dialogs.ts index dda74c3614c0..4c0bf84e71c7 100644 --- a/src/vs/platform/dialogs/common/dialogs.ts +++ b/src/vs/platform/dialogs/common/dialogs.ts @@ -172,6 +172,11 @@ export interface IDialogService { * option then promise with index `0` is returned. */ show(severity: Severity, message: string, buttons: string[], options?: IDialogOptions): Promise; + + /** + * Present the about dialog to the user. + */ + about(): Promise; } export const IFileDialogService = createDecorator('fileDialogService'); @@ -235,11 +240,10 @@ export interface IFileDialogService { * Shows a open file dialog and returns the chosen file URI. */ showOpenDialog(options: IOpenDialogOptions): Promise; - } const MAX_CONFIRM_FILES = 10; -export function getConfirmMessage(start: string, resourcesToConfirm: URI[]): string { +export function getConfirmMessage(start: string, resourcesToConfirm: readonly URI[]): string { const message = [start]; message.push(''); message.push(...resourcesToConfirm.slice(0, MAX_CONFIRM_FILES).map(r => basename(r))); diff --git a/src/vs/platform/dialogs/node/dialogIpc.ts b/src/vs/platform/dialogs/electron-browser/dialogIpc.ts similarity index 58% rename from src/vs/platform/dialogs/node/dialogIpc.ts rename to src/vs/platform/dialogs/electron-browser/dialogIpc.ts index 9f71b02b8294..5ff5faa82481 100644 --- a/src/vs/platform/dialogs/node/dialogIpc.ts +++ b/src/vs/platform/dialogs/electron-browser/dialogIpc.ts @@ -3,9 +3,8 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; -import { IDialogService, IConfirmation, IConfirmationResult, IShowResult } from 'vs/platform/dialogs/common/dialogs'; -import Severity from 'vs/base/common/severity'; +import { IServerChannel } from 'vs/base/parts/ipc/common/ipc'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { Event } from 'vs/base/common/event'; export class DialogChannel implements IServerChannel { @@ -20,22 +19,8 @@ export class DialogChannel implements IServerChannel { switch (command) { case 'show': return this.dialogService.show(args![0], args![1], args![2]); case 'confirm': return this.dialogService.confirm(args![0]); + case 'about': return this.dialogService.about(); } return Promise.reject(new Error('invalid command')); } } - -export class DialogChannelClient implements IDialogService { - - _serviceBrand: undefined; - - constructor(private channel: IChannel) { } - - show(severity: Severity, message: string, options: string[]): Promise { - return this.channel.call('show', [severity, message, options]); - } - - confirm(confirmation: IConfirmation): Promise { - return this.channel.call('confirm', [confirmation]); - } -} diff --git a/src/vs/platform/driver/electron-main/driver.ts b/src/vs/platform/driver/electron-main/driver.ts index 4621c0e333f6..5e1a5efa45f3 100644 --- a/src/vs/platform/driver/electron-main/driver.ts +++ b/src/vs/platform/driver/electron-main/driver.ts @@ -34,7 +34,7 @@ export class Driver implements IDriver, IWindowDriverRegistry { constructor( private windowServer: IPCServer, private options: IDriverOptions, - @IWindowsMainService private readonly windowsService: IWindowsMainService + @IWindowsMainService private readonly windowsMainService: IWindowsMainService ) { } async registerWindowDriver(windowId: number): Promise { @@ -49,7 +49,7 @@ export class Driver implements IDriver, IWindowDriverRegistry { } async getWindowIds(): Promise { - return this.windowsService.getWindows() + return this.windowsMainService.getWindows() .map(w => w.id) .filter(id => this.registeredWindowIds.has(id) && !this.reloadingWindowIds.has(id)); } @@ -57,7 +57,7 @@ export class Driver implements IDriver, IWindowDriverRegistry { async capturePage(windowId: number): Promise { await this.whenUnfrozen(windowId); - const window = this.windowsService.getWindowById(windowId); + const window = this.windowsMainService.getWindowById(windowId); if (!window) { throw new Error('Invalid window'); } @@ -69,16 +69,16 @@ export class Driver implements IDriver, IWindowDriverRegistry { async reloadWindow(windowId: number): Promise { await this.whenUnfrozen(windowId); - const window = this.windowsService.getWindowById(windowId); + const window = this.windowsMainService.getWindowById(windowId); if (!window) { throw new Error('Invalid window'); } this.reloadingWindowIds.add(windowId); - this.windowsService.reload(window); + this.windowsMainService.reload(window); } async exitApplication(): Promise { - return this.windowsService.quit(); + return this.windowsMainService.quit(); } async dispatchKeybinding(windowId: number, keybinding: string): Promise { @@ -96,7 +96,7 @@ export class Driver implements IDriver, IWindowDriverRegistry { throw new Error('ScanCodeBindings not supported'); } - const window = this.windowsService.getWindowById(windowId); + const window = this.windowsMainService.getWindowById(windowId); if (!window) { throw new Error('Invalid window'); } diff --git a/src/vs/platform/electron/electron-browser/electronService.ts b/src/vs/platform/electron/electron-browser/electronService.ts new file mode 100644 index 000000000000..a580b0197db3 --- /dev/null +++ b/src/vs/platform/electron/electron-browser/electronService.ts @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; + +export class ElectronService { + + _serviceBrand: undefined; + + constructor(@IMainProcessService mainProcessService: IMainProcessService) { + const channel = mainProcessService.getChannel('electron'); + + // Proxy: forward any property access to the channel + return new Proxy({}, { + get(_target, propKey, _receiver) { + if (typeof propKey === 'string') { + return function (...args: any[]) { + return channel.call(propKey, ...args); + }; + } + + throw new Error(`Not Implemented in ElectronService: ${String(propKey)}`); + } + }) as ElectronService; + } +} diff --git a/src/vs/platform/electron/electron-main/electronMainService.ts b/src/vs/platform/electron/electron-main/electronMainService.ts new file mode 100644 index 000000000000..648f3deb6577 --- /dev/null +++ b/src/vs/platform/electron/electron-main/electronMainService.ts @@ -0,0 +1,59 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IElectronService } from 'vs/platform/electron/node/electron'; +import { IWindowsMainService, ICodeWindow } from 'vs/platform/windows/electron-main/windows'; +import { MessageBoxOptions, MessageBoxReturnValue } from 'electron'; +import { IServerChannel } from 'vs/base/parts/ipc/common/ipc'; +import { Event } from 'vs/base/common/event'; + +export class ElectronMainService implements IElectronService { + + _serviceBrand: undefined; + + constructor( + @IWindowsMainService private readonly windowsMainService: IWindowsMainService + ) { + } + + private get window(): ICodeWindow | undefined { + return this.windowsMainService.getFocusedWindow() || this.windowsMainService.getLastActiveWindow(); + } + + async showMessageBox(options: MessageBoxOptions): Promise { + const result = await this.windowsMainService.showMessageBox(options, this.window); + + return { + response: result.button, + checkboxChecked: !!result.checkboxChecked + }; + } +} + +export class ElectronChannel implements IServerChannel { + + private service: { [key: string]: unknown }; + + constructor(service: IElectronService) { + this.service = service as unknown as { [key: string]: unknown }; + } + + listen(_: unknown, event: string): Event { + throw new Error(`Event not found: ${event}`); + } + + call(_: unknown, command: string, arg?: any): Promise { + const target = this.service[command]; + if (typeof target === 'function') { + if (Array.isArray(arg)) { + return target.apply(this.service, arg); + } + + return target.call(this.service, arg); + } + + throw new Error(`Call Not Found in ElectronService: ${command}`); + } +} diff --git a/src/vs/platform/electron/node/electron.ts b/src/vs/platform/electron/node/electron.ts new file mode 100644 index 000000000000..c2d0f78f1eca --- /dev/null +++ b/src/vs/platform/electron/node/electron.ts @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { MessageBoxOptions, MessageBoxReturnValue } from 'electron'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; + +export const IElectronService = createDecorator('electronService'); + +export interface IElectronService { + + _serviceBrand: undefined; + + // Dialogs + showMessageBox(options: MessageBoxOptions): Promise; +} diff --git a/src/vs/platform/environment/node/environmentService.ts b/src/vs/platform/environment/node/environmentService.ts index 52caa265e2b9..5cfdf8583925 100644 --- a/src/vs/platform/environment/node/environmentService.ts +++ b/src/vs/platform/environment/node/environmentService.ts @@ -10,8 +10,7 @@ import * as os from 'os'; import * as path from 'vs/base/common/path'; import * as resources from 'vs/base/common/resources'; import { memoize } from 'vs/base/common/decorators'; -import pkg from 'vs/platform/product/node/package'; -import product from 'vs/platform/product/node/product'; +import product from 'vs/platform/product/common/product'; import { toLocalISOString } from 'vs/base/common/date'; import { isWindows, isLinux } from 'vs/base/common/platform'; import { getPathFromAmdModule } from 'vs/base/common/amd'; @@ -26,16 +25,16 @@ function getNixIPCHandle(userDataPath: string, type: string): string { if (xdgRuntimeDir && !vscodePortable) { const scope = crypto.createHash('md5').update(userDataPath).digest('hex').substr(0, 8); - return path.join(xdgRuntimeDir, `vscode-${scope}-${pkg.version}-${type}.sock`); + return path.join(xdgRuntimeDir, `vscode-${scope}-${product.version}-${type}.sock`); } - return path.join(userDataPath, `${pkg.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}-${pkg.version}-${type}-sock`; + return `\\\\.\\pipe\\${scope}-${product.version}-${type}-sock`; } function getIPCHandle(userDataPath: string, type: string): string { diff --git a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts index e3ae633b4e3a..12610f14af20 100644 --- a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts +++ b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts @@ -26,7 +26,7 @@ import { IFileService } from 'vs/platform/files/common/files'; import { URI } from 'vs/base/common/uri'; import { joinPath } from 'vs/base/common/resources'; import { VSBuffer } from 'vs/base/common/buffer'; -import { IProductService } from 'vs/platform/product/common/product'; +import { IProductService } from 'vs/platform/product/common/productService'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { optional } from 'vs/platform/instantiation/common/instantiation'; diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index db428a58e63c..26fd2e7e019d 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -28,7 +28,7 @@ import { Limiter, createCancelablePromise, CancelablePromise, Queue } from 'vs/b import { Event, Emitter } from 'vs/base/common/event'; import * as semver from 'semver-umd'; import { URI } from 'vs/base/common/uri'; -import pkg from 'vs/platform/product/node/package'; +import product from 'vs/platform/product/common/product'; import { isMacintosh, isWindows } from 'vs/base/common/platform'; import { ILogService } from 'vs/platform/log/common/log'; import { ExtensionsManifestCache } from 'vs/platform/extensionManagement/node/extensionsManifestCache'; @@ -46,9 +46,6 @@ import { getPathFromAmdModule } from 'vs/base/common/amd'; import { getManifest } from 'vs/platform/extensionManagement/node/extensionManagementUtil'; import { IExtensionManifest, ExtensionType } from 'vs/platform/extensions/common/extensions'; -// {{SQL CARBON EDIT} -import product from 'vs/platform/product/node/product'; - const ERROR_SCANNING_SYS_EXTENSIONS = 'scanningSystem'; const ERROR_SCANNING_USER_EXTENSIONS = 'scanningUser'; const INSTALL_ERROR_UNSET_UNINSTALLED = 'unsetUninstalled'; @@ -210,8 +207,8 @@ export class ExtensionManagementService extends Disposable implements IExtension const identifier = { id: getGalleryExtensionId(manifest.publisher, manifest.name) }; let operation: InstallOperation = InstallOperation.Install; // {{SQL CARBON EDIT - Check VSCode and ADS version}} - if (manifest.engines && ((manifest.engines.vscode && !isEngineValid(manifest.engines.vscode, product.vscodeVersion)) || (manifest.engines.azdata && !isEngineValid(manifest.engines.azdata, pkg.version)))) { - return Promise.reject(new Error(nls.localize('incompatible', "Unable to install version '{2}' of extension '{0}' as it is not compatible with Azure Data Studio '{1}'.", identifier.id, pkg.version, manifest.version))); + if (manifest.engines && ((manifest.engines.vscode && !isEngineValid(manifest.engines.vscode, product.vscodeVersion)) || (manifest.engines.azdata && !isEngineValid(manifest.engines.azdata, product.version)))) { + return Promise.reject(new Error(nls.localize('incompatible', "Unable to install extension '{0}' as it is not compatible with Azure Data Studio '{1}'.", identifier.id, product.version))); } const identifierWithVersion = new ExtensionIdentifierWithVersion(identifier, manifest.version); return this.getInstalled(ExtensionType.User) @@ -376,7 +373,7 @@ export class ExtensionManagementService extends Disposable implements IExtension const compatibleExtension = await this.galleryService.getCompatibleExtension(extension); if (!compatibleExtension) { - return Promise.reject(new ExtensionManagementError(nls.localize('notFoundCompatibleDependency', "Unable to install '{0}' extension because it is not compatible with the current version of VS Code (version {1}).", extension.identifier.id, pkg.version), INSTALL_ERROR_INCOMPATIBLE)); + return Promise.reject(new ExtensionManagementError(nls.localize('notFoundCompatibleDependency', "Unable to install '{0}' extension because it is not compatible with the current version of VS Code (version {1}).", extension.identifier.id, product.version), INSTALL_ERROR_INCOMPATIBLE)); } return compatibleExtension; diff --git a/src/vs/platform/extensionManagement/test/node/extensionGalleryService.test.ts b/src/vs/platform/extensionManagement/test/node/extensionGalleryService.test.ts index cfb00237a3e4..fde16be7dbfa 100644 --- a/src/vs/platform/extensionManagement/test/node/extensionGalleryService.test.ts +++ b/src/vs/platform/extensionManagement/test/node/extensionGalleryService.test.ts @@ -18,7 +18,7 @@ import { FileService } from 'vs/platform/files/common/fileService'; import { NullLogService } from 'vs/platform/log/common/log'; import { DiskFileSystemProvider } from 'vs/platform/files/node/diskFileSystemProvider'; import { Schemas } from 'vs/base/common/network'; -import pkg from 'vs/platform/product/node/package'; +import product from 'vs/platform/product/common/product'; suite('Extension Gallery Service', () => { const parentDir = getRandomTestPath(os.tmpdir(), 'vsctests', 'extensiongalleryservice'); @@ -53,10 +53,10 @@ suite('Extension Gallery Service', () => { const args = ['--user-data-dir', marketplaceHome]; const environmentService = new EnvironmentService(parseArgs(args, OPTIONS), process.execPath); - return resolveMarketplaceHeaders(pkg.version, environmentService, fileService).then(headers => { + return resolveMarketplaceHeaders(product.version, environmentService, fileService).then(headers => { assert.ok(isUUID(headers['X-Market-User-Id'])); - return resolveMarketplaceHeaders(pkg.version, environmentService, fileService).then(headers2 => { + return resolveMarketplaceHeaders(product.version, environmentService, fileService).then(headers2 => { assert.equal(headers['X-Market-User-Id'], headers2['X-Market-User-Id']); }); }); diff --git a/src/vs/platform/files/node/watcher/unix/chokidarWatcherService.ts b/src/vs/platform/files/node/watcher/unix/chokidarWatcherService.ts index 7944b70982d6..43115e0a63d9 100644 --- a/src/vs/platform/files/node/watcher/unix/chokidarWatcherService.ts +++ b/src/vs/platform/files/node/watcher/unix/chokidarWatcherService.ts @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as chokidar from 'vscode-chokidar'; +import * as chokidar from 'chokidar'; import * as fs from 'fs'; import * as gracefulFs from 'graceful-fs'; gracefulFs.gracefulify(fs); diff --git a/src/vs/platform/history/common/history.ts b/src/vs/platform/history/common/history.ts index c082a5b5b786..7fbdfc505f94 100644 --- a/src/vs/platform/history/common/history.ts +++ b/src/vs/platform/history/common/history.ts @@ -3,13 +3,8 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event as CommonEvent } from 'vs/base/common/event'; -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { URI } from 'vs/base/common/uri'; -import { IPath } from 'vs/platform/windows/common/windows'; - -export const IHistoryMainService = createDecorator('historyMainService'); export interface IRecentlyOpened { workspaces: Array; @@ -44,17 +39,3 @@ export function isRecentFolder(curr: IRecent): curr is IRecentFolder { export function isRecentFile(curr: IRecent): curr is IRecentFile { return curr.hasOwnProperty('fileUri'); } - - -export interface IHistoryMainService { - _serviceBrand: undefined; - - onRecentlyOpenedChange: CommonEvent; - - addRecentlyOpened(recents: IRecent[]): void; - getRecentlyOpened(currentWorkspace?: IWorkspaceIdentifier, currentFolder?: ISingleFolderWorkspaceIdentifier, currentFiles?: IPath[]): IRecentlyOpened; - removeFromRecentlyOpened(paths: URI[]): void; - clearRecentlyOpened(): void; - - updateWindowsJumpList(): void; -} \ No newline at end of file diff --git a/src/vs/platform/history/electron-main/historyMainService.ts b/src/vs/platform/history/electron-main/historyMainService.ts index ab2806962db7..3fba419c5659 100644 --- a/src/vs/platform/history/electron-main/historyMainService.ts +++ b/src/vs/platform/history/electron-main/historyMainService.ts @@ -12,8 +12,9 @@ import { getBaseLabel, getPathLabel } from 'vs/base/common/labels'; import { IPath } from 'vs/platform/windows/common/windows'; import { Event as CommonEvent, Emitter } from 'vs/base/common/event'; import { isWindows, isMacintosh } from 'vs/base/common/platform'; -import { IWorkspaceIdentifier, IWorkspacesMainService, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; -import { IHistoryMainService, IRecentlyOpened, isRecentWorkspace, isRecentFolder, IRecent, isRecentFile, IRecentFolder, IRecentWorkspace, IRecentFile } from 'vs/platform/history/common/history'; +import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService'; +import { IRecentlyOpened, isRecentWorkspace, isRecentFolder, IRecent, isRecentFile, IRecentFolder, IRecentWorkspace, IRecentFile } from 'vs/platform/history/common/history'; import { ThrottledDelayer } from 'vs/base/common/async'; import { isEqual as areResourcesEqual, dirname, originalFSPath, basename } from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; @@ -22,7 +23,24 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { getSimpleWorkspaceLabel } from 'vs/platform/label/common/label'; import { toStoreData, restoreRecentlyOpened, RecentlyOpenedStorageData } from 'vs/platform/history/common/historyStorage'; import { exists } from 'vs/base/node/pfs'; -import { ILifecycleService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMain'; +import { ILifecycleMainService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; + +export const IHistoryMainService = createDecorator('historyMainService'); + +export interface IHistoryMainService { + + _serviceBrand: undefined; + + onRecentlyOpenedChange: CommonEvent; + + addRecentlyOpened(recents: IRecent[]): void; + getRecentlyOpened(currentWorkspace?: IWorkspaceIdentifier, currentFolder?: ISingleFolderWorkspaceIdentifier, currentFiles?: IPath[]): IRecentlyOpened; + removeFromRecentlyOpened(paths: URI[]): void; + clearRecentlyOpened(): void; + + updateWindowsJumpList(): void; +} export class HistoryMainService implements IHistoryMainService { @@ -51,11 +69,11 @@ export class HistoryMainService implements IHistoryMainService { @ILogService private readonly logService: ILogService, @IWorkspacesMainService private readonly workspacesMainService: IWorkspacesMainService, @IEnvironmentService private readonly environmentService: IEnvironmentService, - @ILifecycleService lifecycleService: ILifecycleService + @ILifecycleMainService lifecycleMainService: ILifecycleMainService ) { this.macOSRecentDocumentsUpdater = new ThrottledDelayer(800); - lifecycleService.when(LifecycleMainPhase.AfterWindowOpen).then(() => this.handleWindowsJumpList()); + lifecycleMainService.when(LifecycleMainPhase.AfterWindowOpen).then(() => this.handleWindowsJumpList()); } private handleWindowsJumpList(): void { diff --git a/src/vs/platform/issue/node/issueIpc.ts b/src/vs/platform/issue/electron-main/issueIpc.ts similarity index 100% rename from src/vs/platform/issue/node/issueIpc.ts rename to src/vs/platform/issue/electron-main/issueIpc.ts diff --git a/src/vs/platform/issue/electron-main/issueService.ts b/src/vs/platform/issue/electron-main/issueMainService.ts similarity index 84% rename from src/vs/platform/issue/electron-main/issueService.ts rename to src/vs/platform/issue/electron-main/issueMainService.ts index 297260707cc2..30a99094384b 100644 --- a/src/vs/platform/issue/electron-main/issueService.ts +++ b/src/vs/platform/issue/electron-main/issueMainService.ts @@ -7,8 +7,8 @@ import { localize } from 'vs/nls'; import * as objects from 'vs/base/common/objects'; import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv'; import { IIssueService, IssueReporterData, IssueReporterFeatures, ProcessExplorerData } from 'vs/platform/issue/node/issue'; -import { BrowserWindow, ipcMain, screen, Event, dialog } from 'electron'; -import { ILaunchService } from 'vs/platform/launch/electron-main/launchService'; +import { BrowserWindow, ipcMain, screen, dialog } from 'electron'; +import { ILaunchMainService } from 'vs/platform/launch/electron-main/launchService'; 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'; @@ -20,7 +20,7 @@ import { listProcesses } from 'vs/base/node/ps'; const DEFAULT_BACKGROUND_COLOR = '#1E1E1E'; -export class IssueService implements IIssueService { +export class IssueMainService implements IIssueService { _serviceBrand: undefined; _issueWindow: BrowserWindow | null = null; _issueParentWindow: BrowserWindow | null = null; @@ -31,7 +31,7 @@ export class IssueService implements IIssueService { private machineId: string, private userEnv: IProcessEnvironment, @IEnvironmentService private readonly environmentService: IEnvironmentService, - @ILaunchService private readonly launchService: ILaunchService, + @ILaunchMainService private readonly launchMainService: ILaunchMainService, @ILogService private readonly logService: ILogService, @IDiagnosticsService private readonly diagnosticsService: IDiagnosticsService, @IWindowsService private readonly windowsService: IWindowsService @@ -40,8 +40,8 @@ export class IssueService implements IIssueService { } private registerListeners(): void { - ipcMain.on('vscode:issueSystemInfoRequest', async (event: Event) => { - Promise.all([this.launchService.getMainProcessInfo(), this.launchService.getRemoteDiagnostics({ includeProcesses: false, includeWorkspaceMetadata: false })]) + ipcMain.on('vscode:issueSystemInfoRequest', async (event: Electron.IpcMainEvent) => { + Promise.all([this.launchMainService.getMainProcessInfo(), this.launchMainService.getRemoteDiagnostics({ includeProcesses: false, includeWorkspaceMetadata: false })]) .then(result => { const [info, remoteData] = result; this.diagnosticsService.getSystemInfo(info, remoteData).then(msg => { @@ -50,13 +50,13 @@ export class IssueService implements IIssueService { }); }); - ipcMain.on('vscode:listProcesses', async (event: Event) => { + ipcMain.on('vscode:listProcesses', async (event: Electron.IpcMainEvent) => { const processes = []; try { - const mainPid = await this.launchService.getMainProcessId(); + const mainPid = await this.launchMainService.getMainProcessId(); processes.push({ name: localize('local', "Local"), rootProcess: await listProcesses(mainPid) }); - (await this.launchService.getRemoteDiagnostics({ includeProcesses: true })) + (await this.launchMainService.getRemoteDiagnostics({ includeProcesses: true })) .forEach(data => { if (isRemoteDiagnosticError(data)) { processes.push({ @@ -79,7 +79,7 @@ export class IssueService implements IIssueService { event.sender.send('vscode:listProcessesResponse', processes); }); - ipcMain.on('vscode:issueReporterClipboard', (event: Event) => { + ipcMain.on('vscode:issueReporterClipboard', (event: Electron.IpcMainEvent) => { const messageOptions = { message: localize('issueReporterWriteToClipboard', "There is too much data to send to GitHub. Would you like to write the information to the clipboard so that it can be pasted?"), type: 'warning', @@ -90,13 +90,14 @@ export class IssueService implements IIssueService { }; if (this._issueWindow) { - dialog.showMessageBox(this._issueWindow, messageOptions, response => { - event.sender.send('vscode:issueReporterClipboardResponse', response === 0); - }); + dialog.showMessageBox(this._issueWindow, messageOptions) + .then(result => { + event.sender.send('vscode:issueReporterClipboardResponse', result.response === 0); + }); } }); - ipcMain.on('vscode:issuePerformanceInfoRequest', (event: Event) => { + ipcMain.on('vscode:issuePerformanceInfoRequest', (event: Electron.IpcMainEvent) => { this.getPerformanceInfo().then(msg => { event.sender.send('vscode:issuePerformanceInfoResponse', msg); }); @@ -113,14 +114,15 @@ export class IssueService implements IIssueService { }; if (this._issueWindow) { - dialog.showMessageBox(this._issueWindow, messageOptions, (response) => { - if (response === 0) { - if (this._issueWindow) { - this._issueWindow.destroy(); - this._issueWindow = null; + dialog.showMessageBox(this._issueWindow, messageOptions) + .then(result => { + if (result.response === 0) { + if (this._issueWindow) { + this._issueWindow.destroy(); + this._issueWindow = null; + } } - } - }); + }); } }); @@ -148,14 +150,14 @@ export class IssueService implements IIssueService { this.windowsService.openExternal(arg); }); - ipcMain.on('vscode:closeIssueReporter', (event: Event) => { + ipcMain.on('vscode:closeIssueReporter', (event: Electron.IpcMainEvent) => { if (this._issueWindow) { this._issueWindow.close(); } }); - ipcMain.on('windowsInfoRequest', (event: Event) => { - this.launchService.getMainProcessInfo().then(info => { + ipcMain.on('windowsInfoRequest', (event: Electron.IpcMainEvent) => { + this.launchMainService.getMainProcessInfo().then(info => { event.sender.send('vscode:windowsInfoResponse', info.windows); }); }); @@ -178,7 +180,10 @@ export class IssueService implements IIssueService { x: position.x, y: position.y, title: localize('issueReporter', "Issue Reporter"), - backgroundColor: data.styles.backgroundColor || DEFAULT_BACKGROUND_COLOR + backgroundColor: data.styles.backgroundColor || DEFAULT_BACKGROUND_COLOR, + webPreferences: { + nodeIntegration: true + } }); this._issueWindow.setMenuBarVisibility(false); // workaround for now, until a menu is implemented @@ -224,7 +229,10 @@ export class IssueService implements IIssueService { x: position.x, y: position.y, backgroundColor: data.styles.backgroundColor, - title: localize('processExplorer', "Process Explorer") + title: localize('processExplorer', "Process Explorer"), + webPreferences: { + nodeIntegration: true + } }); this._processExplorerWindow.setMenuBarVisibility(false); @@ -260,7 +268,7 @@ export class IssueService implements IIssueService { } public async getSystemStatus(): Promise { - return Promise.all([this.launchService.getMainProcessInfo(), this.launchService.getRemoteDiagnostics({ includeProcesses: false, includeWorkspaceMetadata: false })]) + return Promise.all([this.launchMainService.getMainProcessInfo(), this.launchMainService.getRemoteDiagnostics({ includeProcesses: false, includeWorkspaceMetadata: false })]) .then(result => { const [info, remoteData] = result; return this.diagnosticsService.getDiagnostics(info, remoteData); @@ -337,7 +345,7 @@ export class IssueService implements IIssueService { private getPerformanceInfo(): Promise { return new Promise(async (resolve, reject) => { - Promise.all([this.launchService.getMainProcessInfo(), this.launchService.getRemoteDiagnostics({ includeProcesses: true, includeWorkspaceMetadata: true })]) + Promise.all([this.launchMainService.getMainProcessInfo(), this.launchMainService.getRemoteDiagnostics({ includeProcesses: true, includeWorkspaceMetadata: true })]) .then(result => { const [info, remoteData] = result; this.diagnosticsService.getPerformanceInfo(info, remoteData) diff --git a/src/vs/platform/launch/common/launchService.ts b/src/vs/platform/launch/common/launch.ts similarity index 99% rename from src/vs/platform/launch/common/launchService.ts rename to src/vs/platform/launch/common/launch.ts index 9e865a62f56d..19add1c3e02a 100644 --- a/src/vs/platform/launch/common/launchService.ts +++ b/src/vs/platform/launch/common/launch.ts @@ -2,6 +2,7 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import { UriComponents } from 'vs/base/common/uri'; export interface IWindowInfo { @@ -18,4 +19,4 @@ export interface IMainProcessInfo { windows: IWindowInfo[]; screenReader: boolean; gpuFeatureStatus: any; -} \ No newline at end of file +} diff --git a/src/vs/platform/launch/electron-main/launchService.ts b/src/vs/platform/launch/electron-main/launchService.ts index 133e304bf947..b3ac21e37ef1 100644 --- a/src/vs/platform/launch/electron-main/launchService.ts +++ b/src/vs/platform/launch/electron-main/launchService.ts @@ -12,17 +12,17 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' import { OpenContext, IWindowSettings } from 'vs/platform/windows/common/windows'; import { IWindowsMainService, ICodeWindow } from 'vs/platform/windows/electron-main/windows'; import { whenDeleted } from 'vs/base/node/pfs'; -import { IWorkspacesMainService } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { URI } from 'vs/base/common/uri'; import { BrowserWindow, ipcMain, Event as IpcEvent, app } from 'electron'; import { Event } from 'vs/base/common/event'; import { coalesce } from 'vs/base/common/arrays'; import { IDiagnosticInfoOptions, IDiagnosticInfo, IRemoteDiagnosticInfo, IRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnostics'; -import { IMainProcessInfo, IWindowInfo } from 'vs/platform/launch/common/launchService'; +import { IMainProcessInfo, IWindowInfo } from 'vs/platform/launch/common/launch'; -export const ID = 'launchService'; -export const ILaunchService = createDecorator(ID); +export const ID = 'launchMainService'; +export const ILaunchMainService = createDecorator(ID); export interface IStartArguments { args: ParsedArgs; @@ -51,7 +51,7 @@ function parseOpenUrl(args: ParsedArgs): URI[] { return []; } -export interface ILaunchService { +export interface ILaunchMainService { _serviceBrand: undefined; start(args: ParsedArgs, userEnv: IProcessEnvironment): Promise; getMainProcessId(): Promise; @@ -62,7 +62,7 @@ export interface ILaunchService { export class LaunchChannel implements IServerChannel { - constructor(private service: ILaunchService) { } + constructor(private service: ILaunchMainService) { } listen(_: unknown, event: string): Event { throw new Error(`Event not found: ${event}`); @@ -91,7 +91,7 @@ export class LaunchChannel implements IServerChannel { } } -export class LaunchChannelClient implements ILaunchService { +export class LaunchChannelClient implements ILaunchMainService { _serviceBrand: undefined; @@ -118,7 +118,7 @@ export class LaunchChannelClient implements ILaunchService { } } -export class LaunchService implements ILaunchService { +export class LaunchMainService implements ILaunchMainService { _serviceBrand: undefined; @@ -276,7 +276,7 @@ export class LaunchService implements ILaunchService { mainPID: process.pid, mainArguments: process.argv.slice(1), windows, - screenReader: app.isAccessibilitySupportEnabled(), + screenReader: !!app.accessibilitySupportEnabled, gpuFeatureStatus: app.getGPUFeatureStatus() }); } diff --git a/src/vs/platform/lifecycle/electron-browser/lifecycleService.ts b/src/vs/platform/lifecycle/electron-browser/lifecycleService.ts index c0cf5edbc641..2ff4981aed86 100644 --- a/src/vs/platform/lifecycle/electron-browser/lifecycleService.ts +++ b/src/vs/platform/lifecycle/electron-browser/lifecycleService.ts @@ -19,7 +19,7 @@ export class LifecycleService extends AbstractLifecycleService { _serviceBrand: undefined; - private shutdownReason: ShutdownReason; + private shutdownReason: ShutdownReason | undefined; constructor( @INotificationService private readonly notificationService: INotificationService, diff --git a/src/vs/platform/lifecycle/electron-main/lifecycleMain.ts b/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts similarity index 96% rename from src/vs/platform/lifecycle/electron-main/lifecycleMain.ts rename to src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts index 86a1006bf756..62b00af9d594 100644 --- a/src/vs/platform/lifecycle/electron-main/lifecycleMain.ts +++ b/src/vs/platform/lifecycle/electron-main/lifecycleMainService.ts @@ -14,7 +14,7 @@ import { isMacintosh, isWindows } from 'vs/base/common/platform'; import { Disposable } from 'vs/base/common/lifecycle'; import { Barrier } from 'vs/base/common/async'; -export const ILifecycleService = createDecorator('lifecycleService'); +export const ILifecycleMainService = createDecorator('lifecycleMainService'); export const enum UnloadReason { CLOSE = 1, @@ -38,7 +38,7 @@ export interface ShutdownEvent { join(promise: Promise): void; } -export interface ILifecycleService { +export interface ILifecycleMainService { _serviceBrand: undefined; @@ -129,7 +129,7 @@ export const enum LifecycleMainPhase { AfterWindowOpen = 3 } -export class LifecycleService extends Disposable implements ILifecycleService { +export class LifecycleMainService extends Disposable implements ILifecycleMainService { _serviceBrand: undefined; @@ -178,10 +178,10 @@ export class LifecycleService extends Disposable implements ILifecycleService { } private handleRestarted(): void { - this._wasRestarted = !!this.stateService.getItem(LifecycleService.QUIT_FROM_RESTART_MARKER); + this._wasRestarted = !!this.stateService.getItem(LifecycleMainService.QUIT_FROM_RESTART_MARKER); if (this._wasRestarted) { - this.stateService.removeItem(LifecycleService.QUIT_FROM_RESTART_MARKER); // remove the marker right after if found + this.stateService.removeItem(LifecycleMainService.QUIT_FROM_RESTART_MARKER); // remove the marker right after if found } } @@ -468,7 +468,7 @@ export class LifecycleService extends Disposable implements ILifecycleService { // Remember the reason for quit was to restart if (fromUpdate) { - this.stateService.setItem(LifecycleService.QUIT_FROM_RESTART_MARKER, true); + this.stateService.setItem(LifecycleMainService.QUIT_FROM_RESTART_MARKER, true); } this.pendingQuitPromise = new Promise(resolve => { @@ -507,7 +507,7 @@ export class LifecycleService extends Disposable implements ILifecycleService { if (!quitVetoed) { // Remember the reason for quit was to restart - this.stateService.setItem(LifecycleService.QUIT_FROM_RESTART_MARKER, true); + this.stateService.setItem(LifecycleMainService.QUIT_FROM_RESTART_MARKER, true); // Windows: we are about to restart and as such we need to restore the original // current working directory we had on startup to get the exact same startup diff --git a/src/vs/platform/localizations/node/localizations.ts b/src/vs/platform/localizations/node/localizations.ts index 25144e562e17..c5e1987b8ca9 100644 --- a/src/vs/platform/localizations/node/localizations.ts +++ b/src/vs/platform/localizations/node/localizations.ts @@ -12,7 +12,7 @@ import { Queue } from 'vs/base/common/async'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { ILogService } from 'vs/platform/log/common/log'; import { isValidLocalization, ILocalizationsService, LanguageType } from 'vs/platform/localizations/common/localizations'; -import product from 'vs/platform/product/node/product'; +import product from 'vs/platform/product/common/product'; import { distinct, equals } from 'vs/base/common/arrays'; import { Event, Emitter } from 'vs/base/common/event'; import { Schemas } from 'vs/base/common/network'; diff --git a/src/vs/platform/log/common/log.ts b/src/vs/platform/log/common/log.ts index 86cedcca7fd1..9ebc4e3d43fe 100644 --- a/src/vs/platform/log/common/log.ts +++ b/src/vs/platform/log/common/log.ts @@ -8,6 +8,7 @@ import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; import { isWindows } from 'vs/base/common/platform'; import { Event, Emitter } from 'vs/base/common/event'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { LoggerChannelClient } from 'vs/platform/log/common/logIpc'; export const ILogService = createServiceDecorator('logService'); @@ -183,6 +184,54 @@ export class ConsoleLogService extends AbstractLogService implements ILogService dispose(): void { } } +export class ConsoleLogInMainService extends AbstractLogService implements ILogService { + + _serviceBrand: undefined; + + constructor(private readonly client: LoggerChannelClient, logLevel: LogLevel = DEFAULT_LOG_LEVEL) { + super(); + this.setLevel(logLevel); + } + + trace(message: string, ...args: any[]): void { + if (this.getLevel() <= LogLevel.Trace) { + this.client.consoleLog('trace', [message, ...args]); + } + } + + debug(message: string, ...args: any[]): void { + if (this.getLevel() <= LogLevel.Debug) { + this.client.consoleLog('debug', [message, ...args]); + } + } + + info(message: string, ...args: any[]): void { + if (this.getLevel() <= LogLevel.Info) { + this.client.consoleLog('info', [message, ...args]); + } + } + + warn(message: string | Error, ...args: any[]): void { + if (this.getLevel() <= LogLevel.Warning) { + this.client.consoleLog('warn', [message, ...args]); + } + } + + error(message: string, ...args: any[]): void { + if (this.getLevel() <= LogLevel.Error) { + this.client.consoleLog('error', [message, ...args]); + } + } + + critical(message: string, ...args: any[]): void { + if (this.getLevel() <= LogLevel.Critical) { + this.client.consoleLog('critical', [message, ...args]); + } + } + + dispose(): void { } +} + export class MultiplexLogService extends AbstractLogService implements ILogService { _serviceBrand: undefined; @@ -326,4 +375,4 @@ export function getLogLevel(environmentService: IEnvironmentService): LogLevel { } } return DEFAULT_LOG_LEVEL; -} \ No newline at end of file +} diff --git a/src/vs/platform/log/common/logIpc.ts b/src/vs/platform/log/common/logIpc.ts index 967d08b78b1b..aff760b50c22 100644 --- a/src/vs/platform/log/common/logIpc.ts +++ b/src/vs/platform/log/common/logIpc.ts @@ -7,7 +7,7 @@ import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; import { LogLevel, ILogService, DelegatedLogService } from 'vs/platform/log/common/log'; import { Event } from 'vs/base/common/event'; -export class LogLevelSetterChannel implements IServerChannel { +export class LoggerChannel implements IServerChannel { onDidChangeLogLevel: Event; @@ -26,13 +26,32 @@ export class LogLevelSetterChannel implements IServerChannel { call(_: unknown, command: string, arg?: any): Promise { switch (command) { case 'setLevel': this.service.setLevel(arg); return Promise.resolve(); + case 'consoleLog': this.consoleLog(arg[0], arg[1]); return Promise.resolve(); } throw new Error(`Call not found: ${command}`); } + + private consoleLog(severity: string, args: string[]): void { + let consoleFn = console.log; + + switch (severity) { + case 'error': + consoleFn = console.error; + break; + case 'warn': + consoleFn = console.warn; + break; + case 'info': + consoleFn = console.info; + break; + } + + consoleFn.call(console, ...args); + } } -export class LogLevelSetterChannelClient { +export class LoggerChannelClient { constructor(private channel: IChannel) { } @@ -43,12 +62,16 @@ export class LogLevelSetterChannelClient { setLevel(level: LogLevel): void { this.channel.call('setLevel', level); } + + consoleLog(severity: string, args: string[]): void { + this.channel.call('consoleLog', [severity, args]); + } } export class FollowerLogService extends DelegatedLogService implements ILogService { _serviceBrand: undefined; - constructor(private master: LogLevelSetterChannelClient, logService: ILogService) { + constructor(private master: LoggerChannelClient, logService: ILogService) { super(logService); this._register(master.onDidChangeLogLevel(level => logService.setLevel(level))); } @@ -56,4 +79,4 @@ export class FollowerLogService extends DelegatedLogService implements ILogServi setLevel(level: LogLevel): void { this.master.setLevel(level); } -} \ No newline at end of file +} diff --git a/src/vs/platform/menubar/electron-main/menubar.ts b/src/vs/platform/menubar/electron-main/menubar.ts index 1dafdd4c5fcc..0a7566ac1f0d 100644 --- a/src/vs/platform/menubar/electron-main/menubar.ts +++ b/src/vs/platform/menubar/electron-main/menubar.ts @@ -11,16 +11,16 @@ import { OpenContext, IRunActionInWindowRequest, getTitleBarStyle, IRunKeybindin 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'; -import product from 'vs/platform/product/node/product'; +import product from 'vs/platform/product/common/product'; import { RunOnceScheduler } from 'vs/base/common/async'; import { ILogService } from 'vs/platform/log/common/log'; import { mnemonicMenuLabel as baseMnemonicLabel } from 'vs/base/common/labels'; import { IWindowsMainService, IWindowsCountChangedEvent } from 'vs/platform/windows/electron-main/windows'; -import { IHistoryMainService } from 'vs/platform/history/common/history'; +import { IHistoryMainService } from 'vs/platform/history/electron-main/historyMainService'; import { IMenubarData, IMenubarKeybinding, MenubarMenuItem, isMenubarMenuItemSeparator, isMenubarMenuItemSubmenu, isMenubarMenuItemAction, IMenubarMenu, isMenubarMenuItemUriAction } from 'vs/platform/menubar/node/menubar'; import { URI } from 'vs/base/common/uri'; import { IStateService } from 'vs/platform/state/common/state'; -import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain'; +import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; import { WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification } from 'vs/base/common/actions'; const telemetryFrom = 'menu'; @@ -68,7 +68,7 @@ export class Menubar { @ITelemetryService private readonly telemetryService: ITelemetryService, @IHistoryMainService private readonly historyMainService: IHistoryMainService, @IStateService private readonly stateService: IStateService, - @ILifecycleService private readonly lifecycleService: ILifecycleService, + @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, @ILogService private readonly logService: ILogService ) { this.menuUpdater = new RunOnceScheduler(() => this.doUpdateMenu(), 0); @@ -160,7 +160,7 @@ export class Menubar { private registerListeners(): void { // Keep flag when app quits - this.lifecycleService.onWillShutdown(() => this.willShutdown = true); + this.lifecycleMainService.onWillShutdown(() => this.willShutdown = true); // // Listen to some events from window service to update menu this.windowsMainService.onWindowsCountChanged(e => this.onWindowsCountChanged(e)); @@ -368,7 +368,7 @@ export class Menubar { const servicesMenu = new Menu(); const services = new MenuItem({ label: nls.localize('mServices', "Services"), role: 'services', submenu: servicesMenu }); const hide = new MenuItem({ label: nls.localize('mHide', "Hide {0}", product.nameLong), role: 'hide', accelerator: 'Command+H' }); - const hideOthers = new MenuItem({ label: nls.localize('mHideOthers', "Hide Others"), role: 'hideothers', accelerator: 'Command+Alt+H' }); + const hideOthers = new MenuItem({ label: nls.localize('mHideOthers', "Hide Others"), role: 'hideOthers', accelerator: 'Command+Alt+H' }); const showAll = new MenuItem({ label: nls.localize('mShowAll', "Show All"), role: 'unhide' }); const quit = new MenuItem(this.likeAction('workbench.action.quit', { label: nls.localize('miQuit', "Quit {0}", product.nameLong), click: () => { @@ -503,7 +503,7 @@ export class Menubar { }, false)); } - private isOptionClick(event: Electron.Event): boolean { + private isOptionClick(event: Electron.KeyboardEvent): boolean { return !!(event && ((!isMacintosh && (event.ctrlKey || event.shiftKey)) || (isMacintosh && (event.metaKey || event.altKey)))); } @@ -597,7 +597,7 @@ export class Menubar { } } - private static _menuItemIsTriggeredViaKeybinding(event: Electron.Event, userSettingsLabel: string): boolean { + private static _menuItemIsTriggeredViaKeybinding(event: Electron.KeyboardEvent, userSettingsLabel: string): boolean { // The event coming in from Electron will inform us only about the modifier keys pressed. // The strategy here is to check if the modifier keys match those of the keybinding, // since it is highly unlikely to use modifier keys when clicking with the mouse diff --git a/src/vs/platform/menubar/electron-main/menubarService.ts b/src/vs/platform/menubar/electron-main/menubarMainService.ts similarity index 94% rename from src/vs/platform/menubar/electron-main/menubarService.ts rename to src/vs/platform/menubar/electron-main/menubarMainService.ts index f139b522db80..e227d807c11e 100644 --- a/src/vs/platform/menubar/electron-main/menubarService.ts +++ b/src/vs/platform/menubar/electron-main/menubarMainService.ts @@ -8,7 +8,8 @@ import { Menubar } from 'vs/platform/menubar/electron-main/menubar'; import { ILogService } from 'vs/platform/log/common/log'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -export class MenubarService implements IMenubarService { +export class MenubarMainService implements IMenubarService { + _serviceBrand: undefined; private _menubar: Menubar; @@ -30,4 +31,4 @@ export class MenubarService implements IMenubarService { return Promise.resolve(undefined); } -} \ No newline at end of file +} diff --git a/src/vs/platform/opener/common/opener.ts b/src/vs/platform/opener/common/opener.ts index fa40e7a67830..28cb4de16111 100644 --- a/src/vs/platform/opener/common/opener.ts +++ b/src/vs/platform/opener/common/opener.ts @@ -5,7 +5,7 @@ import { URI } from 'vs/base/common/uri'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IDisposable } from 'vs/base/common/lifecycle'; +import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; export const IOpenerService = createDecorator('openerService'); @@ -18,6 +18,10 @@ export interface IValidator { shouldOpen(resource: URI): Promise; } +export interface IExternalUriResolver { + resolveExternalUri(resource: URI): Promise; +} + export interface IOpenerService { _serviceBrand: undefined; @@ -29,10 +33,15 @@ export interface IOpenerService { /** * Register a participant that can validate if the URI resource be opened. - * validators are run before openers. + * Validators are run before openers. */ registerValidator(validator: IValidator): IDisposable; + /** + * Register a participant that can resolve an external URI resource to be opened. + */ + registerExternalUriResolver(resolver: IExternalUriResolver): IDisposable; + /** * Opens a resource, like a webaddress, a document uri, or executes command. * @@ -45,7 +54,8 @@ export interface IOpenerService { export const NullOpenerService: IOpenerService = Object.freeze({ _serviceBrand: undefined, - registerOpener() { return { dispose() { } }; }, - registerValidator() { return { dispose() { } }; }, + registerOpener() { return Disposable.None; }, + registerValidator() { return Disposable.None; }, + registerExternalUriResolver() { return Disposable.None; }, open() { return Promise.resolve(false); }, }); diff --git a/src/vs/platform/product/browser/product.ts b/src/vs/platform/product/browser/product.ts deleted file mode 100644 index 2370c9d61532..000000000000 --- a/src/vs/platform/product/browser/product.ts +++ /dev/null @@ -1,21 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { IProductConfiguration } from 'vs/platform/product/common/product'; -import { assign } from 'vs/base/common/objects'; - -// Built time configuration (do NOT modify) -const product = { /*BUILD->INSERT_PRODUCT_CONFIGURATION*/ } as IProductConfiguration; - -// Running out of sources -if (Object.keys(product).length === 0) { - assign(product, { - version: '1.39.0-dev', - nameLong: 'Visual Studio Code Web Dev', - nameShort: 'VSCode Web Dev' - }); -} - -export default product; diff --git a/src/vs/platform/product/common/product.ts b/src/vs/platform/product/common/product.ts index 40af9658f41e..5cb97af4c8a7 100644 --- a/src/vs/platform/product/common/product.ts +++ b/src/vs/platform/product/common/product.ts @@ -3,125 +3,57 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; - -export const IProductService = createDecorator('productService'); - -export interface IProductService extends Readonly { - - _serviceBrand: undefined; - +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'; +import { env } from 'vs/base/common/process'; + +let product: IProductConfiguration; + +// Web +if (isWeb) { + + // Built time configuration (do NOT modify) + product = { /*BUILD->INSERT_PRODUCT_CONFIGURATION*/ } as IProductConfiguration; + + // Running out of sources + if (Object.keys(product).length === 0) { + assign(product, { + version: '1.39.0-dev', + nameLong: 'Visual Studio Code Web Dev', + nameShort: 'VSCode Web Dev' + }); + } } -export interface IProductConfiguration { - readonly version: string; - readonly date?: string; - readonly quality?: string; - readonly commit?: string; - - readonly nameShort: string; - readonly nameLong: string; - - readonly win32AppUserModelId?: string; - readonly win32MutexName?: string; - readonly applicationName: string; - - readonly urlProtocol: string; - readonly dataFolderName: string; - - readonly downloadUrl?: string; - readonly updateUrl?: string; - readonly target?: string; - - readonly settingsSearchBuildId?: number; - readonly settingsSearchUrl?: string; - - readonly experimentsUrl?: string; - - readonly extensionsGallery?: { - readonly serviceUrl: string; - readonly itemUrl: string; - readonly controlUrl: string; - readonly recommendationsUrl: string; - }; +// Node: AMD loader +else if (typeof require !== 'undefined' && typeof require.__$__nodeRequire === 'function') { - readonly extensionTips?: { [id: string]: string; }; - readonly extensionImportantTips?: { [id: string]: { name: string; pattern: string; isExtensionPack?: boolean }; }; - readonly exeBasedExtensionTips?: { [id: string]: IExeBasedExtensionTip; }; - readonly extensionKeywords?: { [extension: string]: readonly string[]; }; - readonly keymapExtensionTips?: readonly string[]; + // Obtain values from product.json and package.json + const rootPath = path.dirname(getPathFromAmdModule(require, '')); - readonly recommendedExtensions: string[]; // {{SQL CARBON EDIT}} - readonly recommendedExtensionsByScenario: { [area: string]: Array }; // {{SQL CARBON EDIT}} - readonly vscodeVersion: string; // {{SQL CARBON EDIT}} add vscode version - readonly gettingStartedUrl: string; // {SQL CARBON EDIT} + product = assign({}, require.__$__nodeRequire(path.join(rootPath, 'product.json')) as IProductConfiguration); + const pkg = require.__$__nodeRequire(path.join(rootPath, 'package.json')) as { version: string; }; - readonly crashReporter?: { - readonly companyName: string; - readonly productName: string; - }; + // Running out of sources + if (env['VSCODE_DEV']) { + assign(product, { + nameShort: `${product.nameShort} Dev`, + nameLong: `${product.nameLong} Dev`, + dataFolderName: `${product.dataFolderName}-dev` + }); + } - readonly welcomePage?: string; - - readonly enableTelemetry?: boolean; - readonly aiConfig?: { - readonly asimovKey: string; - }; - - readonly sendASmile?: { - readonly reportIssueUrl: string, - readonly requestFeatureUrl: string - }; - - readonly documentationUrl?: string; - readonly releaseNotesUrl?: string; - readonly keyboardShortcutsUrlMac?: string; - readonly keyboardShortcutsUrlLinux?: string; - readonly keyboardShortcutsUrlWin?: string; - readonly introductoryVideosUrl?: string; - readonly tipsAndTricksUrl?: string; - readonly newsletterSignupUrl?: string; - readonly twitterUrl?: string; - readonly requestFeatureUrl?: string; - readonly reportIssueUrl?: string; - readonly licenseUrl?: string; - readonly privacyStatementUrl?: string; - readonly telemetryOptOutUrl?: string; - - readonly npsSurveyUrl?: string; - readonly surveys?: readonly ISurveyData[]; - - readonly checksums?: { [path: string]: string; }; - readonly checksumFailMoreInfoUrl?: string; - - readonly hockeyApp?: { - readonly 'win32-ia32': string; - readonly 'win32-x64': string; - readonly 'linux-x64': string; - readonly 'darwin': string; - }; - - readonly portable?: string; - - readonly uiExtensions?: readonly string[]; - readonly extensionAllowedProposedApi?: readonly string[]; - - readonly msftInternalDomains?: string[]; - readonly linkProtectionTrustedDomains?: readonly string[]; + assign(product, { + version: pkg.version + }); } -export interface IExeBasedExtensionTip { - friendlyName: string; - windowsPath?: string; - recommendations: readonly string[]; - important?: boolean; - exeFriendlyName?: string; +// Unknown +else { + throw new Error('Unable to resolve product configuration'); } -export interface ISurveyData { - surveyId: string; - surveyUrl: string; - languageId: string; - editCount: number; - userProbability: number; -} +export default product; diff --git a/src/vs/platform/product/common/productService.ts b/src/vs/platform/product/common/productService.ts new file mode 100644 index 000000000000..40af9658f41e --- /dev/null +++ b/src/vs/platform/product/common/productService.ts @@ -0,0 +1,127 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; + +export const IProductService = createDecorator('productService'); + +export interface IProductService extends Readonly { + + _serviceBrand: undefined; + +} + +export interface IProductConfiguration { + readonly version: string; + readonly date?: string; + readonly quality?: string; + readonly commit?: string; + + readonly nameShort: string; + readonly nameLong: string; + + readonly win32AppUserModelId?: string; + readonly win32MutexName?: string; + readonly applicationName: string; + + readonly urlProtocol: string; + readonly dataFolderName: string; + + readonly downloadUrl?: string; + readonly updateUrl?: string; + readonly target?: string; + + readonly settingsSearchBuildId?: number; + readonly settingsSearchUrl?: string; + + readonly experimentsUrl?: string; + + readonly extensionsGallery?: { + readonly serviceUrl: string; + readonly itemUrl: string; + readonly controlUrl: string; + readonly recommendationsUrl: string; + }; + + readonly extensionTips?: { [id: string]: string; }; + readonly extensionImportantTips?: { [id: string]: { name: string; pattern: string; isExtensionPack?: boolean }; }; + readonly exeBasedExtensionTips?: { [id: string]: IExeBasedExtensionTip; }; + readonly extensionKeywords?: { [extension: string]: readonly string[]; }; + readonly keymapExtensionTips?: readonly string[]; + + readonly recommendedExtensions: string[]; // {{SQL CARBON EDIT}} + readonly recommendedExtensionsByScenario: { [area: string]: Array }; // {{SQL CARBON EDIT}} + readonly vscodeVersion: string; // {{SQL CARBON EDIT}} add vscode version + readonly gettingStartedUrl: string; // {SQL CARBON EDIT} + + readonly crashReporter?: { + readonly companyName: string; + readonly productName: string; + }; + + readonly welcomePage?: string; + + readonly enableTelemetry?: boolean; + readonly aiConfig?: { + readonly asimovKey: string; + }; + + readonly sendASmile?: { + readonly reportIssueUrl: string, + readonly requestFeatureUrl: string + }; + + readonly documentationUrl?: string; + readonly releaseNotesUrl?: string; + readonly keyboardShortcutsUrlMac?: string; + readonly keyboardShortcutsUrlLinux?: string; + readonly keyboardShortcutsUrlWin?: string; + readonly introductoryVideosUrl?: string; + readonly tipsAndTricksUrl?: string; + readonly newsletterSignupUrl?: string; + readonly twitterUrl?: string; + readonly requestFeatureUrl?: string; + readonly reportIssueUrl?: string; + readonly licenseUrl?: string; + readonly privacyStatementUrl?: string; + readonly telemetryOptOutUrl?: string; + + readonly npsSurveyUrl?: string; + readonly surveys?: readonly ISurveyData[]; + + readonly checksums?: { [path: string]: string; }; + readonly checksumFailMoreInfoUrl?: string; + + readonly hockeyApp?: { + readonly 'win32-ia32': string; + readonly 'win32-x64': string; + readonly 'linux-x64': string; + readonly 'darwin': string; + }; + + readonly portable?: string; + + readonly uiExtensions?: readonly string[]; + readonly extensionAllowedProposedApi?: readonly string[]; + + readonly msftInternalDomains?: string[]; + readonly linkProtectionTrustedDomains?: readonly string[]; +} + +export interface IExeBasedExtensionTip { + friendlyName: string; + windowsPath?: string; + recommendations: readonly string[]; + important?: boolean; + exeFriendlyName?: string; +} + +export interface ISurveyData { + surveyId: string; + surveyUrl: string; + languageId: string; + editCount: number; + userProbability: number; +} diff --git a/src/vs/platform/product/node/package.ts b/src/vs/platform/product/node/package.ts deleted file mode 100644 index 51154fcfa148..000000000000 --- a/src/vs/platform/product/node/package.ts +++ /dev/null @@ -1,16 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as path from 'vs/base/common/path'; -import { getPathFromAmdModule } from 'vs/base/common/amd'; - -export interface IPackageConfiguration { - name: string; - version: string; -} - -const rootPath = path.dirname(getPathFromAmdModule(require, '')); -const packageJsonPath = path.join(rootPath, 'package.json'); -export default require.__$__nodeRequire(packageJsonPath) as IPackageConfiguration; diff --git a/src/vs/platform/product/node/product.ts b/src/vs/platform/product/node/product.ts deleted file mode 100644 index c3a295ebef40..000000000000 --- a/src/vs/platform/product/node/product.ts +++ /dev/null @@ -1,28 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as path from 'vs/base/common/path'; -import { getPathFromAmdModule } from 'vs/base/common/amd'; -import { IProductConfiguration } from 'vs/platform/product/common/product'; -import pkg from 'vs/platform/product/node/package'; -import { assign } from 'vs/base/common/objects'; - -const rootPath = path.dirname(getPathFromAmdModule(require, '')); -const productJsonPath = path.join(rootPath, 'product.json'); -const product = require.__$__nodeRequire(productJsonPath) as IProductConfiguration; - -if (process.env['VSCODE_DEV']) { - assign(product, { - nameShort: `${product.nameShort} Dev`, - nameLong: `${product.nameLong} Dev`, - dataFolderName: `${product.dataFolderName}-dev` - }); -} - -assign(product, { - version: pkg.version -}); - -export default product; diff --git a/src/vs/platform/remote/browser/browserSocketFactory.ts b/src/vs/platform/remote/browser/browserSocketFactory.ts index 519e9081544e..ff112be60a58 100644 --- a/src/vs/platform/remote/browser/browserSocketFactory.ts +++ b/src/vs/platform/remote/browser/browserSocketFactory.ts @@ -8,6 +8,9 @@ import { ISocket } from 'vs/base/parts/ipc/common/ipc.net'; import { VSBuffer } from 'vs/base/common/buffer'; import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; import { Event, Emitter } from 'vs/base/common/event'; +import * as dom from 'vs/base/browser/dom'; +import { RunOnceScheduler } from 'vs/base/common/async'; +import { RemoteAuthorityResolverError, RemoteAuthorityResolverErrorCode } from 'vs/platform/remote/common/remoteAuthorityResolver'; export interface IWebSocketFactory { create(url: string): IWebSocket; @@ -23,27 +26,34 @@ export interface IWebSocket { close(): void; } -class BrowserWebSocket implements IWebSocket { +class BrowserWebSocket extends Disposable implements IWebSocket { private readonly _onData = new Emitter(); public readonly onData = this._onData.event; public readonly onOpen: Event; - public readonly onClose: Event; - public readonly onError: Event; + + private readonly _onClose = this._register(new Emitter()); + public readonly onClose = this._onClose.event; + + private readonly _onError = this._register(new Emitter()); + public readonly onError = this._onError.event; private readonly _socket: WebSocket; private readonly _fileReader: FileReader; private readonly _queue: Blob[]; private _isReading: boolean; + private _isClosed: boolean; private readonly _socketMessageListener: (ev: MessageEvent) => void; constructor(socket: WebSocket) { + super(); this._socket = socket; this._fileReader = new FileReader(); this._queue = []; this._isReading = false; + this._isClosed = false; this._fileReader.onload = (event) => { this._isReading = false; @@ -71,17 +81,79 @@ class BrowserWebSocket implements IWebSocket { this._socket.addEventListener('message', this._socketMessageListener); this.onOpen = Event.fromDOMEventEmitter(this._socket, 'open'); - this.onClose = Event.fromDOMEventEmitter(this._socket, 'close'); - this.onError = Event.fromDOMEventEmitter(this._socket, 'error'); + + // WebSockets emit error events that do not contain any real information + // Our only chance of getting to the root cause of an error is to + // listen to the close event which gives out some real information: + // - https://www.w3.org/TR/websockets/#closeevent + // - https://tools.ietf.org/html/rfc6455#section-11.7 + // + // But the error event is emitted before the close event, so we therefore + // delay the error event processing in the hope of receiving a close event + // with more information + + let pendingErrorEvent: any | null = null; + + const sendPendingErrorNow = () => { + const err = pendingErrorEvent; + pendingErrorEvent = null; + this._onError.fire(err); + }; + + const errorRunner = this._register(new RunOnceScheduler(sendPendingErrorNow, 0)); + + const sendErrorSoon = (err: any) => { + errorRunner.cancel(); + pendingErrorEvent = err; + errorRunner.schedule(); + }; + + const sendErrorNow = (err: any) => { + errorRunner.cancel(); + pendingErrorEvent = err; + sendPendingErrorNow(); + }; + + this._register(dom.addDisposableListener(this._socket, 'close', (e: CloseEvent) => { + this._isClosed = true; + + if (pendingErrorEvent) { + if (!window.navigator.onLine) { + // The browser is offline => this is a temporary error which might resolve itself + sendErrorNow(new RemoteAuthorityResolverError('Browser is offline', RemoteAuthorityResolverErrorCode.TemporarilyNotAvailable, e)); + } else { + // An error event is pending + // The browser appears to be online... + if (!e.wasClean) { + // Let's be optimistic and hope that perhaps the server could not be reached or something + sendErrorNow(new RemoteAuthorityResolverError(e.reason || `WebSocket close with status code ${e.code}`, RemoteAuthorityResolverErrorCode.TemporarilyNotAvailable, e)); + } else { + // this was a clean close => send existing error + errorRunner.cancel(); + sendPendingErrorNow(); + } + } + } + + this._onClose.fire(); + })); + + this._register(dom.addDisposableListener(this._socket, 'error', sendErrorSoon)); } send(data: ArrayBuffer | ArrayBufferView): void { + if (this._isClosed) { + // Refuse to write data to closed WebSocket... + return; + } this._socket.send(data); } close(): void { + this._isClosed = true; this._socket.close(); this._socket.removeEventListener('message', this._socketMessageListener); + this.dispose(); } } diff --git a/src/vs/platform/remote/browser/remoteAuthorityResolverService.ts b/src/vs/platform/remote/browser/remoteAuthorityResolverService.ts index 8dd2f5ffa4ed..ae9f2e7e8771 100644 --- a/src/vs/platform/remote/browser/remoteAuthorityResolverService.ts +++ b/src/vs/platform/remote/browser/remoteAuthorityResolverService.ts @@ -5,14 +5,14 @@ import { ResolvedAuthority, IRemoteAuthorityResolverService, ResolverResult } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { RemoteAuthorities } from 'vs/base/common/network'; -import { URI, UriComponents } from 'vs/base/common/uri'; +import { URI } from 'vs/base/common/uri'; export class RemoteAuthorityResolverService implements IRemoteAuthorityResolverService { _serviceBrand: undefined; constructor( - resourceUriProvider: ((uri: URI) => UriComponents) | undefined + resourceUriProvider: ((uri: URI) => URI) | undefined ) { if (resourceUriProvider) { RemoteAuthorities.setDelegate(resourceUriProvider); diff --git a/src/vs/platform/request/electron-main/requestService.ts b/src/vs/platform/request/electron-main/requestMainService.ts similarity index 94% rename from src/vs/platform/request/electron-main/requestService.ts rename to src/vs/platform/request/electron-main/requestMainService.ts index efc7819c11e9..83ff6c29da22 100644 --- a/src/vs/platform/request/electron-main/requestService.ts +++ b/src/vs/platform/request/electron-main/requestMainService.ts @@ -13,7 +13,7 @@ function getRawRequest(options: IRequestOptions): IRawRequestFunction { return net.request as any as IRawRequestFunction; } -export class RequestService extends NodeRequestService { +export class RequestMainService extends NodeRequestService { request(options: IRequestOptions, token: CancellationToken): Promise { return super.request(assign({}, options || {}, { getRawRequest }), token); diff --git a/src/vs/platform/storage/browser/storageService.ts b/src/vs/platform/storage/browser/storageService.ts index bb087f5e5d1b..ddebe7d2cf7c 100644 --- a/src/vs/platform/storage/browser/storageService.ts +++ b/src/vs/platform/storage/browser/storageService.ts @@ -150,7 +150,7 @@ export class BrowserStorageService extends Disposable implements IStorageService } async migrate(toWorkspace: IWorkspaceInitializationPayload): Promise { - // TODO@ben implement storage migration in web + throw new Error('Migrating storage is currently unsupported in Web'); } close(): void { diff --git a/src/vs/platform/telemetry/node/commonProperties.ts b/src/vs/platform/telemetry/node/commonProperties.ts index 28a0f1fdecce..e3d46f1891a3 100644 --- a/src/vs/platform/telemetry/node/commonProperties.ts +++ b/src/vs/platform/telemetry/node/commonProperties.ts @@ -8,7 +8,7 @@ import * as os from 'os'; import * as uuid from 'vs/base/common/uuid'; import { readFile } from 'vs/base/node/pfs'; -import product from 'vs/platform/product/node/product'; // {{SQL CARBON EDIT}} +import product from 'vs/platform/product/common/product'; // {{SQL CARBON EDIT}} const productObject = product; // {{SQL CARBON EDIT}} export async function resolveCommonProperties(commit: string | undefined, version: string | undefined, machineId: string | undefined, msftInternalDomains: string[] | undefined, installSourcePath: string, product?: string): Promise<{ [name: string]: string | boolean | undefined; }> { const result: { [name: string]: string | boolean | undefined; } = Object.create(null); diff --git a/src/vs/platform/telemetry/node/workbenchCommonProperties.ts b/src/vs/platform/telemetry/node/workbenchCommonProperties.ts index 8ebd27a479cb..975f81423866 100644 --- a/src/vs/platform/telemetry/node/workbenchCommonProperties.ts +++ b/src/vs/platform/telemetry/node/workbenchCommonProperties.ts @@ -8,7 +8,7 @@ import { resolveCommonProperties } from 'vs/platform/telemetry/node/commonProper import { instanceStorageKey, firstSessionDateStorageKey, lastSessionDateStorageKey } from 'vs/platform/telemetry/common/telemetry'; import { cleanRemoteAuthority } from 'vs/platform/telemetry/common/telemetryUtils'; -import product from 'vs/platform/product/node/product'; // {{ SQL CARBON EDIT }} +import product from 'vs/platform/product/common/product'; // {{ SQL CARBON EDIT }} export async function resolveWorkbenchCommonProperties(storageService: IStorageService, commit: string | undefined, version: string | undefined, machineId: string, msftInternalDomains: string[] | undefined, installSourcePath: string, remoteAuthority?: string): Promise<{ [name: string]: string | boolean | undefined }> { const result = await resolveCommonProperties(commit, version, machineId, msftInternalDomains, installSourcePath); diff --git a/src/vs/platform/theme/common/colorRegistry.ts b/src/vs/platform/theme/common/colorRegistry.ts index 69f60e56ad40..0bb269a4dd49 100644 --- a/src/vs/platform/theme/common/colorRegistry.ts +++ b/src/vs/platform/theme/common/colorRegistry.ts @@ -189,6 +189,8 @@ export const activeContrastBorder = registerColor('contrastActiveBorder', { ligh export const selectionBackground = registerColor('selection.background', { light: null, dark: null, hc: null }, nls.localize('selectionBackground', "The background color of text selections in the workbench (e.g. for input fields or text areas). Note that this does not apply to selections within the editor.")); +export const iconForeground = registerColor('icon.foreground', { light: '#424242', dark: '#C5C5C5', hc: '#FFFFFF' }, nls.localize('iconForeground', "The default color for icons in the workbench.")); + // ------ text colors export const textSeparatorForeground = registerColor('textSeparator.foreground', { light: '#0000002e', dark: '#ffffff2e', hc: Color.black }, nls.localize('textSeparatorForeground', "Color for text separators.")); diff --git a/src/vs/platform/theme/electron-main/themeMainService.ts b/src/vs/platform/theme/electron-main/themeMainService.ts index 6acb02025599..cbd219670366 100644 --- a/src/vs/platform/theme/electron-main/themeMainService.ts +++ b/src/vs/platform/theme/electron-main/themeMainService.ts @@ -41,7 +41,7 @@ export class ThemeMainService implements IThemeMainService { this.stateService.setItem(THEME_BG_STORAGE_KEY, data.background); } - public getBackgroundColor(): string { + getBackgroundColor(): string { if (isWindows && systemPreferences.isInvertedColorScheme()) { return DEFAULT_BG_HC_BLACK; } @@ -64,4 +64,4 @@ export class ThemeMainService implements IThemeMainService { return background; } -} \ No newline at end of file +} diff --git a/src/vs/platform/update/electron-main/abstractUpdateService.ts b/src/vs/platform/update/electron-main/abstractUpdateService.ts index 7863278d8b95..5cf87f42b73e 100644 --- a/src/vs/platform/update/electron-main/abstractUpdateService.ts +++ b/src/vs/platform/update/electron-main/abstractUpdateService.ts @@ -6,8 +6,8 @@ import { Event, Emitter } from 'vs/base/common/event'; import { timeout } from 'vs/base/common/async'; import { IConfigurationService, getMigratedSettingValue } from 'vs/platform/configuration/common/configuration'; -import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain'; -import product from 'vs/platform/product/node/product'; +import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; +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 { ILogService } from 'vs/platform/log/common/log'; @@ -44,7 +44,7 @@ export abstract class AbstractUpdateService implements IUpdateService { } constructor( - @ILifecycleService private readonly lifecycleService: ILifecycleService, + @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, @IConfigurationService protected configurationService: IConfigurationService, @IEnvironmentService private readonly environmentService: IEnvironmentService, @IRequestService protected requestService: IRequestService, @@ -152,7 +152,7 @@ export abstract class AbstractUpdateService implements IUpdateService { this.logService.trace('update#quitAndInstall(): before lifecycle quit()'); - this.lifecycleService.quit(true /* from update */).then(vetod => { + this.lifecycleMainService.quit(true /* from update */).then(vetod => { this.logService.trace(`update#quitAndInstall(): after lifecycle quit() with veto: ${vetod}`); if (vetod) { return; diff --git a/src/vs/platform/update/electron-main/updateService.darwin.ts b/src/vs/platform/update/electron-main/updateService.darwin.ts index 539f9d79c142..783d487d5d04 100644 --- a/src/vs/platform/update/electron-main/updateService.darwin.ts +++ b/src/vs/platform/update/electron-main/updateService.darwin.ts @@ -8,7 +8,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { Event } from 'vs/base/common/event'; import { memoize } from 'vs/base/common/decorators'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain'; +import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; 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'; @@ -28,14 +28,14 @@ export class DarwinUpdateService extends AbstractUpdateService { @memoize private get onRawUpdateDownloaded(): Event { return Event.fromNodeEventEmitter(electron.autoUpdater, 'update-downloaded', (_, releaseNotes, version, date) => ({ releaseNotes, version, productVersion: version, date })); } constructor( - @ILifecycleService lifecycleService: ILifecycleService, + @ILifecycleMainService lifecycleMainService: ILifecycleMainService, @IConfigurationService configurationService: IConfigurationService, @ITelemetryService private readonly telemetryService: ITelemetryService, @IEnvironmentService environmentService: IEnvironmentService, @IRequestService requestService: IRequestService, @ILogService logService: ILogService ) { - super(lifecycleService, configurationService, environmentService, requestService, logService); + super(lifecycleMainService, configurationService, environmentService, requestService, logService); this.onRawError(this.onError, this, this.disposables); this.onRawUpdateAvailable(this.onUpdateAvailable, this, this.disposables); this.onRawUpdateDownloaded(this.onUpdateDownloaded, this, this.disposables); diff --git a/src/vs/platform/update/electron-main/updateService.linux.ts b/src/vs/platform/update/electron-main/updateService.linux.ts index d895054ad11e..944006ed9559 100644 --- a/src/vs/platform/update/electron-main/updateService.linux.ts +++ b/src/vs/platform/update/electron-main/updateService.linux.ts @@ -3,9 +3,9 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import product from 'vs/platform/product/node/product'; +import product from 'vs/platform/product/common/product'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain'; +import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; 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'; @@ -20,14 +20,14 @@ export class LinuxUpdateService extends AbstractUpdateService { _serviceBrand: undefined; constructor( - @ILifecycleService lifecycleService: ILifecycleService, + @ILifecycleMainService lifecycleMainService: ILifecycleMainService, @IConfigurationService configurationService: IConfigurationService, @ITelemetryService private readonly telemetryService: ITelemetryService, @IEnvironmentService environmentService: IEnvironmentService, @IRequestService requestService: IRequestService, @ILogService logService: ILogService ) { - super(lifecycleService, configurationService, environmentService, requestService, logService); + super(lifecycleMainService, configurationService, environmentService, requestService, logService); } protected buildUpdateFeedUrl(quality: string): string { diff --git a/src/vs/platform/update/electron-main/updateService.snap.ts b/src/vs/platform/update/electron-main/updateService.snap.ts index e5449de3828b..21d2ae486658 100644 --- a/src/vs/platform/update/electron-main/updateService.snap.ts +++ b/src/vs/platform/update/electron-main/updateService.snap.ts @@ -5,7 +5,7 @@ import { Event, Emitter } from 'vs/base/common/event'; import { timeout } from 'vs/base/common/async'; -import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain'; +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 { ILogService } from 'vs/platform/log/common/log'; @@ -35,7 +35,7 @@ abstract class AbstractUpdateService2 implements IUpdateService { } constructor( - @ILifecycleService private readonly lifecycleService: ILifecycleService, + @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, @IEnvironmentService environmentService: IEnvironmentService, @ILogService protected logService: ILogService, ) { @@ -106,7 +106,7 @@ abstract class AbstractUpdateService2 implements IUpdateService { this.logService.trace('update#quitAndInstall(): before lifecycle quit()'); - this.lifecycleService.quit(true /* from update */).then(vetod => { + this.lifecycleMainService.quit(true /* from update */).then(vetod => { this.logService.trace(`update#quitAndInstall(): after lifecycle quit() with veto: ${vetod}`); if (vetod) { return; @@ -139,12 +139,12 @@ export class SnapUpdateService extends AbstractUpdateService2 { constructor( private snap: string, private snapRevision: string, - @ILifecycleService lifecycleService: ILifecycleService, + @ILifecycleMainService lifecycleMainService: ILifecycleMainService, @IEnvironmentService environmentService: IEnvironmentService, @ILogService logService: ILogService, @ITelemetryService private readonly telemetryService: ITelemetryService ) { - super(lifecycleService, environmentService, logService); + super(lifecycleMainService, environmentService, logService); const watcher = watch(path.dirname(this.snap)); const onChange = Event.fromNodeEventEmitter(watcher, 'change', (_, fileName: string) => fileName); @@ -152,7 +152,7 @@ export class SnapUpdateService extends AbstractUpdateService2 { const onDebouncedCurrentChange = Event.debounce(onCurrentChange, (_, e) => e, 2000); const listener = onDebouncedCurrentChange(this.checkForUpdates, this); - lifecycleService.onWillShutdown(() => { + lifecycleMainService.onWillShutdown(() => { listener.dispose(); watcher.close(); }); diff --git a/src/vs/platform/update/electron-main/updateService.win32.ts b/src/vs/platform/update/electron-main/updateService.win32.ts index 489894981977..41b8a7e1e395 100644 --- a/src/vs/platform/update/electron-main/updateService.win32.ts +++ b/src/vs/platform/update/electron-main/updateService.win32.ts @@ -8,8 +8,8 @@ import * as path from 'vs/base/common/path'; import * as pfs from 'vs/base/node/pfs'; import { memoize } from 'vs/base/common/decorators'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain'; -import product from 'vs/platform/product/node/product'; +import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; +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'; @@ -61,7 +61,7 @@ export class Win32UpdateService extends AbstractUpdateService { } constructor( - @ILifecycleService lifecycleService: ILifecycleService, + @ILifecycleMainService lifecycleMainService: ILifecycleMainService, @IConfigurationService configurationService: IConfigurationService, @ITelemetryService private readonly telemetryService: ITelemetryService, @IEnvironmentService environmentService: IEnvironmentService, @@ -69,7 +69,7 @@ export class Win32UpdateService extends AbstractUpdateService { @ILogService logService: ILogService, @IFileService private readonly fileService: IFileService ) { - super(lifecycleService, configurationService, environmentService, requestService, logService); + super(lifecycleMainService, configurationService, environmentService, requestService, logService); if (getUpdateType() === UpdateType.Setup) { /* __GDPR__ diff --git a/src/vs/platform/url/electron-main/electronUrlListener.ts b/src/vs/platform/url/electron-main/electronUrlListener.ts index 5ea97de746fc..8933049e68a2 100644 --- a/src/vs/platform/url/electron-main/electronUrlListener.ts +++ b/src/vs/platform/url/electron-main/electronUrlListener.ts @@ -5,7 +5,7 @@ import { Event } from 'vs/base/common/event'; import { IURLService } from 'vs/platform/url/common/url'; -import product from 'vs/platform/product/node/product'; +import product from 'vs/platform/product/common/product'; import { app } from 'electron'; import { URI } from 'vs/base/common/uri'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; @@ -28,7 +28,7 @@ export class ElectronURLListener { constructor( initial: string | string[], @IURLService private readonly urlService: IURLService, - @IWindowsMainService windowsService: IWindowsMainService + @IWindowsMainService windowsMainService: IWindowsMainService ) { const globalBuffer = ((global).getOpenUrls() || []) as string[]; const rawBuffer = [ @@ -58,18 +58,18 @@ export class ElectronURLListener { const onOpenUrl = Event.filter(Event.map(onOpenElectronUrl, uriFromRawUrl), uri => !!uri); onOpenUrl(this.urlService.open, this.urlService, this.disposables); - const isWindowReady = windowsService.getWindows() + const isWindowReady = windowsMainService.getWindows() .filter(w => w.isReady) .length > 0; if (isWindowReady) { flush(); } else { - Event.once(windowsService.onWindowReady)(flush); + Event.once(windowsMainService.onWindowReady)(flush); } } dispose(): void { this.disposables = dispose(this.disposables); } -} \ No newline at end of file +} diff --git a/src/vs/platform/url/node/urlService.ts b/src/vs/platform/url/node/urlService.ts index 25928b624032..fdfe1e4d5cde 100644 --- a/src/vs/platform/url/node/urlService.ts +++ b/src/vs/platform/url/node/urlService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { URI, UriComponents } from 'vs/base/common/uri'; -import product from 'vs/platform/product/node/product'; +import product from 'vs/platform/product/common/product'; import { AbstractURLService } from 'vs/platform/url/common/urlService'; export class URLService extends AbstractURLService { diff --git a/src/vs/platform/userDataSync/common/settingsSync.ts b/src/vs/platform/userDataSync/common/settingsSync.ts new file mode 100644 index 000000000000..c45fbd97f572 --- /dev/null +++ b/src/vs/platform/userDataSync/common/settingsSync.ts @@ -0,0 +1,256 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable } from 'vs/base/common/lifecycle'; +import { IFileService, FileSystemProviderErrorCode, FileSystemProviderError, IFileContent } from 'vs/platform/files/common/files'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IUserData, UserDataSyncStoreError, UserDataSyncStoreErrorCode, ISynchroniser, SyncStatus, ISettingsMergeService, IUserDataSyncStoreService, SETTINGS_CONFLICTS_RESOURCE } from 'vs/platform/userDataSync/common/userDataSync'; +import { VSBuffer } from 'vs/base/common/buffer'; +import { parse, ParseError } from 'vs/base/common/json'; +import { localize } from 'vs/nls'; +import { Emitter, Event } from 'vs/base/common/event'; +import { ILogService } from 'vs/platform/log/common/log'; +import { CancelablePromise, createCancelablePromise, ThrottledDelayer } from 'vs/base/common/async'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { URI } from 'vs/base/common/uri'; + +interface ISyncPreviewResult { + readonly fileContent: IFileContent | null; + readonly remoteUserData: IUserData | null; + readonly hasLocalChanged: boolean; + readonly hasRemoteChanged: boolean; + readonly hasConflicts: boolean; +} + +export class SettingsSynchroniser extends Disposable implements ISynchroniser { + + private static LAST_SYNC_SETTINGS_STORAGE_KEY: string = 'LAST_SYNC_SETTINGS_CONTENTS'; + private static EXTERNAL_USER_DATA_SETTINGS_KEY: string = 'settings'; + + private syncPreviewResultPromise: CancelablePromise | null = null; + + private _status: SyncStatus = SyncStatus.Idle; + get status(): SyncStatus { return this._status; } + private _onDidChangStatus: Emitter = this._register(new Emitter()); + readonly onDidChangeStatus: Event = this._onDidChangStatus.event; + + private readonly throttledDelayer: ThrottledDelayer; + private _onDidChangeLocal: Emitter = this._register(new Emitter()); + readonly onDidChangeLocal: Event = this._onDidChangeLocal.event; + + readonly conflicts: URI = SETTINGS_CONFLICTS_RESOURCE; + + constructor( + @IFileService private readonly fileService: IFileService, + @IEnvironmentService private readonly environmentService: IEnvironmentService, + @IStorageService private readonly storageService: IStorageService, + @IUserDataSyncStoreService private readonly userDataSyncStoreService: IUserDataSyncStoreService, + @ISettingsMergeService private readonly settingsMergeService: ISettingsMergeService, + @ILogService private readonly logService: ILogService, + ) { + super(); + this.throttledDelayer = this._register(new ThrottledDelayer(500)); + this._register(Event.filter(this.fileService.onFileChanges, e => e.contains(this.environmentService.settingsResource))(() => this.throttledDelayer.trigger(() => this.onDidChangeSettings()))); + } + + private async onDidChangeSettings(): Promise { + const localFileContent = await this.getLocalFileContent(); + const lastSyncData = this.getLastSyncUserData(); + if (localFileContent && lastSyncData) { + if (localFileContent.value.toString() !== lastSyncData.content) { + this._onDidChangeLocal.fire(); + return; + } + } + if (!localFileContent || !lastSyncData) { + this._onDidChangeLocal.fire(); + return; + } + } + + private setStatus(status: SyncStatus): void { + if (this._status !== status) { + this._status = status; + this._onDidChangStatus.fire(status); + } + } + + async sync(): Promise { + + if (this.status !== SyncStatus.Idle) { + return false; + } + + this.setStatus(SyncStatus.Syncing); + + try { + const result = await this.getPreview(); + if (result.hasConflicts) { + this.setStatus(SyncStatus.HasConflicts); + return false; + } + await this.apply(); + return true; + } catch (e) { + this.syncPreviewResultPromise = null; + this.setStatus(SyncStatus.Idle); + if (e instanceof UserDataSyncStoreError && e.code === UserDataSyncStoreErrorCode.Rejected) { + // Rejected as there is a new remote version. Syncing again, + this.logService.info('Failed to Synchronise settings as there is a new remote version available. Synchronising again...'); + return this.sync(); + } + if (e instanceof FileSystemProviderError && e.code === FileSystemProviderErrorCode.FileExists) { + // Rejected as there is a new local version. Syncing again. + this.logService.info('Failed to Synchronise settings as there is a new local version available. Synchronising again...'); + return this.sync(); + } + throw e; + } + } + + async continueSync(): Promise { + if (this.status !== SyncStatus.HasConflicts) { + return false; + } + await this.apply(); + return true; + } + + private async apply(): Promise { + if (!this.syncPreviewResultPromise) { + return; + } + + if (await this.fileService.exists(this.conflicts)) { + const settingsPreivew = await this.fileService.readFile(this.conflicts); + const content = settingsPreivew.value.toString(); + if (this.hasErrors(content)) { + return Promise.reject(localize('errorInvalidSettings', "Unable to sync settings. Please resolve conflicts without any errors/warnings and try again.")); + } + + let { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged } = await this.syncPreviewResultPromise; + if (hasRemoteChanged) { + const ref = await this.writeToRemote(content, remoteUserData ? remoteUserData.ref : null); + remoteUserData = { ref, content }; + } + if (hasLocalChanged) { + await this.writeToLocal(content, fileContent); + } + if (remoteUserData) { + this.updateLastSyncValue(remoteUserData); + } + + // Delete the preview + await this.fileService.del(this.conflicts); + } + + this.syncPreviewResultPromise = null; + this.setStatus(SyncStatus.Idle); + } + + private hasErrors(content: string): boolean { + const parseErrors: ParseError[] = []; + parse(content, parseErrors); + return parseErrors.length > 0; + } + + private getPreview(): Promise { + if (!this.syncPreviewResultPromise) { + this.syncPreviewResultPromise = createCancelablePromise(token => this.generatePreview()); + } + return this.syncPreviewResultPromise; + } + + private async generatePreview(): Promise { + const remoteUserData = await this.userDataSyncStoreService.read(SettingsSynchroniser.EXTERNAL_USER_DATA_SETTINGS_KEY); + // Get file content last to get the latest + const fileContent = await this.getLocalFileContent(); + let hasLocalChanged: boolean = false; + let hasRemoteChanged: boolean = false; + let hasConflicts: boolean = false; + + // First time sync to remote + if (fileContent && !remoteUserData) { + this.logService.trace('Settings Sync: Remote contents does not exist. So sync with settings file.'); + hasRemoteChanged = true; + await this.fileService.writeFile(this.conflicts, VSBuffer.fromString(fileContent.value.toString())); + return { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged, hasConflicts }; + } + + // Settings file does not exist, so sync with remote contents. + if (remoteUserData && !fileContent) { + this.logService.trace('Settings Sync: Settings file does not exist. So sync with remote contents'); + hasLocalChanged = true; + await this.fileService.writeFile(this.conflicts, VSBuffer.fromString(remoteUserData.content)); + return { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged, hasConflicts }; + } + + if (fileContent && remoteUserData) { + const localContent: string = fileContent.value.toString(); + const remoteContent: string = remoteUserData.content; + const lastSyncData = this.getLastSyncUserData(); + if (!lastSyncData // First time sync + || lastSyncData.content !== localContent // Local has moved forwarded + || lastSyncData.content !== remoteContent // Remote has moved forwarded + ) { + this.logService.trace('Settings Sync: Merging remote contents with settings file.'); + const mergeContent = await this.settingsMergeService.merge(localContent, remoteContent, lastSyncData ? lastSyncData.content : null); + hasLocalChanged = mergeContent !== localContent; + hasRemoteChanged = mergeContent !== remoteContent; + if (hasLocalChanged || hasRemoteChanged) { + // Sync only if there are changes + hasConflicts = this.hasErrors(mergeContent); + await this.fileService.writeFile(this.conflicts, VSBuffer.fromString(mergeContent)); + return { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged, hasConflicts }; + } + } + } + + this.logService.trace('Settings Sync: No changes.'); + return { fileContent, remoteUserData, hasLocalChanged, hasRemoteChanged, hasConflicts }; + } + + private getLastSyncUserData(): IUserData | null { + const lastSyncStorageContents = this.storageService.get(SettingsSynchroniser.LAST_SYNC_SETTINGS_STORAGE_KEY, StorageScope.GLOBAL, undefined); + if (lastSyncStorageContents) { + return JSON.parse(lastSyncStorageContents); + } + return null; + } + + private async getLocalFileContent(): Promise { + try { + return await this.fileService.readFile(this.environmentService.settingsResource); + } catch (error) { + if (error instanceof FileSystemProviderError && error.code !== FileSystemProviderErrorCode.FileNotFound) { + return null; + } + throw error; + } + } + + private async writeToRemote(content: string, ref: string | null): Promise { + return this.userDataSyncStoreService.write(SettingsSynchroniser.EXTERNAL_USER_DATA_SETTINGS_KEY, content, ref); + } + + private async writeToLocal(newContent: string, oldContent: IFileContent | null): Promise { + if (oldContent) { + // file exists already + await this.fileService.writeFile(this.environmentService.settingsResource, VSBuffer.fromString(newContent), oldContent); + } else { + // file does not exist + await this.fileService.createFile(this.environmentService.settingsResource, VSBuffer.fromString(newContent), { overwrite: false }); + } + } + + private updateLastSyncValue(remoteUserData: IUserData): void { + const lastSyncUserData = this.getLastSyncUserData(); + if (lastSyncUserData && lastSyncUserData.ref === remoteUserData.ref) { + return; + } + this.storageService.store(SettingsSynchroniser.LAST_SYNC_SETTINGS_STORAGE_KEY, JSON.stringify(remoteUserData), StorageScope.GLOBAL); + } + +} diff --git a/src/vs/platform/userDataSync/common/userDataSync.ts b/src/vs/platform/userDataSync/common/userDataSync.ts new file mode 100644 index 000000000000..6ec821062a66 --- /dev/null +++ b/src/vs/platform/userDataSync/common/userDataSync.ts @@ -0,0 +1,118 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { Event } from 'vs/base/common/event'; +import { URI } from 'vs/base/common/uri'; + +export interface IUserData { + ref: string; + content: string; +} + +export enum UserDataSyncStoreErrorCode { + Rejected = 'Rejected', + Unknown = 'Unknown' +} + +export function markAsUserDataSyncStoreError(error: Error, code: UserDataSyncStoreErrorCode): Error { + error.name = code ? `${code} (UserDataSyncStoreError)` : `UserDataSyncStoreError`; + + return error; +} + +export function toUserDataSyncStoreErrorCode(error: Error | undefined | null): UserDataSyncStoreErrorCode { + + // Guard against abuse + if (!error) { + return UserDataSyncStoreErrorCode.Unknown; + } + + // FileSystemProviderError comes with the code + if (error instanceof UserDataSyncStoreError) { + return error.code; + } + + // Any other error, check for name match by assuming that the error + // went through the markAsUserDataSyncStoreError() method + const match = /^(.+) \(UserDataSyncStoreError\)$/.exec(error.name); + if (!match) { + return UserDataSyncStoreErrorCode.Unknown; + } + + switch (match[1]) { + case UserDataSyncStoreErrorCode.Rejected: return UserDataSyncStoreErrorCode.Rejected; + } + + return UserDataSyncStoreErrorCode.Unknown; +} + +export class UserDataSyncStoreError extends Error { + + constructor(message: string, public readonly code: UserDataSyncStoreErrorCode) { + super(message); + } + +} + +export interface IUserDataSyncStore { + readonly id: string; + readonly name: string; + read(key: string): Promise; + write(key: string, content: string, ref: string | null): Promise; +} + +export const IUserDataSyncStoreService = createDecorator('IUserDataSyncStoreService'); + +export interface IUserDataSyncStoreService { + _serviceBrand: undefined; + + readonly onDidChangeEnablement: Event; + readonly enabled: boolean; + + registerUserDataSyncStore(userDataSyncStore: IUserDataSyncStore): void; + deregisterUserDataSyncStore(): void; + + read(key: string): Promise; + write(key: string, content: string, ref: string | null): Promise; +} + + +export enum SyncStatus { + Uninitialized = 'uninitialized', + Idle = 'idle', + Syncing = 'syncing', + HasConflicts = 'hasConflicts', +} + +export const USER_DATA_PREVIEW_SCHEME = 'vscode-userdata-preview'; +export const SETTINGS_CONFLICTS_RESOURCE = URI.file('Settings-Preview').with({ scheme: USER_DATA_PREVIEW_SCHEME }); + +export interface ISynchroniser { + + readonly conflicts: URI | null; + readonly status: SyncStatus; + readonly onDidChangeStatus: Event; + readonly onDidChangeLocal: Event; + + sync(): Promise; + continueSync(): Promise; +} + +export const IUserDataSyncService = createDecorator('IUserDataSyncService'); + +export interface IUserDataSyncService extends ISynchroniser { + _serviceBrand: any; +} + +export const ISettingsMergeService = createDecorator('ISettingsMergeService'); + +export interface ISettingsMergeService { + + _serviceBrand: undefined; + + merge(localContent: string, remoteContent: string, baseContent: string | null): Promise; + +} diff --git a/src/vs/platform/userDataSync/common/userDataSyncService.ts b/src/vs/platform/userDataSync/common/userDataSyncService.ts new file mode 100644 index 000000000000..3983e12048d3 --- /dev/null +++ b/src/vs/platform/userDataSync/common/userDataSyncService.ts @@ -0,0 +1,92 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IUserDataSyncService, SyncStatus, ISynchroniser, IUserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSync'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { SettingsSynchroniser } from 'vs/platform/userDataSync/common/settingsSync'; +import { Emitter, Event } from 'vs/base/common/event'; +import { URI } from 'vs/base/common/uri'; + +export class UserDataSyncService extends Disposable implements IUserDataSyncService { + + _serviceBrand: any; + + private readonly synchronisers: ISynchroniser[]; + + private _status: SyncStatus = SyncStatus.Uninitialized; + get status(): SyncStatus { return this._status; } + private _onDidChangStatus: Emitter = this._register(new Emitter()); + readonly onDidChangeStatus: Event = this._onDidChangStatus.event; + + readonly onDidChangeLocal: Event; + + constructor( + @IUserDataSyncStoreService private readonly userDataSyncStoreService: IUserDataSyncStoreService, + @IInstantiationService private readonly instantiationService: IInstantiationService, + ) { + super(); + this.synchronisers = [ + this.instantiationService.createInstance(SettingsSynchroniser) + ]; + this.updateStatus(); + this._register(Event.any(this.userDataSyncStoreService.onDidChangeEnablement, ...this.synchronisers.map(s => Event.map(s.onDidChangeStatus, () => undefined)))(() => this.updateStatus())); + this.onDidChangeLocal = Event.any(...this.synchronisers.map(s => s.onDidChangeLocal)); + } + + get conflicts(): URI | null { + const synchroniser = this.synchronisers.filter(s => s.status === SyncStatus.HasConflicts)[0]; + return synchroniser ? synchroniser.conflicts : null; + } + + async sync(): Promise { + if (!this.userDataSyncStoreService.enabled) { + throw new Error('Not enabled'); + } + for (const synchroniser of this.synchronisers) { + if (!await synchroniser.sync()) { + return false; + } + } + return true; + } + + async continueSync(): Promise { + if (!this.userDataSyncStoreService.enabled) { + throw new Error('Not enabled'); + } + for (const synchroniser of this.synchronisers) { + if (await synchroniser.continueSync()) { + return true; + } + } + return false; + } + + private updateStatus(): void { + this.setStatus(this.computeStatus()); + } + + private setStatus(status: SyncStatus): void { + if (this._status !== status) { + this._status = status; + this._onDidChangStatus.fire(status); + } + } + + private computeStatus(): SyncStatus { + if (!this.userDataSyncStoreService.enabled) { + return SyncStatus.Uninitialized; + } + if (this.synchronisers.some(s => s.status === SyncStatus.HasConflicts)) { + return SyncStatus.HasConflicts; + } + if (this.synchronisers.some(s => s.status === SyncStatus.Syncing)) { + return SyncStatus.Syncing; + } + return SyncStatus.Idle; + } + +} diff --git a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts new file mode 100644 index 000000000000..0fca9080abc6 --- /dev/null +++ b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts @@ -0,0 +1,57 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable, } from 'vs/base/common/lifecycle'; +import { Emitter, Event } from 'vs/base/common/event'; +import { IUserDataSyncStore, IUserData, UserDataSyncStoreError, toUserDataSyncStoreErrorCode, IUserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSync'; +import { ILogService } from 'vs/platform/log/common/log'; + +export class UserDataSyncStoreService extends Disposable implements IUserDataSyncStoreService { + + _serviceBrand: any; + + private userDataSyncStore: IUserDataSyncStore | null = null; + + get enabled(): boolean { return !!this.userDataSyncStore; } + private readonly _onDidChangeEnablement: Emitter = this._register(new Emitter()); + readonly onDidChangeEnablement: Event = this._onDidChangeEnablement.event; + + constructor( + @ILogService private logService: ILogService + ) { + super(); + } + + registerUserDataSyncStore(userDataSyncStore: IUserDataSyncStore): void { + if (this.userDataSyncStore) { + this.logService.warn(`A user data sync store '${this.userDataSyncStore.name}' already registered. Hence ignoring the newly registered '${userDataSyncStore.name}' store.`); + return; + } + this.userDataSyncStore = userDataSyncStore; + this._onDidChangeEnablement.fire(true); + } + + deregisterUserDataSyncStore(): void { + this.userDataSyncStore = null; + this._onDidChangeEnablement.fire(false); + } + + read(key: string): Promise { + if (!this.userDataSyncStore) { + throw new Error('No user sync store exists.'); + } + return this.userDataSyncStore.read(key) + .then(null, error => Promise.reject(new UserDataSyncStoreError(error.message, toUserDataSyncStoreErrorCode(error)))); + } + + write(key: string, content: string, ref: string | null): Promise { + if (!this.userDataSyncStore) { + throw new Error('No user sync store exists.'); + } + return this.userDataSyncStore.write(key, content, ref) + .then(null, error => Promise.reject(new UserDataSyncStoreError(error.message, toUserDataSyncStoreErrorCode(error)))); + } + +} diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index 9f91c8760c80..661ab3892f9a 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -156,7 +156,6 @@ export interface IWindowsService { openExtensionDevelopmentHostWindow(args: ParsedArgs, env: IProcessEnvironment): Promise; getWindows(): Promise<{ id: number; workspace?: IWorkspaceIdentifier; folderUri?: ISingleFolderWorkspaceIdentifier; title: string; filename?: string; }[]>; getWindowCount(): Promise; - log(severity: string, args: string[]): Promise; showItemInFolder(path: URI): Promise; getActiveWindowId(): Promise; @@ -167,7 +166,6 @@ export interface IWindowsService { // TODO: this is a bit backwards startCrashReporter(config: CrashReporterStartOptions): Promise; - openAboutDialog(): Promise; resolveProxy(windowId: number, url: string): Promise; } diff --git a/src/vs/platform/windows/common/windowsIpc.ts b/src/vs/platform/windows/common/windowsIpc.ts index 2323c97e43cd..4e8606350f15 100644 --- a/src/vs/platform/windows/common/windowsIpc.ts +++ b/src/vs/platform/windows/common/windowsIpc.ts @@ -109,12 +109,10 @@ export class WindowsChannel implements IServerChannel { case 'whenSharedProcessReady': return this.service.whenSharedProcessReady(); case 'toggleSharedProcess': return this.service.toggleSharedProcess(); case 'quit': return this.service.quit(); - case 'log': return this.service.log(arg[0], arg[1]); case 'showItemInFolder': return this.service.showItemInFolder(URI.revive(arg)); case 'getActiveWindowId': return this.service.getActiveWindowId(); case 'openExternal': return this.service.openExternal(arg); case 'startCrashReporter': return this.service.startCrashReporter(arg); - case 'openAboutDialog': return this.service.openAboutDialog(); case 'resolveProxy': return this.service.resolveProxy(arg[0], arg[1]); } diff --git a/src/vs/platform/windows/electron-browser/windowsService.ts b/src/vs/platform/windows/electron-browser/windowsService.ts index 39d7cf5cb8bb..be618f3db01c 100644 --- a/src/vs/platform/windows/electron-browser/windowsService.ts +++ b/src/vs/platform/windows/electron-browser/windowsService.ts @@ -226,10 +226,6 @@ export class WindowsService implements IWindowsService { return this.channel.call('getWindowCount'); } - log(severity: string, args: string[]): Promise { - return this.channel.call('log', [severity, args]); - } - showItemInFolder(path: URI): Promise { return this.channel.call('showItemInFolder', path); } @@ -250,10 +246,6 @@ export class WindowsService implements IWindowsService { return this.channel.call('updateTouchBar', [windowId, items]); } - openAboutDialog(): Promise { - return this.channel.call('openAboutDialog'); - } - resolveProxy(windowId: number, url: string): Promise { return Promise.resolve(this.channel.call('resolveProxy', [windowId, url])); } diff --git a/src/vs/platform/windows/electron-main/windowsService.ts b/src/vs/platform/windows/electron-main/windowsService.ts index 709c9af83ff5..4f21baa49b2d 100644 --- a/src/vs/platform/windows/electron-main/windowsService.ts +++ b/src/vs/platform/windows/electron-main/windowsService.ts @@ -3,25 +3,22 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as nls from 'vs/nls'; -import * as os from 'os'; import { Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { assign } from 'vs/base/common/objects'; import { URI } from 'vs/base/common/uri'; -import product from 'vs/platform/product/node/product'; import { IWindowsService, OpenContext, INativeOpenDialogOptions, IEnterWorkspaceResult, IMessageBoxResult, IDevToolsOptions, INewWindowOptions, IOpenSettings, IURIToOpen } from 'vs/platform/windows/common/windows'; import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment'; -import { shell, crashReporter, app, Menu, clipboard } from 'electron'; +import { shell, crashReporter, app, Menu } from 'electron'; import { Event } from 'vs/base/common/event'; import { IURLService, IURLHandler } from 'vs/platform/url/common/url'; -import { ILifecycleService } from 'vs/platform/lifecycle/electron-main/lifecycleMain'; +import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; import { IWindowsMainService, ISharedProcess, ICodeWindow } from 'vs/platform/windows/electron-main/windows'; -import { IHistoryMainService, IRecentlyOpened, IRecent } from 'vs/platform/history/common/history'; +import { IRecentlyOpened, IRecent } from 'vs/platform/history/common/history'; +import { IHistoryMainService } from 'vs/platform/history/electron-main/historyMainService'; import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; import { Schemas } from 'vs/base/common/network'; -import { mnemonicButtonLabel } from 'vs/base/common/labels'; -import { isMacintosh, isLinux, IProcessEnvironment } from 'vs/base/common/platform'; +import { isMacintosh, IProcessEnvironment } from 'vs/base/common/platform'; import { ILogService } from 'vs/platform/log/common/log'; export class WindowsService extends Disposable implements IWindowsService, IURLHandler { @@ -41,15 +38,15 @@ export class WindowsService extends Disposable implements IWindowsService, IURLH Event.filter(Event.fromNodeEventEmitter(app, 'browser-window-focus', (_, w: Electron.BrowserWindow) => w.id), id => !!this.windowsMainService.getWindowById(id)) ); - readonly onRecentlyOpenedChange: Event = this.historyService.onRecentlyOpenedChange; + readonly onRecentlyOpenedChange: Event = this.historyMainService.onRecentlyOpenedChange; constructor( private sharedProcess: ISharedProcess, @IWindowsMainService private readonly windowsMainService: IWindowsMainService, @IEnvironmentService private readonly environmentService: IEnvironmentService, @IURLService urlService: IURLService, - @ILifecycleService private readonly lifecycleService: ILifecycleService, - @IHistoryMainService private readonly historyService: IHistoryMainService, + @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, + @IHistoryMainService private readonly historyMainService: IHistoryMainService, @ILogService private readonly logService: ILogService ) { super(); @@ -160,25 +157,25 @@ export class WindowsService extends Disposable implements IWindowsService, IURLH async addRecentlyOpened(recents: IRecent[]): Promise { this.logService.trace('windowsService#addRecentlyOpened'); - this.historyService.addRecentlyOpened(recents); + this.historyMainService.addRecentlyOpened(recents); } async removeFromRecentlyOpened(paths: URI[]): Promise { this.logService.trace('windowsService#removeFromRecentlyOpened'); - this.historyService.removeFromRecentlyOpened(paths); + this.historyMainService.removeFromRecentlyOpened(paths); } async clearRecentlyOpened(): Promise { this.logService.trace('windowsService#clearRecentlyOpened'); - this.historyService.clearRecentlyOpened(); + this.historyMainService.clearRecentlyOpened(); } async getRecentlyOpened(windowId: number): Promise { this.logService.trace('windowsService#getRecentlyOpened', windowId); - return this.withWindow(windowId, codeWindow => this.historyService.getRecentlyOpened(codeWindow.config.workspace, codeWindow.config.folderUri, codeWindow.config.filesToOpenOrCreate), () => this.historyService.getRecentlyOpened())!; + return this.withWindow(windowId, codeWindow => this.historyMainService.getRecentlyOpened(codeWindow.config.workspace, codeWindow.config.folderUri, codeWindow.config.filesToOpenOrCreate), () => this.historyMainService.getRecentlyOpened())!; } async newWindowTab(): Promise { @@ -339,24 +336,6 @@ export class WindowsService extends Disposable implements IWindowsService, IURLH return this.windowsMainService.getWindows().length; } - async log(severity: string, args: string[]): Promise { - let consoleFn = console.log; - - switch (severity) { - case 'error': - consoleFn = console.error; - break; - case 'warn': - consoleFn = console.warn; - break; - case 'info': - consoleFn = console.info; - break; - } - - consoleFn.call(console, ...args); - } - async showItemInFolder(resource: URI): Promise { this.logService.trace('windowsService#showItemInFolder'); @@ -391,7 +370,7 @@ export class WindowsService extends Disposable implements IWindowsService, IURLH async relaunch(options: { addArgs?: string[], removeArgs?: string[] }): Promise { this.logService.trace('windowsService#relaunch'); - this.lifecycleService.relaunch(options); + this.lifecycleMainService.relaunch(options); } async whenSharedProcessReady(): Promise { @@ -407,54 +386,6 @@ export class WindowsService extends Disposable implements IWindowsService, IURLH } - async openAboutDialog(): Promise { - this.logService.trace('windowsService#openAboutDialog'); - - let version = app.getVersion(); - if (product.target) { - version = `${version} (${product.target} setup)`; - } - - const isSnap = process.platform === 'linux' && process.env.SNAP && process.env.SNAP_REVISION; - // {{SQL CARBON EDIT}} - const detail = nls.localize('aboutDetail', - "Version: {0}\nCommit: {1}\nDate: {2}\nVS Code {8}\nElectron: {3}\nChrome: {4}\nNode.js: {5}\nV8: {6}\nOS: {7}", - version, - product.commit || 'Unknown', - product.date || 'Unknown', - process.versions['electron'], - process.versions['chrome'], - process.versions['node'], - process.versions['v8'], - `${os.type()} ${os.arch()} ${os.release()}${isSnap ? ' snap' : ''}`, - product.vscodeVersion - ); - - const ok = nls.localize('okButton', "OK"); - const copy = mnemonicButtonLabel(nls.localize({ key: 'copy', comment: ['&& denotes a mnemonic'] }, "&&Copy")); - let buttons: string[]; - if (isLinux) { - buttons = [copy, ok]; - } else { - buttons = [ok, copy]; - } - - const result = await this.windowsMainService.showMessageBox({ - title: product.nameLong, - type: 'info', - message: product.nameLong, - detail: `\n${detail}`, - buttons, - noLink: true, - defaultId: buttons.indexOf(ok), - cancelId: buttons.indexOf(ok) - }, this.windowsMainService.getFocusedWindow() || this.windowsMainService.getLastActiveWindow()); - - if (buttons[result.button] === copy) { - clipboard.writeText(detail); - } - } - async handleURL(uri: URI): Promise { // Catch file URLs diff --git a/src/vs/platform/workspace/common/workspace.ts b/src/vs/platform/workspace/common/workspace.ts index f558e667d0d1..47ea5ab022f1 100644 --- a/src/vs/platform/workspace/common/workspace.ts +++ b/src/vs/platform/workspace/common/workspace.ts @@ -143,7 +143,7 @@ export interface IWorkspaceFolder extends IWorkspaceFolderData { export class Workspace implements IWorkspace { private _foldersMap: TernarySearchTree = TernarySearchTree.forPaths(); - private _folders: WorkspaceFolder[]; + private _folders!: WorkspaceFolder[]; constructor( private _id: string, @@ -265,4 +265,4 @@ export function toWorkspaceFolders(configuredFolders: IStoredWorkspaceFolder[], } } return result; -} \ No newline at end of file +} diff --git a/src/vs/platform/workspaces/common/workspaces.ts b/src/vs/platform/workspaces/common/workspaces.ts index 353de67924e0..149e0e9c6335 100644 --- a/src/vs/platform/workspaces/common/workspaces.ts +++ b/src/vs/platform/workspaces/common/workspaces.ts @@ -5,7 +5,6 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { localize } from 'vs/nls'; -import { Event } from 'vs/base/common/event'; import { IWorkspaceFolder, IWorkspace } from 'vs/platform/workspace/common/workspace'; import { URI, UriComponents } from 'vs/base/common/uri'; import { isWindows, isLinux, isMacintosh } from 'vs/base/common/platform'; @@ -19,9 +18,6 @@ import { toSlashes } from 'vs/base/common/extpath'; import { FormattingOptions } from 'vs/base/common/jsonFormatter'; import { getRemoteAuthority } from 'vs/platform/remote/common/remoteHosts'; -export const IWorkspacesMainService = createDecorator('workspacesMainService'); -export const IWorkspacesService = createDecorator('workspacesService'); - export const WORKSPACE_EXTENSION = 'code-workspace'; export const WORKSPACE_FILTER = [{ name: localize('codeWorkspace', "Code Workspace"), extensions: [WORKSPACE_EXTENSION] }]; export const UNTITLED_WORKSPACE_NAME = 'workspace.json'; @@ -95,23 +91,10 @@ export interface IUntitledWorkspaceInfo { remoteAuthority?: string; } -export interface IWorkspacesMainService extends IWorkspacesService { - _serviceBrand: undefined; - - onUntitledWorkspaceDeleted: Event; - - createUntitledWorkspaceSync(folders?: IWorkspaceFolderCreationData[]): IWorkspaceIdentifier; - - resolveLocalWorkspaceSync(path: URI): IResolvedWorkspace | null; - - isUntitledWorkspace(workspace: IWorkspaceIdentifier): boolean; - - deleteUntitledWorkspaceSync(workspace: IWorkspaceIdentifier): void; - - getUntitledWorkspacesSync(): IUntitledWorkspaceInfo[]; -} +export const IWorkspacesService = createDecorator('workspacesService'); export interface IWorkspacesService { + _serviceBrand: undefined; createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[], remoteAuthority?: string): Promise; @@ -276,4 +259,4 @@ export function useSlashForPath(storedFolders: IStoredWorkspaceFolder[]): boolea return false; } return true; -} \ No newline at end of file +} diff --git a/src/vs/platform/workspaces/node/workspacesIpc.ts b/src/vs/platform/workspaces/electron-main/workspacesIpc.ts similarity index 88% rename from src/vs/platform/workspaces/node/workspacesIpc.ts rename to src/vs/platform/workspaces/electron-main/workspacesIpc.ts index 1212a505a384..17450695ae54 100644 --- a/src/vs/platform/workspaces/node/workspacesIpc.ts +++ b/src/vs/platform/workspaces/electron-main/workspacesIpc.ts @@ -4,7 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { IServerChannel } from 'vs/base/parts/ipc/common/ipc'; -import { IWorkspaceIdentifier, IWorkspaceFolderCreationData, IWorkspacesMainService } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspaceIdentifier, IWorkspaceFolderCreationData } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService'; import { URI } from 'vs/base/common/uri'; import { Event } from 'vs/base/common/event'; diff --git a/src/vs/platform/workspaces/electron-main/workspacesMainService.ts b/src/vs/platform/workspaces/electron-main/workspacesMainService.ts index 5ec16e495014..e6bca89a3637 100644 --- a/src/vs/platform/workspaces/electron-main/workspacesMainService.ts +++ b/src/vs/platform/workspaces/electron-main/workspacesMainService.ts @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IWorkspacesMainService, IWorkspaceIdentifier, hasWorkspaceFileExtension, UNTITLED_WORKSPACE_NAME, IResolvedWorkspace, IStoredWorkspaceFolder, isStoredWorkspaceFolder, IWorkspaceFolderCreationData, IUntitledWorkspaceInfo, getStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspaceIdentifier, hasWorkspaceFileExtension, UNTITLED_WORKSPACE_NAME, IResolvedWorkspace, IStoredWorkspaceFolder, isStoredWorkspaceFolder, IWorkspaceFolderCreationData, IUntitledWorkspaceInfo, getStoredWorkspaceFolder } from 'vs/platform/workspaces/common/workspaces'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { join, dirname } from 'vs/base/common/path'; import { mkdirp, writeFile, rimrafSync, readdirSync, writeFileSync } from 'vs/base/node/pfs'; @@ -18,12 +18,38 @@ import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; import { Disposable } from 'vs/base/common/lifecycle'; import { originalFSPath, isEqualOrParent, joinPath } from 'vs/base/common/resources'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; export interface IStoredWorkspace { folders: IStoredWorkspaceFolder[]; remoteAuthority?: string; } +export const IWorkspacesMainService = createDecorator('workspacesMainService'); + +export interface IWorkspacesMainService { + + _serviceBrand: undefined; + + onUntitledWorkspaceDeleted: Event; + + createUntitledWorkspaceSync(folders?: IWorkspaceFolderCreationData[]): IWorkspaceIdentifier; + + resolveLocalWorkspaceSync(path: URI): IResolvedWorkspace | null; + + isUntitledWorkspace(workspace: IWorkspaceIdentifier): boolean; + + deleteUntitledWorkspaceSync(workspace: IWorkspaceIdentifier): void; + + getUntitledWorkspacesSync(): IUntitledWorkspaceInfo[]; + + createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[], remoteAuthority?: string): Promise; + + deleteUntitledWorkspace(workspace: IWorkspaceIdentifier): Promise; + + getWorkspaceIdentifier(workspacePath: URI): Promise; +} + export class WorkspacesMainService extends Disposable implements IWorkspacesMainService { _serviceBrand: undefined; diff --git a/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts b/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts index 3981d935ca99..369bb7e22917 100644 --- a/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts +++ b/src/vs/platform/workspaces/test/electron-main/workspacesMainService.test.ts @@ -11,7 +11,7 @@ import * as pfs from 'vs/base/node/pfs'; import { EnvironmentService } from 'vs/platform/environment/node/environmentService'; import { parseArgs, OPTIONS } from 'vs/platform/environment/node/argv'; import { WorkspacesMainService, IStoredWorkspace } from 'vs/platform/workspaces/electron-main/workspacesMainService'; -import { WORKSPACE_EXTENSION, IWorkspaceIdentifier, IRawFileWorkspaceFolder, IWorkspaceFolderCreationData, IRawUriWorkspaceFolder, rewriteWorkspaceFileForNewLocation } from 'vs/platform/workspaces/common/workspaces'; +import { WORKSPACE_EXTENSION, IRawFileWorkspaceFolder, IWorkspaceFolderCreationData, IRawUriWorkspaceFolder, rewriteWorkspaceFileForNewLocation } from 'vs/platform/workspaces/common/workspaces'; import { NullLogService } from 'vs/platform/log/common/log'; import { URI } from 'vs/base/common/uri'; import { getRandomTestPath } from 'vs/base/test/node/testUtils'; @@ -29,16 +29,6 @@ suite('WorkspacesMainService', () => { } } - class TestWorkspacesMainService extends WorkspacesMainService { - public deleteWorkspaceCall: IWorkspaceIdentifier; - - public deleteUntitledWorkspaceSync(workspace: IWorkspaceIdentifier): void { - this.deleteWorkspaceCall = workspace; - - super.deleteUntitledWorkspaceSync(workspace); - } - } - function createWorkspace(folders: string[], names?: string[]) { return service.createUntitledWorkspace(folders.map((folder, index) => ({ uri: URI.file(folder), name: names ? names[index] : undefined } as IWorkspaceFolderCreationData))); } @@ -50,10 +40,10 @@ suite('WorkspacesMainService', () => { const environmentService = new TestEnvironmentService(parseArgs(process.argv, OPTIONS), process.execPath); const logService = new NullLogService(); - let service: TestWorkspacesMainService; + let service: WorkspacesMainService; setup(async () => { - service = new TestWorkspacesMainService(environmentService, logService); + service = new WorkspacesMainService(environmentService, logService); // Delete any existing backups completely and then re-create it. await pfs.rimraf(untitledWorkspacesHomePath, pfs.RimRafMode.MOVE); diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index d7e7145185aa..7caa11a98aa3 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -7014,6 +7014,11 @@ declare module 'vscode' { */ readonly onDidChangeVisibility: Event; + /** + * An optional human-readable message that will be rendered in the view. + */ + message?: string; + /** * Reveals the given element in the tree view. * If the tree view is not visible then the tree view is shown and element is revealed. diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 59ea920f5fc9..0c8590aebd3f 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -820,12 +820,6 @@ declare module 'vscode' { //#region Tree View export interface TreeView { - - /** - * An optional human-readable message that will be rendered in the view. - */ - message?: string; - /** * The name of the tree view. It is set from the extension package.json and can be changed later. */ @@ -1011,16 +1005,68 @@ declare module 'vscode' { //#endregion + // #region Ben - UIKind + + /** + * Possible kinds of UI that can use extensions. + */ + export enum UIKind { + + /** + * Extensions are accessed from a desktop application. + */ + Desktop = 1, + + /** + * Extensions are accessed from a web browser. + */ + Web = 2 + } + + export namespace env { + + /** + * The UI kind property indicates from which UI extensions + * are accessed from. For example, extensions could be accessed + * from a desktop application or a web browser. + */ + export const uiKind: UIKind; + } + + //#endregion + //#region Custom editors, mjbvz export enum WebviewEditorState { + /** + * The webview editor's content cannot be modified. + * + * This disables save + */ Readonly = 1, + + /** + * The webview editor's content has not been changed but they can be modified and saved. + */ Unchanged = 2, + + /** + * The webview editor's content has been changed and can be saved. + */ Dirty = 3, } export interface WebviewEditor extends WebviewPanel { state: WebviewEditorState; + + /** + * Fired when the webview editor is saved. + * + * Both `Unchanged` and `Dirty` editors can be saved. + * + * Extensions should call `waitUntil` to signal when the save operation complete + */ + readonly onWillSave: Event<{ waitUntil: (thenable: Thenable) => void }>; } export interface WebviewEditorProvider { @@ -1043,4 +1089,59 @@ declare module 'vscode' { } //#endregion + + // #region Sandy - User data synchronization + + export namespace window { + + /** + * Register an [UserDataSyncProvider](#UserDataSyncProvider) to read and write user data. + * @param name Name of the user data sync provider + * @param userDataSyncProvider [UserDataSyncProvider](#UserDataSyncProvider) to read and write user data + */ + export function registerUserDataSyncProvider(name: string, userDataSyncProvider: UserDataSyncProvider): Disposable; + + } + + export class UserDataError extends Error { + + /** + * Create an error to signal that writing user data with given ref is rejected, becase of new ref. + */ + static Rejected(): FileSystemError; + + /** + * Creates a new userData error. + */ + constructor(); + } + + /** + * User data sync provider to read and write user data. + */ + export interface UserDataSyncProvider { + + /** + * Reads the content and its ref for the given key. + * Return null if key does not exists. + * + * @param key key of the content to read + * @returns the content and its ref for the given key. Return null if key does not exists. + */ + read(key: string): Promise<{ content: string, ref: string } | null>; + + /** + * Writes the new content based on the given ref for the given key. + * + * @param key key of the content to write + * @param content new content to write + * @param ref ref of the content on which the content to write is based on + * @throws [Rejected](#UserDataError.Rejected) if the ref is not the latest. + * @returns the latest ref of the content. + */ + write(key: string, content: string, ref: string | null): Promise; + + } + + //#endregion } diff --git a/src/vs/workbench/api/browser/extensionHost.contribution.ts b/src/vs/workbench/api/browser/extensionHost.contribution.ts index 623b6d42afb5..c4897a3af767 100644 --- a/src/vs/workbench/api/browser/extensionHost.contribution.ts +++ b/src/vs/workbench/api/browser/extensionHost.contribution.ts @@ -18,6 +18,7 @@ import './mainThreadCodeInsets'; import './mainThreadClipboard'; import './mainThreadCommands'; import './mainThreadConfiguration'; +import './mainThreadUserData'; import './mainThreadConsole'; // import './mainThreadDebugService'; {{SQL CARBON EDIT}} @anthonydresser comment out debug service import './mainThreadDecorations'; diff --git a/src/vs/workbench/api/browser/mainThreadConsole.ts b/src/vs/workbench/api/browser/mainThreadConsole.ts index 773b92aa8a01..69c18adfdebb 100644 --- a/src/vs/workbench/api/browser/mainThreadConsole.ts +++ b/src/vs/workbench/api/browser/mainThreadConsole.ts @@ -6,9 +6,10 @@ import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { MainContext, MainThreadConsoleShape, IExtHostContext } from 'vs/workbench/api/common/extHost.protocol'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { IRemoteConsoleLog, log, parse } from 'vs/base/common/console'; +import { IRemoteConsoleLog, log } from 'vs/base/common/console'; +import { logRemoteEntry } from 'vs/workbench/services/extensions/common/remoteConsoleUtil'; import { parseExtensionDevOptions } from 'vs/workbench/services/extensions/common/extensionDevOptions'; -import { IWindowsService } from 'vs/platform/windows/common/windows'; +import { ILogService } from 'vs/platform/log/common/log'; import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug'; @extHostNamedCustomer(MainContext.MainThreadConsole) @@ -20,7 +21,7 @@ export class MainThreadConsole implements MainThreadConsoleShape { constructor( extHostContext: IExtHostContext, @IEnvironmentService private readonly _environmentService: IEnvironmentService, - @IWindowsService private readonly _windowsService: IWindowsService, + @ILogService private readonly _logService: ILogService, @IExtensionHostDebugService private readonly _extensionHostDebugService: IExtensionHostDebugService, ) { const devOpts = parseExtensionDevOptions(this._environmentService); @@ -40,7 +41,7 @@ export class MainThreadConsole implements MainThreadConsoleShape { // Log on main side if running tests from cli if (this._isExtensionDevTestFromCli) { - this._windowsService.log(entry.severity, parse(entry).args); + logRemoteEntry(this._logService, entry); } // Broadcast to other windows if we are in development mode diff --git a/src/vs/workbench/api/browser/mainThreadTask.ts b/src/vs/workbench/api/browser/mainThreadTask.ts index 8aa5dd3cba66..55f7a210650d 100644 --- a/src/vs/workbench/api/browser/mainThreadTask.ts +++ b/src/vs/workbench/api/browser/mainThreadTask.ts @@ -422,6 +422,7 @@ export class MainThreadTask implements MainThreadTaskShape { this._proxy.$OnDidEndTask(TaskExecutionDTO.from(task.getTaskExecution())); } }); + this._taskService.setJsonTasksSupported(Promise.resolve(this._proxy.$jsonTasksSupported())); } public dispose(): void { diff --git a/src/vs/workbench/api/browser/mainThreadUserData.ts b/src/vs/workbench/api/browser/mainThreadUserData.ts new file mode 100644 index 000000000000..451a2b3b704a --- /dev/null +++ b/src/vs/workbench/api/browser/mainThreadUserData.ts @@ -0,0 +1,43 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable, toDisposable } from 'vs/base/common/lifecycle'; +import { MainContext, ExtHostContext, IExtHostContext, MainThreadUserDataShape, ExtHostUserDataShape } from '../common/extHost.protocol'; +import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; +import { IUserData, IUserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSync'; + +@extHostNamedCustomer(MainContext.MainThreadUserData) +export class MainThreadUserData extends Disposable implements MainThreadUserDataShape { + + private readonly proxy: ExtHostUserDataShape; + + constructor( + extHostContext: IExtHostContext, + @IUserDataSyncStoreService private readonly userDataSyncStoreService: IUserDataSyncStoreService + ) { + super(); + this.proxy = extHostContext.getProxy(ExtHostContext.ExtHostUserData); + this._register(toDisposable(() => this.userDataSyncStoreService.deregisterUserDataSyncStore())); + } + + $registerUserDataProvider(id: string, name: string): void { + const proxy = this.proxy; + this.userDataSyncStoreService.registerUserDataSyncStore({ + id, + name, + read(key: string): Promise { + return proxy.$read(key); + }, + write(key: string, content: string, ref: string): Promise { + return proxy.$write(key, content, ref); + } + }); + } + + $deregisterUserDataProvider(): void { + this.userDataSyncStoreService.deregisterUserDataSyncStore(); + } + +} diff --git a/src/vs/workbench/api/browser/mainThreadWebview.ts b/src/vs/workbench/api/browser/mainThreadWebview.ts index f3f6f482c74f..c90189c2654d 100644 --- a/src/vs/workbench/api/browser/mainThreadWebview.ts +++ b/src/vs/workbench/api/browser/mainThreadWebview.ts @@ -8,39 +8,42 @@ import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { isWeb } from 'vs/base/common/platform'; import { startsWith } from 'vs/base/common/strings'; import { URI, UriComponents } from 'vs/base/common/uri'; +import { generateUuid } from 'vs/base/common/uuid'; import * as modes from 'vs/editor/common/modes'; import { localize } from 'vs/nls'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { IProductService } from 'vs/platform/product/common/product'; +import { IProductService } from 'vs/platform/product/common/productService'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { ExtHostContext, ExtHostWebviewsShape, IExtHostContext, MainContext, MainThreadWebviewsShape, WebviewPanelHandle, WebviewPanelShowOptions, WebviewPanelViewStateData } from 'vs/workbench/api/common/extHost.protocol'; import { editorGroupToViewColumn, EditorViewColumn, viewColumnToEditorGroup } from 'vs/workbench/api/common/shared/editor'; -import { WebviewEditorInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput'; +import { IEditorInput } from 'vs/workbench/common/editor'; +import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; +import { CustomFileEditorInput } from 'vs/workbench/contrib/customEditor/browser/customEditorInput'; +import { WebviewInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput'; import { ICreateWebViewShowOptions, IWebviewEditorService, WebviewInputOptions } from 'vs/workbench/contrib/webview/browser/webviewEditorService'; -import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; +import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { extHostNamedCustomer } from '../common/extHostCustomers'; -import { CustomFileEditorInput } from 'vs/workbench/contrib/customEditor/browser/customEditorInput'; /** * Bi-directional map between webview handles and inputs. */ class WebviewHandleStore { - private readonly _handlesToInputs = new Map(); - private readonly _inputsToHandles = new Map(); + private readonly _handlesToInputs = new Map(); + private readonly _inputsToHandles = new Map(); - public add(handle: string, input: WebviewEditorInput): void { + public add(handle: string, input: WebviewInput): void { this._handlesToInputs.set(handle, input); this._inputsToHandles.set(input, handle); } - public getHandleForInput(input: WebviewEditorInput): string | undefined { + public getHandleForInput(input: WebviewInput): string | undefined { return this._inputsToHandles.get(input); } - public getInputForHandle(handle: string): WebviewEditorInput | undefined { + public getInputForHandle(handle: string): WebviewInput | undefined { return this._handlesToInputs.get(handle); } @@ -68,8 +71,6 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews 'vscode-insider', ]); - private static revivalPool = 0; - private readonly _proxy: ExtHostWebviewsShape; private readonly _webviewEditorInputs = new WebviewHandleStore(); private readonly _revivers = new Map(); @@ -94,8 +95,8 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews // This reviver's only job is to activate webview panel extensions // This should trigger the real reviver to be registered from the extension host side. this._register(_webviewEditorService.registerResolver({ - canResolve: (webview: WebviewEditorInput) => { - if (!webview.webview.state && webview.getTypeId() === WebviewEditorInput.typeId) { // TODO: The typeid check is a workaround for the CustomFileEditorInput case + canResolve: (webview: WebviewInput) => { + if (!webview.webview.state && webview.getTypeId() === WebviewInput.typeId) { // TODO: The typeid check is a workaround for the CustomFileEditorInput case return false; } @@ -206,7 +207,7 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews return; } - const handle = `revival-${MainThreadWebviews.revivalPool++}`; + const handle = generateUuid(); this._webviewEditorInputs.add(handle, webviewEditorInput); this.hookupWebviewEventDelegate(handle, webviewEditorInput); @@ -246,13 +247,19 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews this._editorProviders.set(viewType, this._webviewEditorService.registerResolver({ canResolve: (webviewEditorInput) => { - return webviewEditorInput.getTypeId() !== WebviewEditorInput.typeId && webviewEditorInput.viewType === viewType; + return webviewEditorInput.getTypeId() !== WebviewInput.typeId && webviewEditorInput.viewType === viewType; }, resolveWebview: async (webview) => { - const handle = `resolved-${MainThreadWebviews.revivalPool++}`; + const handle = generateUuid(); this._webviewEditorInputs.add(handle, webview); this.hookupWebviewEventDelegate(handle, webview); + if (webview instanceof CustomFileEditorInput) { + webview.onWillSave(e => { + e.waitUntil(this._proxy.$save(handle)); + }); + } + try { await this._proxy.$resolveWebviewEditor( webview.getResource(), @@ -292,7 +299,7 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews return viewType.replace(/^mainThreadWebview-/, ''); } - private hookupWebviewEventDelegate(handle: WebviewPanelHandle, input: WebviewEditorInput) { + private hookupWebviewEventDelegate(handle: WebviewPanelHandle, input: WebviewInput) { input.webview.onDidClickLink((uri: URI) => this.onDidClickLink(handle, uri)); input.webview.onMessage((message: any) => this._proxy.$onMessage(handle, message)); input.onDispose(() => { @@ -317,21 +324,31 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews const activeInput = this._editorService.activeControl && this._editorService.activeControl.input; const viewStates: WebviewPanelViewStateData = {}; - for (const group of this._editorGroupService.groups) { - for (const input of group.editors) { - if (!(input instanceof WebviewEditorInput)) { - continue; - } - input.updateGroup(group.id); + const updateViewStatesForInput = (group: IEditorGroup, topLevelInput: IEditorInput, editorInput: IEditorInput) => { + if (!(editorInput instanceof WebviewInput)) { + return; + } - const handle = this._webviewEditorInputs.getHandleForInput(input); - if (handle) { - viewStates[handle] = { - visible: input === group.activeEditor, - active: input === activeInput, - position: editorGroupToViewColumn(this._editorGroupService, group.id), - }; + editorInput.updateGroup(group.id); + + const handle = this._webviewEditorInputs.getHandleForInput(editorInput); + if (handle) { + viewStates[handle] = { + visible: topLevelInput.matches(group.activeEditor), + active: topLevelInput.matches(activeInput), + position: editorGroupToViewColumn(this._editorGroupService, group.id), + }; + } + }; + + for (const group of this._editorGroupService.groups) { + for (const input of group.editors) { + if (input instanceof DiffEditorInput) { + updateViewStatesForInput(group, input, input.master); + updateViewStatesForInput(group, input, input.details); + } else { + updateViewStatesForInput(group, input, input); } } } @@ -348,7 +365,7 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews } } - private isSupportedLink(webview: WebviewEditorInput, link: URI): boolean { + private isSupportedLink(webview: WebviewInput, link: URI): boolean { if (MainThreadWebviews.standardSupportedLinkSchemes.has(link.scheme)) { return true; } @@ -358,7 +375,7 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews return !!webview.webview.contentOptions.enableCommandUris && link.scheme === 'command'; } - private getWebviewEditorInput(handle: WebviewPanelHandle): WebviewEditorInput { + private getWebviewEditorInput(handle: WebviewPanelHandle): WebviewInput { const webview = this.tryGetWebviewEditorInput(handle); if (!webview) { throw new Error('Unknown webview handle:' + handle); @@ -366,7 +383,7 @@ export class MainThreadWebviews extends Disposable implements MainThreadWebviews return webview; } - private tryGetWebviewEditorInput(handle: WebviewPanelHandle): WebviewEditorInput | undefined { + private tryGetWebviewEditorInput(handle: WebviewPanelHandle): WebviewInput | undefined { return this._webviewEditorInputs.getInputForHandle(handle); } diff --git a/src/vs/workbench/api/browser/mainThreadWorkspace.ts b/src/vs/workbench/api/browser/mainThreadWorkspace.ts index f21fa19000e8..31ec9b9e7a37 100644 --- a/src/vs/workbench/api/browser/mainThreadWorkspace.ts +++ b/src/vs/workbench/api/browser/mainThreadWorkspace.ts @@ -8,16 +8,14 @@ import { isPromiseCanceledError } from 'vs/base/common/errors'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { URI, UriComponents } from 'vs/base/common/uri'; import { localize } from 'vs/nls'; -import { CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; -import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { isNative } from 'vs/base/common/platform'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ILabelService } from 'vs/platform/label/common/label'; import { IFileMatch, IPatternInfo, ISearchProgressItem, ISearchService } from 'vs/workbench/services/search/common/search'; import { IWindowService } from 'vs/platform/windows/common/windows'; import { IWorkspaceContextService, WorkbenchState, IWorkspace } from 'vs/platform/workspace/common/workspace'; import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { ITextQueryBuilderOptions, QueryBuilder } from 'vs/workbench/contrib/search/common/queryBuilder'; -import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; import { ExtHostContext, ExtHostWorkspaceShape, IExtHostContext, MainContext, MainThreadWorkspaceShape, IWorkspaceData, ITextSearchComplete } from '../common/extHost.protocol'; @@ -25,6 +23,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { isEqualOrParent } from 'vs/base/common/resources'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { withNullAsUndefined } from 'vs/base/common/types'; +import { IFileService } from 'vs/platform/files/common/files'; @extHostNamedCustomer(MainContext.MainThreadWorkspace) export class MainThreadWorkspace implements MainThreadWorkspaceShape { @@ -44,10 +43,18 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape { @IWindowService private readonly _windowService: IWindowService, @IInstantiationService private readonly _instantiationService: IInstantiationService, @ILabelService private readonly _labelService: ILabelService, - @IEnvironmentService private readonly _environmentService: IEnvironmentService + @IEnvironmentService private readonly _environmentService: IEnvironmentService, + @IFileService fileService: IFileService ) { this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostWorkspace); - this._contextService.getCompleteWorkspace().then(workspace => this._proxy.$initializeWorkspace(this.getWorkspaceData(workspace))); + const workspace = this._contextService.getWorkspace(); + // The workspace file is provided be a unknown file system provider. It might come + // from the extension host. So initialize now knowing that `rootPath` is undefined. + if (workspace.configuration && !isNative && !fileService.canHandleResource(workspace.configuration)) { + this._proxy.$initializeWorkspace(this.getWorkspaceData(workspace)); + } else { + this._contextService.getCompleteWorkspace().then(workspace => this._proxy.$initializeWorkspace(this.getWorkspaceData(workspace))); + } this._contextService.onDidChangeWorkspaceFolders(this._onDidChangeWorkspace, this, this._toDispose); this._contextService.onDidChangeWorkbenchState(this._onDidChangeWorkspace, this, this._toDispose); } @@ -214,19 +221,3 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape { return this._windowService.resolveProxy(url); } } - -CommandsRegistry.registerCommand('_workbench.enterWorkspace', async function (accessor: ServicesAccessor, workspace: URI, disableExtensions: string[]) { - const workspaceEditingService = accessor.get(IWorkspaceEditingService); - const extensionService = accessor.get(IExtensionService); - const windowService = accessor.get(IWindowService); - - if (disableExtensions && disableExtensions.length) { - const runningExtensions = await extensionService.getExtensions(); - // If requested extension to disable is running, then reload window with given workspace - if (disableExtensions && runningExtensions.some(runningExtension => disableExtensions.some(id => ExtensionIdentifier.equals(runningExtension.identifier, id)))) { - return windowService.openWindow([{ workspaceUri: workspace }], { args: { _: [], 'disable-extension': disableExtensions } }); - } - } - - return workspaceEditingService.enterWorkspace(workspace); -}); diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index f7f103acc3f8..c2ae01b9e43a 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -15,7 +15,7 @@ import { OverviewRulerLane } from 'vs/editor/common/model'; import * as languageConfiguration from 'vs/editor/common/modes/languageConfiguration'; import { score } from 'vs/editor/common/modes/languageSelector'; import * as files from 'vs/platform/files/common/files'; -import { ExtHostContext, MainContext, ExtHostLogServiceShape } from 'vs/workbench/api/common/extHost.protocol'; +import { ExtHostContext, MainContext, ExtHostLogServiceShape, UIKind } from 'vs/workbench/api/common/extHost.protocol'; import { ExtHostApiCommands } from 'vs/workbench/api/common/extHostApiCommands'; import { ExtHostClipboard } from 'vs/workbench/api/common/extHostClipboard'; import { IExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; @@ -68,6 +68,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { IURITransformerService } from 'vs/workbench/api/common/extHostUriTransformerService'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; +import { ExtHostUserData } from 'vs/workbench/api/common/extHostUserData'; export interface IExtensionApiFactory { (extension: IExtensionDescription, registry: ExtensionDescriptionRegistry, configProvider: ExtHostConfigProvider): typeof vscode; @@ -124,6 +125,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const extHostWindow = rpcProtocol.set(ExtHostContext.ExtHostWindow, new ExtHostWindow(rpcProtocol)); const extHostProgress = rpcProtocol.set(ExtHostContext.ExtHostProgress, new ExtHostProgress(rpcProtocol.getProxy(MainContext.MainThreadProgress))); const extHostLabelService = rpcProtocol.set(ExtHostContext.ExtHosLabelService, new ExtHostLabelService(rpcProtocol)); + const extHostUserData = rpcProtocol.set(ExtHostContext.ExtHostUserData, new ExtHostUserData(rpcProtocol.getProxy(MainContext.MainThreadUserData), extHostLogService)); // Check that no named customers are missing // {{SQL CARBON EDIT}} filter out the services we don't expose @@ -252,6 +254,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I }, get remoteName() { return getRemoteName(initData.remote.authority); + }, + get uiKind() { + checkProposedApiEnabled(extension); + return initData.uiKind; } }; if (!initData.environment.extensionTestsLocationURI) { @@ -544,6 +550,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I }, createInputBox(): vscode.InputBox { return extHostQuickOpen.createInputBox(extension.identifier); + }, + registerUserDataSyncProvider: (name: string, userDataProvider: vscode.UserDataSyncProvider): vscode.Disposable => { + checkProposedApiEnabled(extension); + return extHostUserData.registerUserDataProvider(extension.identifier.value, name, userDataProvider); } }; @@ -904,7 +914,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I CallHierarchyIncomingCall: extHostTypes.CallHierarchyIncomingCall, CallHierarchyItem: extHostTypes.CallHierarchyItem, Decoration: extHostTypes.Decoration, + UserDataError: extHostTypes.UserDataError, WebviewEditorState: extHostTypes.WebviewEditorState, + UIKind: UIKind }; }; } diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 0d5ac6869ddd..252d53038ec2 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -46,6 +46,7 @@ import { ExtensionActivationError } from 'vs/workbench/services/extensions/commo import { createExtHostContextProxyIdentifier as createExtId, createMainContextProxyIdentifier as createMainId, IRPCProtocol } from 'vs/workbench/services/extensions/common/proxyIdentifier'; import * as search from 'vs/workbench/services/search/common/search'; import { SaveReason } from 'vs/workbench/services/textfile/common/textfiles'; +import { IUserData } from 'vs/platform/userDataSync/common/userDataSync'; // {{SQL CARBON EDIT}} import { ITreeItem as sqlITreeItem } from 'sql/workbench/common/views'; @@ -91,6 +92,7 @@ export interface IInitData { logsLocation: URI; autoStart: boolean; remote: { isRemote: boolean; authority: string | undefined; }; + uiKind: UIKind; } export interface IConfigurationInitData extends IConfigurationData { @@ -109,6 +111,11 @@ export interface IExtHostContext extends IRPCProtocol { export interface IMainContext extends IRPCProtocol { } +export enum UIKind { + Desktop = 1, + Web = 2 +} + // --- main thread export interface MainThreadClipboardShape extends IDisposable { @@ -143,6 +150,11 @@ export interface MainThreadConfigurationShape extends IDisposable { $removeConfigurationOption(target: ConfigurationTarget | null, key: string, resource: UriComponents | undefined): Promise; } +export interface MainThreadUserDataShape extends IDisposable { + $registerUserDataProvider(id: string, name: string): void; + $deregisterUserDataProvider(): void; +} + export interface MainThreadDiagnosticsShape extends IDisposable { $changeMany(owner: string, entries: [UriComponents, IMarkerData[] | undefined][]): void; $clear(owner: string): void; @@ -571,6 +583,7 @@ export interface ExtHostWebviewsShape { $onDidDisposeWebviewPanel(handle: WebviewPanelHandle): Promise; $deserializeWebviewPanel(newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, state: any, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise; $resolveWebviewEditor(resource: UriComponents, newWebviewHandle: WebviewPanelHandle, viewType: string, title: string, state: any, position: EditorViewColumn, options: modes.IWebviewOptions & modes.IWebviewPanelOptions): Promise; + $save(handle: WebviewPanelHandle): Promise; } export interface MainThreadUrlsShape extends IDisposable { @@ -749,6 +762,11 @@ export interface ExtHostConfigurationShape { $acceptConfigurationChanged(data: IConfigurationInitData, eventData: IWorkspaceConfigurationChangeEventData): void; } +export interface ExtHostUserDataShape { + $read(key: string): Promise; + $write(key: string, content: string, ref: string): Promise; +} + export interface ExtHostDiagnosticsShape { $acceptMarkersChange(data: [UriComponents, IMarkerData[]][]): void; } @@ -1191,6 +1209,7 @@ export interface ExtHostTaskShape { $OnDidEndTask(execution: tasks.TaskExecutionDTO): void; $resolveVariables(workspaceFolder: UriComponents, toResolve: { process?: { name: string; cwd?: string }, variables: string[] }): Promise<{ process?: string; variables: { [key: string]: string } }>; $getDefaultShellAndArgs(): Thenable<{ shell: string, args: string[] | string | undefined }>; + $jsonTasksSupported(): Thenable; } export interface IBreakpointDto { @@ -1321,6 +1340,7 @@ export const MainContext = { MainThreadCommands: createMainId('MainThreadCommands'), MainThreadComments: createMainId('MainThreadComments'), MainThreadConfiguration: createMainId('MainThreadConfiguration'), + MainThreadUserData: createMainId('MainThreadUserData'), MainThreadConsole: createMainId('MainThreadConsole'), MainThreadDebugService: createMainId('MainThreadDebugService'), MainThreadDecorations: createMainId('MainThreadDecorations'), @@ -1360,6 +1380,7 @@ export const MainContext = { export const ExtHostContext = { ExtHostCommands: createExtId('ExtHostCommands'), ExtHostConfiguration: createExtId('ExtHostConfiguration'), + ExtHostUserData: createExtId('ExtHostUserData'), ExtHostDiagnostics: createExtId('ExtHostDiagnostics'), ExtHostDebugService: createExtId('ExtHostDebugService'), ExtHostDecorations: createExtId('ExtHostDecorations'), diff --git a/src/vs/workbench/api/common/extHostTask.ts b/src/vs/workbench/api/common/extHostTask.ts index d0302877a0a9..8adc9c3b168c 100644 --- a/src/vs/workbench/api/common/extHostTask.ts +++ b/src/vs/workbench/api/common/extHostTask.ts @@ -3,12 +3,27 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event } from 'vs/base/common/event'; -import { ExtHostTaskShape } from 'vs/workbench/api/common/extHost.protocol'; +import { URI, UriComponents } from 'vs/base/common/uri'; +import * as Objects from 'vs/base/common/objects'; +import { asPromise } from 'vs/base/common/async'; +import { Event, Emitter } from 'vs/base/common/event'; + +import { MainContext, MainThreadTaskShape, ExtHostTaskShape } from 'vs/workbench/api/common/extHost.protocol'; + +import * as types from 'vs/workbench/api/common/extHostTypes'; +import { IExtHostWorkspaceProvider, IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; import * as vscode from 'vscode'; -import { TaskSystemInfoDTO } from '../common/shared/tasks'; +import * as tasks from '../common/shared/tasks'; +import { IExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; +import { IExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration'; +import { CancellationToken } from 'vs/base/common/cancellation'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; +import { IExtHostTerminalService } from 'vs/workbench/api/common/extHostTerminalService'; +import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; +import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { Schemas } from 'vs/base/common/network'; +import * as Platform from 'vs/base/common/platform'; export interface IExtHostTask extends ExtHostTaskShape { @@ -21,10 +36,703 @@ export interface IExtHostTask extends ExtHostTaskShape { onDidEndTaskProcess: Event; registerTaskProvider(extension: IExtensionDescription, type: string, provider: vscode.TaskProvider): vscode.Disposable; - registerTaskSystem(scheme: string, info: TaskSystemInfoDTO): void; + registerTaskSystem(scheme: string, info: tasks.TaskSystemInfoDTO): void; fetchTasks(filter?: vscode.TaskFilter): Promise; executeTask(extension: IExtensionDescription, task: vscode.Task): Promise; terminateTask(execution: vscode.TaskExecution): Promise; } +export namespace TaskDefinitionDTO { + export function from(value: vscode.TaskDefinition): tasks.TaskDefinitionDTO | undefined { + if (value === undefined || value === null) { + return undefined; + } + return value; + } + export function to(value: tasks.TaskDefinitionDTO): vscode.TaskDefinition | undefined { + if (value === undefined || value === null) { + return undefined; + } + return value; + } +} + +export namespace TaskPresentationOptionsDTO { + export function from(value: vscode.TaskPresentationOptions): tasks.TaskPresentationOptionsDTO | undefined { + if (value === undefined || value === null) { + return undefined; + } + return value; + } + export function to(value: tasks.TaskPresentationOptionsDTO): vscode.TaskPresentationOptions | undefined { + if (value === undefined || value === null) { + return undefined; + } + return value; + } +} + +export namespace ProcessExecutionOptionsDTO { + export function from(value: vscode.ProcessExecutionOptions): tasks.ProcessExecutionOptionsDTO | undefined { + if (value === undefined || value === null) { + return undefined; + } + return value; + } + export function to(value: tasks.ProcessExecutionOptionsDTO): vscode.ProcessExecutionOptions | undefined { + if (value === undefined || value === null) { + return undefined; + } + return value; + } +} + +export namespace ProcessExecutionDTO { + export function is(value: tasks.ShellExecutionDTO | tasks.ProcessExecutionDTO | tasks.CustomExecution2DTO | undefined): value is tasks.ProcessExecutionDTO { + if (value) { + const candidate = value as tasks.ProcessExecutionDTO; + return candidate && !!candidate.process; + } else { + return false; + } + } + export function from(value: vscode.ProcessExecution): tasks.ProcessExecutionDTO | undefined { + if (value === undefined || value === null) { + return undefined; + } + const result: tasks.ProcessExecutionDTO = { + process: value.process, + args: value.args + }; + if (value.options) { + result.options = ProcessExecutionOptionsDTO.from(value.options); + } + return result; + } + export function to(value: tasks.ProcessExecutionDTO): types.ProcessExecution | undefined { + if (value === undefined || value === null) { + return undefined; + } + return new types.ProcessExecution(value.process, value.args, value.options); + } +} + +export namespace ShellExecutionOptionsDTO { + export function from(value: vscode.ShellExecutionOptions): tasks.ShellExecutionOptionsDTO | undefined { + if (value === undefined || value === null) { + return undefined; + } + return value; + } + export function to(value: tasks.ShellExecutionOptionsDTO): vscode.ShellExecutionOptions | undefined { + if (value === undefined || value === null) { + return undefined; + } + return value; + } +} + +export namespace ShellExecutionDTO { + export function is(value: tasks.ShellExecutionDTO | tasks.ProcessExecutionDTO | tasks.CustomExecution2DTO | undefined): value is tasks.ShellExecutionDTO { + if (value) { + const candidate = value as tasks.ShellExecutionDTO; + return candidate && (!!candidate.commandLine || !!candidate.command); + } else { + return false; + } + } + export function from(value: vscode.ShellExecution): tasks.ShellExecutionDTO | undefined { + if (value === undefined || value === null) { + return undefined; + } + const result: tasks.ShellExecutionDTO = { + }; + if (value.commandLine !== undefined) { + result.commandLine = value.commandLine; + } else { + result.command = value.command; + result.args = value.args; + } + if (value.options) { + result.options = ShellExecutionOptionsDTO.from(value.options); + } + return result; + } + export function to(value: tasks.ShellExecutionDTO): types.ShellExecution | undefined { + if (value === undefined || value === null || (value.command === undefined && value.commandLine === undefined)) { + return undefined; + } + if (value.commandLine) { + return new types.ShellExecution(value.commandLine, value.options); + } else { + return new types.ShellExecution(value.command!, value.args ? value.args : [], value.options); + } + } +} + +export namespace CustomExecution2DTO { + export function is(value: tasks.ShellExecutionDTO | tasks.ProcessExecutionDTO | tasks.CustomExecution2DTO | undefined): value is tasks.CustomExecution2DTO { + if (value) { + let candidate = value as tasks.CustomExecution2DTO; + return candidate && candidate.customExecution === 'customExecution2'; + } else { + return false; + } + } + + export function from(value: vscode.CustomExecution2): tasks.CustomExecution2DTO { + return { + customExecution: 'customExecution2' + }; + } +} + + +export namespace TaskHandleDTO { + export function from(value: types.Task): tasks.TaskHandleDTO { + let folder: UriComponents | undefined; + if (value.scope !== undefined && typeof value.scope !== 'number') { + folder = value.scope.uri; + } + return { + id: value._id!, + workspaceFolder: folder! + }; + } +} + +export namespace TaskDTO { + export function fromMany(tasks: vscode.Task[], extension: IExtensionDescription): tasks.TaskDTO[] { + if (tasks === undefined || tasks === null) { + return []; + } + const result: tasks.TaskDTO[] = []; + for (let task of tasks) { + const converted = from(task, extension); + if (converted) { + result.push(converted); + } + } + return result; + } + + export function from(value: vscode.Task, extension: IExtensionDescription): tasks.TaskDTO | undefined { + if (value === undefined || value === null) { + return undefined; + } + let execution: tasks.ShellExecutionDTO | tasks.ProcessExecutionDTO | tasks.CustomExecution2DTO | undefined; + if (value.execution instanceof types.ProcessExecution) { + execution = ProcessExecutionDTO.from(value.execution); + } else if (value.execution instanceof types.ShellExecution) { + execution = ShellExecutionDTO.from(value.execution); + } else if ((value).execution2 && (value).execution2 instanceof types.CustomExecution2) { + execution = CustomExecution2DTO.from((value).execution2); + } + + const definition: tasks.TaskDefinitionDTO | undefined = TaskDefinitionDTO.from(value.definition); + let scope: number | UriComponents; + if (value.scope) { + if (typeof value.scope === 'number') { + scope = value.scope; + } else { + scope = value.scope.uri; + } + } else { + // To continue to support the deprecated task constructor that doesn't take a scope, we must add a scope here: + scope = types.TaskScope.Workspace; + } + if (!definition || !scope) { + return undefined; + } + const group = (value.group as types.TaskGroup) ? (value.group as types.TaskGroup).id : undefined; + const result: tasks.TaskDTO = { + _id: (value as types.Task)._id!, + definition, + name: value.name, + source: { + extensionId: extension.identifier.value, + label: value.source, + scope: scope + }, + execution: execution!, + isBackground: value.isBackground, + group: group, + presentationOptions: TaskPresentationOptionsDTO.from(value.presentationOptions), + problemMatchers: value.problemMatchers, + hasDefinedMatchers: (value as types.Task).hasDefinedMatchers, + runOptions: (value).runOptions ? (value).runOptions : { reevaluateOnRerun: true }, + }; + return result; + } + export async function to(value: tasks.TaskDTO | undefined, workspace: IExtHostWorkspaceProvider): Promise { + if (value === undefined || value === null) { + return undefined; + } + let execution: types.ShellExecution | types.ProcessExecution | undefined; + if (ProcessExecutionDTO.is(value.execution)) { + execution = ProcessExecutionDTO.to(value.execution); + } else if (ShellExecutionDTO.is(value.execution)) { + execution = ShellExecutionDTO.to(value.execution); + } + const definition: vscode.TaskDefinition | undefined = TaskDefinitionDTO.to(value.definition); + let scope: vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder | undefined; + if (value.source) { + if (value.source.scope !== undefined) { + if (typeof value.source.scope === 'number') { + scope = value.source.scope; + } else { + scope = await workspace.resolveWorkspaceFolder(URI.revive(value.source.scope)); + } + } else { + scope = types.TaskScope.Workspace; + } + } + if (!definition || !scope) { + return undefined; + } + const result = new types.Task(definition, scope, value.name!, value.source.label, execution, value.problemMatchers); + if (value.isBackground !== undefined) { + result.isBackground = value.isBackground; + } + if (value.group !== undefined) { + result.group = types.TaskGroup.from(value.group); + } + if (value.presentationOptions) { + result.presentationOptions = TaskPresentationOptionsDTO.to(value.presentationOptions)!; + } + if (value._id) { + result._id = value._id; + } + return result; + } +} + +export namespace TaskFilterDTO { + export function from(value: vscode.TaskFilter | undefined): tasks.TaskFilterDTO | undefined { + return value; + } + + export function to(value: tasks.TaskFilterDTO): vscode.TaskFilter | undefined { + if (!value) { + return undefined; + } + return Objects.assign(Object.create(null), value); + } +} + +class TaskExecutionImpl implements vscode.TaskExecution { + + constructor(private readonly _tasks: ExtHostTaskBase, readonly _id: string, private readonly _task: vscode.Task) { + } + + public get task(): vscode.Task { + return this._task; + } + + public terminate(): void { + this._tasks.terminateTask(this); + } + + public fireDidStartProcess(value: tasks.TaskProcessStartedDTO): void { + } + + public fireDidEndProcess(value: tasks.TaskProcessEndedDTO): void { + } +} + +export namespace TaskExecutionDTO { + export async function to(value: tasks.TaskExecutionDTO, tasks: ExtHostTaskBase, workspaceProvider: IExtHostWorkspaceProvider): Promise { + const task = await TaskDTO.to(value.task, workspaceProvider); + if (!task) { + throw new Error('Unexpected: Task cannot be created.'); + } + return new TaskExecutionImpl(tasks, value.id, task); + } + export function from(value: vscode.TaskExecution): tasks.TaskExecutionDTO { + return { + id: (value as TaskExecutionImpl)._id, + task: undefined + }; + } +} + +export interface HandlerData { + type: string; + provider: vscode.TaskProvider; + extension: IExtensionDescription; +} + +export abstract class ExtHostTaskBase implements ExtHostTaskShape { + readonly _serviceBrand: undefined; + + protected readonly _proxy: MainThreadTaskShape; + protected readonly _workspaceProvider: IExtHostWorkspaceProvider; + protected readonly _editorService: IExtHostDocumentsAndEditors; + protected readonly _configurationService: IExtHostConfiguration; + protected readonly _terminalService: IExtHostTerminalService; + protected _handleCounter: number; + protected _handlers: Map; + protected _taskExecutions: Map; + protected _providedCustomExecutions2: Map; + private _notProvidedCustomExecutions: Set; // Used for custom executions tasks that are created and run through executeTask. + protected _activeCustomExecutions2: Map; + private _lastStartedTask: string | undefined; + protected readonly _onDidExecuteTask: Emitter = new Emitter(); + protected readonly _onDidTerminateTask: Emitter = new Emitter(); + + protected readonly _onDidTaskProcessStarted: Emitter = new Emitter(); + protected readonly _onDidTaskProcessEnded: Emitter = new Emitter(); + + constructor( + @IExtHostRpcService extHostRpc: IExtHostRpcService, + @IExtHostInitDataService initData: IExtHostInitDataService, + @IExtHostWorkspace workspaceService: IExtHostWorkspace, + @IExtHostDocumentsAndEditors editorService: IExtHostDocumentsAndEditors, + @IExtHostConfiguration configurationService: IExtHostConfiguration, + @IExtHostTerminalService extHostTerminalService: IExtHostTerminalService + ) { + this._proxy = extHostRpc.getProxy(MainContext.MainThreadTask); + this._workspaceProvider = workspaceService; + this._editorService = editorService; + this._configurationService = configurationService; + this._terminalService = extHostTerminalService; + this._handleCounter = 0; + this._handlers = new Map(); + this._taskExecutions = new Map(); + this._providedCustomExecutions2 = new Map(); + this._notProvidedCustomExecutions = new Set(); + this._activeCustomExecutions2 = new Map(); + } + + public registerTaskProvider(extension: IExtensionDescription, type: string, provider: vscode.TaskProvider): vscode.Disposable { + if (!provider) { + return new types.Disposable(() => { }); + } + const handle = this.nextHandle(); + this._handlers.set(handle, { type, provider, extension }); + this._proxy.$registerTaskProvider(handle, type); + return new types.Disposable(() => { + this._handlers.delete(handle); + this._proxy.$unregisterTaskProvider(handle); + }); + } + + public registerTaskSystem(scheme: string, info: tasks.TaskSystemInfoDTO): void { + this._proxy.$registerTaskSystem(scheme, info); + } + + public fetchTasks(filter?: vscode.TaskFilter): Promise { + return this._proxy.$fetchTasks(TaskFilterDTO.from(filter)).then(async (values) => { + const result: vscode.Task[] = []; + for (let value of values) { + const task = await TaskDTO.to(value, this._workspaceProvider); + if (task) { + result.push(task); + } + } + return result; + }); + } + + public abstract async executeTask(extension: IExtensionDescription, task: vscode.Task): Promise; + + public get taskExecutions(): vscode.TaskExecution[] { + const result: vscode.TaskExecution[] = []; + this._taskExecutions.forEach(value => result.push(value)); + return result; + } + + public terminateTask(execution: vscode.TaskExecution): Promise { + if (!(execution instanceof TaskExecutionImpl)) { + throw new Error('No valid task execution provided'); + } + return this._proxy.$terminateTask((execution as TaskExecutionImpl)._id); + } + + public get onDidStartTask(): Event { + return this._onDidExecuteTask.event; + } + + public async $onDidStartTask(execution: tasks.TaskExecutionDTO, terminalId: number): Promise { + const execution2: vscode.CustomExecution2 | undefined = this._providedCustomExecutions2.get(execution.id); + if (execution2) { + if (this._activeCustomExecutions2.get(execution.id) !== undefined) { + throw new Error('We should not be trying to start the same custom task executions twice.'); + } + + // Clone the custom execution to keep the original untouched. This is important for multiple runs of the same task. + this._activeCustomExecutions2.set(execution.id, execution2); + this._terminalService.attachPtyToTerminal(terminalId, await execution2.callback()); + } + this._lastStartedTask = execution.id; + + this._onDidExecuteTask.fire({ + execution: await this.getTaskExecution(execution) + }); + } + + public get onDidEndTask(): Event { + return this._onDidTerminateTask.event; + } + + public async $OnDidEndTask(execution: tasks.TaskExecutionDTO): Promise { + const _execution = await this.getTaskExecution(execution); + this._taskExecutions.delete(execution.id); + this.customExecutionComplete(execution); + this._onDidTerminateTask.fire({ + execution: _execution + }); + } + + public get onDidStartTaskProcess(): Event { + return this._onDidTaskProcessStarted.event; + } + + public async $onDidStartTaskProcess(value: tasks.TaskProcessStartedDTO): Promise { + const execution = await this.getTaskExecution(value.id); + if (execution) { + this._onDidTaskProcessStarted.fire({ + execution: execution, + processId: value.processId + }); + } + } + + public get onDidEndTaskProcess(): Event { + return this._onDidTaskProcessEnded.event; + } + + public async $onDidEndTaskProcess(value: tasks.TaskProcessEndedDTO): Promise { + const execution = await this.getTaskExecution(value.id); + if (execution) { + this._onDidTaskProcessEnded.fire({ + execution: execution, + exitCode: value.exitCode + }); + } + } + + protected abstract provideTasksInternal(validTypes: { [key: string]: boolean; }, taskIdPromises: Promise[], handler: HandlerData, value: vscode.Task[] | null | undefined): { tasks: tasks.TaskDTO[], extension: IExtensionDescription }; + + public $provideTasks(handle: number, validTypes: { [key: string]: boolean; }): Thenable { + const handler = this._handlers.get(handle); + if (!handler) { + return Promise.reject(new Error('no handler found')); + } + + // Set up a list of task ID promises that we can wait on + // before returning the provided tasks. The ensures that + // our task IDs are calculated for any custom execution tasks. + // Knowing this ID ahead of time is needed because when a task + // start event is fired this is when the custom execution is called. + // The task start event is also the first time we see the ID from the main + // thread, which is too late for us because we need to save an map + // from an ID to the custom execution function. (Kind of a cart before the horse problem). + const taskIdPromises: Promise[] = []; + const fetchPromise = asPromise(() => handler.provider.provideTasks(CancellationToken.None)).then(value => { + return this.provideTasksInternal(validTypes, taskIdPromises, handler, value); + }); + + return new Promise((resolve) => { + fetchPromise.then((result) => { + Promise.all(taskIdPromises).then(() => { + resolve(result); + }); + }); + }); + } + + protected abstract async resolveTaskInternal(resolvedTaskDTO: tasks.TaskDTO): Promise; + + public async $resolveTask(handle: number, taskDTO: tasks.TaskDTO): Promise { + const handler = this._handlers.get(handle); + if (!handler) { + return Promise.reject(new Error('no handler found')); + } + + if (taskDTO.definition.type !== handler.type) { + throw new Error(`Unexpected: Task of type [${taskDTO.definition.type}] cannot be resolved by provider of type [${handler.type}].`); + } + + const task = await TaskDTO.to(taskDTO, this._workspaceProvider); + if (!task) { + throw new Error('Unexpected: Task cannot be resolved.'); + } + + const resolvedTask = await handler.provider.resolveTask(task, CancellationToken.None); + if (!resolvedTask) { + return undefined; // {{SQL CARBON EDIT}} strict-null-check + } + + const resolvedTaskDTO: tasks.TaskDTO | undefined = TaskDTO.from(resolvedTask, handler.extension); + if (!resolvedTaskDTO) { + throw new Error('Unexpected: Task cannot be resolved.'); + } + + if (resolvedTask.definition !== task.definition) { + throw new Error('Unexpected: The resolved task definition must be the same object as the original task definition. The task definition cannot be changed.'); + } + + if (CustomExecution2DTO.is(resolvedTaskDTO.execution)) { + await this.addCustomExecution2(resolvedTaskDTO, resolvedTask, true); + } + + return await this.resolveTaskInternal(resolvedTaskDTO); + } + + public abstract async $resolveVariables(uriComponents: UriComponents, toResolve: { process?: { name: string; cwd?: string; path?: string }, variables: string[] }): Promise<{ process?: string, variables: { [key: string]: string; } }>; + + public abstract $getDefaultShellAndArgs(): Promise<{ shell: string, args: string[] | string | undefined }>; + + private nextHandle(): number { + return this._handleCounter++; + } + + protected async addCustomExecution2(taskDTO: tasks.TaskDTO, task: vscode.Task2, isProvided: boolean): Promise { + const taskId = await this._proxy.$createTaskId(taskDTO); + if (!isProvided && !this._providedCustomExecutions2.has(taskId)) { + this._notProvidedCustomExecutions.add(taskId); + } + this._providedCustomExecutions2.set(taskId, (task).execution2); + } + + protected async getTaskExecution(execution: tasks.TaskExecutionDTO | string, task?: vscode.Task): Promise { + if (typeof execution === 'string') { + const taskExecution = this._taskExecutions.get(execution); + if (!taskExecution) { + throw new Error('Unexpected: The specified task is missing an execution'); + } + return taskExecution; + } + + let result: TaskExecutionImpl | undefined = this._taskExecutions.get(execution.id); + if (result) { + return result; + } + const taskToCreate = task ? task : await TaskDTO.to(execution.task, this._workspaceProvider); + if (!taskToCreate) { + throw new Error('Unexpected: Task does not exist.'); + } + const createdResult: TaskExecutionImpl = new TaskExecutionImpl(this, execution.id, taskToCreate); + this._taskExecutions.set(execution.id, createdResult); + return createdResult; + } + + private customExecutionComplete(execution: tasks.TaskExecutionDTO): void { + const extensionCallback2: vscode.CustomExecution2 | undefined = this._activeCustomExecutions2.get(execution.id); + if (extensionCallback2) { + this._activeCustomExecutions2.delete(execution.id); + } + + // Technically we don't really need to do this, however, if an extension + // is executing a task through "executeTask" over and over again + // with different properties in the task definition, then the map of executions + // could grow indefinitely, something we don't want. + if (this._notProvidedCustomExecutions.has(execution.id) && (this._lastStartedTask !== execution.id)) { + this._providedCustomExecutions2.delete(execution.id); + this._notProvidedCustomExecutions.delete(execution.id); + } + let iterator = this._notProvidedCustomExecutions.values(); + let iteratorResult = iterator.next(); + while (!iteratorResult.done) { + if (!this._activeCustomExecutions2.has(iteratorResult.value) && (this._lastStartedTask !== iteratorResult.value)) { + this._providedCustomExecutions2.delete(iteratorResult.value); + this._notProvidedCustomExecutions.delete(iteratorResult.value); + } + iteratorResult = iterator.next(); + } + } + + public abstract async $jsonTasksSupported(): Promise; +} + +export class WorkerExtHostTask extends ExtHostTaskBase { + constructor( + @IExtHostRpcService extHostRpc: IExtHostRpcService, + @IExtHostInitDataService initData: IExtHostInitDataService, + @IExtHostWorkspace workspaceService: IExtHostWorkspace, + @IExtHostDocumentsAndEditors editorService: IExtHostDocumentsAndEditors, + @IExtHostConfiguration configurationService: IExtHostConfiguration, + @IExtHostTerminalService extHostTerminalService: IExtHostTerminalService + ) { + super(extHostRpc, initData, workspaceService, editorService, configurationService, extHostTerminalService); + if (initData.remote.isRemote && initData.remote.authority) { + this.registerTaskSystem(Schemas.vscodeRemote, { + scheme: Schemas.vscodeRemote, + authority: initData.remote.authority, + platform: Platform.PlatformToString(Platform.Platform.Web) + }); + } + } + + public async executeTask(extension: IExtensionDescription, task: vscode.Task): Promise { + const dto = TaskDTO.from(task, extension); + if (dto === undefined) { + return Promise.reject(new Error('Task is not valid')); + } + + // If this task is a custom execution, then we need to save it away + // in the provided custom execution map that is cleaned up after the + // task is executed. + if (CustomExecution2DTO.is(dto.execution)) { + await this.addCustomExecution2(dto, task, false); + } else { + throw new Error('Not implemented'); + } + + return this._proxy.$executeTask(dto).then(value => this.getTaskExecution(value, task)); + } + + protected provideTasksInternal(validTypes: { [key: string]: boolean; }, taskIdPromises: Promise[], handler: HandlerData, value: vscode.Task[] | null | undefined): { tasks: tasks.TaskDTO[], extension: IExtensionDescription } { + const taskDTOs: tasks.TaskDTO[] = []; + if (value) { + for (let task of value) { + if (!task.definition || !validTypes[task.definition.type]) { + console.warn(`The task [${task.source}, ${task.name}] uses an undefined task type. The task will be ignored in the future.`); + } + + const taskDTO: tasks.TaskDTO | undefined = TaskDTO.from(task, handler.extension); + if (taskDTO && CustomExecution2DTO.is(taskDTO.execution)) { + taskDTOs.push(taskDTO); + // The ID is calculated on the main thread task side, so, let's call into it here. + // We need the task id's pre-computed for custom task executions because when OnDidStartTask + // is invoked, we have to be able to map it back to our data. + taskIdPromises.push(this.addCustomExecution2(taskDTO, task, true)); + } else { + console.warn('Only custom execution tasks supported.'); + } + } + } + return { + tasks: taskDTOs, + extension: handler.extension + }; + } + + protected async resolveTaskInternal(resolvedTaskDTO: tasks.TaskDTO): Promise { + if (CustomExecution2DTO.is(resolvedTaskDTO.execution)) { + return resolvedTaskDTO; + } else { + console.warn('Only custom execution tasks supported.'); + } + return undefined; + } + + public async $resolveVariables(uriComponents: UriComponents, toResolve: { process?: { name: string; cwd?: string; path?: string }, variables: string[] }): Promise<{ process?: string, variables: { [key: string]: string; } }> { + const result = { + process: undefined as string, + variables: Object.create(null) + }; + return result; + } + + public $getDefaultShellAndArgs(): Promise<{ shell: string, args: string[] | string | undefined }> { + throw new Error('Not implemented'); + } + + public async $jsonTasksSupported(): Promise { + return false; + } +} + export const IExtHostTask = createDecorator('IExtHostTask'); diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index a05b4c48c637..7ab4bdae4f5e 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -14,6 +14,7 @@ import { generateUuid } from 'vs/base/common/uuid'; import * as vscode from 'vscode'; import { FileSystemProviderErrorCode, markAsFileSystemProviderError } from 'vs/platform/files/common/files'; import { RemoteAuthorityResolverErrorCode } from 'vs/platform/remote/common/remoteAuthorityResolver'; +import { UserDataSyncStoreErrorCode, markAsUserDataSyncStoreError } from 'vs/platform/userDataSync/common/userDataSync'; function es5ClassCompat(target: Function): any { ///@ts-ignore @@ -2383,6 +2384,28 @@ export class Decoration { bubble?: boolean; } +@es5ClassCompat +export class UserDataError extends Error { + + static Rejected(message?: string): UserDataError { + return new UserDataError(message, UserDataSyncStoreErrorCode.Rejected); + } + + constructor(message?: string, code: UserDataSyncStoreErrorCode = UserDataSyncStoreErrorCode.Unknown) { + super(message); + + // mark the error as user data provider error so that + // we can extract the error code on the receiving side + markAsUserDataSyncStoreError(this, code); + + // workaround when extending builtin objects and when compiling to ES5, see: + // https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work + if (typeof (Object).setPrototypeOf === 'function') { + (Object).setPrototypeOf(this, UserDataError.prototype); + } + } +} + export enum WebviewEditorState { Readonly = 1, Unchanged = 2, diff --git a/src/vs/workbench/api/common/extHostUserData.ts b/src/vs/workbench/api/common/extHostUserData.ts new file mode 100644 index 000000000000..dc605b1b2431 --- /dev/null +++ b/src/vs/workbench/api/common/extHostUserData.ts @@ -0,0 +1,48 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ExtHostUserDataShape, MainThreadUserDataShape } from './extHost.protocol'; +import * as vscode from 'vscode'; +import { toDisposable, Disposable } from 'vs/base/common/lifecycle'; +import { ILogService } from 'vs/platform/log/common/log'; +import { IUserData } from 'vs/platform/userDataSync/common/userDataSync'; + +export class ExtHostUserData implements ExtHostUserDataShape { + + private name: string | null = null; + private userDataProvider: vscode.UserDataSyncProvider | null = null; + + constructor( + private readonly proxy: MainThreadUserDataShape, + private readonly logService: ILogService, + ) { + } + + registerUserDataProvider(id: string, name: string, userDataProvider: vscode.UserDataSyncProvider): vscode.Disposable { + if (this.userDataProvider) { + this.logService.warn(`A user data provider '${this.name}' already exists hence ignoring the remote user data provider '${name}'.`); + return Disposable.None; + } + this.userDataProvider = userDataProvider; + this.name = name; + this.proxy.$registerUserDataProvider(id, name); + return toDisposable(() => this.proxy.$deregisterUserDataProvider()); + } + + $read(key: string): Promise { + if (!this.userDataProvider) { + throw new Error('No remote user data provider exists.'); + } + return this.userDataProvider.read(key); + } + + $write(key: string, content: string, ref: string): Promise { + if (!this.userDataProvider) { + throw new Error('No remote user data provider exists.'); + } + return this.userDataProvider.write(key, content, ref); + } + +} diff --git a/src/vs/workbench/api/common/extHostWebview.ts b/src/vs/workbench/api/common/extHostWebview.ts index e9a66e62a9f3..a3d167b31204 100644 --- a/src/vs/workbench/api/common/extHostWebview.ts +++ b/src/vs/workbench/api/common/extHostWebview.ts @@ -41,8 +41,7 @@ export class ExtHostWebview implements vscode.Webview { public get cspSource(): string { return this._initData.webviewCspSource - .replace('{{uuid}}', this._handle) - .replace('{{commit}}', this._initData.commit || '211fa02efe8c041fd7baa8ec3dce199d5185aa44'); + .replace('{{uuid}}', this._handle); } public get html(): string { @@ -224,6 +223,18 @@ export class ExtHostWebviewEditor implements vscode.WebviewEditor { this._proxy.$setState(this._handle, typeConverters.WebviewEditorState.from(newState)); } + private readonly _onWillSave = new Emitter<{ waitUntil: (thenable: Thenable) => void }>(); + public readonly onWillSave = this._onWillSave.event; + + async _save(): Promise { + const waitingOn: Thenable[] = []; + this._onWillSave.fire({ + waitUntil: (thenable: Thenable): void => { waitingOn.push(thenable); }, + }); + const result = await Promise.all(waitingOn); + return result.every(x => x); + } + public postMessage(message: any): Promise { this.assertNotDisposed(); return this._proxy.$postMessage(this._handle, message); @@ -422,6 +433,13 @@ export class ExtHostWebviews implements ExtHostWebviewsShape { return Promise.resolve(provider.resolveWebviewEditor(URI.revive(resource), revivedPanel)); } + async $save(handle: WebviewPanelHandle): Promise { + const panel = this.getWebviewPanel(handle); + if (panel) { + return panel._save(); + } + return false; + } } function convertWebviewOptions( diff --git a/src/vs/workbench/api/common/shared/webview.ts b/src/vs/workbench/api/common/shared/webview.ts index 4f61c3c04abb..9837cdfc15f4 100644 --- a/src/vs/workbench/api/common/shared/webview.ts +++ b/src/vs/workbench/api/common/shared/webview.ts @@ -7,7 +7,6 @@ import { URI } from 'vs/base/common/uri'; import * as vscode from 'vscode'; export interface WebviewInitData { - readonly commit?: string; readonly webviewResourceRoot: string; readonly webviewCspSource: string; } @@ -18,7 +17,6 @@ export function asWebviewUri( resource: vscode.Uri, ): vscode.Uri { const uri = initData.webviewResourceRoot - .replace('{{commit}}', initData.commit || '211fa02efe8c041fd7baa8ec3dce199d5185aa44') .replace('{{resource}}', resource.toString().replace(/^\S+?:/, '')) .replace('{{uuid}}', uuid); return URI.parse(uri); diff --git a/src/vs/workbench/api/node/extHostTask.ts b/src/vs/workbench/api/node/extHostTask.ts index 3c327050ba6e..07c6082d599f 100644 --- a/src/vs/workbench/api/node/extHostTask.ts +++ b/src/vs/workbench/api/node/extHostTask.ts @@ -6,375 +6,23 @@ import * as path from 'vs/base/common/path'; import { URI, UriComponents } from 'vs/base/common/uri'; -import * as Objects from 'vs/base/common/objects'; -import { asPromise } from 'vs/base/common/async'; -import { Event, Emitter } from 'vs/base/common/event'; import { win32 } from 'vs/base/node/processes'; - - -import { MainContext, MainThreadTaskShape, ExtHostTaskShape } from 'vs/workbench/api/common/extHost.protocol'; - import * as types from 'vs/workbench/api/common/extHostTypes'; -import { IExtHostWorkspaceProvider, IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; +import { IExtHostWorkspace } from 'vs/workbench/api/common/extHostWorkspace'; import * as vscode from 'vscode'; -import { - TaskDefinitionDTO, TaskExecutionDTO, TaskPresentationOptionsDTO, - ProcessExecutionOptionsDTO, ProcessExecutionDTO, - ShellExecutionOptionsDTO, ShellExecutionDTO, - CustomExecution2DTO, - TaskDTO, TaskHandleDTO, TaskFilterDTO, TaskProcessStartedDTO, TaskProcessEndedDTO, TaskSystemInfoDTO, TaskSetDTO -} from '../common/shared/tasks'; +import * as tasks from '../common/shared/tasks'; import { ExtHostVariableResolverService } from 'vs/workbench/api/node/extHostDebugService'; import { IExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; import { IExtHostConfiguration } from 'vs/workbench/api/common/extHostConfiguration'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; -import { CancellationToken } from 'vs/base/common/cancellation'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { IExtHostTerminalService } from 'vs/workbench/api/common/extHostTerminalService'; import { IExtHostRpcService } from 'vs/workbench/api/common/extHostRpcService'; import { IExtHostInitDataService } from 'vs/workbench/api/common/extHostInitDataService'; +import { ExtHostTaskBase, TaskHandleDTO, TaskDTO, CustomExecution2DTO, HandlerData } from 'vs/workbench/api/common/extHostTask'; import { Schemas } from 'vs/base/common/network'; -namespace TaskDefinitionDTO { - export function from(value: vscode.TaskDefinition): TaskDefinitionDTO | undefined { - if (value === undefined || value === null) { - return undefined; - } - return value; - } - export function to(value: TaskDefinitionDTO): vscode.TaskDefinition | undefined { - if (value === undefined || value === null) { - return undefined; - } - return value; - } -} - -namespace TaskPresentationOptionsDTO { - export function from(value: vscode.TaskPresentationOptions): TaskPresentationOptionsDTO | undefined { - if (value === undefined || value === null) { - return undefined; - } - return value; - } - export function to(value: TaskPresentationOptionsDTO): vscode.TaskPresentationOptions | undefined { - if (value === undefined || value === null) { - return undefined; - } - return value; - } -} - -namespace ProcessExecutionOptionsDTO { - export function from(value: vscode.ProcessExecutionOptions): ProcessExecutionOptionsDTO | undefined { - if (value === undefined || value === null) { - return undefined; - } - return value; - } - export function to(value: ProcessExecutionOptionsDTO): vscode.ProcessExecutionOptions | undefined { - if (value === undefined || value === null) { - return undefined; - } - return value; - } -} - -namespace ProcessExecutionDTO { - export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecution2DTO | undefined): value is ProcessExecutionDTO { - if (value) { - const candidate = value as ProcessExecutionDTO; - return candidate && !!candidate.process; - } else { - return false; - } - } - export function from(value: vscode.ProcessExecution): ProcessExecutionDTO | undefined { - if (value === undefined || value === null) { - return undefined; - } - const result: ProcessExecutionDTO = { - process: value.process, - args: value.args - }; - if (value.options) { - result.options = ProcessExecutionOptionsDTO.from(value.options); - } - return result; - } - export function to(value: ProcessExecutionDTO): types.ProcessExecution | undefined { - if (value === undefined || value === null) { - return undefined; - } - return new types.ProcessExecution(value.process, value.args, value.options); - } -} - -namespace ShellExecutionOptionsDTO { - export function from(value: vscode.ShellExecutionOptions): ShellExecutionOptionsDTO | undefined { - if (value === undefined || value === null) { - return undefined; - } - return value; - } - export function to(value: ShellExecutionOptionsDTO): vscode.ShellExecutionOptions | undefined { - if (value === undefined || value === null) { - return undefined; - } - return value; - } -} - -namespace ShellExecutionDTO { - export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecution2DTO | undefined): value is ShellExecutionDTO { - if (value) { - const candidate = value as ShellExecutionDTO; - return candidate && (!!candidate.commandLine || !!candidate.command); - } else { - return false; - } - } - export function from(value: vscode.ShellExecution): ShellExecutionDTO | undefined { - if (value === undefined || value === null) { - return undefined; - } - const result: ShellExecutionDTO = { - }; - if (value.commandLine !== undefined) { - result.commandLine = value.commandLine; - } else { - result.command = value.command; - result.args = value.args; - } - if (value.options) { - result.options = ShellExecutionOptionsDTO.from(value.options); - } - return result; - } - export function to(value: ShellExecutionDTO): types.ShellExecution | undefined { - if (value === undefined || value === null || (value.command === undefined && value.commandLine === undefined)) { - return undefined; - } - if (value.commandLine) { - return new types.ShellExecution(value.commandLine, value.options); - } else { - return new types.ShellExecution(value.command!, value.args ? value.args : [], value.options); - } - } -} - -namespace CustomExecution2DTO { - export function is(value: ShellExecutionDTO | ProcessExecutionDTO | CustomExecution2DTO | undefined): value is CustomExecution2DTO { - if (value) { - let candidate = value as CustomExecution2DTO; - return candidate && candidate.customExecution === 'customExecution2'; - } else { - return false; - } - } - - export function from(value: vscode.CustomExecution2): CustomExecution2DTO { - return { - customExecution: 'customExecution2' - }; - } -} - -namespace TaskHandleDTO { - export function from(value: types.Task): TaskHandleDTO { - let folder: UriComponents | undefined; - if (value.scope !== undefined && typeof value.scope !== 'number') { - folder = value.scope.uri; - } - return { - id: value._id!, - workspaceFolder: folder! - }; - } -} - -namespace TaskDTO { - - export function fromMany(tasks: vscode.Task[], extension: IExtensionDescription): TaskDTO[] { - if (tasks === undefined || tasks === null) { - return []; - } - const result: TaskDTO[] = []; - for (let task of tasks) { - const converted = from(task, extension); - if (converted) { - result.push(converted); - } - } - return result; - } - - export function from(value: vscode.Task, extension: IExtensionDescription): TaskDTO | undefined { - if (value === undefined || value === null) { - return undefined; - } - let execution: ShellExecutionDTO | ProcessExecutionDTO | CustomExecution2DTO | undefined; - if (value.execution instanceof types.ProcessExecution) { - execution = ProcessExecutionDTO.from(value.execution); - } else if (value.execution instanceof types.ShellExecution) { - execution = ShellExecutionDTO.from(value.execution); - } else if ((value).execution2 && (value).execution2 instanceof types.CustomExecution2) { - execution = CustomExecution2DTO.from((value).execution2); - } - - const definition: TaskDefinitionDTO | undefined = TaskDefinitionDTO.from(value.definition); - let scope: number | UriComponents; - if (value.scope) { - if (typeof value.scope === 'number') { - scope = value.scope; - } else { - scope = value.scope.uri; - } - } else { - // To continue to support the deprecated task constructor that doesn't take a scope, we must add a scope here: - scope = types.TaskScope.Workspace; - } - if (!definition || !scope) { - return undefined; - } - const group = (value.group as types.TaskGroup) ? (value.group as types.TaskGroup).id : undefined; - const result: TaskDTO = { - _id: (value as types.Task)._id!, - definition, - name: value.name, - source: { - extensionId: extension.identifier.value, - label: value.source, - scope: scope - }, - execution: execution!, - isBackground: value.isBackground, - group: group, - presentationOptions: TaskPresentationOptionsDTO.from(value.presentationOptions), - problemMatchers: value.problemMatchers, - hasDefinedMatchers: (value as types.Task).hasDefinedMatchers, - runOptions: (value).runOptions ? (value).runOptions : { reevaluateOnRerun: true }, - }; - return result; - } - export async function to(value: TaskDTO | undefined, workspace: IExtHostWorkspaceProvider): Promise { - if (value === undefined || value === null) { - return undefined; - } - let execution: types.ShellExecution | types.ProcessExecution | undefined; - if (ProcessExecutionDTO.is(value.execution)) { - execution = ProcessExecutionDTO.to(value.execution); - } else if (ShellExecutionDTO.is(value.execution)) { - execution = ShellExecutionDTO.to(value.execution); - } - const definition: vscode.TaskDefinition | undefined = TaskDefinitionDTO.to(value.definition); - let scope: vscode.TaskScope.Global | vscode.TaskScope.Workspace | vscode.WorkspaceFolder | undefined; - if (value.source) { - if (value.source.scope !== undefined) { - if (typeof value.source.scope === 'number') { - scope = value.source.scope; - } else { - scope = await workspace.resolveWorkspaceFolder(URI.revive(value.source.scope)); - } - } else { - scope = types.TaskScope.Workspace; - } - } - if (!definition || !scope) { - return undefined; - } - const result = new types.Task(definition, scope, value.name!, value.source.label, execution, value.problemMatchers); - if (value.isBackground !== undefined) { - result.isBackground = value.isBackground; - } - if (value.group !== undefined) { - result.group = types.TaskGroup.from(value.group); - } - if (value.presentationOptions) { - result.presentationOptions = TaskPresentationOptionsDTO.to(value.presentationOptions)!; - } - if (value._id) { - result._id = value._id; - } - return result; - } -} - -namespace TaskFilterDTO { - export function from(value: vscode.TaskFilter | undefined): TaskFilterDTO | undefined { - return value; - } - - export function to(value: TaskFilterDTO): vscode.TaskFilter | undefined { - if (!value) { - return undefined; - } - return Objects.assign(Object.create(null), value); - } -} - -class TaskExecutionImpl implements vscode.TaskExecution { - - constructor(private readonly _tasks: ExtHostTask, readonly _id: string, private readonly _task: vscode.Task) { - } - - public get task(): vscode.Task { - return this._task; - } - - public terminate(): void { - this._tasks.terminateTask(this); - } - - public fireDidStartProcess(value: TaskProcessStartedDTO): void { - } - - public fireDidEndProcess(value: TaskProcessEndedDTO): void { - } -} - -namespace TaskExecutionDTO { - export async function to(value: TaskExecutionDTO, tasks: ExtHostTask, workspaceProvider: IExtHostWorkspaceProvider): Promise { - const task = await TaskDTO.to(value.task, workspaceProvider); - if (!task) { - throw new Error('Unexpected: Task cannot be created.'); - } - return new TaskExecutionImpl(tasks, value.id, task); - } - export function from(value: vscode.TaskExecution): TaskExecutionDTO { - return { - id: (value as TaskExecutionImpl)._id, - task: undefined - }; - } -} - -interface HandlerData { - type: string; - provider: vscode.TaskProvider; - extension: IExtensionDescription; -} - -export class ExtHostTask implements ExtHostTaskShape { - - readonly _serviceBrand: undefined; - - private readonly _proxy: MainThreadTaskShape; - private readonly _workspaceProvider: IExtHostWorkspaceProvider; - private readonly _editorService: IExtHostDocumentsAndEditors; - private readonly _configurationService: IExtHostConfiguration; - private readonly _terminalService: IExtHostTerminalService; - private _handleCounter: number; - private _handlers: Map; - private _taskExecutions: Map; - private _providedCustomExecutions2: Map; - private _activeCustomExecutions2: Map; - - private readonly _onDidExecuteTask: Emitter = new Emitter(); - private readonly _onDidTerminateTask: Emitter = new Emitter(); - - private readonly _onDidTaskProcessStarted: Emitter = new Emitter(); - private readonly _onDidTaskProcessEnded: Emitter = new Emitter(); +export class ExtHostTask extends ExtHostTaskBase { constructor( @IExtHostRpcService extHostRpc: IExtHostRpcService, @@ -384,17 +32,7 @@ export class ExtHostTask implements ExtHostTaskShape { @IExtHostConfiguration configurationService: IExtHostConfiguration, @IExtHostTerminalService extHostTerminalService: IExtHostTerminalService ) { - this._proxy = extHostRpc.getProxy(MainContext.MainThreadTask); - this._workspaceProvider = workspaceService; - this._editorService = editorService; - this._configurationService = configurationService; - this._terminalService = extHostTerminalService; - this._handleCounter = 0; - this._handlers = new Map(); - this._taskExecutions = new Map(); - this._providedCustomExecutions2 = new Map(); - this._activeCustomExecutions2 = new Map(); - + super(extHostRpc, initData, workspaceService, editorService, configurationService, extHostTerminalService); if (initData.remote.isRemote && initData.remote.authority) { this.registerTaskSystem(Schemas.vscodeRemote, { scheme: Schemas.vscodeRemote, @@ -404,36 +42,6 @@ export class ExtHostTask implements ExtHostTaskShape { } } - public registerTaskProvider(extension: IExtensionDescription, type: string, provider: vscode.TaskProvider): vscode.Disposable { - if (!provider) { - return new types.Disposable(() => { }); - } - const handle = this.nextHandle(); - this._handlers.set(handle, { type, provider, extension }); - this._proxy.$registerTaskProvider(handle, type); - return new types.Disposable(() => { - this._handlers.delete(handle); - this._proxy.$unregisterTaskProvider(handle); - }); - } - - public registerTaskSystem(scheme: string, info: TaskSystemInfoDTO): void { - this._proxy.$registerTaskSystem(scheme, info); - } - - public fetchTasks(filter?: vscode.TaskFilter): Promise { - return this._proxy.$fetchTasks(TaskFilterDTO.from(filter)).then(async (values) => { - const result: vscode.Task[] = []; - for (let value of values) { - const task = await TaskDTO.to(value, this._workspaceProvider); - if (task) { - result.push(task); - } - } - return result; - }); - } - public async executeTask(extension: IExtensionDescription, task: vscode.Task): Promise { const tTask = (task as types.Task); // We have a preserved ID. So the task didn't change. @@ -449,175 +57,42 @@ export class ExtHostTask implements ExtHostTaskShape { // in the provided custom execution map that is cleaned up after the // task is executed. if (CustomExecution2DTO.is(dto.execution)) { - await this.addCustomExecution2(dto, task); + await this.addCustomExecution2(dto, task, false); } return this._proxy.$executeTask(dto).then(value => this.getTaskExecution(value, task)); } } - public get taskExecutions(): vscode.TaskExecution[] { - const result: vscode.TaskExecution[] = []; - this._taskExecutions.forEach(value => result.push(value)); - return result; - } - - public terminateTask(execution: vscode.TaskExecution): Promise { - if (!(execution instanceof TaskExecutionImpl)) { - throw new Error('No valid task execution provided'); - } - return this._proxy.$terminateTask((execution as TaskExecutionImpl)._id); - } - - public get onDidStartTask(): Event { - return this._onDidExecuteTask.event; - } - - public async $onDidStartTask(execution: TaskExecutionDTO, terminalId: number): Promise { - const execution2: vscode.CustomExecution2 | undefined = this._providedCustomExecutions2.get(execution.id); - if (execution2) { - if (this._activeCustomExecutions2.get(execution.id) !== undefined) { - throw new Error('We should not be trying to start the same custom task executions twice.'); - } - - // Clone the custom execution to keep the original untouched. This is important for multiple runs of the same task. - this._activeCustomExecutions2.set(execution.id, execution2); - this._terminalService.attachPtyToTerminal(terminalId, await execution2.callback()); - } - - this._onDidExecuteTask.fire({ - execution: await this.getTaskExecution(execution) - }); - } - - public get onDidEndTask(): Event { - return this._onDidTerminateTask.event; - } - - public async $OnDidEndTask(execution: TaskExecutionDTO): Promise { - const _execution = await this.getTaskExecution(execution); - this._taskExecutions.delete(execution.id); - this.customExecutionComplete(execution); - this._onDidTerminateTask.fire({ - execution: _execution - }); - } - - public get onDidStartTaskProcess(): Event { - return this._onDidTaskProcessStarted.event; - } - - public async $onDidStartTaskProcess(value: TaskProcessStartedDTO): Promise { - const execution = await this.getTaskExecution(value.id); - if (execution) { - this._onDidTaskProcessStarted.fire({ - execution: execution, - processId: value.processId - }); - } - } - - public get onDidEndTaskProcess(): Event { - return this._onDidTaskProcessEnded.event; - } - - public async $onDidEndTaskProcess(value: TaskProcessEndedDTO): Promise { - const execution = await this.getTaskExecution(value.id); - if (execution) { - this._onDidTaskProcessEnded.fire({ - execution: execution, - exitCode: value.exitCode - }); - } - } - - public $provideTasks(handle: number, validTypes: { [key: string]: boolean; }): Thenable { - const handler = this._handlers.get(handle); - if (!handler) { - return Promise.reject(new Error('no handler found')); - } - - // Set up a list of task ID promises that we can wait on - // before returning the provided tasks. The ensures that - // our task IDs are calculated for any custom execution tasks. - // Knowing this ID ahead of time is needed because when a task - // start event is fired this is when the custom execution is called. - // The task start event is also the first time we see the ID from the main - // thread, which is too late for us because we need to save an map - // from an ID to the custom execution function. (Kind of a cart before the horse problem). - const taskIdPromises: Promise[] = []; - const fetchPromise = asPromise(() => handler.provider.provideTasks(CancellationToken.None)).then(value => { - const taskDTOs: TaskDTO[] = []; - if (value) { - for (let task of value) { - if (!task.definition || !validTypes[task.definition.type]) { - console.warn(`The task [${task.source}, ${task.name}] uses an undefined task type. The task will be ignored in the future.`); - } + protected provideTasksInternal(validTypes: { [key: string]: boolean; }, taskIdPromises: Promise[], handler: HandlerData, value: vscode.Task[] | null | undefined): { tasks: tasks.TaskDTO[], extension: IExtensionDescription } { + const taskDTOs: tasks.TaskDTO[] = []; + if (value) { + for (let task of value) { + if (!task.definition || !validTypes[task.definition.type]) { + console.warn(`The task [${task.source}, ${task.name}] uses an undefined task type. The task will be ignored in the future.`); + } - const taskDTO: TaskDTO | undefined = TaskDTO.from(task, handler.extension); - if (taskDTO) { - taskDTOs.push(taskDTO); + const taskDTO: tasks.TaskDTO | undefined = TaskDTO.from(task, handler.extension); + if (taskDTO) { + taskDTOs.push(taskDTO); - if (CustomExecution2DTO.is(taskDTO.execution)) { - // The ID is calculated on the main thread task side, so, let's call into it here. - // We need the task id's pre-computed for custom task executions because when OnDidStartTask - // is invoked, we have to be able to map it back to our data. - taskIdPromises.push(this.addCustomExecution2(taskDTO, task)); - } + if (CustomExecution2DTO.is(taskDTO.execution)) { + // The ID is calculated on the main thread task side, so, let's call into it here. + // We need the task id's pre-computed for custom task executions because when OnDidStartTask + // is invoked, we have to be able to map it back to our data. + taskIdPromises.push(this.addCustomExecution2(taskDTO, task, true)); } } } - return { - tasks: taskDTOs, - extension: handler.extension - }; - }); - - return new Promise((resolve) => { - fetchPromise.then((result) => { - Promise.all(taskIdPromises).then(() => { - resolve(result); - }); - }); - }); - } - - // {{SQL CARBON EDIT}} disable debug related method - public async $resolveTask(handle: number, taskDTO: TaskDTO): Promise { - /*const handler = this._handlers.get(handle); - if (!handler) { - return Promise.reject(new Error('no handler found')); - } - - if (taskDTO.definition.type !== handler.type) { - throw new Error(`Unexpected: Task of type [${taskDTO.definition.type}] cannot be resolved by provider of type [${handler.type}].`); - } - - const task = await TaskDTO.to(taskDTO, this._workspaceProvider); - if (!task) { - throw new Error('Unexpected: Task cannot be resolved.'); - } - - const resolvedTask = await handler.provider.resolveTask(task, CancellationToken.None); - if (!resolvedTask) { - return; - } - - const resolvedTaskDTO: TaskDTO | undefined = TaskDTO.from(resolvedTask, handler.extension); - if (!resolvedTaskDTO) { - throw new Error('Unexpected: Task cannot be resolved.'); - } - - if (resolvedTask.definition !== task.definition) { - throw new Error('Unexpected: The resolved task definition must be the same object as the original task definition. The task definition cannot be changed.'); - } - - if (CustomExecution2DTO.is(resolvedTaskDTO.execution)) { - await this.addCustomExecution2(resolvedTaskDTO, resolvedTask); } + return { + tasks: taskDTOs, + extension: handler.extension + }; + } - return resolvedTaskDTO;*/ - return undefined; + protected async resolveTaskInternal(resolvedTaskDTO: tasks.TaskDTO): Promise { + return resolvedTaskDTO; } public async $resolveVariables(uriComponents: UriComponents, toResolve: { process?: { name: string; cwd?: string; path?: string }, variables: string[] }): Promise<{ process?: string, variables: { [key: string]: string; } }> { @@ -666,53 +141,7 @@ export class ExtHostTask implements ExtHostTaskShape { return this._terminalService.$requestDefaultShellAndArgs(true); } - private nextHandle(): number { - return this._handleCounter++; - } - - private async addCustomExecution2(taskDTO: TaskDTO, task: vscode.Task2): Promise { - const taskId = await this._proxy.$createTaskId(taskDTO); - this._providedCustomExecutions2.set(taskId, (task).execution2); - } - - private async getTaskExecution(execution: TaskExecutionDTO | string, task?: vscode.Task): Promise { - if (typeof execution === 'string') { - const taskExecution = this._taskExecutions.get(execution); - if (!taskExecution) { - throw new Error('Unexpected: The specified task is missing an execution'); - } - return taskExecution; - } - - let result: TaskExecutionImpl | undefined = this._taskExecutions.get(execution.id); - if (result) { - return result; - } - const taskToCreate = task ? task : await TaskDTO.to(execution.task, this._workspaceProvider); - if (!taskToCreate) { - throw new Error('Unexpected: Task does not exist.'); - } - const createdResult: TaskExecutionImpl = new TaskExecutionImpl(this, execution.id, taskToCreate); - this._taskExecutions.set(execution.id, createdResult); - return createdResult; - } - - private customExecutionComplete(execution: TaskExecutionDTO): void { - const extensionCallback2: vscode.CustomExecution2 | undefined = this._activeCustomExecutions2.get(execution.id); - if (extensionCallback2) { - this._activeCustomExecutions2.delete(execution.id); - } - - const lastCustomExecution = this._providedCustomExecutions2.get(execution.id); - // Technically we don't really need to do this, however, if an extension - // is executing a task through "executeTask" over and over again - // with different properties in the task definition, then this list - // could grow indefinitely, something we don't want. - this._providedCustomExecutions2.clear(); - // We do still need to hang on to the last custom execution so that the - // Rerun Task command doesn't choke when it tries to rerun a custom execution - if (lastCustomExecution) { - this._providedCustomExecutions2.set(execution.id, lastCustomExecution); - } + public async $jsonTasksSupported(): Promise { + return true; } } diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index 7c0ac3512891..dd0e36a8de35 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as vscode from 'vscode'; -import pkg from 'vs/platform/product/node/package'; +import product from 'vs/platform/product/common/product'; import * as os from 'os'; import { URI, UriComponents } from 'vs/base/common/uri'; import * as platform from 'vs/base/common/platform'; @@ -181,7 +181,7 @@ export class ExtHostTerminalService extends BaseExtHostTerminalService { envFromConfig, this._variableResolver, isWorkspaceShellAllowed, - pkg.version, + product.version, terminalConfig.get<'auto' | 'off' | 'on'>('detectLocale', 'auto'), baseEnv ); diff --git a/src/vs/workbench/browser/actions/developerActions.ts b/src/vs/workbench/browser/actions/developerActions.ts index a9b1a8a35842..48469cbcc3f4 100644 --- a/src/vs/workbench/browser/actions/developerActions.ts +++ b/src/vs/workbench/browser/actions/developerActions.ts @@ -27,7 +27,7 @@ import { clamp } from 'vs/base/common/numbers'; import { KeyCode } from 'vs/base/common/keyCodes'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; -export class InspectContextKeysAction extends Action { +class InspectContextKeysAction extends Action { static readonly ID = 'workbench.action.inspectContextKeys'; static LABEL = nls.localize('inspect context keys', "Inspect Context Keys"); @@ -91,7 +91,7 @@ export class InspectContextKeysAction extends Action { } } -export class ToggleScreencastModeAction extends Action { +class ToggleScreencastModeAction extends Action { static readonly ID = 'workbench.action.toggleScreencastMode'; static LABEL = nls.localize('toggle screencast mode', "Toggle Screencast Mode"); @@ -195,7 +195,7 @@ export class ToggleScreencastModeAction extends Action { } } -export class LogStorageAction extends Action { +class LogStorageAction extends Action { static readonly ID = 'workbench.action.logStorage'; static LABEL = nls.localize({ key: 'logStorage', comment: ['A developer only action to log the contents of the storage for the current window.'] }, "Log Storage Database Contents"); @@ -224,8 +224,6 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(InspectContextKeysActi registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleScreencastModeAction, ToggleScreencastModeAction.ID, ToggleScreencastModeAction.LABEL), 'Developer: Toggle Screencast Mode', developerCategory); registry.registerWorkbenchAction(new SyncActionDescriptor(LogStorageAction, LogStorageAction.ID, LogStorageAction.LABEL), 'Developer: Log Storage Database Contents', developerCategory); -// --- Menu Registration - // Screencast Mode const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); configurationRegistry.registerConfiguration({ diff --git a/src/vs/workbench/browser/actions/helpActions.ts b/src/vs/workbench/browser/actions/helpActions.ts new file mode 100644 index 000000000000..7c901cfb84b6 --- /dev/null +++ b/src/vs/workbench/browser/actions/helpActions.ts @@ -0,0 +1,388 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Action } from 'vs/base/common/actions'; +import * as nls from 'vs/nls'; +import product from 'vs/platform/product/common/product'; +import { isMacintosh, isLinux, language } from 'vs/base/common/platform'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { URI } from 'vs/base/common/uri'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; +import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; +import { KeyChord, KeyMod, KeyCode } from 'vs/base/common/keyCodes'; +import { IProductService } from 'vs/platform/product/common/productService'; + +class KeybindingsReferenceAction extends Action { + + static readonly ID = 'workbench.action.keybindingsReference'; + static readonly LABEL = nls.localize('keybindingsReference', "Keyboard Shortcuts Reference"); + static readonly AVAILABLE = !!(isLinux ? product.keyboardShortcutsUrlLinux : isMacintosh ? product.keyboardShortcutsUrlMac : product.keyboardShortcutsUrlWin); + + constructor( + id: string, + label: string, + @IOpenerService private readonly openerService: IOpenerService, + @IProductService private readonly productService: IProductService + ) { + super(id, label); + } + + run(): Promise { + const url = isLinux ? this.productService.keyboardShortcutsUrlLinux : isMacintosh ? this.productService.keyboardShortcutsUrlMac : this.productService.keyboardShortcutsUrlWin; + if (url) { + this.openerService.open(URI.parse(url)); + } + + return Promise.resolve(); + } +} + +class OpenDocumentationUrlAction extends Action { + + static readonly ID = 'workbench.action.openDocumentationUrl'; + static readonly LABEL = nls.localize('openDocumentationUrl', "Documentation"); + static readonly AVAILABLE = !!product.documentationUrl; + + constructor( + id: string, + label: string, + @IOpenerService private readonly openerService: IOpenerService, + @IProductService private readonly productService: IProductService + ) { + super(id, label); + } + + run(): Promise { + if (this.productService.documentationUrl) { + this.openerService.open(URI.parse(this.productService.documentationUrl)); + } + + return Promise.resolve(); + } +} + +class OpenIntroductoryVideosUrlAction extends Action { + + static readonly ID = 'workbench.action.openIntroductoryVideosUrl'; + static readonly LABEL = nls.localize('openIntroductoryVideosUrl', "Introductory Videos"); + static readonly AVAILABLE = !!product.introductoryVideosUrl; + + constructor( + id: string, + label: string, + @IOpenerService private readonly openerService: IOpenerService, + @IProductService private readonly productService: IProductService + ) { + super(id, label); + } + + run(): Promise { + if (this.productService.introductoryVideosUrl) { + this.openerService.open(URI.parse(this.productService.introductoryVideosUrl)); + } + + return Promise.resolve(); + } +} + +class OpenTipsAndTricksUrlAction extends Action { + + static readonly ID = 'workbench.action.openTipsAndTricksUrl'; + static readonly LABEL = nls.localize('openTipsAndTricksUrl', "Tips and Tricks"); + static readonly AVAILABLE = !!product.tipsAndTricksUrl; + + constructor( + id: string, + label: string, + @IOpenerService private readonly openerService: IOpenerService, + @IProductService private readonly productService: IProductService + ) { + super(id, label); + } + + run(): Promise { + if (this.productService.tipsAndTricksUrl) { + this.openerService.open(URI.parse(this.productService.tipsAndTricksUrl)); + } + + return Promise.resolve(); + } +} + +class OpenNewsletterSignupUrlAction extends Action { + + static readonly ID = 'workbench.action.openNewsletterSignupUrl'; + static readonly LABEL = nls.localize('newsletterSignup', "Signup for the VS Code Newsletter"); + static readonly AVAILABLE = !!product.newsletterSignupUrl; + + constructor( + id: string, + label: string, + @IOpenerService private readonly openerService: IOpenerService, + @ITelemetryService private readonly telemetryService: ITelemetryService, + @IProductService private readonly productService: IProductService + ) { + super(id, label); + } + + async run(): Promise { + const info = await this.telemetryService.getTelemetryInfo(); + + this.openerService.open(URI.parse(`${this.productService.newsletterSignupUrl}?machineId=${encodeURIComponent(info.machineId)}`)); + } +} + +class OpenTwitterUrlAction extends Action { + + static readonly ID = 'workbench.action.openTwitterUrl'; + static readonly LABEL = nls.localize('openTwitterUrl', "Join Us on Twitter"); + static readonly AVAILABLE = !!product.twitterUrl; + + constructor( + id: string, + label: string, + @IOpenerService private readonly openerService: IOpenerService, + @IProductService private readonly productService: IProductService + ) { + super(id, label); + } + + run(): Promise { + if (this.productService.twitterUrl) { + this.openerService.open(URI.parse(this.productService.twitterUrl)); + } + + return Promise.resolve(); + } +} + +class OpenRequestFeatureUrlAction extends Action { + + static readonly ID = 'workbench.action.openRequestFeatureUrl'; + static readonly LABEL = nls.localize('openUserVoiceUrl', "Search Feature Requests"); + static readonly AVAILABLE = !!product.requestFeatureUrl; + + constructor( + id: string, + label: string, + @IOpenerService private readonly openerService: IOpenerService, + @IProductService private readonly productService: IProductService + ) { + super(id, label); + } + + run(): Promise { + if (this.productService.requestFeatureUrl) { + this.openerService.open(URI.parse(this.productService.requestFeatureUrl)); + } + + return Promise.resolve(); + } +} + +class OpenLicenseUrlAction extends Action { + + static readonly ID = 'workbench.action.openLicenseUrl'; + static readonly LABEL = nls.localize('openLicenseUrl', "View License"); + static readonly AVAILABLE = !!product.licenseUrl; + + constructor( + id: string, + label: string, + @IOpenerService private readonly openerService: IOpenerService, + @IProductService private readonly productService: IProductService + ) { + super(id, label); + } + + run(): Promise { + if (this.productService.licenseUrl) { + if (language) { + const queryArgChar = this.productService.licenseUrl.indexOf('?') > 0 ? '&' : '?'; + this.openerService.open(URI.parse(`${this.productService.licenseUrl}${queryArgChar}lang=${language}`)); + } else { + this.openerService.open(URI.parse(this.productService.licenseUrl)); + } + } + + return Promise.resolve(); + } +} + +class OpenPrivacyStatementUrlAction extends Action { + + static readonly ID = 'workbench.action.openPrivacyStatementUrl'; + static readonly LABEL = nls.localize('openPrivacyStatement', "Privacy Statement"); + static readonly AVAILABE = !!product.privacyStatementUrl; + + constructor( + id: string, + label: string, + @IOpenerService private readonly openerService: IOpenerService, + @IProductService private readonly productService: IProductService + ) { + super(id, label); + } + + run(): Promise { + if (this.productService.privacyStatementUrl) { + if (language) { + const queryArgChar = this.productService.privacyStatementUrl.indexOf('?') > 0 ? '&' : '?'; + this.openerService.open(URI.parse(`${this.productService.privacyStatementUrl}${queryArgChar}lang=${language}`)); + } else { + this.openerService.open(URI.parse(this.productService.privacyStatementUrl)); + } + } + + return Promise.resolve(); + } +} + +// --- Actions Registration + +const registry = Registry.as(Extensions.WorkbenchActions); +const helpCategory = nls.localize('help', "Help"); + +if (KeybindingsReferenceAction.AVAILABLE) { + registry.registerWorkbenchAction(new SyncActionDescriptor(KeybindingsReferenceAction, KeybindingsReferenceAction.ID, KeybindingsReferenceAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_R) }), 'Help: Keyboard Shortcuts Reference', helpCategory); +} + +if (OpenDocumentationUrlAction.AVAILABLE) { + registry.registerWorkbenchAction(new SyncActionDescriptor(OpenDocumentationUrlAction, OpenDocumentationUrlAction.ID, OpenDocumentationUrlAction.LABEL), 'Help: Documentation', helpCategory); +} + +if (OpenIntroductoryVideosUrlAction.AVAILABLE) { + registry.registerWorkbenchAction(new SyncActionDescriptor(OpenIntroductoryVideosUrlAction, OpenIntroductoryVideosUrlAction.ID, OpenIntroductoryVideosUrlAction.LABEL), 'Help: Introductory Videos', helpCategory); +} + +if (OpenTipsAndTricksUrlAction.AVAILABLE) { + registry.registerWorkbenchAction(new SyncActionDescriptor(OpenTipsAndTricksUrlAction, OpenTipsAndTricksUrlAction.ID, OpenTipsAndTricksUrlAction.LABEL), 'Help: Tips and Tricks', helpCategory); +} + +if (OpenNewsletterSignupUrlAction.AVAILABLE) { + registry.registerWorkbenchAction(new SyncActionDescriptor(OpenNewsletterSignupUrlAction, OpenNewsletterSignupUrlAction.ID, OpenNewsletterSignupUrlAction.LABEL), 'Help: Tips and Tricks', helpCategory); +} + +if (OpenTwitterUrlAction.AVAILABLE) { + registry.registerWorkbenchAction(new SyncActionDescriptor(OpenTwitterUrlAction, OpenTwitterUrlAction.ID, OpenTwitterUrlAction.LABEL), 'Help: Join Us on Twitter', helpCategory); +} + +if (OpenRequestFeatureUrlAction.AVAILABLE) { + registry.registerWorkbenchAction(new SyncActionDescriptor(OpenRequestFeatureUrlAction, OpenRequestFeatureUrlAction.ID, OpenRequestFeatureUrlAction.LABEL), 'Help: Search Feature Requests', helpCategory); +} + +if (OpenLicenseUrlAction.AVAILABLE) { + registry.registerWorkbenchAction(new SyncActionDescriptor(OpenLicenseUrlAction, OpenLicenseUrlAction.ID, OpenLicenseUrlAction.LABEL), 'Help: View License', helpCategory); +} + +if (OpenPrivacyStatementUrlAction.AVAILABE) { + registry.registerWorkbenchAction(new SyncActionDescriptor(OpenPrivacyStatementUrlAction, OpenPrivacyStatementUrlAction.ID, OpenPrivacyStatementUrlAction.LABEL), 'Help: Privacy Statement', helpCategory); +} + +// --- Menu Registration + +// Help + +if (OpenDocumentationUrlAction.AVAILABLE) { + MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { + group: '1_welcome', + command: { + id: OpenDocumentationUrlAction.ID, + title: nls.localize({ key: 'miDocumentation', comment: ['&& denotes a mnemonic'] }, "&&Documentation") + }, + order: 3 + }); +} +/* // {{SQL CARBON EDIT}} - Disable unused menu item +MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { + group: '1_welcome', + command: { + id: 'update.showCurrentReleaseNotes', + title: nls.localize({ key: 'miReleaseNotes', comment: ['&& denotes a mnemonic'] }, "&&Release Notes") + }, + order: 4 +}); + +// Reference +if (KeybindingsReferenceAction.AVAILABLE) { + MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { + group: '2_reference', + command: { + id: KeybindingsReferenceAction.ID, + title: nls.localize({ key: 'miKeyboardShortcuts', comment: ['&& denotes a mnemonic'] }, "&&Keyboard Shortcuts Reference") + }, + order: 1 + }); +} + +if (OpenIntroductoryVideosUrlAction.AVAILABLE) { + MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { + group: '2_reference', + command: { + id: OpenIntroductoryVideosUrlAction.ID, + title: nls.localize({ key: 'miIntroductoryVideos', comment: ['&& denotes a mnemonic'] }, "Introductory &&Videos") + }, + order: 2 + }); +} + +if (OpenTipsAndTricksUrlAction.AVAILABLE) { + MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { + group: '2_reference', + command: { + id: OpenTipsAndTricksUrlAction.ID, + title: nls.localize({ key: 'miTipsAndTricks', comment: ['&& denotes a mnemonic'] }, "Tips and Tri&&cks") + }, + order: 3 + }); +} + +// Feedback +if (OpenTwitterUrlAction.AVAILABLE) { + MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { + group: '3_feedback', + command: { + id: OpenTwitterUrlAction.ID, + title: nls.localize({ key: 'miTwitter', comment: ['&& denotes a mnemonic'] }, "&&Join Us on Twitter") + }, + order: 1 + }); +} + +if (OpenRequestFeatureUrlAction.AVAILABLE) { + MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { + group: '3_feedback', + command: { + id: OpenRequestFeatureUrlAction.ID, + title: nls.localize({ key: 'miUserVoice', comment: ['&& denotes a mnemonic'] }, "&&Search Feature Requests") + }, + order: 2 + }); +}*/ + +// Legal +if (OpenLicenseUrlAction.AVAILABLE) { + MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { + group: '4_legal', + command: { + id: OpenLicenseUrlAction.ID, + title: nls.localize({ key: 'miLicense', comment: ['&& denotes a mnemonic'] }, "View &&License") + }, + order: 1 + }); +} + +if (OpenPrivacyStatementUrlAction.AVAILABE) { + MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { + group: '4_legal', + command: { + id: OpenPrivacyStatementUrlAction.ID, + title: nls.localize({ key: 'miPrivacyStatement', comment: ['&& denotes a mnemonic'] }, "Privac&&y Statement") + }, + order: 2 + }); +} diff --git a/src/vs/workbench/browser/actions/layoutActions.ts b/src/vs/workbench/browser/actions/layoutActions.ts index 014d6e8a8a7c..dc7545096841 100644 --- a/src/vs/workbench/browser/actions/layoutActions.ts +++ b/src/vs/workbench/browser/actions/layoutActions.ts @@ -12,7 +12,6 @@ import { SyncActionDescriptor, MenuId, MenuRegistry } from 'vs/platform/actions/ import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { IWorkbenchLayoutService, Parts, Position } from 'vs/workbench/services/layout/browser/layoutService'; -import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { IEditorGroupsService, GroupOrientation } from 'vs/workbench/services/editor/common/editorGroupsService'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { KeyMod, KeyCode, KeyChord } from 'vs/base/common/keyCodes'; @@ -142,15 +141,6 @@ export class ToggleEditorLayoutAction extends Action { } } -CommandsRegistry.registerCommand('_workbench.editor.setGroupOrientation', function (accessor: ServicesAccessor, args: [GroupOrientation]) { - const editorGroupService = accessor.get(IEditorGroupsService); - const [orientation] = args; - - editorGroupService.setGroupOrientation(orientation); - - return Promise.resolve(); -}); - const group = viewCategory; registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleEditorLayoutAction, ToggleEditorLayoutAction.ID, ToggleEditorLayoutAction.LABEL, { primary: KeyMod.Shift | KeyMod.Alt | KeyCode.KEY_0, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_0 } }), 'View: Toggle Vertical/Horizontal Editor Layout', group); diff --git a/src/vs/workbench/browser/actions/windowActions.ts b/src/vs/workbench/browser/actions/windowActions.ts index 283d570fe7be..1b38d90ee72d 100644 --- a/src/vs/workbench/browser/actions/windowActions.ts +++ b/src/vs/workbench/browser/actions/windowActions.ts @@ -7,7 +7,8 @@ import 'vs/css!./media/actions'; import * as nls from 'vs/nls'; import { Action } from 'vs/base/common/actions'; -import { IWindowService, IURIToOpen, IWindowsService } from 'vs/platform/windows/common/windows'; +import { IWindowService, IURIToOpen } from 'vs/platform/windows/common/windows'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { Registry } from 'vs/platform/registry/common/platform'; import { KeyCode, KeyMod } from 'vs/base/common/keyCodes'; @@ -162,7 +163,7 @@ export class OpenRecentAction extends BaseOpenRecentAction { } } -export class QuickOpenRecentAction extends BaseOpenRecentAction { +class QuickOpenRecentAction extends BaseOpenRecentAction { static readonly ID = 'workbench.action.quickOpenRecent'; static readonly LABEL = nls.localize('quickOpenRecent', "Quick Open Recent..."); @@ -186,7 +187,7 @@ export class QuickOpenRecentAction extends BaseOpenRecentAction { } } -export class ToggleFullScreenAction extends Action { +class ToggleFullScreenAction extends Action { static readonly ID = 'workbench.action.toggleFullScreen'; static LABEL = nls.localize('toggleFullScreen', "Toggle Full Screen"); @@ -226,7 +227,7 @@ export class ReloadWindowAction extends Action { } } -export class ShowAboutDialogAction extends Action { +class ShowAboutDialogAction extends Action { static readonly ID = 'workbench.action.showAboutDialog'; static readonly LABEL = nls.localize('about', "About"); @@ -234,13 +235,13 @@ export class ShowAboutDialogAction extends Action { constructor( id: string, label: string, - @IWindowsService private readonly windowsService: IWindowsService + @IDialogService private readonly dialogService: IDialogService ) { super(id, label); } run(): Promise { - return this.windowsService.openAboutDialog(); + return this.dialogService.about(); } } diff --git a/src/vs/workbench/browser/actions/workspaceActions.ts b/src/vs/workbench/browser/actions/workspaceActions.ts index 7cefca23a28c..1c6e1f184082 100644 --- a/src/vs/workbench/browser/actions/workspaceActions.ts +++ b/src/vs/workbench/browser/actions/workspaceActions.ts @@ -5,24 +5,18 @@ import { Action } from 'vs/base/common/actions'; import * as nls from 'vs/nls'; -import { IWindowService } from 'vs/platform/windows/common/windows'; import { ITelemetryData } from 'vs/platform/telemetry/common/telemetry'; import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; -import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; -import { ICommandService, ICommandHandler, CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/commands'; import { ADD_ROOT_FOLDER_COMMAND_ID, ADD_ROOT_FOLDER_LABEL, PICK_WORKSPACE_FOLDER_COMMAND_ID } from 'vs/workbench/browser/actions/workspaceCommands'; import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { INotificationService } from 'vs/platform/notification/common/notification'; -import { Schemas } from 'vs/base/common/network'; -import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { ITextFileService, ISaveOptions } from 'vs/workbench/services/textfile/common/textfiles'; -import { toResource } from 'vs/workbench/common/editor'; -import { URI } from 'vs/base/common/uri'; -import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; -import { WorkbenchStateContext } from 'vs/workbench/browser/contextkeys'; +import { MenuRegistry, MenuId, SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { WorkbenchStateContext, SupportsWorkspacesContext } from 'vs/workbench/browser/contextkeys'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; export class OpenFileAction extends Action { @@ -42,36 +36,6 @@ export class OpenFileAction extends Action { } } -export namespace OpenLocalFileCommand { - export const ID = 'workbench.action.files.openLocalFile'; - export const LABEL = nls.localize('openLocalFile', "Open Local File..."); - - export function handler(): ICommandHandler { - return accessor => { - const dialogService = accessor.get(IFileDialogService); - return dialogService.pickFileAndOpen({ forceNewWindow: false, availableFileSystems: [Schemas.file] }); - }; - } -} - -export namespace SaveLocalFileCommand { - export const ID = 'workbench.action.files.saveLocalFile'; - export const LABEL = nls.localize('saveLocalFile', "Save Local File..."); - - export function handler(): ICommandHandler { - return accessor => { - const textFileService = accessor.get(ITextFileService); - const editorService = accessor.get(IEditorService); - let resource: URI | undefined = toResource(editorService.activeEditor); - const options: ISaveOptions = { force: true, availableFileSystems: [Schemas.file] }; - if (resource) { - return textFileService.saveAs(resource, undefined, options); - } - return Promise.resolve(undefined); - }; - } -} - export class OpenFolderAction extends Action { static readonly ID = 'workbench.action.files.openFolder'; @@ -90,19 +54,6 @@ export class OpenFolderAction extends Action { } } -export namespace OpenLocalFolderCommand { - export const ID = 'workbench.action.files.openLocalFolder'; - export const LABEL = nls.localize('openLocalFolder', "Open Local Folder..."); - - export function handler(): ICommandHandler { - return accessor => { - const dialogService = accessor.get(IFileDialogService); - return dialogService.pickFolderAndOpen({ forceNewWindow: false, availableFileSystems: [Schemas.file] }); - }; - } -} - - export class OpenFileFolderAction extends Action { static readonly ID = 'workbench.action.files.openFileFolder'; @@ -121,97 +72,6 @@ export class OpenFileFolderAction extends Action { } } -export namespace OpenLocalFileFolderCommand { - - export const ID = 'workbench.action.files.openLocalFileFolder'; - export const LABEL = nls.localize('openLocalFileFolder', "Open Local..."); - - export function handler(): ICommandHandler { - return accessor => { - const dialogService = accessor.get(IFileDialogService); - return dialogService.pickFileFolderAndOpen({ forceNewWindow: false, availableFileSystems: [Schemas.file] }); - }; - } -} - -export class AddRootFolderAction extends Action { - - static readonly ID = 'workbench.action.addRootFolder'; - static LABEL = ADD_ROOT_FOLDER_LABEL; - - constructor( - id: string, - label: string, - @ICommandService private readonly commandService: ICommandService - ) { - super(id, label); - } - - run(): Promise { - return this.commandService.executeCommand(ADD_ROOT_FOLDER_COMMAND_ID); - } -} - -export class GlobalRemoveRootFolderAction extends Action { - - static readonly ID = 'workbench.action.removeRootFolder'; - static LABEL = nls.localize('globalRemoveFolderFromWorkspace', "Remove Folder from Workspace..."); - - constructor( - id: string, - label: string, - @IWorkspaceEditingService private readonly workspaceEditingService: IWorkspaceEditingService, - @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, - @ICommandService private readonly commandService: ICommandService - ) { - super(id, label); - } - - async run(): Promise { - const state = this.contextService.getWorkbenchState(); - - // Workspace / Folder - if (state === WorkbenchState.WORKSPACE || state === WorkbenchState.FOLDER) { - const folder = await this.commandService.executeCommand(PICK_WORKSPACE_FOLDER_COMMAND_ID); - if (folder) { - await this.workspaceEditingService.removeFolders([folder.uri]); - } - } - - return true; - } -} - -export class SaveWorkspaceAsAction extends Action { - - static readonly ID = 'workbench.action.saveWorkspaceAs'; - static LABEL = nls.localize('saveWorkspaceAsAction', "Save Workspace As..."); - - constructor( - id: string, - label: string, - @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, - @IWorkspaceEditingService private readonly workspaceEditingService: IWorkspaceEditingService - - ) { - super(id, label); - } - - async run(): Promise { - const configPathUri = await this.workspaceEditingService.pickNewWorkspacePath(); - if (configPathUri) { - switch (this.contextService.getWorkbenchState()) { - case WorkbenchState.EMPTY: - case WorkbenchState.FOLDER: - const folders = this.contextService.getWorkspace().folders.map(folder => ({ uri: folder.uri })); - return this.workspaceEditingService.createAndEnterWorkspace(folders, configPathUri); - case WorkbenchState.WORKSPACE: - return this.workspaceEditingService.saveAndEnterWorkspace(configPathUri); - } - } - } -} - export class OpenWorkspaceAction extends Action { static readonly ID = 'workbench.action.openWorkspace'; @@ -230,93 +90,103 @@ export class OpenWorkspaceAction extends Action { } } -export class CloseWorkspaceAction extends Action { +export class OpenWorkspaceConfigFileAction extends Action { - static readonly ID = 'workbench.action.closeFolder'; - static LABEL = nls.localize('closeWorkspace', "Close Workspace"); + static readonly ID = 'workbench.action.openWorkspaceConfigFile'; + static readonly LABEL = nls.localize('openWorkspaceConfigFile', "Open Workspace Configuration File"); constructor( id: string, label: string, - @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, - @INotificationService private readonly notificationService: INotificationService, - @IWindowService private readonly windowService: IWindowService + @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, + @IEditorService private readonly editorService: IEditorService ) { super(id, label); - } - run(): Promise { - if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { - this.notificationService.info(nls.localize('noWorkspaceOpened', "There is currently no workspace opened in this instance to close.")); + this.enabled = !!this.workspaceContextService.getWorkspace().configuration; + } - return Promise.resolve(undefined); + run(): Promise { + const configuration = this.workspaceContextService.getWorkspace().configuration; + if (configuration) { + return this.editorService.openEditor({ resource: configuration }); } - - return this.windowService.closeWorkspace(); + return Promise.resolve(); } } -export class OpenWorkspaceConfigFileAction extends Action { +export class AddRootFolderAction extends Action { - static readonly ID = 'workbench.action.openWorkspaceConfigFile'; - static readonly LABEL = nls.localize('openWorkspaceConfigFile', "Open Workspace Configuration File"); + static readonly ID = 'workbench.action.addRootFolder'; + static LABEL = ADD_ROOT_FOLDER_LABEL; constructor( id: string, label: string, - @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, - @IEditorService private readonly editorService: IEditorService + @ICommandService private readonly commandService: ICommandService ) { super(id, label); - - this.enabled = !!this.workspaceContextService.getWorkspace().configuration; } run(): Promise { - const configuration = this.workspaceContextService.getWorkspace().configuration; - if (configuration) { - return this.editorService.openEditor({ resource: configuration }); - } - return Promise.resolve(); + return this.commandService.executeCommand(ADD_ROOT_FOLDER_COMMAND_ID); } } -export class DuplicateWorkspaceInNewWindowAction extends Action { +export class GlobalRemoveRootFolderAction extends Action { - static readonly ID = 'workbench.action.duplicateWorkspaceInNewWindow'; - static readonly LABEL = nls.localize('duplicateWorkspaceInNewWindow', "Duplicate Workspace in New Window"); + static readonly ID = 'workbench.action.removeRootFolder'; + static LABEL = nls.localize('globalRemoveFolderFromWorkspace', "Remove Folder from Workspace..."); constructor( id: string, label: string, - @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, @IWorkspaceEditingService private readonly workspaceEditingService: IWorkspaceEditingService, - @IWindowService private readonly windowService: IWindowService, - @IWorkspacesService private readonly workspacesService: IWorkspacesService, - @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService + @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, + @ICommandService private readonly commandService: ICommandService ) { super(id, label); } async run(): Promise { - const folders = this.workspaceContextService.getWorkspace().folders; - const remoteAuthority = this.environmentService.configuration.remoteAuthority; + const state = this.contextService.getWorkbenchState(); - const newWorkspace = await this.workspacesService.createUntitledWorkspace(folders, remoteAuthority); - await this.workspaceEditingService.copyWorkspaceSettings(newWorkspace); + // Workspace / Folder + if (state === WorkbenchState.WORKSPACE || state === WorkbenchState.FOLDER) { + const folder = await this.commandService.executeCommand(PICK_WORKSPACE_FOLDER_COMMAND_ID); + if (folder) { + await this.workspaceEditingService.removeFolders([folder.uri]); + } + } - return this.windowService.openWindow([{ workspaceUri: newWorkspace.configPath }], { forceNewWindow: true }); + return true; } } -// --- Menu Registration +// --- Actions Registration +const registry = Registry.as(Extensions.WorkbenchActions); const workspacesCategory = nls.localize('workspaces', "Workspaces"); +registry.registerWorkbenchAction(new SyncActionDescriptor(AddRootFolderAction, AddRootFolderAction.ID, AddRootFolderAction.LABEL), 'Workspaces: Add Folder to Workspace...', workspacesCategory, SupportsWorkspacesContext); +registry.registerWorkbenchAction(new SyncActionDescriptor(GlobalRemoveRootFolderAction, GlobalRemoveRootFolderAction.ID, GlobalRemoveRootFolderAction.LABEL), 'Workspaces: Remove Folder from Workspace...', workspacesCategory, SupportsWorkspacesContext); + +// --- Menu Registration + CommandsRegistry.registerCommand(OpenWorkspaceConfigFileAction.ID, serviceAccessor => { serviceAccessor.get(IInstantiationService).createInstance(OpenWorkspaceConfigFileAction, OpenWorkspaceConfigFileAction.ID, OpenWorkspaceConfigFileAction.LABEL).run(); }); +MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { + group: '3_workspace', + command: { + id: ADD_ROOT_FOLDER_COMMAND_ID, + title: nls.localize({ key: 'miAddFolderToWorkspace', comment: ['&& denotes a mnemonic'] }, "A&&dd Folder to Workspace...") + }, + order: 1, + when: SupportsWorkspacesContext +}); + MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { id: OpenWorkspaceConfigFileAction.ID, diff --git a/src/vs/workbench/browser/contextkeys.ts b/src/vs/workbench/browser/contextkeys.ts index b84d724bd612..1498390c80ab 100644 --- a/src/vs/workbench/browser/contextkeys.ts +++ b/src/vs/workbench/browser/contextkeys.ts @@ -141,8 +141,10 @@ export class WorkbenchContextKeysHandler extends Disposable { // Development IsDevelopmentContext.bindTo(this.contextKeyService).set(!this.environmentService.isBuilt || this.environmentService.isExtensionDevelopment); - // File Pickers - SupportsWorkspacesContext.bindTo(this.contextKeyService); + // Workspaces Support + // - web: only if already in workspace state + // - desktop: always + SupportsWorkspacesContext.bindTo(this.contextKeyService).set(isWeb ? this.contextService.getWorkbenchState() === WorkbenchState.WORKSPACE : true); // Editors this.activeEditorContext = ActiveEditorContext.bindTo(this.contextKeyService); diff --git a/src/vs/workbench/browser/legacyLayout.ts b/src/vs/workbench/browser/legacyLayout.ts index 3239b25572ff..56df927d4b57 100644 --- a/src/vs/workbench/browser/legacyLayout.ts +++ b/src/vs/workbench/browser/legacyLayout.ts @@ -43,20 +43,20 @@ export class WorkbenchLegacyLayout extends Disposable implements IVerticalSashLa private static readonly sashYHeightSettingsKey = 'workbench.panel.height'; private static readonly panelSizeBeforeMaximizedKey = 'workbench.panel.sizeBeforeMaximized'; - private workbenchSize: Dimension; + private workbenchSize!: Dimension; private sashXOne: Sash; private sashXTwo: Sash; private sashY: Sash; - private _sidebarWidth: number; - private sidebarHeight: number; - private titlebarHeight: number; - private statusbarHeight: number; - private panelSizeBeforeMaximized: number; - private panelMaximized: boolean; - private _panelHeight: number; - private _panelWidth: number; + private _sidebarWidth!: number; + private sidebarHeight!: number; + private titlebarHeight!: number; + private statusbarHeight!: number; + private panelSizeBeforeMaximized!: number; + private panelMaximized!: boolean; + private _panelHeight!: number; + private _panelWidth!: number; constructor( private parent: HTMLElement, @@ -413,7 +413,7 @@ export class WorkbenchLegacyLayout extends Disposable implements IVerticalSashLa this.titlebarHeight = isTitlebarHidden ? 0 : this.partLayoutInfo.titlebar.height / (isMacintosh || !menubarVisibility || menubarVisibility === 'hidden' ? getZoomFactor() : 1); // adjust for zoom prevention this.sidebarHeight = this.workbenchSize.height - this.statusbarHeight - this.titlebarHeight; - let sidebarSize = new Dimension(this.sidebarWidth, this.sidebarHeight); + let sidebarSize = { width: this.sidebarWidth, height: this.sidebarHeight }; // Activity Bar let activityBarSize = new Dimension(this.activitybarWidth, sidebarSize.height); @@ -463,7 +463,7 @@ export class WorkbenchLegacyLayout extends Disposable implements IVerticalSashLa this.storageService.store(WorkbenchLegacyLayout.panelSizeBeforeMaximizedKey, this.panelSizeBeforeMaximized, StorageScope.GLOBAL); - const panelDimension = new Dimension(panelWidth, panelHeight); + const panelDimension = { width: panelWidth, height: panelHeight }; // Editor let editorSize = { diff --git a/src/vs/workbench/browser/part.ts b/src/vs/workbench/browser/part.ts index acf79e48df95..fc7c9078fa89 100644 --- a/src/vs/workbench/browser/part.ts +++ b/src/vs/workbench/browser/part.ts @@ -156,13 +156,14 @@ class PartLayout { titleSize = new Dimension(0, 0); } - // Content Size: Width (Fill), Height (Variable) - const contentSize = new Dimension(width, height - titleSize.height); - + let contentWidth = width; if (this.options && typeof this.options.borderWidth === 'function') { - contentSize.width -= this.options.borderWidth(); // adjust for border size + contentWidth -= this.options.borderWidth(); // adjust for border size } + // Content Size: Width (Fill), Height (Variable) + const contentSize = new Dimension(contentWidth, height - titleSize.height); + // Content if (this.contentArea) { size(this.contentArea, contentSize.width, contentSize.height); diff --git a/src/vs/workbench/browser/parts/panel/media/chevron-down-dark.svg b/src/vs/workbench/browser/parts/panel/media/chevron-down-dark.svg deleted file mode 100644 index a1df6a8d44ae..000000000000 --- a/src/vs/workbench/browser/parts/panel/media/chevron-down-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/browser/parts/panel/media/chevron-down-hc.svg b/src/vs/workbench/browser/parts/panel/media/chevron-down-hc.svg deleted file mode 100644 index 4f2ec1469272..000000000000 --- a/src/vs/workbench/browser/parts/panel/media/chevron-down-hc.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/browser/parts/panel/media/chevron-down-light.svg b/src/vs/workbench/browser/parts/panel/media/chevron-down-light.svg deleted file mode 100644 index e60e357f573d..000000000000 --- a/src/vs/workbench/browser/parts/panel/media/chevron-down-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/browser/parts/panel/media/chevron-up-dark.svg b/src/vs/workbench/browser/parts/panel/media/chevron-up-dark.svg deleted file mode 100644 index 5b9da8932e19..000000000000 --- a/src/vs/workbench/browser/parts/panel/media/chevron-up-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/browser/parts/panel/media/chevron-up-hc.svg b/src/vs/workbench/browser/parts/panel/media/chevron-up-hc.svg deleted file mode 100644 index 13bad5373642..000000000000 --- a/src/vs/workbench/browser/parts/panel/media/chevron-up-hc.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/browser/parts/panel/media/chevron-up-light.svg b/src/vs/workbench/browser/parts/panel/media/chevron-up-light.svg deleted file mode 100644 index 5498cda5bc44..000000000000 --- a/src/vs/workbench/browser/parts/panel/media/chevron-up-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/browser/parts/panel/media/close-dark.svg b/src/vs/workbench/browser/parts/panel/media/close-dark.svg deleted file mode 100644 index e0475f7b85a3..000000000000 --- a/src/vs/workbench/browser/parts/panel/media/close-dark.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/vs/workbench/browser/parts/panel/media/close-hc.svg b/src/vs/workbench/browser/parts/panel/media/close-hc.svg deleted file mode 100644 index 64618b61760c..000000000000 --- a/src/vs/workbench/browser/parts/panel/media/close-hc.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/vs/workbench/browser/parts/panel/media/close-light.svg b/src/vs/workbench/browser/parts/panel/media/close-light.svg deleted file mode 100644 index 3bd44674699c..000000000000 --- a/src/vs/workbench/browser/parts/panel/media/close-light.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/vs/workbench/browser/parts/panel/media/panelpart.css b/src/vs/workbench/browser/parts/panel/media/panelpart.css index 3538f3ba1234..25f565d95406 100644 --- a/src/vs/workbench/browser/parts/panel/media/panelpart.css +++ b/src/vs/workbench/browser/parts/panel/media/panelpart.css @@ -124,20 +124,6 @@ margin-right: 10px; } -/* Close */ -.monaco-workbench .hide-panel-action { - background: url('close-light.svg') center center no-repeat; -} - -.vs-dark .monaco-workbench .hide-panel-action { - background: url('close-dark.svg') center center no-repeat; -} - -.hc-black .monaco-workbench .hide-panel-action { - background: url('close-hc.svg') center center no-repeat; -} - - /* Up */ .monaco-workbench .maximize-panel-action { background-image: url('chevron-up-light.svg'); diff --git a/src/vs/workbench/browser/parts/panel/panelActions.ts b/src/vs/workbench/browser/parts/panel/panelActions.ts index 0b385e41f2f8..1c8fc26b2b99 100644 --- a/src/vs/workbench/browser/parts/panel/panelActions.ts +++ b/src/vs/workbench/browser/parts/panel/panelActions.ts @@ -28,7 +28,7 @@ export class ClosePanelAction extends Action { name: string, @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService ) { - super(id, name, 'hide-panel-action'); + super(id, name, 'codicon-close'); } run(): Promise { @@ -141,11 +141,11 @@ export class ToggleMaximizedPanelAction extends Action { @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService, @IEditorGroupsService editorGroupsService: IEditorGroupsService ) { - super(id, label, layoutService.isPanelMaximized() ? 'minimize-panel-action' : 'maximize-panel-action'); + super(id, label, layoutService.isPanelMaximized() ? 'codicon-chevron-down' : 'codicon-chevron-up'); this.toDispose.add(editorGroupsService.onDidLayout(() => { const maximized = this.layoutService.isPanelMaximized(); - this.class = maximized ? 'minimize-panel-action' : 'maximize-panel-action'; + this.class = maximized ? 'codicon-chevron-down' : 'codicon-chevron-up'; this.label = maximized ? ToggleMaximizedPanelAction.RESTORE_LABEL : ToggleMaximizedPanelAction.MAXIMIZE_LABEL; })); } diff --git a/src/vs/workbench/browser/parts/views/customView.ts b/src/vs/workbench/browser/parts/views/customView.ts index b95a18365cd2..b716a9afacf7 100644 --- a/src/vs/workbench/browser/parts/views/customView.ts +++ b/src/vs/workbench/browser/parts/views/customView.ts @@ -768,6 +768,7 @@ class TreeRenderer extends Disposable implements ITreeRenderer{ $treeViewId: this.treeViewId, $treeItemHandle: node.handle }; templateData.actionBar.push(this.menus.getResourceActions(node), { icon: true, label: false }); diff --git a/src/vs/workbench/browser/style.ts b/src/vs/workbench/browser/style.ts index a7e694a04b6b..20ca2d89cf25 100644 --- a/src/vs/workbench/browser/style.ts +++ b/src/vs/workbench/browser/style.ts @@ -6,11 +6,17 @@ import 'vs/css!./media/style'; import { registerThemingParticipant, ITheme, ICssStyleCollector, HIGH_CONTRAST } from 'vs/platform/theme/common/themeService'; -import { foreground, selectionBackground, focusBorder, scrollbarShadow, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, listHighlightForeground, inputPlaceholderForeground } from 'vs/platform/theme/common/colorRegistry'; +import { iconForeground, foreground, selectionBackground, focusBorder, scrollbarShadow, scrollbarSliderActiveBackground, scrollbarSliderBackground, scrollbarSliderHoverBackground, listHighlightForeground, inputPlaceholderForeground } from 'vs/platform/theme/common/colorRegistry'; import { WORKBENCH_BACKGROUND } from 'vs/workbench/common/theme'; registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { + // Icon defaults + const iconForegroundColor = theme.getColor(iconForeground); + if (iconForegroundColor) { + collector.addRule(`.monaco-workbench .codicon { color: ${iconForegroundColor}; }`); + } + // Foreground const windowForeground = theme.getColor(foreground); if (windowForeground) { @@ -136,4 +142,5 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { } `); } + }); diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index 13c9d55d5097..7e608ee6d78f 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -13,8 +13,8 @@ import { Workbench } from 'vs/workbench/browser/workbench'; import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { REMOTE_FILE_SYSTEM_CHANNEL_NAME, RemoteExtensionsFileSystemProvider } from 'vs/platform/remote/common/remoteAgentFileSystemChannel'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { IProductService } from 'vs/platform/product/common/product'; -import product from 'vs/platform/product/browser/product'; +import { IProductService } from 'vs/platform/product/common/productService'; +import product from 'vs/platform/product/common/product'; import { RemoteAgentService } from 'vs/workbench/services/remote/browser/remoteAgentServiceImpl'; import { RemoteAuthorityResolverService } from 'vs/platform/remote/browser/remoteAuthorityResolverService'; import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; @@ -39,7 +39,7 @@ import { joinPath } from 'vs/base/common/resources'; import { BrowserStorageService } from 'vs/platform/storage/browser/storageService'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { getThemeTypeSelector, DARK, HIGH_CONTRAST, LIGHT } from 'vs/platform/theme/common/themeService'; -import { InMemoryUserDataProvider } from 'vs/workbench/services/userData/common/inMemoryUserDataProvider'; +import { InMemoryFileSystemProvider } from 'vs/workbench/services/userData/common/inMemoryUserDataProvider'; import { registerWindowDriver } from 'vs/platform/driver/browser/driver'; import { BufferLogService } from 'vs/platform/log/common/bufferLog'; import { FileLogService } from 'vs/platform/log/common/fileLogService'; @@ -75,6 +75,11 @@ class CodeRendererMain extends Disposable { // Layout this._register(addDisposableListener(window, EventType.RESIZE, () => workbench.layout())); + // Prevent the back/forward gestures in macOS + this._register(addDisposableListener(this.domElement, EventType.WHEEL, (e) => { + e.preventDefault(); + }, { passive: false })); + // Workbench Lifecycle this._register(workbench.onBeforeShutdown(event => { if (services.storageService.hasPendingUpdate) { @@ -222,7 +227,7 @@ class CodeRendererMain extends Disposable { // User data if (!this.configuration.userDataProvider) { - this.configuration.userDataProvider = this._register(new InMemoryUserDataProvider()); + this.configuration.userDataProvider = this._register(new InMemoryFileSystemProvider()); } fileService.registerProvider(Schemas.userData, this.configuration.userDataProvider); } @@ -261,12 +266,12 @@ class CodeRendererMain extends Disposable { // Multi-root workspace if (this.configuration.workspaceUri) { - return { id: hash(URI.revive(this.configuration.workspaceUri).toString()).toString(16), configPath: URI.revive(this.configuration.workspaceUri) }; + return { id: hash(this.configuration.workspaceUri.toString()).toString(16), configPath: this.configuration.workspaceUri }; } // Single-folder workspace if (this.configuration.folderUri) { - return { id: hash(URI.revive(this.configuration.folderUri).toString()).toString(16), folder: URI.revive(this.configuration.folderUri) }; + return { id: hash(this.configuration.folderUri.toString()).toString(16), folder: this.configuration.folderUri }; } return { id: 'empty-window' }; diff --git a/src/vs/workbench/browser/web.simpleservices.ts b/src/vs/workbench/browser/web.simpleservices.ts index bbb8eebb02a1..ed69b54ab1f2 100644 --- a/src/vs/workbench/browser/web.simpleservices.ts +++ b/src/vs/workbench/browser/web.simpleservices.ts @@ -11,7 +11,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { Disposable } from 'vs/base/common/lifecycle'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IWindowService, INativeOpenDialogOptions, IEnterWorkspaceResult, IURIToOpen, IMessageBoxResult, IWindowsService, IOpenSettings, IWindowSettings } from 'vs/platform/windows/common/windows'; -import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, IWorkspaceFolderCreationData, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IRecentlyOpened, IRecent, isRecentFile, isRecentFolder } from 'vs/platform/history/common/history'; import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; @@ -23,11 +23,6 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { ParsedArgs } from 'vs/platform/environment/common/environment'; import { IProcessEnvironment } from 'vs/base/common/platform'; import { toStoreData, restoreRecentlyOpened } from 'vs/platform/history/common/historyStorage'; -import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { IProductService } from 'vs/platform/product/common/product'; -import Severity from 'vs/base/common/severity'; -import { localize } from 'vs/nls'; -import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; //#region Window @@ -332,13 +327,6 @@ export class SimpleWindowsService implements IWindowsService { readonly onWindowUnmaximize: Event = Event.None; readonly onRecentlyOpenedChange: Event = Event.None; - constructor( - @IDialogService private readonly dialogService: IDialogService, - @IProductService private readonly productService: IProductService, - @IClipboardService private readonly clipboardService: IClipboardService - ) { - } - isFocused(_windowId: number): Promise { return Promise.resolve(true); } @@ -466,49 +454,6 @@ export class SimpleWindowsService implements IWindowsService { } openExtensionDevelopmentHostWindow(args: ParsedArgs, env: IProcessEnvironment): Promise { - - // we pass the "ParsedArgs" as query parameters of the URL - - let newAddress = `${document.location.origin}/?`; - let gotFolder = false; - - const addQueryParameter = (key: string, value: string) => { - const lastChar = newAddress.charAt(newAddress.length - 1); - if (lastChar !== '?' && lastChar !== '&') { - newAddress += '&'; - } - newAddress += `${key}=${encodeURIComponent(value)}`; - }; - - const f = args['folder-uri']; - if (f) { - const u = URI.parse(f[0]); - gotFolder = true; - addQueryParameter('folder', u.path); - } - if (!gotFolder) { - // request empty window - addQueryParameter('ew', 'true'); - } - - const ep = args['extensionDevelopmentPath']; - if (ep) { - let u = ep[0]; - addQueryParameter('edp', u); - } - - const di = args['debugId']; - if (di) { - addQueryParameter('di', di); - } - - const ibe = args['inspect-brk-extensions']; - if (ibe) { - addQueryParameter('ibe', ibe); - } - - window.open(newAddress); - return Promise.resolve(); } @@ -520,10 +465,6 @@ export class SimpleWindowsService implements IWindowsService { return Promise.resolve(this.windowCount); } - log(_severity: string, _args: string[]): Promise { - return Promise.resolve(); - } - showItemInFolder(_path: URI): Promise { return Promise.resolve(); } @@ -585,22 +526,6 @@ export class SimpleWindowsService implements IWindowsService { throw new Error('not implemented'); } - async openAboutDialog(): Promise { - const detail = localize('aboutDetail', - "Version: {0}\nCommit: {1}\nDate: {2}\nBrowser: {3}", - this.productService.version || 'Unknown', - this.productService.commit || 'Unknown', - this.productService.date || 'Unknown', - navigator.userAgent - ); - - const { choice } = await this.dialogService.show(Severity.Info, this.productService.nameLong, [localize('copy', "Copy"), localize('ok', "OK")], { detail }); - - if (choice === 0) { - this.clipboardService.writeText(detail); - } - } - resolveProxy(windowId: number, url: string): Promise { return Promise.resolve(undefined); } @@ -609,28 +534,3 @@ export class SimpleWindowsService implements IWindowsService { registerSingleton(IWindowsService, SimpleWindowsService); //#endregion - -//#region Workspaces - -export class SimpleWorkspacesService implements IWorkspacesService { - - _serviceBrand: undefined; - - createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[], remoteAuthority?: string): Promise { - // @ts-ignore - return Promise.resolve(undefined); - } - - deleteUntitledWorkspace(workspace: IWorkspaceIdentifier): Promise { - return Promise.resolve(undefined); - } - - getWorkspaceIdentifier(workspacePath: URI): Promise { - // @ts-ignore - return Promise.resolve(undefined); - } -} - -registerSingleton(IWorkspacesService, SimpleWorkspacesService); - -//#endregion diff --git a/src/vs/workbench/contrib/cli/node/cli.contribution.ts b/src/vs/workbench/contrib/cli/node/cli.contribution.ts index 9fa727e74cf0..cf2e492a58ea 100644 --- a/src/vs/workbench/contrib/cli/node/cli.contribution.ts +++ b/src/vs/workbench/contrib/cli/node/cli.contribution.ts @@ -14,7 +14,7 @@ import { Action } from 'vs/base/common/actions'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; import { Registry } from 'vs/platform/registry/common/platform'; import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; -import product from 'vs/platform/product/node/product'; +import product from 'vs/platform/product/common/product'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import Severity from 'vs/base/common/severity'; diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts b/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts index 182b391c0042..7bc98fcae2f5 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts @@ -4,19 +4,22 @@ *--------------------------------------------------------------------------------------------*/ import { memoize } from 'vs/base/common/decorators'; +import { Emitter } from 'vs/base/common/event'; import { UnownedDisposable } from 'vs/base/common/lifecycle'; import { basename } from 'vs/base/common/path'; import { URI } from 'vs/base/common/uri'; +import { WebviewEditorState } from 'vs/editor/common/modes'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IEditorModel } from 'vs/platform/editor/common/editor'; import { ILabelService } from 'vs/platform/label/common/label'; -import { IEditorInput, Verbosity } from 'vs/workbench/common/editor'; +import { ConfirmResult, IEditorInput, Verbosity } from 'vs/workbench/common/editor'; import { WebviewEditorOverlay } from 'vs/workbench/contrib/webview/browser/webview'; -import { WebviewEditorInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput'; +import { WebviewInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput'; import { IWebviewEditorService } from 'vs/workbench/contrib/webview/browser/webviewEditorService'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; -import { WebviewEditorState } from 'vs/editor/common/modes'; +import { promptSave } from 'vs/workbench/services/textfile/common/textFileService'; -export class CustomFileEditorInput extends WebviewEditorInput { +export class CustomFileEditorInput extends WebviewInput { public static typeId = 'workbench.editors.webviewEditor'; @@ -30,12 +33,10 @@ export class CustomFileEditorInput extends WebviewEditorInput { viewType: string, id: string, webview: UnownedDisposable, - @ILabelService - private readonly labelService: ILabelService, - @IWebviewEditorService - private readonly _webviewEditorService: IWebviewEditorService, - @IExtensionService - private readonly _extensionService: IExtensionService + @ILabelService private readonly labelService: ILabelService, + @IWebviewEditorService private readonly _webviewEditorService: IWebviewEditorService, + @IExtensionService private readonly _extensionService: IExtensionService, + @IDialogService private readonly dialogService: IDialogService, ) { super(id, viewType, '', undefined, webview); this._editorResource = resource; @@ -77,7 +78,7 @@ export class CustomFileEditorInput extends WebviewEditorInput { return this.labelService.getUriLabel(this.getResource()); } - getTitle(verbosity?: Verbosity): string { + public getTitle(verbosity?: Verbosity): string { switch (verbosity) { case Verbosity.SHORT: return this.shortTitle; @@ -106,4 +107,26 @@ export class CustomFileEditorInput extends WebviewEditorInput { public isDirty() { return this._state === WebviewEditorState.Dirty; } + + public async confirmSave(): Promise { + if (!this.isDirty()) { + return ConfirmResult.DONT_SAVE; + } + return promptSave(this.dialogService, [this.getResource()]); + } + + public async save(): Promise { + if (!this.isDirty) { + return true; + } + const waitingOn: Promise[] = []; + this._onWillSave.fire({ + waitUntil: (thenable: Promise): void => { waitingOn.push(thenable); }, + }); + const result = await Promise.all(waitingOn); + return result.every(x => x); + } + + private readonly _onWillSave = this._register(new Emitter<{ waitUntil: (thenable: Thenable) => void }>()); + public readonly onWillSave = this._onWillSave.event; } diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts index 4f25896b2102..9882920c379e 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditors.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditors.ts @@ -6,7 +6,8 @@ import { coalesce, distinct } from 'vs/base/common/arrays'; import * as glob from 'vs/base/common/glob'; import { UnownedDisposable } from 'vs/base/common/lifecycle'; -import { basename } from 'vs/base/common/resources'; +import { Schemas } from 'vs/base/common/network'; +import { basename, DataUri } from 'vs/base/common/resources'; import { withNullAsUndefined } from 'vs/base/common/types'; import { URI } from 'vs/base/common/uri'; import { generateUuid } from 'vs/base/common/uuid'; @@ -16,7 +17,8 @@ import { IEditorOptions, ITextEditorOptions } from 'vs/platform/editor/common/ed import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { EditorOptions, IEditor, IEditorInput } from 'vs/workbench/common/editor'; +import { EditorInput, EditorOptions, IEditor, IEditorInput } from 'vs/workbench/common/editor'; +import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import { webviewEditorsExtensionPoint } from 'vs/workbench/contrib/customEditor/browser/extensionPoint'; import { CustomEditorDiscretion, CustomEditorInfo, CustomEditorSelector, ICustomEditorService } from 'vs/workbench/contrib/customEditor/common/customEditor'; import { FileEditorInput } from 'vs/workbench/contrib/files/common/editors/fileEditorInput'; @@ -25,10 +27,48 @@ import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsSe import { IEditorService, IOpenEditorOverride } from 'vs/workbench/services/editor/common/editorService'; import { CustomFileEditorInput } from './customEditorInput'; +const defaultEditorId = 'default'; + +const defaultEditorInfo: CustomEditorInfo = { + id: defaultEditorId, + displayName: nls.localize('promptOpenWith.defaultEditor', "Default built-in editor"), + selector: [ + { filenamePattern: '*' } + ], + discretion: CustomEditorDiscretion.default, +}; + +export class CustomEditorStore { + private readonly contributedEditors = new Map(); + + public clear() { + this.contributedEditors.clear(); + } + + public get(viewType: string): CustomEditorInfo | undefined { + return viewType === defaultEditorId + ? defaultEditorInfo + : this.contributedEditors.get(viewType); + } + + public add(info: CustomEditorInfo): void { + if (info.id === defaultEditorId || this.contributedEditors.has(info.id)) { + console.log(`Custom editor with id '${info.id}' already registered`); + return; + } + this.contributedEditors.set(info.id, info); + } + + public getContributedEditors(resource: URI): readonly CustomEditorInfo[] { + return Array.from(this.contributedEditors.values()).filter(customEditor => + customEditor.selector.some(selector => matches(selector, resource))); + } +} + export class CustomEditorService implements ICustomEditorService { _serviceBrand: any; - private readonly customEditors = new Map(); + private readonly editors = new CustomEditorStore(); constructor( @IConfigurationService private readonly configurationService: IConfigurationService, @@ -38,9 +78,11 @@ export class CustomEditorService implements ICustomEditorService { @IWebviewService private readonly webviewService: IWebviewService, ) { webviewEditorsExtensionPoint.setHandler(extensions => { + this.editors.clear(); + for (const extension of extensions) { for (const webviewEditorContribution of extension.value) { - this.customEditors.set(webviewEditorContribution.viewType, { + this.editors.add({ id: webviewEditorContribution.viewType, displayName: webviewEditorContribution.displayName, selector: webviewEditorContribution.selector || [], @@ -52,15 +94,14 @@ export class CustomEditorService implements ICustomEditorService { } public getContributedCustomEditors(resource: URI): readonly CustomEditorInfo[] { - return Array.from(this.customEditors.values()).filter(customEditor => - customEditor.selector.some(selector => matches(selector, resource))); + return this.editors.getContributedEditors(resource); } public getUserConfiguredCustomEditors(resource: URI): readonly CustomEditorInfo[] { const rawAssociations = this.configurationService.getValue(customEditorsAssociationsKey) || []; return coalesce(rawAssociations .filter(association => matches(association, resource)) - .map(association => this.customEditors.get(association.viewType))); + .map(association => this.editors.get(association.viewType))); } public async promptOpenWith( @@ -69,34 +110,23 @@ export class CustomEditorService implements ICustomEditorService { group?: IEditorGroup, ): Promise { const customEditors = distinct([ + defaultEditorInfo, ...this.getUserConfiguredCustomEditors(resource), ...this.getContributedCustomEditors(resource), ], editor => editor.id); - const defaultEditorId = 'default'; - const pick = await this.quickInputService.pick([ - { - label: nls.localize('promptOpenWith.defaultEditor', "Default built-in editor"), - id: defaultEditorId, - }, - ...customEditors.map((editorDescriptor): IQuickPickItem => ({ + const pick = await this.quickInputService.pick( + customEditors.map((editorDescriptor): IQuickPickItem => ({ label: editorDescriptor.displayName, id: editorDescriptor.id, - })) - ], { + })), { placeHolder: nls.localize('promptOpenWith.placeHolder', "Select editor to use for '{0}'...", basename(resource)), }); - if (!pick) { + if (!pick || !pick.id) { return undefined; // {{SQL CARBON EDIT}} strict-null-check } - - if (pick.id === defaultEditorId) { - const fileInput = this.instantiationService.createInstance(FileEditorInput, resource, undefined, undefined); - return this.openEditorForResource(resource, fileInput, { ...options, ignoreOverrides: true }, group); - } else { - return this.openWith(resource, pick.id!, options, group); - } + return this.openWith(resource, pick.id, options, group); } public openWith( @@ -105,17 +135,31 @@ export class CustomEditorService implements ICustomEditorService { options?: ITextEditorOptions, group?: IEditorGroup, ): Promise { - if (!this.customEditors.has(viewType)) { + if (viewType === defaultEditorId) { + const fileInput = this.instantiationService.createInstance(FileEditorInput, resource, undefined, undefined); + return this.openEditorForResource(resource, fileInput, { ...options, ignoreOverrides: true }, group); + } + + if (!this.editors.get(viewType)) { return this.promptOpenWith(resource, options, group); } + const input = this.createInput(resource, viewType, group); + return this.openEditorForResource(resource, input, options, group); + } + + public createInput( + resource: URI, + viewType: string, + group: IEditorGroup | undefined + ): CustomFileEditorInput { const id = generateUuid(); const webview = this.webviewService.createWebviewEditorOverlay(id, {}, {}); const input = this.instantiationService.createInstance(CustomFileEditorInput, resource, viewType, id, new UnownedDisposable(webview)); if (group) { input.updateGroup(group!.id); } - return this.openEditorForResource(resource, input, options, group); + return input; } private async openEditorForResource( @@ -159,6 +203,42 @@ export class CustomEditorContribution implements IWorkbenchContribution { return undefined; // {{SQL CARBON EDIT}} strict-null-check } + if (editor instanceof DiffEditorInput) { + const getCustomEditorOverrideForSubInput = (subInput: IEditorInput): EditorInput | undefined => { + if (subInput instanceof CustomFileEditorInput) { + return undefined; // {{SQL CARBON EDIT}} strict-null-check + } + const resource = subInput.getResource(); + if (!resource) { + return undefined; // {{SQL CARBON EDIT}} strict-null-check + } + + const editors = distinct([ + ...this.customEditorService.getUserConfiguredCustomEditors(resource), + ...this.customEditorService.getContributedCustomEditors(resource), + ], editor => editor.id); + + // Always prefer the first editor in the diff editor case + return editors.length + ? this.customEditorService.createInput(resource, editors[0].id, group) + : undefined; + }; + + const modifiedOverride = getCustomEditorOverrideForSubInput(editor.modifiedInput); + const originalOverride = getCustomEditorOverrideForSubInput(editor.originalInput); + + if (modifiedOverride || originalOverride) { + return { + override: (async () => { + const input = new DiffEditorInput(editor.getName(), editor.getDescription(), originalOverride || editor.originalInput, modifiedOverride || editor.modifiedInput); + return this.editorService.openEditor(input, { ...options, ignoreOverrides: true }, group); + })(), + }; + } + + return undefined; + } + const resource = editor.getResource(); if (!resource) { return undefined; // {{SQL CARBON EDIT}} strict-null-check @@ -214,6 +294,15 @@ export class CustomEditorContribution implements IWorkbenchContribution { } function matches(selector: CustomEditorSelector, resource: URI): boolean { + if (resource.scheme === Schemas.data) { + const metadata = DataUri.parseMetaData(resource); + const mime = metadata.get(DataUri.META_DATA_MIME); + if (!selector.mime || !mime) { + return false; + } + return glob.match(selector.mime, mime.toLowerCase()); + } + if (!selector.filenamePattern && !selector.scheme) { return false; } diff --git a/src/vs/workbench/contrib/customEditor/common/customEditor.ts b/src/vs/workbench/contrib/customEditor/common/customEditor.ts index 9298b6278964..b827fb333d24 100644 --- a/src/vs/workbench/contrib/customEditor/common/customEditor.ts +++ b/src/vs/workbench/contrib/customEditor/common/customEditor.ts @@ -6,10 +6,9 @@ import { URI } from 'vs/base/common/uri'; import { ITextEditorOptions } from 'vs/platform/editor/common/editor'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IEditor } from 'vs/workbench/common/editor'; +import { EditorInput, IEditor } from 'vs/workbench/common/editor'; import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; - export const ICustomEditorService = createDecorator('customEditorService'); export interface ICustomEditorService { @@ -18,6 +17,8 @@ export interface ICustomEditorService { getContributedCustomEditors(resource: URI): readonly CustomEditorInfo[]; getUserConfiguredCustomEditors(resource: URI): readonly CustomEditorInfo[]; + createInput(resource: URI, viewType: string, group: IEditorGroup | undefined): EditorInput; + openWith(resource: URI, customEditorViewType: string, options?: ITextEditorOptions, group?: IEditorGroup): Promise; promptOpenWith(resource: URI, options?: ITextEditorOptions, group?: IEditorGroup): Promise; } @@ -30,6 +31,7 @@ export const enum CustomEditorDiscretion { export interface CustomEditorSelector { readonly scheme?: string; readonly filenamePattern?: string; + readonly mime?: string; } export interface CustomEditorInfo { diff --git a/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts new file mode 100644 index 000000000000..c2b73856b7dc --- /dev/null +++ b/src/vs/workbench/contrib/debug/browser/breakpointEditorContribution.ts @@ -0,0 +1,559 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as nls from 'vs/nls'; +import * as env from 'vs/base/common/platform'; +import * as dom from 'vs/base/browser/dom'; +import { URI as uri } from 'vs/base/common/uri'; +import severity from 'vs/base/common/severity'; +import { IAction, Action } from 'vs/base/common/actions'; +import { Range } from 'vs/editor/common/core/range'; +import { ICodeEditor, IEditorMouseEvent, MouseTargetType, IContentWidget, IActiveCodeEditor, IContentWidgetPosition, ContentWidgetPositionPreference } from 'vs/editor/browser/editorBrowser'; +import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; +import { IModelDecorationOptions, IModelDeltaDecoration, TrackedRangeStickiness, ITextModel } from 'vs/editor/common/model'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; +import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; +import { RemoveBreakpointAction } from 'vs/workbench/contrib/debug/browser/debugActions'; +import { IDebugService, IBreakpoint, CONTEXT_BREAKPOINT_WIDGET_VISIBLE, BreakpointWidgetContext, BREAKPOINT_EDITOR_CONTRIBUTION_ID, IBreakpointEditorContribution, IBreakpointUpdateData } from 'vs/workbench/contrib/debug/common/debug'; +import { IMarginData } from 'vs/editor/browser/controller/mouseTarget'; +import { ContextSubMenu } from 'vs/base/browser/contextmenu'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { BreakpointWidget } from 'vs/workbench/contrib/debug/browser/breakpointWidget'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; +import { MarkdownString } from 'vs/base/common/htmlContent'; +import { getBreakpointMessageAndClassName } from 'vs/workbench/contrib/debug/browser/breakpointsView'; +import { generateUuid } from 'vs/base/common/uuid'; +import { memoize } from 'vs/base/common/decorators'; +import { StandardMouseEvent } from 'vs/base/browser/mouseEvent'; +import { distinct } from 'vs/base/common/arrays'; + +const $ = dom.$; + +interface IBreakpointDecoration { + decorationId: string; + breakpointId: string; + range: Range; + inlineWidget?: InlineBreakpointWidget; +} + +const breakpointHelperDecoration: IModelDecorationOptions = { + glyphMarginClassName: 'debug-breakpoint-hint', + stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges +}; + +function createBreakpointDecorations(model: ITextModel, breakpoints: ReadonlyArray, debugService: IDebugService): { range: Range; options: IModelDecorationOptions; }[] { + const result: { range: Range; options: IModelDecorationOptions; }[] = []; + breakpoints.forEach((breakpoint) => { + if (breakpoint.lineNumber <= model.getLineCount()) { + const column = model.getLineFirstNonWhitespaceColumn(breakpoint.lineNumber); + const range = model.validateRange( + breakpoint.column ? new Range(breakpoint.lineNumber, breakpoint.column, breakpoint.lineNumber, breakpoint.column + 1) + : new Range(breakpoint.lineNumber, column, breakpoint.lineNumber, column + 1) // Decoration has to have a width #20688 + ); + + result.push({ + options: getBreakpointDecorationOptions(model, breakpoint, debugService), + range + }); + } + }); + + return result; +} + +async function createCandidateDecorations(model: ITextModel, lineNumbers: number[], debugService: IDebugService): Promise<{ range: Range; options: IModelDecorationOptions; }[]> { + const result: { range: Range; options: IModelDecorationOptions; }[] = []; + const session = debugService.getViewModel().focusedSession; + if (session && session.capabilities.supportsBreakpointLocationsRequest) { + lineNumbers.forEach(async lineNumber => { + const positions = await session.breakpointsLocations(model.uri, lineNumber); + positions.forEach(p => { + result.push({ + range: new Range(p.lineNumber, p.column, p.lineNumber, p.column + 1), + options: { + stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + beforeContentClassName: `debug-breakpoint-placeholder` + } + }); + }); + }); + } + + return result; +} + + +function getBreakpointDecorationOptions(model: ITextModel, breakpoint: IBreakpoint, debugService: IDebugService): IModelDecorationOptions { + const { className, message } = getBreakpointMessageAndClassName(debugService, breakpoint); + let glyphMarginHoverMessage: MarkdownString | undefined; + + if (message) { + if (breakpoint.condition || breakpoint.hitCondition) { + const modeId = model.getLanguageIdentifier().language; + glyphMarginHoverMessage = new MarkdownString().appendCodeblock(modeId, message); + } else { + glyphMarginHoverMessage = new MarkdownString().appendText(message); + } + } + + return { + glyphMarginClassName: className, + glyphMarginHoverMessage, + stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, + beforeContentClassName: breakpoint.column ? `debug-breakpoint-placeholder` : undefined + }; +} + +class BreakpointEditorContribution implements IBreakpointEditorContribution { + + private breakpointHintDecoration: string[] = []; + private breakpointWidget: BreakpointWidget | undefined; + private breakpointWidgetVisible: IContextKey; + private toDispose: IDisposable[] = []; + private ignoreDecorationsChangedEvent = false; + private ignoreFirstBreakpointsChangeEvent = false; + private breakpointDecorations: IBreakpointDecoration[] = []; + private candidateDecoraions: { decorationId: string, inlineWidget: InlineBreakpointWidget }[] = []; + + constructor( + private readonly editor: ICodeEditor, + @IDebugService private readonly debugService: IDebugService, + @IContextMenuService private readonly contextMenuService: IContextMenuService, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @IContextKeyService contextKeyService: IContextKeyService, + @IDialogService private readonly dialogService: IDialogService, + ) { + this.breakpointWidgetVisible = CONTEXT_BREAKPOINT_WIDGET_VISIBLE.bindTo(contextKeyService); + this.registerListeners(); + } + + getId(): string { + return BREAKPOINT_EDITOR_CONTRIBUTION_ID; + } + + private registerListeners(): void { + this.toDispose.push(this.editor.onMouseDown(async (e: IEditorMouseEvent) => { + const data = e.target.detail as IMarginData; + const model = this.editor.getModel(); + if (!e.target.position || !model || e.target.type !== MouseTargetType.GUTTER_GLYPH_MARGIN || data.isAfterLines || !this.marginFreeFromNonDebugDecorations(e.target.position.lineNumber)) { + return; + } + const canSetBreakpoints = this.debugService.getConfigurationManager().canSetBreakpointsIn(model); + const lineNumber = e.target.position.lineNumber; + const uri = model.uri; + + if (e.event.rightButton || (env.isMacintosh && e.event.leftButton && e.event.ctrlKey)) { + if (!canSetBreakpoints) { + return; + } + + const anchor = { x: e.event.posx, y: e.event.posy }; + const breakpoints = this.debugService.getModel().getBreakpoints({ lineNumber, uri }); + + this.contextMenuService.showContextMenu({ + getAnchor: () => anchor, + getActions: () => this.getContextMenuActions(breakpoints, uri, lineNumber), + getActionsContext: () => breakpoints.length ? breakpoints[0] : undefined + }); + } else { + const breakpoints = this.debugService.getModel().getBreakpoints({ uri, lineNumber }); + + if (breakpoints.length) { + // Show the dialog if there is a potential condition to be accidently lost. + // Do not show dialog on linux due to electron issue freezing the mouse #50026 + if (!env.isLinux && breakpoints.some(bp => !!bp.condition || !!bp.logMessage || !!bp.hitCondition)) { + const logPoint = breakpoints.every(bp => !!bp.logMessage); + const breakpointType = logPoint ? nls.localize('logPoint', "Logpoint") : nls.localize('breakpoint', "Breakpoint"); + const disable = breakpoints.some(bp => bp.enabled); + + const enabling = nls.localize('breakpointHasConditionDisabled', + "This {0} has a {1} that will get lost on remove. Consider enabling the {0} instead.", + breakpointType.toLowerCase(), + logPoint ? nls.localize('message', "message") : nls.localize('condition', "condition") + ); + const disabling = nls.localize('breakpointHasConditionEnabled', + "This {0} has a {1} that will get lost on remove. Consider disabling the {0} instead.", + breakpointType.toLowerCase(), + logPoint ? nls.localize('message', "message") : nls.localize('condition', "condition") + ); + + const { choice } = await this.dialogService.show(severity.Info, disable ? disabling : enabling, [ + nls.localize('removeLogPoint', "Remove {0}", breakpointType), + nls.localize('disableLogPoint', "{0} {1}", disable ? nls.localize('disable', "Disable") : nls.localize('enable', "Enable"), breakpointType), + nls.localize('cancel', "Cancel") + ], { cancelId: 2 }); + + if (choice === 0) { + breakpoints.forEach(bp => this.debugService.removeBreakpoints(bp.getId())); + } + if (choice === 1) { + breakpoints.forEach(bp => this.debugService.enableOrDisableBreakpoints(!disable, bp)); + } + } else { + breakpoints.forEach(bp => this.debugService.removeBreakpoints(bp.getId())); + } + } else if (canSetBreakpoints) { + this.debugService.addBreakpoints(uri, [{ lineNumber }], `debugEditorGutter`); + } + } + })); + + this.toDispose.push(this.editor.onMouseMove((e: IEditorMouseEvent) => { + let showBreakpointHintAtLineNumber = -1; + const model = this.editor.getModel(); + if (model && e.target.position && e.target.type === MouseTargetType.GUTTER_GLYPH_MARGIN && this.debugService.getConfigurationManager().canSetBreakpointsIn(model) && + this.marginFreeFromNonDebugDecorations(e.target.position.lineNumber)) { + const data = e.target.detail as IMarginData; + if (!data.isAfterLines) { + showBreakpointHintAtLineNumber = e.target.position.lineNumber; + } + } + this.ensureBreakpointHintDecoration(showBreakpointHintAtLineNumber); + })); + this.toDispose.push(this.editor.onMouseLeave((e: IEditorMouseEvent) => { + this.ensureBreakpointHintDecoration(-1); + })); + + this.toDispose.push(this.editor.onDidChangeModel(async () => { + this.closeBreakpointWidget(); + await this.setDecorations(); + })); + this.toDispose.push(this.debugService.getModel().onDidChangeBreakpoints(async () => { + if (this.ignoreFirstBreakpointsChangeEvent) { + this.ignoreFirstBreakpointsChangeEvent = false; + return; + } + await this.setDecorations(); + })); + this.toDispose.push(this.editor.onDidChangeModelDecorations(() => this.onModelDecorationsChanged())); + } + + private getContextMenuActions(breakpoints: ReadonlyArray, uri: uri, lineNumber: number, column?: number): Array { + const actions: Array = []; + if (breakpoints.length === 1) { + const breakpointType = breakpoints[0].logMessage ? nls.localize('logPoint', "Logpoint") : nls.localize('breakpoint', "Breakpoint"); + actions.push(new RemoveBreakpointAction(RemoveBreakpointAction.ID, nls.localize('removeBreakpoint', "Remove {0}", breakpointType), this.debugService)); + actions.push(new Action( + 'workbench.debug.action.editBreakpointAction', + nls.localize('editBreakpoint', "Edit {0}...", breakpointType), + undefined, + true, + () => Promise.resolve(this.showBreakpointWidget(breakpoints[0].lineNumber, breakpoints[0].column)) + )); + + actions.push(new Action( + `workbench.debug.viewlet.action.toggleBreakpoint`, + breakpoints[0].enabled ? nls.localize('disableBreakpoint', "Disable {0}", breakpointType) : nls.localize('enableBreakpoint', "Enable {0}", breakpointType), + undefined, + true, + () => this.debugService.enableOrDisableBreakpoints(!breakpoints[0].enabled, breakpoints[0]) + )); + } else if (breakpoints.length > 1) { + const sorted = breakpoints.slice().sort((first, second) => (first.column && second.column) ? first.column - second.column : 1); + actions.push(new ContextSubMenu(nls.localize('removeBreakpoints', "Remove Breakpoints"), sorted.map(bp => new Action( + 'removeInlineBreakpoint', + bp.column ? nls.localize('removeInlineBreakpointOnColumn', "Remove Inline Breakpoint on Column {0}", bp.column) : nls.localize('removeLineBreakpoint', "Remove Line Breakpoint"), + undefined, + true, + () => this.debugService.removeBreakpoints(bp.getId()) + )))); + + actions.push(new ContextSubMenu(nls.localize('editBreakpoints', "Edit Breakpoints"), sorted.map(bp => + new Action('editBreakpoint', + bp.column ? nls.localize('editInlineBreakpointOnColumn', "Edit Inline Breakpoint on Column {0}", bp.column) : nls.localize('editLineBrekapoint', "Edit Line Breakpoint"), + undefined, + true, + () => Promise.resolve(this.showBreakpointWidget(bp.lineNumber, bp.column)) + ) + ))); + + actions.push(new ContextSubMenu(nls.localize('enableDisableBreakpoints', "Enable/Disable Breakpoints"), sorted.map(bp => new Action( + bp.enabled ? 'disableColumnBreakpoint' : 'enableColumnBreakpoint', + bp.enabled ? (bp.column ? nls.localize('disableInlineColumnBreakpoint', "Disable Inline Breakpoint on Column {0}", bp.column) : nls.localize('disableBreakpointOnLine', "Disable Line Breakpoint")) + : (bp.column ? nls.localize('enableBreakpoints', "Enable Inline Breakpoint on Column {0}", bp.column) : nls.localize('enableBreakpointOnLine', "Enable Line Breakpoint")), + undefined, + true, + () => this.debugService.enableOrDisableBreakpoints(!bp.enabled, bp) + )))); + } else { + actions.push(new Action( + 'addBreakpoint', + nls.localize('addBreakpoint', "Add Breakpoint"), + undefined, + true, + () => this.debugService.addBreakpoints(uri, [{ lineNumber, column }], `debugEditorContextMenu`) + )); + actions.push(new Action( + 'addConditionalBreakpoint', + nls.localize('addConditionalBreakpoint', "Add Conditional Breakpoint..."), + undefined, + true, + () => Promise.resolve(this.showBreakpointWidget(lineNumber, column)) + )); + actions.push(new Action( + 'addLogPoint', + nls.localize('addLogPoint', "Add Logpoint..."), + undefined, + true, + () => Promise.resolve(this.showBreakpointWidget(lineNumber, BreakpointWidgetContext.LOG_MESSAGE)) + )); + } + + return actions; + } + + private marginFreeFromNonDebugDecorations(line: number): boolean { + const decorations = this.editor.getLineDecorations(line); + if (decorations) { + for (const { options } of decorations) { + if (options.glyphMarginClassName && options.glyphMarginClassName.indexOf('debug') === -1) { + return false; + } + } + } + + return true; + } + + private ensureBreakpointHintDecoration(showBreakpointHintAtLineNumber: number): void { + const newDecoration: IModelDeltaDecoration[] = []; + if (showBreakpointHintAtLineNumber !== -1) { + newDecoration.push({ + options: breakpointHelperDecoration, + range: { + startLineNumber: showBreakpointHintAtLineNumber, + startColumn: 1, + endLineNumber: showBreakpointHintAtLineNumber, + endColumn: 1 + } + }); + } + + this.breakpointHintDecoration = this.editor.deltaDecorations(this.breakpointHintDecoration, newDecoration); + } + + private async setDecorations(): Promise { + if (!this.editor.hasModel()) { + return; + } + + const activeCodeEditor = this.editor; + const model = activeCodeEditor.getModel(); + const breakpoints = this.debugService.getModel().getBreakpoints({ uri: model.uri }); + const desiredBreakpointDecorations = createBreakpointDecorations(model, breakpoints, this.debugService); + + try { + this.ignoreDecorationsChangedEvent = true; + + // Set breakpoint decorations + const decorationIds = activeCodeEditor.deltaDecorations(this.breakpointDecorations.map(bpd => bpd.decorationId), desiredBreakpointDecorations); + this.breakpointDecorations.forEach(bpd => { + if (bpd.inlineWidget) { + bpd.inlineWidget.dispose(); + } + }); + this.breakpointDecorations = decorationIds.map((decorationId, index) => { + let inlineWidget: InlineBreakpointWidget | undefined = undefined; + const breakpoint = breakpoints[index]; + if (breakpoint.column) { + inlineWidget = new InlineBreakpointWidget(activeCodeEditor, decorationId, desiredBreakpointDecorations[index].options.glyphMarginClassName, breakpoint, this.debugService, this.contextMenuService, () => this.getContextMenuActions([breakpoint], activeCodeEditor.getModel().uri, breakpoint.lineNumber, breakpoint.column)); + } + + return { + decorationId, + breakpointId: breakpoint.getId(), + range: desiredBreakpointDecorations[index].range, + inlineWidget + }; + }); + + } finally { + this.ignoreDecorationsChangedEvent = false; + } + + // Set breakpoint candidate decorations + const lineNumbers = distinct(this.breakpointDecorations.map(bpd => bpd.range.startLineNumber)); + let desiredCandidateDecorations = await createCandidateDecorations(this.editor.getModel(), lineNumbers, this.debugService); + desiredCandidateDecorations = desiredCandidateDecorations.filter(dbd => { + const breakpointDecorationAlreadyAtCandidateLocation = this.breakpointDecorations.filter(bd => bd.range.equalsRange(dbd.range)).length >= 0; + return !breakpointDecorationAlreadyAtCandidateLocation; + }); + const candidateDecorationids = this.editor.deltaDecorations(this.candidateDecoraions.map(c => c.decorationId), desiredCandidateDecorations); + this.candidateDecoraions.forEach(candidate => { + candidate.inlineWidget.dispose(); + }); + this.candidateDecoraions = candidateDecorationids.map((decorationId, index) => { + const candidate = desiredCandidateDecorations[index]; + const inlineWidget = new InlineBreakpointWidget(activeCodeEditor, decorationId, 'debug-breakpoint-disabled', undefined, this.debugService, this.contextMenuService, () => this.getContextMenuActions([], activeCodeEditor.getModel().uri, candidate.range.startLineNumber, candidate.range.startColumn)); + + return { + decorationId, + inlineWidget + }; + }); + } + + private async onModelDecorationsChanged(): Promise { + if (this.breakpointDecorations.length === 0 || this.ignoreDecorationsChangedEvent || !this.editor.hasModel()) { + // I have no decorations + return; + } + let somethingChanged = false; + const model = this.editor.getModel(); + this.breakpointDecorations.forEach(breakpointDecoration => { + if (somethingChanged) { + return; + } + const newBreakpointRange = model.getDecorationRange(breakpointDecoration.decorationId); + if (newBreakpointRange && (!breakpointDecoration.range.equalsRange(newBreakpointRange))) { + somethingChanged = true; + } + }); + if (!somethingChanged) { + // nothing to do, my decorations did not change. + return; + } + + const data = new Map(); + const breakpoints = this.debugService.getModel().getBreakpoints(); + for (let i = 0, len = this.breakpointDecorations.length; i < len; i++) { + const breakpointDecoration = this.breakpointDecorations[i]; + const decorationRange = model.getDecorationRange(breakpointDecoration.decorationId); + // check if the line got deleted. + if (decorationRange) { + const breakpoint = breakpoints.filter(bp => bp.getId() === breakpointDecoration.breakpointId).pop(); + // since we know it is collapsed, it cannot grow to multiple lines + if (breakpoint) { + data.set(breakpoint.getId(), { + lineNumber: decorationRange.startLineNumber, + column: breakpoint.column ? decorationRange.startColumn : undefined, + }); + } + } + } + + try { + this.ignoreFirstBreakpointsChangeEvent = true; + await this.debugService.updateBreakpoints(model.uri, data, true); + } finally { + this.ignoreFirstBreakpointsChangeEvent = false; + } + } + + // breakpoint widget + showBreakpointWidget(lineNumber: number, column: number | undefined, context?: BreakpointWidgetContext): void { + if (this.breakpointWidget) { + this.breakpointWidget.dispose(); + } + + this.breakpointWidget = this.instantiationService.createInstance(BreakpointWidget, this.editor, lineNumber, column, context); + this.breakpointWidget.show({ lineNumber, column: 1 }); + this.breakpointWidgetVisible.set(true); + } + + closeBreakpointWidget(): void { + if (this.breakpointWidget) { + this.breakpointWidget.dispose(); + this.breakpointWidget = undefined; + this.breakpointWidgetVisible.reset(); + this.editor.focus(); + } + } + + dispose(): void { + if (this.breakpointWidget) { + this.breakpointWidget.dispose(); + } + this.editor.deltaDecorations(this.breakpointDecorations.map(bpd => bpd.decorationId), []); + dispose(this.toDispose); + } +} + +class InlineBreakpointWidget implements IContentWidget, IDisposable { + + // editor.IContentWidget.allowEditorOverflow + allowEditorOverflow = false; + suppressMouseDown = true; + + private domNode!: HTMLElement; + private range: Range | null; + private toDispose: IDisposable[] = []; + + constructor( + private readonly editor: IActiveCodeEditor, + private readonly decorationId: string, + cssClass: string | null | undefined, + private readonly breakpoint: IBreakpoint | undefined, + private readonly debugService: IDebugService, + private readonly contextMenuService: IContextMenuService, + private readonly getContextMenuActions: () => ReadonlyArray + ) { + this.range = this.editor.getModel().getDecorationRange(decorationId); + this.toDispose.push(this.editor.onDidChangeModelDecorations(() => { + const model = this.editor.getModel(); + const range = model.getDecorationRange(this.decorationId); + if (this.range && !this.range.equalsRange(range)) { + this.range = range; + this.editor.layoutContentWidget(this); + } + })); + this.create(cssClass); + + this.editor.addContentWidget(this); + this.editor.layoutContentWidget(this); + } + + private create(cssClass: string | null | undefined): void { + this.domNode = $('.inline-breakpoint-widget'); + if (cssClass) { + this.domNode.classList.add(cssClass); + } + this.toDispose.push(dom.addDisposableListener(this.domNode, dom.EventType.CLICK, async e => { + if (this.breakpoint) { + await this.debugService.removeBreakpoints(this.breakpoint.getId()); + } else { + await this.debugService.addBreakpoints(this.editor.getModel().uri, [{ lineNumber: this.range!.startLineNumber, column: this.range!.startColumn }], 'debugEditorInlineWidget'); + } + })); + this.toDispose.push(dom.addDisposableListener(this.domNode, dom.EventType.CONTEXT_MENU, async e => { + const event = new StandardMouseEvent(e); + const anchor = { x: event.posx, y: event.posy }; + + this.contextMenuService.showContextMenu({ + getAnchor: () => anchor, + getActions: () => this.getContextMenuActions(), + getActionsContext: () => this.breakpoint + }); + })); + } + + @memoize + getId(): string { + return generateUuid(); + } + + getDomNode(): HTMLElement { + return this.domNode; + } + + getPosition(): IContentWidgetPosition | null { + if (!this.range) { + return null; + } + + return { + position: { lineNumber: this.range.startLineNumber, column: this.range.startColumn - 1 }, + preference: [ContentWidgetPositionPreference.EXACT] + }; + } + + dispose(): void { + this.editor.removeContentWidget(this); + dispose(this.toDispose); + } +} + +registerEditorContribution(BreakpointEditorContribution); diff --git a/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts b/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts index 651d9dde7904..b79c78c46081 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointWidget.ts @@ -13,7 +13,7 @@ import { Position, IPosition } from 'vs/editor/common/core/position'; import { ICodeEditor, IActiveCodeEditor } from 'vs/editor/browser/editorBrowser'; import { ZoneWidget } from 'vs/editor/contrib/zoneWidget/zoneWidget'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; -import { IDebugService, IBreakpoint, BreakpointWidgetContext as Context, CONTEXT_BREAKPOINT_WIDGET_VISIBLE, DEBUG_SCHEME, IDebugEditorContribution, EDITOR_CONTRIBUTION_ID, CONTEXT_IN_BREAKPOINT_WIDGET, IBreakpointUpdateData } from 'vs/workbench/contrib/debug/common/debug'; +import { IDebugService, IBreakpoint, BreakpointWidgetContext as Context, CONTEXT_BREAKPOINT_WIDGET_VISIBLE, DEBUG_SCHEME, CONTEXT_IN_BREAKPOINT_WIDGET, IBreakpointUpdateData, IBreakpointEditorContribution, BREAKPOINT_EDITOR_CONTRIBUTION_ID } from 'vs/workbench/contrib/debug/common/debug'; import { attachSelectBoxStyler } from 'vs/platform/theme/common/styler'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { createDecorator, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -37,7 +37,7 @@ import { IRange, Range } from 'vs/editor/common/core/range'; import { onUnexpectedError } from 'vs/base/common/errors'; const $ = dom.$; -const IPrivateBreakpointWidgetService = createDecorator('privateBreakopintWidgetService'); +const IPrivateBreakpointWidgetService = createDecorator('privateBreakpointWidgetService'); export interface IPrivateBreakpointWidgetService { _serviceBrand: undefined; close(success: boolean): void; @@ -55,7 +55,7 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi private logMessageInput = ''; private breakpoint: IBreakpoint | undefined; - constructor(editor: ICodeEditor, private lineNumber: number, private context: Context, + constructor(editor: ICodeEditor, private lineNumber: number, private column: number | undefined, private context: Context, @IContextViewService private readonly contextViewService: IContextViewService, @IDebugService private readonly debugService: IDebugService, @IThemeService private readonly themeService: IThemeService, @@ -70,7 +70,7 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi const model = this.editor.getModel(); if (model) { const uri = model.uri; - const breakpoints = this.debugService.getModel().getBreakpoints({ lineNumber: this.lineNumber, uri }); + const breakpoints = this.debugService.getModel().getBreakpoints({ lineNumber: this.lineNumber, column: this.column, uri }); this.breakpoint = breakpoints.length ? breakpoints[0] : undefined; } @@ -130,12 +130,12 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi } } - show(rangeOrPos: IRange | IPosition, heightInLines: number) { + show(rangeOrPos: IRange | IPosition): void { const lineNum = this.input.getModel().getLineCount(); super.show(rangeOrPos, lineNum + 1); } - fitHeightToContent() { + fitHeightToContent(): void { const lineNum = this.input.getModel().getLineCount(); this._relayout(lineNum + 1); } @@ -293,6 +293,7 @@ export class BreakpointWidget extends ZoneWidget implements IPrivateBreakpointWi if (model) { this.debugService.addBreakpoints(model.uri, [{ lineNumber: this.lineNumber, + column: this.column, enabled: true, condition, hitCondition, @@ -348,7 +349,7 @@ class CloseBreakpointWidgetCommand extends EditorCommand { } runEditorCommand(accessor: ServicesAccessor, editor: ICodeEditor, args: any): void { - const debugContribution = editor.getContribution(EDITOR_CONTRIBUTION_ID); + const debugContribution = editor.getContribution(BREAKPOINT_EDITOR_CONTRIBUTION_ID); if (debugContribution) { // if focus is in outer editor we need to use the debug contribution to close return debugContribution.closeBreakpointWidget(); diff --git a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts index 1eca288468c7..67b08c6236aa 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts @@ -7,7 +7,7 @@ import * as nls from 'vs/nls'; import * as resources from 'vs/base/common/resources'; import * as dom from 'vs/base/browser/dom'; import { IAction, Action } from 'vs/base/common/actions'; -import { IDebugService, IBreakpoint, CONTEXT_BREAKPOINTS_FOCUSED, EDITOR_CONTRIBUTION_ID, State, DEBUG_SCHEME, IFunctionBreakpoint, IExceptionBreakpoint, IEnablement, IDebugEditorContribution } from 'vs/workbench/contrib/debug/common/debug'; +import { IDebugService, IBreakpoint, CONTEXT_BREAKPOINTS_FOCUSED, State, DEBUG_SCHEME, IFunctionBreakpoint, IExceptionBreakpoint, IEnablement, BREAKPOINT_EDITOR_CONTRIBUTION_ID, IBreakpointEditorContribution } from 'vs/workbench/contrib/debug/common/debug'; import { ExceptionBreakpoint, FunctionBreakpoint, Breakpoint, DataBreakpoint } from 'vs/workbench/contrib/debug/common/debugModel'; import { AddFunctionBreakpointAction, ToggleBreakpointsActivatedAction, RemoveAllBreakpointsAction, RemoveBreakpointAction, EnableAllBreakpointsAction, DisableAllBreakpointsAction, ReapplyBreakpointsAction } from 'vs/workbench/contrib/debug/browser/debugActions'; import { IContextMenuService, IContextViewService } from 'vs/platform/contextview/browser/contextView'; @@ -167,7 +167,7 @@ export class BreakpointsView extends ViewletPanel { if (editor) { const codeEditor = editor.getControl(); if (isCodeEditor(codeEditor)) { - codeEditor.getContribution(EDITOR_CONTRIBUTION_ID).showBreakpointWidget(element.lineNumber, element.column); + codeEditor.getContribution(BREAKPOINT_EDITOR_CONTRIBUTION_ID).showBreakpointWidget(element.lineNumber, element.column); } } }); @@ -180,7 +180,7 @@ export class BreakpointsView extends ViewletPanel { actions.push(new Separator()); } - actions.push(new RemoveBreakpointAction(RemoveBreakpointAction.ID, nls.localize('removeBreakpoint', "Remove {0}", breakpointType), this.debugService, this.keybindingService)); + actions.push(new RemoveBreakpointAction(RemoveBreakpointAction.ID, nls.localize('removeBreakpoint', "Remove {0}", breakpointType), this.debugService)); if (this.debugService.getModel().getBreakpoints().length + this.debugService.getModel().getFunctionBreakpoints().length > 1) { actions.push(new RemoveAllBreakpointsAction(RemoveAllBreakpointsAction.ID, RemoveAllBreakpointsAction.LABEL, this.debugService, this.keybindingService)); diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index ba455c72fa81..a8337ee6a73b 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -24,7 +24,6 @@ import { } from 'vs/workbench/contrib/debug/common/debug'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; -import { DebugEditorModelManager } from 'vs/workbench/contrib/debug/browser/debugEditorModelManager'; import { StartAction, AddFunctionBreakpointAction, ConfigureAction, DisableAllBreakpointsAction, EnableAllBreakpointsAction, RemoveAllBreakpointsAction, RunAction, ReapplyBreakpointsAction, SelectAndStartAction } from 'vs/workbench/contrib/debug/browser/debugActions'; import { DebugToolBar } from 'vs/workbench/contrib/debug/browser/debugToolBar'; import * as service from 'vs/workbench/contrib/debug/browser/debugService'; @@ -49,6 +48,7 @@ import { VariablesView } from 'vs/workbench/contrib/debug/browser/variablesView' import { ClearReplAction, Repl } from 'vs/workbench/contrib/debug/browser/repl'; import { DebugContentProvider } from 'vs/workbench/contrib/debug/common/debugContentProvider'; import { registerAndGetAmdImageURL } from 'vs/base/common/amd'; +import { DebugCallStackContribution } from 'vs/workbench/contrib/debug/browser/debugCallStackContribution'; class OpenDebugViewletAction extends ShowViewletAction { public static readonly ID = VIEWLET_ID; @@ -121,7 +121,7 @@ const registry = Registry.as(WorkbenchActionRegistryEx registry.registerWorkbenchAction(new SyncActionDescriptor(OpenDebugPanelAction, OpenDebugPanelAction.ID, OpenDebugPanelAction.LABEL, openPanelKb), 'View: Debug Console', nls.localize('view', "View")); registry.registerWorkbenchAction(new SyncActionDescriptor(OpenDebugViewletAction, OpenDebugViewletAction.ID, OpenDebugViewletAction.LABEL, openViewletKb), 'View: Show Debug', nls.localize('view', "View")); -Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugEditorModelManager, LifecyclePhase.Restored); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugCallStackContribution, LifecyclePhase.Restored); Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugToolBar, LifecyclePhase.Restored); Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(DebugContentProvider, LifecyclePhase.Eventually); Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution(StatusBarColorProvider, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/contrib/debug/browser/debugActions.ts b/src/vs/workbench/contrib/debug/browser/debugActions.ts index f9ed48bf59a6..b223636c23f9 100644 --- a/src/vs/workbench/contrib/debug/browser/debugActions.ts +++ b/src/vs/workbench/contrib/debug/browser/debugActions.ts @@ -181,12 +181,12 @@ export class SelectAndStartAction extends AbstractDebugAction { } } -export class RemoveBreakpointAction extends AbstractDebugAction { +export class RemoveBreakpointAction extends Action { static readonly ID = 'workbench.debug.viewlet.action.removeBreakpoint'; static LABEL = nls.localize('removeBreakpoint', "Remove Breakpoint"); - constructor(id: string, label: string, @IDebugService debugService: IDebugService, @IKeybindingService keybindingService: IKeybindingService) { - super(id, label, 'debug-action remove', debugService, keybindingService); + constructor(id: string, label: string, @IDebugService private readonly debugService: IDebugService) { + super(id, label, 'debug-action remove'); } public run(breakpoint: IBreakpoint): Promise { diff --git a/src/vs/workbench/contrib/debug/browser/debugCallStackContribution.ts b/src/vs/workbench/contrib/debug/browser/debugCallStackContribution.ts new file mode 100644 index 000000000000..2d94e97335f3 --- /dev/null +++ b/src/vs/workbench/contrib/debug/browser/debugCallStackContribution.ts @@ -0,0 +1,182 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Constants } from 'vs/editor/common/core/uint'; +import { Range } from 'vs/editor/common/core/range'; +import { ITextModel, TrackedRangeStickiness, IModelDeltaDecoration, IModelDecorationOptions } from 'vs/editor/common/model'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { IDebugService, State } from 'vs/workbench/contrib/debug/common/debug'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; +import { registerColor } from 'vs/platform/theme/common/colorRegistry'; +import { localize } from 'vs/nls'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; + +interface IDebugEditorModelData { + model: ITextModel; + currentStackDecorations: string[]; + topStackFrameRange: Range | undefined; +} + +const stickiness = TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges; + +export class DebugCallStackContribution implements IWorkbenchContribution { + private modelDataMap = new Map(); + private toDispose: IDisposable[] = []; + + constructor( + @IModelService private readonly modelService: IModelService, + @IDebugService private readonly debugService: IDebugService, + ) { + this.registerListeners(); + } + + private registerListeners(): void { + this.toDispose.push(this.modelService.onModelAdded(this.onModelAdded, this)); + this.modelService.getModels().forEach(model => this.onModelAdded(model)); + this.toDispose.push(this.modelService.onModelRemoved(this.onModelRemoved, this)); + + this.toDispose.push(this.debugService.getViewModel().onDidFocusStackFrame(() => this.onFocusStackFrame())); + this.toDispose.push(this.debugService.onDidChangeState(state => { + if (state === State.Inactive) { + this.modelDataMap.forEach(modelData => { + modelData.topStackFrameRange = undefined; + }); + } + })); + } + + private onModelAdded(model: ITextModel): void { + const modelUriStr = model.uri.toString(); + const currentStackDecorations = model.deltaDecorations([], this.createCallStackDecorations(modelUriStr)); + + this.modelDataMap.set(modelUriStr, { + model: model, + currentStackDecorations: currentStackDecorations, + topStackFrameRange: undefined + }); + } + + private onModelRemoved(model: ITextModel): void { + const modelUriStr = model.uri.toString(); + const data = this.modelDataMap.get(modelUriStr); + if (data) { + this.modelDataMap.delete(modelUriStr); + } + } + + private onFocusStackFrame(): void { + this.modelDataMap.forEach((modelData, uri) => { + modelData.currentStackDecorations = modelData.model.deltaDecorations(modelData.currentStackDecorations, this.createCallStackDecorations(uri)); + }); + } + + private createCallStackDecorations(modelUriStr: string): IModelDeltaDecoration[] { + const result: IModelDeltaDecoration[] = []; + const stackFrame = this.debugService.getViewModel().focusedStackFrame; + if (!stackFrame || stackFrame.source.uri.toString() !== modelUriStr) { + return result; + } + + // only show decorations for the currently focused thread. + const columnUntilEOLRange = new Range(stackFrame.range.startLineNumber, stackFrame.range.startColumn, stackFrame.range.startLineNumber, Constants.MAX_SAFE_SMALL_INTEGER); + const range = new Range(stackFrame.range.startLineNumber, stackFrame.range.startColumn, stackFrame.range.startLineNumber, stackFrame.range.startColumn + 1); + + // compute how to decorate the editor. Different decorations are used if this is a top stack frame, focused stack frame, + // an exception or a stack frame that did not change the line number (we only decorate the columns, not the whole line). + const callStack = stackFrame.thread.getCallStack(); + if (callStack && callStack.length && stackFrame === callStack[0]) { + result.push({ + options: DebugCallStackContribution.TOP_STACK_FRAME_MARGIN, + range + }); + + result.push({ + options: DebugCallStackContribution.TOP_STACK_FRAME_DECORATION, + range: columnUntilEOLRange + }); + + const modelData = this.modelDataMap.get(modelUriStr); + if (modelData) { + if (modelData.topStackFrameRange && modelData.topStackFrameRange.startLineNumber === stackFrame.range.startLineNumber && modelData.topStackFrameRange.startColumn !== stackFrame.range.startColumn) { + result.push({ + options: DebugCallStackContribution.TOP_STACK_FRAME_INLINE_DECORATION, + range: columnUntilEOLRange + }); + } + modelData.topStackFrameRange = columnUntilEOLRange; + } + } else { + result.push({ + options: DebugCallStackContribution.FOCUSED_STACK_FRAME_MARGIN, + range + }); + + result.push({ + options: DebugCallStackContribution.FOCUSED_STACK_FRAME_DECORATION, + range: columnUntilEOLRange + }); + } + + return result; + } + + // editor decorations + + static readonly STICKINESS = TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges; + // we need a separate decoration for glyph margin, since we do not want it on each line of a multi line statement. + private static TOP_STACK_FRAME_MARGIN: IModelDecorationOptions = { + glyphMarginClassName: 'debug-top-stack-frame', + stickiness + }; + + private static FOCUSED_STACK_FRAME_MARGIN: IModelDecorationOptions = { + glyphMarginClassName: 'debug-focused-stack-frame', + stickiness + }; + + private static TOP_STACK_FRAME_DECORATION: IModelDecorationOptions = { + isWholeLine: true, + inlineClassName: 'debug-remove-token-colors', + className: 'debug-top-stack-frame-line', + stickiness + }; + + private static TOP_STACK_FRAME_INLINE_DECORATION: IModelDecorationOptions = { + beforeContentClassName: 'debug-top-stack-frame-column' + }; + + private static FOCUSED_STACK_FRAME_DECORATION: IModelDecorationOptions = { + isWholeLine: true, + inlineClassName: 'debug-remove-token-colors', + className: 'debug-focused-stack-frame-line', + stickiness + }; + + dispose(): void { + this.modelDataMap.forEach(modelData => { + modelData.model.deltaDecorations(modelData.currentStackDecorations, []); + }); + this.toDispose = dispose(this.toDispose); + + this.modelDataMap.clear(); + } +} + +registerThemingParticipant((theme, collector) => { + const topStackFrame = theme.getColor(topStackFrameColor); + if (topStackFrame) { + collector.addRule(`.monaco-editor .view-overlays .debug-top-stack-frame-line { background: ${topStackFrame}; }`); + collector.addRule(`.monaco-editor .view-overlays .debug-top-stack-frame-line { background: ${topStackFrame}; }`); + } + + const focusedStackFrame = theme.getColor(focusedStackFrameColor); + if (focusedStackFrame) { + collector.addRule(`.monaco-editor .view-overlays .debug-focused-stack-frame-line { background: ${focusedStackFrame}; }`); + } +}); + +const topStackFrameColor = registerColor('editor.stackFrameHighlightBackground', { dark: '#ffff0033', light: '#ffff6673', hc: '#fff600' }, localize('topStackFrameLineHighlight', 'Background color for the highlight of line at the top stack frame position.')); +const focusedStackFrameColor = registerColor('editor.focusedStackFrameHighlightBackground', { dark: '#7abd7a4d', light: '#cee7ce73', hc: '#cee7ce' }, localize('focusedStackFrameLineHighlight', 'Background color for the highlight of line at focused stack frame position.')); diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts index d936aeec52f0..6c391d61ce7d 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorActions.ts @@ -9,7 +9,7 @@ import { Range } from 'vs/editor/common/core/range'; import { EditorContextKeys } from 'vs/editor/common/editorContextKeys'; import { ServicesAccessor, registerEditorAction, EditorAction, IActionOptions } from 'vs/editor/browser/editorExtensions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; -import { IDebugService, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE, State, REPL_ID, VIEWLET_ID, IDebugEditorContribution, EDITOR_CONTRIBUTION_ID, BreakpointWidgetContext, IBreakpoint } from 'vs/workbench/contrib/debug/common/debug'; +import { IDebugService, CONTEXT_IN_DEBUG_MODE, CONTEXT_DEBUG_STATE, State, REPL_ID, VIEWLET_ID, IDebugEditorContribution, EDITOR_CONTRIBUTION_ID, BreakpointWidgetContext, IBreakpoint, BREAKPOINT_EDITOR_CONTRIBUTION_ID, IBreakpointEditorContribution } from 'vs/workbench/contrib/debug/common/debug'; import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { ICodeEditor } from 'vs/editor/browser/editorBrowser'; @@ -75,7 +75,7 @@ class ConditionalBreakpointAction extends EditorAction { const position = editor.getPosition(); if (position && editor.hasModel() && debugService.getConfigurationManager().canSetBreakpointsIn(editor.getModel())) { - editor.getContribution(EDITOR_CONTRIBUTION_ID).showBreakpointWidget(position.lineNumber, position.column); + editor.getContribution(BREAKPOINT_EDITOR_CONTRIBUTION_ID).showBreakpointWidget(position.lineNumber, undefined); } } } @@ -97,7 +97,7 @@ class LogPointAction extends EditorAction { const position = editor.getPosition(); if (position && editor.hasModel() && debugService.getConfigurationManager().canSetBreakpointsIn(editor.getModel())) { - editor.getContribution(EDITOR_CONTRIBUTION_ID).showBreakpointWidget(position.lineNumber, position.column, BreakpointWidgetContext.LOG_MESSAGE); + editor.getContribution(BREAKPOINT_EDITOR_CONTRIBUTION_ID).showBreakpointWidget(position.lineNumber, BreakpointWidgetContext.LOG_MESSAGE); } } } diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts index d2148dc75dc4..d182250281da 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts @@ -5,13 +5,9 @@ import * as nls from 'vs/nls'; import { RunOnceScheduler } from 'vs/base/common/async'; -import * as lifecycle from 'vs/base/common/lifecycle'; import * as env from 'vs/base/common/platform'; -import { URI as uri } from 'vs/base/common/uri'; import { visit } from 'vs/base/common/json'; -import severity from 'vs/base/common/severity'; import { Constants } from 'vs/editor/common/core/uint'; -import { IAction, Action } from 'vs/base/common/actions'; import { KeyCode } from 'vs/base/common/keyCodes'; import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { StandardTokenType } from 'vs/editor/common/modes'; @@ -19,32 +15,25 @@ import { DEFAULT_WORD_REGEXP } from 'vs/editor/common/model/wordHelper'; import { ICodeEditor, IEditorMouseEvent, MouseTargetType } from 'vs/editor/browser/editorBrowser'; import { registerEditorContribution } from 'vs/editor/browser/editorExtensions'; import { IDecorationOptions } from 'vs/editor/common/editorCommon'; -import { IModelDecorationOptions, IModelDeltaDecoration, TrackedRangeStickiness, ITextModel } from 'vs/editor/common/model'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { Range } from 'vs/editor/common/core/range'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ICommandService } from 'vs/platform/commands/common/commands'; -import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; -import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; -import { RemoveBreakpointAction } from 'vs/workbench/contrib/debug/browser/debugActions'; -import { IDebugEditorContribution, IDebugService, State, IBreakpoint, EDITOR_CONTRIBUTION_ID, CONTEXT_BREAKPOINT_WIDGET_VISIBLE, IStackFrame, IDebugConfiguration, IExpression, IExceptionInfo, BreakpointWidgetContext } from 'vs/workbench/contrib/debug/common/debug'; +import { IDebugEditorContribution, IDebugService, State, EDITOR_CONTRIBUTION_ID, IStackFrame, IDebugConfiguration, IExpression, IExceptionInfo } from 'vs/workbench/contrib/debug/common/debug'; import { ExceptionWidget } from 'vs/workbench/contrib/debug/browser/exceptionWidget'; import { FloatingClickWidget } from 'vs/workbench/browser/parts/editor/editorWidgets'; import { Position } from 'vs/editor/common/core/position'; import { CoreEditingCommands } from 'vs/editor/browser/controller/coreCommands'; import { first } from 'vs/base/common/arrays'; -import { IMarginData } from 'vs/editor/browser/controller/mouseTarget'; -import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; -import { ContextSubMenu } from 'vs/base/browser/contextmenu'; import { memoize } from 'vs/base/common/decorators'; -import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { getHover } from 'vs/editor/contrib/hover/getHover'; import { IEditorHoverOptions, EditorOption } from 'vs/editor/common/config/editorOptions'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { BreakpointWidget } from 'vs/workbench/contrib/debug/browser/breakpointWidget'; import { DebugHoverWidget } from 'vs/workbench/contrib/debug/browser/debugHover'; +import { ITextModel } from 'vs/editor/common/model'; +import { getHover } from 'vs/editor/contrib/hover/getHover'; +import { dispose, IDisposable } from 'vs/base/common/lifecycle'; const HOVER_DELAY = 300; const LAUNCH_JSON_REGEX = /launch\.json$/; @@ -53,17 +42,14 @@ const MAX_NUM_INLINE_VALUES = 100; // JS Global scope can have 700+ entries. We const MAX_INLINE_DECORATOR_LENGTH = 150; // Max string length of each inline decorator when debugging. If exceeded ... is added const MAX_TOKENIZATION_LINE_LEN = 500; // If line is too long, then inline values for the line are skipped -export class DebugEditorContribution implements IDebugEditorContribution { +class DebugEditorContribution implements IDebugEditorContribution { - private toDispose: lifecycle.IDisposable[]; + private toDispose: IDisposable[]; private hoverWidget: DebugHoverWidget; private nonDebugHoverPosition: Position | undefined; private hoverRange: Range | null = null; private mouseDown = false; - private breakpointHintDecoration: string[]; - private breakpointWidget: BreakpointWidget | undefined; - private breakpointWidgetVisible: IContextKey; private wordToLineNumbersMap: Map | undefined; private exceptionWidget: ExceptionWidget | undefined; @@ -73,182 +59,21 @@ export class DebugEditorContribution implements IDebugEditorContribution { constructor( private editor: ICodeEditor, @IDebugService private readonly debugService: IDebugService, - @IContextMenuService private readonly contextMenuService: IContextMenuService, @IInstantiationService private readonly instantiationService: IInstantiationService, - @IContextKeyService contextKeyService: IContextKeyService, @ICommandService private readonly commandService: ICommandService, @ICodeEditorService private readonly codeEditorService: ICodeEditorService, @ITelemetryService private readonly telemetryService: ITelemetryService, @IConfigurationService private readonly configurationService: IConfigurationService, - @IKeybindingService private readonly keybindingService: IKeybindingService, - @IDialogService private readonly dialogService: IDialogService, ) { - this.breakpointHintDecoration = []; this.hoverWidget = this.instantiationService.createInstance(DebugHoverWidget, this.editor); this.toDispose = []; this.registerListeners(); - this.breakpointWidgetVisible = CONTEXT_BREAKPOINT_WIDGET_VISIBLE.bindTo(contextKeyService); this.updateConfigurationWidgetVisibility(); this.codeEditorService.registerDecorationType(INLINE_VALUE_DECORATION_KEY, {}); this.toggleExceptionWidget(); } - private getContextMenuActions(breakpoints: ReadonlyArray, uri: uri, lineNumber: number): Array { - const actions: Array = []; - if (breakpoints.length === 1) { - const breakpointType = breakpoints[0].logMessage ? nls.localize('logPoint', "Logpoint") : nls.localize('breakpoint', "Breakpoint"); - actions.push(new RemoveBreakpointAction(RemoveBreakpointAction.ID, nls.localize('removeBreakpoint', "Remove {0}", breakpointType), this.debugService, this.keybindingService)); - actions.push(new Action( - 'workbench.debug.action.editBreakpointAction', - nls.localize('editBreakpoint', "Edit {0}...", breakpointType), - undefined, - true, - () => Promise.resolve(this.editor.getContribution(EDITOR_CONTRIBUTION_ID).showBreakpointWidget(breakpoints[0].lineNumber, breakpoints[0].column)) - )); - - actions.push(new Action( - `workbench.debug.viewlet.action.toggleBreakpoint`, - breakpoints[0].enabled ? nls.localize('disableBreakpoint', "Disable {0}", breakpointType) : nls.localize('enableBreakpoint', "Enable {0}", breakpointType), - undefined, - true, - () => this.debugService.enableOrDisableBreakpoints(!breakpoints[0].enabled, breakpoints[0]) - )); - } else if (breakpoints.length > 1) { - const sorted = breakpoints.slice().sort((first, second) => (first.column && second.column) ? first.column - second.column : 1); - actions.push(new ContextSubMenu(nls.localize('removeBreakpoints', "Remove Breakpoints"), sorted.map(bp => new Action( - 'removeInlineBreakpoint', - bp.column ? nls.localize('removeInlineBreakpointOnColumn', "Remove Inline Breakpoint on Column {0}", bp.column) : nls.localize('removeLineBreakpoint', "Remove Line Breakpoint"), - undefined, - true, - () => this.debugService.removeBreakpoints(bp.getId()) - )))); - - actions.push(new ContextSubMenu(nls.localize('editBreakpoints', "Edit Breakpoints"), sorted.map(bp => - new Action('editBreakpoint', - bp.column ? nls.localize('editInlineBreakpointOnColumn', "Edit Inline Breakpoint on Column {0}", bp.column) : nls.localize('editLineBrekapoint', "Edit Line Breakpoint"), - undefined, - true, - () => Promise.resolve(this.editor.getContribution(EDITOR_CONTRIBUTION_ID).showBreakpointWidget(bp.lineNumber, bp.column)) - ) - ))); - - actions.push(new ContextSubMenu(nls.localize('enableDisableBreakpoints', "Enable/Disable Breakpoints"), sorted.map(bp => new Action( - bp.enabled ? 'disableColumnBreakpoint' : 'enableColumnBreakpoint', - bp.enabled ? (bp.column ? nls.localize('disableInlineColumnBreakpoint', "Disable Inline Breakpoint on Column {0}", bp.column) : nls.localize('disableBreakpointOnLine', "Disable Line Breakpoint")) - : (bp.column ? nls.localize('enableBreakpoints', "Enable Inline Breakpoint on Column {0}", bp.column) : nls.localize('enableBreakpointOnLine', "Enable Line Breakpoint")), - undefined, - true, - () => this.debugService.enableOrDisableBreakpoints(!bp.enabled, bp) - )))); - } else { - actions.push(new Action( - 'addBreakpoint', - nls.localize('addBreakpoint', "Add Breakpoint"), - undefined, - true, - () => this.debugService.addBreakpoints(uri, [{ lineNumber }], `debugEditorContextMenu`) - )); - actions.push(new Action( - 'addConditionalBreakpoint', - nls.localize('addConditionalBreakpoint', "Add Conditional Breakpoint..."), - undefined, - true, - () => Promise.resolve(this.editor.getContribution(EDITOR_CONTRIBUTION_ID).showBreakpointWidget(lineNumber, undefined)) - )); - actions.push(new Action( - 'addLogPoint', - nls.localize('addLogPoint', "Add Logpoint..."), - undefined, - true, - () => Promise.resolve(this.editor.getContribution(EDITOR_CONTRIBUTION_ID).showBreakpointWidget(lineNumber, undefined, BreakpointWidgetContext.LOG_MESSAGE)) - )); - } - - return actions; - } - private registerListeners(): void { - this.toDispose.push(this.editor.onMouseDown(async (e: IEditorMouseEvent) => { - const data = e.target.detail as IMarginData; - const model = this.editor.getModel(); - if (!e.target.position || !model || e.target.type !== MouseTargetType.GUTTER_GLYPH_MARGIN || data.isAfterLines || !this.marginFreeFromNonDebugDecorations(e.target.position.lineNumber)) { - return; - } - const canSetBreakpoints = this.debugService.getConfigurationManager().canSetBreakpointsIn(model); - const lineNumber = e.target.position.lineNumber; - const uri = model.uri; - - if (e.event.rightButton || (env.isMacintosh && e.event.leftButton && e.event.ctrlKey)) { - if (!canSetBreakpoints) { - return; - } - - const anchor = { x: e.event.posx, y: e.event.posy }; - const breakpoints = this.debugService.getModel().getBreakpoints({ lineNumber, uri }); - - this.contextMenuService.showContextMenu({ - getAnchor: () => anchor, - getActions: () => this.getContextMenuActions(breakpoints, uri, lineNumber), - getActionsContext: () => breakpoints.length ? breakpoints[0] : undefined - }); - } else { - const breakpoints = this.debugService.getModel().getBreakpoints({ uri, lineNumber }); - - if (breakpoints.length) { - // Show the dialog if there is a potential condition to be accidently lost. - // Do not show dialog on linux due to electron issue freezing the mouse #50026 - if (!env.isLinux && breakpoints.some(bp => !!bp.condition || !!bp.logMessage || !!bp.hitCondition)) { - const logPoint = breakpoints.every(bp => !!bp.logMessage); - const breakpointType = logPoint ? nls.localize('logPoint', "Logpoint") : nls.localize('breakpoint', "Breakpoint"); - const disable = breakpoints.some(bp => bp.enabled); - - const enabling = nls.localize('breakpointHasConditionDisabled', - "This {0} has a {1} that will get lost on remove. Consider enabling the {0} instead.", - breakpointType.toLowerCase(), - logPoint ? nls.localize('message', "message") : nls.localize('condition', "condition") - ); - const disabling = nls.localize('breakpointHasConditionEnabled', - "This {0} has a {1} that will get lost on remove. Consider disabling the {0} instead.", - breakpointType.toLowerCase(), - logPoint ? nls.localize('message', "message") : nls.localize('condition', "condition") - ); - - const { choice } = await this.dialogService.show(severity.Info, disable ? disabling : enabling, [ - nls.localize('removeLogPoint', "Remove {0}", breakpointType), - nls.localize('disableLogPoint', "{0} {1}", disable ? nls.localize('disable', "Disable") : nls.localize('enable', "Enable"), breakpointType), - nls.localize('cancel', "Cancel") - ], { cancelId: 2 }); - - if (choice === 0) { - breakpoints.forEach(bp => this.debugService.removeBreakpoints(bp.getId())); - } - if (choice === 1) { - breakpoints.forEach(bp => this.debugService.enableOrDisableBreakpoints(!disable, bp)); - } - } else { - breakpoints.forEach(bp => this.debugService.removeBreakpoints(bp.getId())); - } - } else if (canSetBreakpoints) { - this.debugService.addBreakpoints(uri, [{ lineNumber }], `debugEditorGutter`); - } - } - })); - - this.toDispose.push(this.editor.onMouseMove((e: IEditorMouseEvent) => { - let showBreakpointHintAtLineNumber = -1; - const model = this.editor.getModel(); - if (model && e.target.position && e.target.type === MouseTargetType.GUTTER_GLYPH_MARGIN && this.debugService.getConfigurationManager().canSetBreakpointsIn(model) && - this.marginFreeFromNonDebugDecorations(e.target.position.lineNumber)) { - const data = e.target.detail as IMarginData; - if (!data.isAfterLines) { - showBreakpointHintAtLineNumber = e.target.position.lineNumber; - } - } - this.ensureBreakpointHintDecoration(showBreakpointHintAtLineNumber); - })); - this.toDispose.push(this.editor.onMouseLeave((e: IEditorMouseEvent) => { - this.ensureBreakpointHintDecoration(-1); - })); this.toDispose.push(this.debugService.getViewModel().onDidFocusStackFrame(e => this.onFocusStackFrame(e.stackFrame))); // hover listeners & hover widget @@ -279,7 +104,6 @@ export class DebugEditorContribution implements IDebugEditorContribution { if (model) { this._applyHoverConfiguration(model, stackFrame); } - this.closeBreakpointWidget(); this.toggleExceptionWidget(); this.hideHoverWidget(); this.updateConfigurationWidgetVisibility(); @@ -317,11 +141,11 @@ export class DebugEditorContribution implements IDebugEditorContribution { } } - public getId(): string { + getId(): string { return EDITOR_CONTRIBUTION_ID; } - public showHover(range: Range, focus: boolean): Promise { + showHover(range: Range, focus: boolean): Promise { const sf = this.debugService.getViewModel().focusedStackFrame; const model = this.editor.getModel(); if (sf && model && sf.source.uri.toString() === model.uri.toString()) { @@ -331,36 +155,6 @@ export class DebugEditorContribution implements IDebugEditorContribution { return Promise.resolve(); } - private marginFreeFromNonDebugDecorations(line: number): boolean { - const decorations = this.editor.getLineDecorations(line); - if (decorations) { - for (const { options } of decorations) { - if (options.glyphMarginClassName && options.glyphMarginClassName.indexOf('debug') === -1) { - return false; - } - } - } - - return true; - } - - private ensureBreakpointHintDecoration(showBreakpointHintAtLineNumber: number): void { - const newDecoration: IModelDeltaDecoration[] = []; - if (showBreakpointHintAtLineNumber !== -1) { - newDecoration.push({ - options: DebugEditorContribution.BREAKPOINT_HELPER_DECORATION, - range: { - startLineNumber: showBreakpointHintAtLineNumber, - startColumn: 1, - endLineNumber: showBreakpointHintAtLineNumber, - endColumn: 1 - } - }); - } - - this.breakpointHintDecoration = this.editor.deltaDecorations(this.breakpointHintDecoration, newDecoration); - } - private onFocusStackFrame(sf: IStackFrame | undefined): void { const model = this.editor.getModel(); if (model) { @@ -464,29 +258,8 @@ export class DebugEditorContribution implements IDebugEditorContribution { this.hideHoverWidget(); } } - // end hover business - // breakpoint widget - public showBreakpointWidget(lineNumber: number, column: number, context?: BreakpointWidgetContext): void { - if (this.breakpointWidget) { - this.breakpointWidget.dispose(); - } - - this.breakpointWidget = this.instantiationService.createInstance(BreakpointWidget, this.editor, lineNumber, context); - this.breakpointWidget.show({ lineNumber, column: 1 }, 2); - this.breakpointWidgetVisible.set(true); - } - - public closeBreakpointWidget(): void { - if (this.breakpointWidget) { - this.breakpointWidget.dispose(); - this.breakpointWidget = undefined; - this.breakpointWidgetVisible.reset(); - this.editor.focus(); - } - } - // exception widget private toggleExceptionWidget(): void { // Toggles exception widget based on the state of the current editor model and debug stack frame @@ -547,7 +320,7 @@ export class DebugEditorContribution implements IDebugEditorContribution { } } - public addLaunchConfiguration(): Promise { + addLaunchConfiguration(): Promise { /* __GDPR__ "debug/addLaunchConfiguration" : {} */ @@ -594,11 +367,6 @@ export class DebugEditorContribution implements IDebugEditorContribution { return insertLine(configurationsArrayPosition).then(() => this.commandService.executeCommand('editor.action.triggerSuggest')); } - private static BREAKPOINT_HELPER_DECORATION: IModelDecorationOptions = { - glyphMarginClassName: 'debug-breakpoint-hint', - stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges - }; - // Inline Decorations @memoize @@ -768,17 +536,14 @@ export class DebugEditorContribution implements IDebugEditorContribution { return this.wordToLineNumbersMap; } - public dispose(): void { - if (this.breakpointWidget) { - this.breakpointWidget.dispose(); - } + dispose(): void { if (this.hoverWidget) { this.hoverWidget.dispose(); } if (this.configurationWidget) { this.configurationWidget.dispose(); } - this.toDispose = lifecycle.dispose(this.toDispose); + this.toDispose = dispose(this.toDispose); } } diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorModelManager.ts b/src/vs/workbench/contrib/debug/browser/debugEditorModelManager.ts deleted file mode 100644 index 2091d6986543..000000000000 --- a/src/vs/workbench/contrib/debug/browser/debugEditorModelManager.ts +++ /dev/null @@ -1,334 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import * as lifecycle from 'vs/base/common/lifecycle'; -import { Constants } from 'vs/editor/common/core/uint'; -import { Range } from 'vs/editor/common/core/range'; -import { ITextModel, TrackedRangeStickiness, IModelDeltaDecoration, IModelDecorationOptions } from 'vs/editor/common/model'; -import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { IDebugService, IBreakpoint, State, IBreakpointUpdateData } from 'vs/workbench/contrib/debug/common/debug'; -import { IModelService } from 'vs/editor/common/services/modelService'; -import { MarkdownString } from 'vs/base/common/htmlContent'; -import { getBreakpointMessageAndClassName } from 'vs/workbench/contrib/debug/browser/breakpointsView'; -import { registerThemingParticipant } from 'vs/platform/theme/common/themeService'; -import { registerColor } from 'vs/platform/theme/common/colorRegistry'; -import { localize } from 'vs/nls'; -import { onUnexpectedError } from 'vs/base/common/errors'; - -interface IBreakpointDecoration { - decorationId: string; - modelId: string; - range: Range; -} - -interface IDebugEditorModelData { - model: ITextModel; - toDispose: lifecycle.IDisposable[]; - breakpointDecorations: IBreakpointDecoration[]; - currentStackDecorations: string[]; - topStackFrameRange: Range | undefined; -} - -export class DebugEditorModelManager implements IWorkbenchContribution { - static readonly ID = 'breakpointManager'; - static readonly STICKINESS = TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges; - private modelDataMap: Map; - private toDispose: lifecycle.IDisposable[]; - private ignoreDecorationsChangedEvent = false; - - constructor( - @IModelService private readonly modelService: IModelService, - @IDebugService private readonly debugService: IDebugService, - ) { - this.modelDataMap = new Map(); - this.toDispose = []; - this.registerListeners(); - } - - public dispose(): void { - this.modelDataMap.forEach(modelData => { - lifecycle.dispose(modelData.toDispose); - modelData.model.deltaDecorations(modelData.breakpointDecorations.map(bpd => bpd.decorationId), []); - modelData.model.deltaDecorations(modelData.currentStackDecorations, []); - }); - this.toDispose = lifecycle.dispose(this.toDispose); - - this.modelDataMap.clear(); - } - - private registerListeners(): void { - this.toDispose.push(this.modelService.onModelAdded(this.onModelAdded, this)); - this.modelService.getModels().forEach(model => this.onModelAdded(model)); - this.toDispose.push(this.modelService.onModelRemoved(this.onModelRemoved, this)); - - this.toDispose.push(this.debugService.getModel().onDidChangeBreakpoints(() => this.onBreakpointsChange())); - this.toDispose.push(this.debugService.getViewModel().onDidFocusStackFrame(() => this.onFocusStackFrame())); - this.toDispose.push(this.debugService.onDidChangeState(state => { - if (state === State.Inactive) { - this.modelDataMap.forEach(modelData => { - modelData.topStackFrameRange = undefined; - }); - } - })); - } - - private onModelAdded(model: ITextModel): void { - const modelUriStr = model.uri.toString(); - const breakpoints = this.debugService.getModel().getBreakpoints({ uri: model.uri }); - - const currentStackDecorations = model.deltaDecorations([], this.createCallStackDecorations(modelUriStr)); - const desiredDecorations = this.createBreakpointDecorations(model, breakpoints); - const breakpointDecorationIds = model.deltaDecorations([], desiredDecorations); - const toDispose: lifecycle.IDisposable[] = [model.onDidChangeDecorations((e) => this.onModelDecorationsChanged(modelUriStr))]; - - this.modelDataMap.set(modelUriStr, { - model: model, - toDispose: toDispose, - breakpointDecorations: breakpointDecorationIds.map((decorationId, index) => ({ decorationId, modelId: breakpoints[index].getId(), range: desiredDecorations[index].range })), - currentStackDecorations: currentStackDecorations, - topStackFrameRange: undefined - }); - } - - private onModelRemoved(model: ITextModel): void { - const modelUriStr = model.uri.toString(); - const data = this.modelDataMap.get(modelUriStr); - if (data) { - lifecycle.dispose(data.toDispose); - this.modelDataMap.delete(modelUriStr); - } - } - - // call stack management. Represent data coming from the debug service. - - private onFocusStackFrame(): void { - this.modelDataMap.forEach((modelData, uri) => { - modelData.currentStackDecorations = modelData.model.deltaDecorations(modelData.currentStackDecorations, this.createCallStackDecorations(uri)); - }); - } - - private createCallStackDecorations(modelUriStr: string): IModelDeltaDecoration[] { - const result: IModelDeltaDecoration[] = []; - const stackFrame = this.debugService.getViewModel().focusedStackFrame; - if (!stackFrame || stackFrame.source.uri.toString() !== modelUriStr) { - return result; - } - - // only show decorations for the currently focused thread. - const columnUntilEOLRange = new Range(stackFrame.range.startLineNumber, stackFrame.range.startColumn, stackFrame.range.startLineNumber, Constants.MAX_SAFE_SMALL_INTEGER); - const range = new Range(stackFrame.range.startLineNumber, stackFrame.range.startColumn, stackFrame.range.startLineNumber, stackFrame.range.startColumn + 1); - - // compute how to decorate the editor. Different decorations are used if this is a top stack frame, focused stack frame, - // an exception or a stack frame that did not change the line number (we only decorate the columns, not the whole line). - const callStack = stackFrame.thread.getCallStack(); - if (callStack && callStack.length && stackFrame === callStack[0]) { - result.push({ - options: DebugEditorModelManager.TOP_STACK_FRAME_MARGIN, - range - }); - - result.push({ - options: DebugEditorModelManager.TOP_STACK_FRAME_DECORATION, - range: columnUntilEOLRange - }); - - const modelData = this.modelDataMap.get(modelUriStr); - if (modelData) { - if (modelData.topStackFrameRange && modelData.topStackFrameRange.startLineNumber === stackFrame.range.startLineNumber && modelData.topStackFrameRange.startColumn !== stackFrame.range.startColumn) { - result.push({ - options: DebugEditorModelManager.TOP_STACK_FRAME_INLINE_DECORATION, - range: columnUntilEOLRange - }); - } - modelData.topStackFrameRange = columnUntilEOLRange; - } - } else { - result.push({ - options: DebugEditorModelManager.FOCUSED_STACK_FRAME_MARGIN, - range - }); - - result.push({ - options: DebugEditorModelManager.FOCUSED_STACK_FRAME_DECORATION, - range: columnUntilEOLRange - }); - } - - return result; - } - - // breakpoints management. Represent data coming from the debug service and also send data back. - private onModelDecorationsChanged(modelUrlStr: string): void { - const modelData = this.modelDataMap.get(modelUrlStr); - if (!modelData || modelData.breakpointDecorations.length === 0 || this.ignoreDecorationsChangedEvent) { - // I have no decorations - return; - } - let somethingChanged = false; - modelData.breakpointDecorations.forEach(breakpointDecoration => { - if (somethingChanged) { - return; - } - const newBreakpointRange = modelData.model.getDecorationRange(breakpointDecoration.decorationId); - if (newBreakpointRange && (!breakpointDecoration.range.equalsRange(newBreakpointRange))) { - somethingChanged = true; - } - }); - if (!somethingChanged) { - // nothing to do, my decorations did not change. - return; - } - - const data = new Map(); - const breakpoints = this.debugService.getModel().getBreakpoints(); - const modelUri = modelData.model.uri; - for (let i = 0, len = modelData.breakpointDecorations.length; i < len; i++) { - const breakpointDecoration = modelData.breakpointDecorations[i]; - const decorationRange = modelData.model.getDecorationRange(breakpointDecoration.decorationId); - // check if the line got deleted. - if (decorationRange) { - const breakpoint = breakpoints.filter(bp => bp.getId() === breakpointDecoration.modelId).pop(); - // since we know it is collapsed, it cannot grow to multiple lines - if (breakpoint) { - data.set(breakpoint.getId(), { - lineNumber: decorationRange.startLineNumber, - column: breakpoint.column ? decorationRange.startColumn : undefined, - }); - } - } - } - - this.debugService.updateBreakpoints(modelUri, data, true).then(undefined, onUnexpectedError); - } - - private onBreakpointsChange(): void { - const breakpointsMap = new Map(); - this.debugService.getModel().getBreakpoints().forEach(bp => { - const uriStr = bp.uri.toString(); - const breakpoints = breakpointsMap.get(uriStr); - if (breakpoints) { - breakpoints.push(bp); - } else { - breakpointsMap.set(uriStr, [bp]); - } - }); - - breakpointsMap.forEach((bps, uri) => { - const data = this.modelDataMap.get(uri); - if (data) { - this.updateBreakpoints(data, breakpointsMap.get(uri)!); - } - }); - this.modelDataMap.forEach((modelData, uri) => { - if (!breakpointsMap.has(uri)) { - this.updateBreakpoints(modelData, []); - } - }); - } - - private updateBreakpoints(modelData: IDebugEditorModelData, newBreakpoints: IBreakpoint[]): void { - const desiredDecorations = this.createBreakpointDecorations(modelData.model, newBreakpoints); - try { - this.ignoreDecorationsChangedEvent = true; - const breakpointDecorationIds = modelData.model.deltaDecorations(modelData.breakpointDecorations.map(bpd => bpd.decorationId), desiredDecorations); - modelData.breakpointDecorations = breakpointDecorationIds.map((decorationId, index) => ({ - decorationId, - modelId: newBreakpoints[index].getId(), - range: desiredDecorations[index].range - })); - } finally { - this.ignoreDecorationsChangedEvent = false; - } - } - - private createBreakpointDecorations(model: ITextModel, breakpoints: ReadonlyArray): { range: Range; options: IModelDecorationOptions; }[] { - const result: { range: Range; options: IModelDecorationOptions; }[] = []; - breakpoints.forEach((breakpoint) => { - if (breakpoint.lineNumber <= model.getLineCount()) { - const column = model.getLineFirstNonWhitespaceColumn(breakpoint.lineNumber); - const range = model.validateRange( - breakpoint.column ? new Range(breakpoint.lineNumber, breakpoint.column, breakpoint.lineNumber, breakpoint.column + 1) - : new Range(breakpoint.lineNumber, column, breakpoint.lineNumber, column + 1) // Decoration has to have a width #20688 - ); - - result.push({ - options: this.getBreakpointDecorationOptions(breakpoint), - range - }); - } - }); - - return result; - } - - private getBreakpointDecorationOptions(breakpoint: IBreakpoint): IModelDecorationOptions { - const { className, message } = getBreakpointMessageAndClassName(this.debugService, breakpoint); - let glyphMarginHoverMessage: MarkdownString | undefined; - - if (message) { - if (breakpoint.condition || breakpoint.hitCondition) { - const modelData = this.modelDataMap.get(breakpoint.uri.toString()); - const modeId = modelData ? modelData.model.getLanguageIdentifier().language : ''; - glyphMarginHoverMessage = new MarkdownString().appendCodeblock(modeId, message); - } else { - glyphMarginHoverMessage = new MarkdownString().appendText(message); - } - } - - return { - glyphMarginClassName: className, - glyphMarginHoverMessage, - stickiness: DebugEditorModelManager.STICKINESS, - beforeContentClassName: breakpoint.column ? `debug-breakpoint-column ${className}-column` : undefined - }; - } - - // editor decorations - - // we need a separate decoration for glyph margin, since we do not want it on each line of a multi line statement. - private static TOP_STACK_FRAME_MARGIN: IModelDecorationOptions = { - glyphMarginClassName: 'debug-top-stack-frame', - stickiness: DebugEditorModelManager.STICKINESS - }; - - private static FOCUSED_STACK_FRAME_MARGIN: IModelDecorationOptions = { - glyphMarginClassName: 'debug-focused-stack-frame', - stickiness: DebugEditorModelManager.STICKINESS - }; - - private static TOP_STACK_FRAME_DECORATION: IModelDecorationOptions = { - isWholeLine: true, - inlineClassName: 'debug-remove-token-colors', - className: 'debug-top-stack-frame-line', - stickiness: DebugEditorModelManager.STICKINESS - }; - - private static TOP_STACK_FRAME_INLINE_DECORATION: IModelDecorationOptions = { - beforeContentClassName: 'debug-top-stack-frame-column' - }; - - private static FOCUSED_STACK_FRAME_DECORATION: IModelDecorationOptions = { - isWholeLine: true, - inlineClassName: 'debug-remove-token-colors', - className: 'debug-focused-stack-frame-line', - stickiness: DebugEditorModelManager.STICKINESS - }; -} - -registerThemingParticipant((theme, collector) => { - const topStackFrame = theme.getColor(topStackFrameColor); - if (topStackFrame) { - collector.addRule(`.monaco-editor .view-overlays .debug-top-stack-frame-line { background: ${topStackFrame}; }`); - collector.addRule(`.monaco-editor .view-overlays .debug-top-stack-frame-line { background: ${topStackFrame}; }`); - } - - const focusedStackFrame = theme.getColor(focusedStackFrameColor); - if (focusedStackFrame) { - collector.addRule(`.monaco-editor .view-overlays .debug-focused-stack-frame-line { background: ${focusedStackFrame}; }`); - } -}); - -const topStackFrameColor = registerColor('editor.stackFrameHighlightBackground', { dark: '#ffff0033', light: '#ffff6673', hc: '#fff600' }, localize('topStackFrameLineHighlight', 'Background color for the highlight of line at the top stack frame position.')); -const focusedStackFrameColor = registerColor('editor.focusedStackFrameHighlightBackground', { dark: '#7abd7a4d', light: '#cee7ce73', hc: '#cee7ce' }, localize('focusedStackFrameLineHighlight', 'Background color for the highlight of line at focused stack frame position.')); diff --git a/src/vs/workbench/contrib/debug/browser/debugSession.ts b/src/vs/workbench/contrib/debug/browser/debugSession.ts index 41bcd6b56385..ca61578fb7ba 100644 --- a/src/vs/workbench/contrib/debug/browser/debugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/debugSession.ts @@ -10,19 +10,20 @@ import * as platform from 'vs/base/common/platform'; import severity from 'vs/base/common/severity'; import { Event, Emitter } from 'vs/base/common/event'; import { CompletionItem, completionKindFromString } from 'vs/editor/common/modes'; -import { Position } from 'vs/editor/common/core/position'; +import { Position, IPosition } from 'vs/editor/common/core/position'; import * as aria from 'vs/base/browser/ui/aria/aria'; import { IDebugSession, IConfig, IThread, IRawModelUpdate, IDebugService, IRawStoppedDetails, State, LoadedSourceEvent, IFunctionBreakpoint, IExceptionBreakpoint, IBreakpoint, IExceptionInfo, AdapterEndEvent, IDebugger, VIEWLET_ID, IDebugConfiguration, IReplElement, IStackFrame, IExpression, IReplElementSource, IDataBreakpoint } from 'vs/workbench/contrib/debug/common/debug'; import { Source } from 'vs/workbench/contrib/debug/common/debugSource'; import { mixin } from 'vs/base/common/objects'; import { Thread, ExpressionContainer, DebugModel } from 'vs/workbench/contrib/debug/common/debugModel'; import { RawDebugSession } from 'vs/workbench/contrib/debug/browser/rawDebugSession'; -import { IProductService } from 'vs/platform/product/common/product'; +import { IProductService } from 'vs/platform/product/common/productService'; import { IWorkspaceFolder, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { RunOnceScheduler } from 'vs/base/common/async'; import { generateUuid } from 'vs/base/common/uuid'; -import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows'; +import { IWindowService } from 'vs/platform/windows/common/windows'; +import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { normalizeDriveLetter } from 'vs/base/common/labels'; import { Range } from 'vs/editor/common/core/range'; @@ -34,6 +35,7 @@ import { INotificationService } from 'vs/platform/notification/common/notificati import { IOpenerService } from 'vs/platform/opener/common/opener'; import { variableSetEmitter } from 'vs/workbench/contrib/debug/browser/variablesView'; import { CancellationTokenSource, CancellationToken } from 'vs/base/common/cancellation'; +import { distinct } from 'vs/base/common/arrays'; export class DebugSession implements IDebugSession { @@ -73,7 +75,7 @@ export class DebugSession implements IDebugSession { @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, @INotificationService private readonly notificationService: INotificationService, @IProductService private readonly productService: IProductService, - @IWindowsService private readonly windowsService: IWindowsService, + @IExtensionHostDebugService private readonly extensionHostDebugService: IExtensionHostDebugService, @IOpenerService private readonly openerService: IOpenerService ) { this.id = generateUuid(); @@ -185,7 +187,7 @@ export class DebugSession implements IDebugSession { return dbgr.createDebugAdapter(this).then(debugAdapter => { - this.raw = new RawDebugSession(debugAdapter, dbgr, this.telemetryService, customTelemetryService, this.windowsService, this.openerService); + this.raw = new RawDebugSession(debugAdapter, dbgr, this.telemetryService, customTelemetryService, this.extensionHostDebugService, this.openerService); return this.raw.start().then(() => { @@ -284,15 +286,7 @@ export class DebugSession implements IDebugSession { return Promise.resolve(undefined); } - const source = this.getSourceForUri(modelUri); - let rawSource: DebugProtocol.Source; - if (source) { - rawSource = source.raw; - } else { - const data = Source.getEncodedDebugData(modelUri); - rawSource = { name: data.name, path: data.path, sourceReference: data.sourceReference }; - } - + const rawSource = this.getRawSource(modelUri); if (breakpointsToSend.length && !rawSource.adapterData) { rawSource.adapterData = breakpointsToSend[0].adapterData; } @@ -376,6 +370,17 @@ export class DebugSession implements IDebugSession { return Promise.reject(new Error('no debug adapter')); } + async breakpointsLocations(uri: URI, lineNumber: number): Promise { + if (this.raw) { + const source = this.getRawSource(uri); + const response = await this.raw.breakpointLocations({ source, line: lineNumber }); + const positions = response.body.breakpoints.map(bp => ({ lineNumber: bp.line, column: bp.column || 1 })); + + return distinct(positions, p => p.toString()); + } + return Promise.reject(new Error('no debug adapter')); + } + customRequest(request: string, args: any): Promise { if (this.raw) { return this.raw.custom(request, args); @@ -914,6 +919,16 @@ export class DebugSession implements IDebugSession { return source; } + private getRawSource(uri: URI): DebugProtocol.Source { + const source = this.getSourceForUri(uri); + if (source) { + return source.raw; + } else { + const data = Source.getEncodedDebugData(uri); + return { name: data.name, path: data.path, sourceReference: data.sourceReference }; + } + } + private getNewCancellationToken(threadId: number): CancellationToken { const tokenSource = new CancellationTokenSource(); const tokens = this.cancellationMap.get(threadId) || []; diff --git a/src/vs/workbench/contrib/debug/browser/extensionHostDebugService.ts b/src/vs/workbench/contrib/debug/browser/extensionHostDebugService.ts index 4f604bdfe4a6..23775421bf80 100644 --- a/src/vs/workbench/contrib/debug/browser/extensionHostDebugService.ts +++ b/src/vs/workbench/contrib/debug/browser/extensionHostDebugService.ts @@ -5,7 +5,7 @@ import { ExtensionHostDebugChannelClient, ExtensionHostDebugBroadcastChannel } from 'vs/platform/debug/common/extensionHostDebugIpc'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug'; import { IDebugHelperService } from 'vs/workbench/contrib/debug/common/debug'; @@ -13,8 +13,10 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { TelemetryService } from 'vs/platform/telemetry/common/telemetryService'; import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { Event } from 'vs/base/common/event'; +import { IProcessEnvironment } from 'vs/base/common/platform'; +import { URI } from 'vs/base/common/uri'; -class BrowserExtensionHostDebugService extends ExtensionHostDebugChannelClient { +class BrowserExtensionHostDebugService extends ExtensionHostDebugChannelClient implements IExtensionHostDebugService { constructor( @IRemoteAgentService remoteAgentService: IRemoteAgentService, @@ -45,6 +47,52 @@ class BrowserExtensionHostDebugService extends ExtensionHostDebugChannelClient { } })); } + + openExtensionDevelopmentHostWindow(args: ParsedArgs, env: IProcessEnvironment): Promise { + // we pass the "ParsedArgs" as query parameters of the URL + + let newAddress = `${document.location.origin}/?`; + let gotFolder = false; + + const addQueryParameter = (key: string, value: string) => { + const lastChar = newAddress.charAt(newAddress.length - 1); + if (lastChar !== '?' && lastChar !== '&') { + newAddress += '&'; + } + newAddress += `${key}=${encodeURIComponent(value)}`; + }; + + const f = args['folder-uri']; + if (f) { + const u = URI.parse(f[0]); + gotFolder = true; + addQueryParameter('folder', u.path); + } + if (!gotFolder) { + // request empty window + addQueryParameter('ew', 'true'); + } + + const ep = args['extensionDevelopmentPath']; + if (ep) { + let u = ep[0]; + addQueryParameter('edp', u); + } + + const di = args['debugId']; + if (di) { + addQueryParameter('di', di); + } + + const ibe = args['inspect-brk-extensions']; + if (ibe) { + addQueryParameter('ibe', ibe); + } + + window.open(newAddress); + + return Promise.resolve(); + } } registerSingleton(IExtensionHostDebugService, BrowserExtensionHostDebugService); diff --git a/src/vs/workbench/contrib/debug/browser/media/clear-dark.svg b/src/vs/workbench/contrib/debug/browser/media/clear-dark.svg deleted file mode 100644 index 04d64ab41ca3..000000000000 --- a/src/vs/workbench/contrib/debug/browser/media/clear-dark.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/src/vs/workbench/contrib/debug/browser/media/clear-hc.svg b/src/vs/workbench/contrib/debug/browser/media/clear-hc.svg deleted file mode 100644 index 44a41edd3b3f..000000000000 --- a/src/vs/workbench/contrib/debug/browser/media/clear-hc.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/src/vs/workbench/contrib/debug/browser/media/clear-light.svg b/src/vs/workbench/contrib/debug/browser/media/clear-light.svg deleted file mode 100644 index f6a51c856f00..000000000000 --- a/src/vs/workbench/contrib/debug/browser/media/clear-light.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/src/vs/workbench/contrib/debug/browser/media/debug.contribution.css b/src/vs/workbench/contrib/debug/browser/media/debug.contribution.css index c4c5072a2969..fbbe55f5aac9 100644 --- a/src/vs/workbench/contrib/debug/browser/media/debug.contribution.css +++ b/src/vs/workbench/contrib/debug/browser/media/debug.contribution.css @@ -17,12 +17,12 @@ } .debug-breakpoint-disabled, -.monaco-editor .debug-breakpoint-column.debug-breakpoint-disabled-column::before { +.monaco-editor .inline-breakpoint-widget.debug-breakpoint-disabled { background: url('breakpoint-disabled.svg') center center no-repeat; } .debug-breakpoint-unverified, -.monaco-editor .debug-breakpoint-column.debug-breakpoint-unverified-column::before { +.monaco-editor .inline-breakpoint-widget.debug-breakpoint-unverified { background: url('breakpoint-unverified.svg') center center no-repeat; } @@ -35,21 +35,30 @@ } .debug-breakpoint, -.monaco-editor .debug-breakpoint-column::before { +.monaco-editor .inline-breakpoint-widget { background: url('breakpoint.svg') center center no-repeat; } -.monaco-editor .debug-breakpoint-column::before, +.monaco-editor .debug-breakpoint-placeholder::before, .monaco-editor .debug-top-stack-frame-column::before { content: " "; width: 1.3em; - height: 1.3em; display: inline-block; vertical-align: text-bottom; margin-right: 2px; margin-left: 2px; } +.monaco-editor .debug-top-stack-frame-column::before { + height: 1.3em; +} + +.monaco-editor .inline-breakpoint-widget { + width: 1.3em; + height: 1.3em; + margin-left: 0.61em; +} + .debug-function-breakpoint { background: url('breakpoint-function.svg') center center no-repeat; } @@ -75,34 +84,34 @@ } .debug-breakpoint-conditional, -.monaco-editor .debug-breakpoint-column.debug-breakpoint-conditional-column::before { +.monaco-editor .inline-breakpoint-widget.debug-breakpoint-conditional { background: url('breakpoint-conditional.svg') center center no-repeat; } .debug-breakpoint-log, -.monaco-editor .debug-breakpoint-column.debug-breakpoint-log-column::before { +.monaco-editor .inline-breakpoint-widget.debug-breakpoint-log { background: url('breakpoint-log.svg') center center no-repeat; } .debug-breakpoint-log-disabled, -.monaco-editor .debug-breakpoint-log-disabled-column::before { +.monaco-editor .inline-breakpoint-widget.debug-breakpoint-log-disabled { background: url('breakpoint-log-disabled.svg') center center no-repeat; } .debug-breakpoint-log-unverified, -.monaco-editor .debug-breakpoint-log-unverified-column::before { +.monaco-editor .inline-breakpoint-widget.debug-breakpoint-log-unverified { background: url('breakpoint-log-unverified.svg') center center no-repeat; } .debug-breakpoint-unsupported, -.monaco-editor .debug-breakpoint-column.debug-breakpoint-unsupported-column::before { +.monaco-editor .inline-breakpoint-widget.debug-breakpoint-unsupported { background: url('breakpoint-unsupported.svg') center center no-repeat; } .monaco-editor .debug-top-stack-frame.debug-breakpoint, .monaco-editor .debug-top-stack-frame.debug-breakpoint-conditional, .monaco-editor .debug-top-stack-frame.debug-breakpoint-log, -.monaco-editor .debug-breakpoint-column.debug-breakpoint-column.debug-top-stack-frame-column::before { +.monaco-editor .inline-breakpoint-widget.debug-top-stack-frame-column { background: url('current-and-breakpoint.svg') center center no-repeat; } diff --git a/src/vs/workbench/contrib/debug/browser/media/repl.css b/src/vs/workbench/contrib/debug/browser/media/repl.css index fae75d5dcc37..cdb38f82bae0 100644 --- a/src/vs/workbench/contrib/debug/browser/media/repl.css +++ b/src/vs/workbench/contrib/debug/browser/media/repl.css @@ -88,19 +88,6 @@ font-size: 9px; } -/* Actions */ -.debug-action.clear-repl { - background: url('clear-light.svg') center center no-repeat; -} - -.vs-dark .debug-action.clear-repl { - background: url('clear-dark.svg') center center no-repeat; -} - -.hc-black .debug-action.clear-repl { - background: url('clear-hc.svg') center center no-repeat; -} - /* Output coloring and styling */ .repl .repl-tree .output.expression > .ignore { font-style: italic; diff --git a/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts b/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts index 1dcb9efe4fb0..3435426e5cde 100644 --- a/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/rawDebugSession.ts @@ -13,7 +13,7 @@ import { formatPII, isUri } from 'vs/workbench/contrib/debug/common/debugUtils'; import { IDebugAdapter, IConfig, AdapterEndEvent, IDebugger } from 'vs/workbench/contrib/debug/common/debug'; import { createErrorWithActions } from 'vs/base/common/errorsWithActions'; import { ParsedArgs } from 'vs/platform/environment/common/environment'; -import { IWindowsService } from 'vs/platform/windows/common/windows'; +import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug'; import { URI } from 'vs/base/common/uri'; import { IProcessEnvironment } from 'vs/base/common/platform'; import { env as processEnv } from 'vs/base/common/process'; @@ -79,7 +79,7 @@ export class RawDebugSession implements IDisposable { dbgr: IDebugger, private readonly telemetryService: ITelemetryService, public readonly customTelemetryService: ITelemetryService | undefined, - private readonly windowsService: IWindowsService, + private readonly extensionHostDebugService: IExtensionHostDebugService, private readonly openerService: IOpenerService ) { @@ -381,6 +381,13 @@ export class RawDebugSession implements IDisposable { return this.send('setExceptionBreakpoints', args); } + breakpointLocations(args: DebugProtocol.BreakpointLocationsArguments): Promise { + if (this.capabilities.supportsBreakpointLocationsRequest) { + return this.send('breakpointLocations', args); + } + return Promise.reject(new Error('breakpointLocations is not supported')); + } + configurationDone(): Promise { if (this.capabilities.supportsConfigurationDoneRequest) { return this.send('configurationDone', null); @@ -625,7 +632,7 @@ export class RawDebugSession implements IDisposable { Object.keys(env).filter(k => env[k] === null).forEach(key => delete env[key]); } - return this.windowsService.openExtensionDevelopmentHostWindow(args, env); + return this.extensionHostDebugService.openExtensionDevelopmentHostWindow(args, env); } private send(command: string, args: any, token?: CancellationToken, timeout?: number): Promise { diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index 88dd5edeb73e..3d5dc4349940 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -1008,7 +1008,7 @@ export class ClearReplAction extends Action { constructor(id: string, label: string, @IPanelService private readonly panelService: IPanelService ) { - super(id, label, 'debug-action clear-repl'); + super(id, label, 'debug-action codicon-clear-all'); } run(): Promise { diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index 76c476e1015f..cfdd333b430b 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -12,7 +12,7 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation' import { IEditorContribution } from 'vs/editor/common/editorCommon'; import { ITextModel as EditorIModel } from 'vs/editor/common/model'; import { IEditor, ITextEditor } from 'vs/workbench/common/editor'; -import { Position } from 'vs/editor/common/core/position'; +import { Position, IPosition } from 'vs/editor/common/core/position'; import { CompletionItem } from 'vs/editor/common/modes'; import { Source } from 'vs/workbench/contrib/debug/common/debugSource'; import { Range, IRange } from 'vs/editor/common/core/range'; @@ -58,6 +58,7 @@ export const CONTEXT_RESTART_FRAME_SUPPORTED = new RawContextKey('resta export const CONTEXT_JUMP_TO_CURSOR_SUPPORTED = new RawContextKey('jumpToCursorSupported', false); export const EDITOR_CONTRIBUTION_ID = 'editor.contrib.debug'; +export const BREAKPOINT_EDITOR_CONTRIBUTION_ID = 'editor.contrib.breakpoint'; export const DEBUG_SCHEME = 'debug'; export const INTERNAL_CONSOLE_OPTIONS_SCHEMA = { enum: ['neverOpen', 'openOnSessionStart', 'openOnFirstSessionStart'], @@ -207,6 +208,7 @@ export interface IDebugSession extends ITreeElement { dataBreakpointInfo(name: string, variablesReference?: number): Promise<{ dataId: string | null, description: string, canPersist?: boolean }>; sendDataBreakpoints(dbps: IDataBreakpoint[]): Promise; sendExceptionBreakpoints(exbpts: IExceptionBreakpoint[]): Promise; + breakpointsLocations(uri: uri, lineNumber: number): Promise; stackTrace(threadId: number, startFrame: number, levels: number): Promise; exceptionInfo(threadId: number): Promise; @@ -850,9 +852,12 @@ export const enum BreakpointWidgetContext { export interface IDebugEditorContribution extends IEditorContribution { showHover(range: Range, focus: boolean): Promise; + addLaunchConfiguration(): Promise; +} + +export interface IBreakpointEditorContribution extends IEditorContribution { showBreakpointWidget(lineNumber: number, column: number | undefined, context?: BreakpointWidgetContext): void; closeBreakpointWidget(): void; - addLaunchConfiguration(): Promise; } // temporary debug helper service diff --git a/src/vs/workbench/contrib/debug/electron-browser/extensionHostDebugService.ts b/src/vs/workbench/contrib/debug/electron-browser/extensionHostDebugService.ts index 4d93e4cae2e8..a357078862a2 100644 --- a/src/vs/workbench/contrib/debug/electron-browser/extensionHostDebugService.ts +++ b/src/vs/workbench/contrib/debug/electron-browser/extensionHostDebugService.ts @@ -7,13 +7,22 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug'; import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; import { ExtensionHostDebugChannelClient, ExtensionHostDebugBroadcastChannel } from 'vs/platform/debug/common/extensionHostDebugIpc'; +import { IWindowsService } from 'vs/platform/windows/common/windows'; +import { IProcessEnvironment } from 'vs/base/common/platform'; +import { ParsedArgs } from 'vs/platform/environment/common/environment'; export class ExtensionHostDebugService extends ExtensionHostDebugChannelClient { constructor( - @IMainProcessService readonly windowService: IMainProcessService, + @IMainProcessService readonly mainProcessService: IMainProcessService, + @IWindowsService private readonly windowsService: IWindowsService ) { - super(windowService.getChannel(ExtensionHostDebugBroadcastChannel.ChannelName)); + super(mainProcessService.getChannel(ExtensionHostDebugBroadcastChannel.ChannelName)); + } + + openExtensionDevelopmentHostWindow(args: ParsedArgs, env: IProcessEnvironment): Promise { + // TODO@Isidor use debug IPC channel + return this.windowsService.openExtensionDevelopmentHostWindow(args, env); } } diff --git a/src/vs/workbench/contrib/debug/test/common/mockDebug.ts b/src/vs/workbench/contrib/debug/test/common/mockDebug.ts index d01a67855ba3..5f0633812a5d 100644 --- a/src/vs/workbench/contrib/debug/test/common/mockDebug.ts +++ b/src/vs/workbench/contrib/debug/test/common/mockDebug.ts @@ -6,7 +6,7 @@ import { URI as uri } from 'vs/base/common/uri'; import { Event } from 'vs/base/common/event'; import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; -import { Position } from 'vs/editor/common/core/position'; +import { Position, IPosition } from 'vs/editor/common/core/position'; import { ILaunch, IDebugService, State, IDebugSession, IConfigurationManager, IStackFrame, IBreakpointData, IBreakpointUpdateData, IConfig, IDebugModel, IViewModel, IBreakpoint, LoadedSourceEvent, IThread, IRawModelUpdate, IFunctionBreakpoint, IExceptionBreakpoint, IDebugger, IExceptionInfo, AdapterEndEvent, IReplElement, IExpression, IReplElementSource, IDataBreakpoint } from 'vs/workbench/contrib/debug/common/debug'; import { Source } from 'vs/workbench/contrib/debug/common/debugSource'; import { CompletionItem } from 'vs/editor/common/modes'; @@ -132,6 +132,11 @@ export class MockDebugService implements IDebugService { } export class MockSession implements IDebugSession { + + breakpointsLocations(uri: uri, lineNumber: number): Promise { + throw new Error('Method not implemented.'); + } + dataBreakpointInfo(name: string, variablesReference?: number | undefined): Promise<{ dataId: string | null; description: string; canPersist?: boolean | undefined; }> { throw new Error('Method not implemented.'); } diff --git a/src/vs/workbench/contrib/experiments/common/experimentService.ts b/src/vs/workbench/contrib/experiments/common/experimentService.ts index 4ef7728f7536..04c6afaac8e7 100644 --- a/src/vs/workbench/contrib/experiments/common/experimentService.ts +++ b/src/vs/workbench/contrib/experiments/common/experimentService.ts @@ -19,7 +19,7 @@ import { ITextFileService, StateChange } from 'vs/workbench/services/textfile/co import { CancellationToken } from 'vs/base/common/cancellation'; import { distinct } from 'vs/base/common/arrays'; import { ExtensionType } from 'vs/platform/extensions/common/extensions'; -import { IProductService } from 'vs/platform/product/common/product'; +import { IProductService } from 'vs/platform/product/common/productService'; import { IWorkspaceStatsService } from 'vs/workbench/contrib/stats/common/workspaceStats'; export const enum ExperimentState { diff --git a/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts b/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts index be089c2858d8..92cccf6e09e6 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionTipsService.ts @@ -42,7 +42,7 @@ import { IExperimentService, ExperimentActionType, ExperimentState } from 'vs/wo import { CancellationToken } from 'vs/base/common/cancellation'; import { ExtensionType } from 'vs/platform/extensions/common/extensions'; import { extname } from 'vs/base/common/resources'; -import { IExeBasedExtensionTip, IProductService } from 'vs/platform/product/common/product'; +import { IExeBasedExtensionTip, IProductService } from 'vs/platform/product/common/productService'; import { timeout } from 'vs/base/common/async'; import { IAdsTelemetryService } from 'sql/platform/telemetry/common/telemetry'; // {{SQL CARBON EDIT}} import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys'; // {{SQL CARBON EDIT}} diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index 3f50ed7d1caa..7bdcd441e66f 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -59,7 +59,7 @@ import { ILabelService } from 'vs/platform/label/common/label'; import { isUIExtension } from 'vs/workbench/services/extensions/common/extensionsUtil'; import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; -import { IProductService } from 'vs/platform/product/common/product'; +import { IProductService } from 'vs/platform/product/common/productService'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; @@ -2073,13 +2073,13 @@ export abstract class AbstractConfigureRecommendedExtensionsAction extends Actio removeFrom = removeFrom.filter(x => x.toLowerCase() !== extensionIdLowerCase); return this.jsonEditingService.write(workspaceConfigurationFile, - { + [{ key: 'extensions', value: { recommendations: shouldRecommend ? insertInto : removeFrom, unwantedRecommendations: shouldRecommend ? removeFrom : insertInto } - }, + }], true); }); } @@ -2102,19 +2102,19 @@ export abstract class AbstractConfigureRecommendedExtensionsAction extends Actio if (removeFrom.some(e => e.toLowerCase() === extensionIdLowerCase)) { removeFrom = removeFrom.filter(x => x.toLowerCase() !== extensionIdLowerCase); removeFromPromise = this.jsonEditingService.write(extensionsFileResource, - { + [{ key: shouldRecommend ? 'unwantedRecommendations' : 'recommendations', value: removeFrom - }, + }], true); } return removeFromPromise.then(() => this.jsonEditingService.write(extensionsFileResource, - { + [{ key: shouldRecommend ? 'recommendations' : 'unwantedRecommendations', value: insertInto - }, + }], true) ); }); @@ -2139,7 +2139,7 @@ export abstract class AbstractConfigureRecommendedExtensionsAction extends Actio .then(content => { const workspaceRecommendations = json.parse(content.value.toString())['extensions']; if (!workspaceRecommendations || !workspaceRecommendations.recommendations) { - return this.jsonEditingService.write(workspaceConfigurationFile, { key: 'extensions', value: { recommendations: [] } }, true) + return this.jsonEditingService.write(workspaceConfigurationFile, [{ key: 'extensions', value: { recommendations: [] } }], true) .then(() => this.fileService.readFile(workspaceConfigurationFile)); } return content; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts index a649f6b095fd..3538d6c0ecbf 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts @@ -44,7 +44,7 @@ import { IAction } from 'vs/base/common/actions'; import { ExtensionType, ExtensionIdentifier, IExtensionDescription, isLanguagePackExtension } from 'vs/platform/extensions/common/extensions'; import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService'; import { CancelablePromise, createCancelablePromise } from 'vs/base/common/async'; -import { IProductService } from 'vs/platform/product/common/product'; +import { IProductService } from 'vs/platform/product/common/productService'; import { SeverityIcon } from 'vs/platform/severityIcon/common/severityIcon'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts index 20b4cb3fd7b6..b09958f639ca 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts @@ -36,7 +36,7 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag import { IFileService } from 'vs/platform/files/common/files'; import { IExtensionManifest, ExtensionType, IExtension as IPlatformExtension, isLanguagePackExtension } from 'vs/platform/extensions/common/extensions'; import { IModeService } from 'vs/editor/common/services/modeService'; -import { IProductService } from 'vs/platform/product/common/product'; +import { IProductService } from 'vs/platform/product/common/productService'; import { asDomUri } from 'vs/base/browser/dom'; import { isEngineValid } from 'vs/platform/extensions/common/extensionValidator'; // {{SQL CARBON EDIT}} diff --git a/src/vs/workbench/contrib/extensions/browser/media/extensionActions.css b/src/vs/workbench/contrib/extensions/browser/media/extensionActions.css index 22c7193cf7ff..585422ab3337 100644 --- a/src/vs/workbench/contrib/extensions/browser/media/extensionActions.css +++ b/src/vs/workbench/contrib/extensions/browser/media/extensionActions.css @@ -78,7 +78,7 @@ margin: 0.15em; } -.monaco-action-bar .action-item .action-label.system-disable.icon { +.monaco-action-bar .action-item .action-label.system-disable.codicon { opacity: 1; height: 18px; width: 10px; diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionProfileService.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionProfileService.ts index fb8f7304a4a7..df80b52e6b0a 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensionProfileService.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionProfileService.ts @@ -15,7 +15,7 @@ import { IEditorService } from 'vs/workbench/services/editor/common/editorServic import { IWindowsService } from 'vs/platform/windows/common/windows'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { randomPort } from 'vs/base/node/ports'; -import product from 'vs/platform/product/node/product'; +import product from 'vs/platform/product/common/product'; import { RuntimeExtensionsInput } from 'vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsInput'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { ExtensionHostProfiler } from 'vs/workbench/services/extensions/electron-browser/extensionHostProfiler'; diff --git a/src/vs/workbench/contrib/extensions/electron-browser/extensionsSlowActions.ts b/src/vs/workbench/contrib/extensions/electron-browser/extensionsSlowActions.ts index a6cf97a01343..375b1a10fe9a 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/extensionsSlowActions.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/extensionsSlowActions.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as os from 'os'; -import pkg from 'vs/platform/product/node/package'; +import product from 'vs/platform/product/common/product'; import { Action } from 'vs/base/common/actions'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { URI } from 'vs/base/common/uri'; @@ -139,7 +139,7 @@ class ReportExtensionSlowAction extends Action { - Extension Name: \`${this.extension.name}\` - Extension Version: \`${this.extension.version}\` - OS Version: \`${osVersion}\` -- VSCode version: \`${pkg.version}\`\n\n${message}`); +- VSCode version: \`${product.version}\`\n\n${message}`); const url = `${this.repoInfo.base}/${this.repoInfo.owner}/${this.repoInfo.repo}/issues/new/?body=${body}&title=${title}`; this._openerService.open(URI.parse(url)); diff --git a/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts b/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts index 2f7a2652933d..449fee92d9a4 100644 --- a/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts +++ b/src/vs/workbench/contrib/extensions/electron-browser/runtimeExtensionsEditor.ts @@ -6,8 +6,7 @@ import 'vs/css!./media/runtimeExtensionsEditor'; import * as nls from 'vs/nls'; import * as os from 'os'; -import product from 'vs/platform/product/node/product'; -import pkg from 'vs/platform/product/node/package'; +import product from 'vs/platform/product/common/product'; import { Action, IAction } from 'vs/base/common/actions'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; @@ -523,7 +522,7 @@ export class ReportExtensionIssueAction extends Action { - Extension Name: \`${extension.description.name}\` - Extension Version: \`${extension.description.version}\` - OS Version: \`${osVersion}\` -- VSCode version: \`${pkg.version}\`\n\n${message}` +- VSCode version: \`${product.version}\`\n\n${message}` ); return `${baseUrl}${queryStringPrefix}body=${body}&title=${encodeURIComponent(title)}`; 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 b002e5c3f51c..84716874c6ff 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 @@ -41,7 +41,7 @@ import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedPr import { CancellationToken } from 'vs/base/common/cancellation'; import { ILabelService } from 'vs/platform/label/common/label'; import { ExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/electron-browser/extensionManagementServerService'; -import { IProductService } from 'vs/platform/product/common/product'; +import { IProductService } from 'vs/platform/product/common/productService'; import { Schemas } from 'vs/base/common/network'; import { REMOTE_HOST_SCHEME } from 'vs/platform/remote/common/remoteHosts'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; diff --git a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsTipsService.test.ts b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsTipsService.test.ts index 36bd1e0ad1de..a9a9cba80204 100644 --- a/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsTipsService.test.ts +++ b/src/vs/workbench/contrib/extensions/test/electron-browser/extensionsTipsService.test.ts @@ -51,7 +51,7 @@ import { NullLogService } from 'vs/platform/log/common/log'; 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/product'; +import { IProductService } from 'vs/platform/product/common/productService'; const mockExtensionGallery: IGalleryExtension[] = [ aGalleryExtension('MockExtension1', { 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 5a3856082516..180a54d04067 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 @@ -41,7 +41,7 @@ import { RemoteAgentService } from 'vs/workbench/services/remote/electron-browse import { ExtensionIdentifier, ExtensionType, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; import { ExtensionManagementServerService } from 'vs/workbench/services/extensionManagement/electron-browser/extensionManagementServerService'; -import { IProductService } from 'vs/platform/product/common/product'; +import { IProductService } from 'vs/platform/product/common/productService'; import { ILabelService } from 'vs/platform/label/common/label'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { MockContextKeyService } from 'vs/platform/keybinding/test/common/mockKeybindingService'; diff --git a/src/vs/workbench/contrib/feedback/browser/feedback.ts b/src/vs/workbench/contrib/feedback/browser/feedback.ts index cf570b3efdcf..58342a72f21f 100644 --- a/src/vs/workbench/contrib/feedback/browser/feedback.ts +++ b/src/vs/workbench/contrib/feedback/browser/feedback.ts @@ -19,7 +19,7 @@ import { Button } from 'vs/base/browser/ui/button/button'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification } from 'vs/base/common/actions'; import { IStatusbarService } from 'vs/platform/statusbar/common/statusbar'; -import { IProductService } from 'vs/platform/product/common/product'; +import { IProductService } from 'vs/platform/product/common/productService'; import { IOpenerService } from 'vs/platform/opener/common/opener'; export interface IFeedback { diff --git a/src/vs/workbench/contrib/feedback/browser/feedbackStatusbarItem.ts b/src/vs/workbench/contrib/feedback/browser/feedbackStatusbarItem.ts index 9896eeccc5ce..7a43e34344a9 100644 --- a/src/vs/workbench/contrib/feedback/browser/feedbackStatusbarItem.ts +++ b/src/vs/workbench/contrib/feedback/browser/feedbackStatusbarItem.ts @@ -7,7 +7,7 @@ import { Disposable } from 'vs/base/common/lifecycle'; import { FeedbackDropdown, IFeedback, IFeedbackDelegate } from 'vs/workbench/contrib/feedback/browser/feedback'; import { IContextViewService } from 'vs/platform/contextview/browser/contextView'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IProductService } from 'vs/platform/product/common/product'; +import { IProductService } from 'vs/platform/product/common/productService'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IStatusbarService, StatusbarAlignment, IStatusbarEntry, IStatusbarEntryAccessor } from 'vs/platform/statusbar/common/statusbar'; import { localize } from 'vs/nls'; diff --git a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts index 2a038c53acd3..7a9415c11f52 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts @@ -14,7 +14,7 @@ import { openWindowCommand, REVEAL_IN_OS_COMMAND_ID, COPY_PATH_COMMAND_ID, REVEA import { CommandsRegistry, ICommandHandler } from 'vs/platform/commands/common/commands'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; -import { isWindows, isMacintosh, isWeb } from 'vs/base/common/platform'; +import { isWindows, isMacintosh } from 'vs/base/common/platform'; import { FilesExplorerFocusCondition, ExplorerRootContext, ExplorerFolderContext, ExplorerResourceNotReadonlyContext, ExplorerResourceCut, IExplorerService, ExplorerResourceMoveableToTrash, ExplorerViewletVisibleContext } from 'vs/workbench/contrib/files/common/files'; import { ADD_ROOT_FOLDER_COMMAND_ID, ADD_ROOT_FOLDER_LABEL } from 'vs/workbench/browser/actions/workspaceCommands'; import { CLOSE_SAVED_EDITORS_COMMAND_ID, CLOSE_EDITORS_IN_GROUP_COMMAND_ID, CLOSE_EDITOR_COMMAND_ID, CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID } from 'vs/workbench/browser/parts/editor/editorCommands'; @@ -23,9 +23,9 @@ import { ResourceContextKey } from 'vs/workbench/common/resources'; import { WorkbenchListDoubleSelection } from 'vs/platform/list/browser/listService'; import { URI } from 'vs/base/common/uri'; import { Schemas } from 'vs/base/common/network'; -import { SupportsWorkspacesContext, IsWebContext, RemoteFileDialogContext, WorkspaceFolderCountContext } from 'vs/workbench/browser/contextkeys'; +import { SupportsWorkspacesContext, IsWebContext, WorkspaceFolderCountContext } from 'vs/workbench/browser/contextkeys'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { OpenFileFolderAction, OpenLocalFileFolderCommand, OpenFileAction, OpenFolderAction, OpenLocalFileCommand, OpenLocalFolderCommand, OpenWorkspaceAction, SaveLocalFileCommand } from 'vs/workbench/browser/actions/workspaceActions'; +import { OpenFileFolderAction, OpenFileAction, OpenFolderAction, OpenWorkspaceAction } from 'vs/workbench/browser/actions/workspaceActions'; import { ActiveEditorIsSaveableContext } from 'vs/workbench/common/editor'; import { SidebarFocusContext } from 'vs/workbench/common/viewlet'; import { registerAndGetAmdImageURL } from 'vs/base/common/amd'; @@ -48,56 +48,8 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(CompareWithClipboardAc registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleAutoSaveAction, ToggleAutoSaveAction.ID, ToggleAutoSaveAction.LABEL), 'File: Toggle Auto Save', category.value); -const fileCategory = nls.localize('file', "File"); - -if (isMacintosh) { - registry.registerWorkbenchAction(new SyncActionDescriptor(OpenFileFolderAction, OpenFileFolderAction.ID, OpenFileFolderAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_O }), 'File: Open...', fileCategory); - if (!isWeb) { - KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: OpenLocalFileFolderCommand.ID, - weight: KeybindingWeight.WorkbenchContrib, - primary: KeyMod.CtrlCmd | KeyCode.KEY_O, - when: RemoteFileDialogContext, - description: { description: OpenLocalFileFolderCommand.LABEL, args: [] }, - handler: OpenLocalFileFolderCommand.handler() - }); - } -} else { - registry.registerWorkbenchAction(new SyncActionDescriptor(OpenFileAction, OpenFileAction.ID, OpenFileAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_O }), 'File: Open File...', fileCategory); - registry.registerWorkbenchAction(new SyncActionDescriptor(OpenFolderAction, OpenFolderAction.ID, OpenFolderAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_O) }), 'File: Open Folder...', fileCategory); - if (!isWeb) { - KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: OpenLocalFileCommand.ID, - weight: KeybindingWeight.WorkbenchContrib, - primary: KeyMod.CtrlCmd | KeyCode.KEY_O, - when: RemoteFileDialogContext, - description: { description: OpenLocalFileCommand.LABEL, args: [] }, - handler: OpenLocalFileCommand.handler() - }); - KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: OpenLocalFolderCommand.ID, - weight: KeybindingWeight.WorkbenchContrib, - primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_O), - when: RemoteFileDialogContext, - description: { description: OpenLocalFolderCommand.LABEL, args: [] }, - handler: OpenLocalFolderCommand.handler() - }); - } -} - -if (!isWeb) { - KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: SaveLocalFileCommand.ID, - weight: KeybindingWeight.WorkbenchContrib, - primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_S, - when: RemoteFileDialogContext, - description: { description: SaveLocalFileCommand.LABEL, args: [] }, - handler: SaveLocalFileCommand.handler() - }); -} - const workspacesCategory = nls.localize('workspaces', "Workspaces"); -registry.registerWorkbenchAction(new SyncActionDescriptor(OpenWorkspaceAction, OpenWorkspaceAction.ID, OpenWorkspaceAction.LABEL), 'Workspaces: Open Workspace...', workspacesCategory, SupportsWorkspacesContext); +registry.registerWorkbenchAction(new SyncActionDescriptor(OpenWorkspaceAction, OpenWorkspaceAction.ID, OpenWorkspaceAction.LABEL), 'Workspaces: Open Workspace...', workspacesCategory); // Commands CommandsRegistry.registerCommand('_files.windowOpen', openWindowCommand); @@ -570,7 +522,7 @@ MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { id: REMOVE_ROOT_FOLDER_COMMAND_ID, title: REMOVE_ROOT_FOLDER_LABEL }, - when: ContextKeyExpr.and(ExplorerRootContext, ExplorerFolderContext) + when: ContextKeyExpr.and(ExplorerRootContext, ExplorerFolderContext, SupportsWorkspacesContext) }); MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { @@ -714,8 +666,7 @@ MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { id: OpenWorkspaceAction.ID, title: nls.localize({ key: 'miOpenWorkspace', comment: ['&& denotes a mnemonic'] }, "Open Wor&&kspace...") }, - order: 3, - when: SupportsWorkspacesContext + order: 3 }); MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { diff --git a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts index 4e8b9805f452..a1d3b67226da 100644 --- a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts +++ b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts @@ -381,13 +381,16 @@ export class OpenEditorsView extends ViewletPanel { private focusActiveEditor(): void { if (this.list.length && this.editorGroupService.activeGroup) { const index = this.getIndex(this.editorGroupService.activeGroup, this.editorGroupService.activeGroup.activeEditor); - this.list.setFocus([index]); - this.list.setSelection([index]); - this.list.reveal(index); - } else { - this.list.setFocus([]); - this.list.setSelection([]); + if (index >= 0) { + this.list.setFocus([index]); + this.list.setSelection([index]); + this.list.reveal(index); + return; + } } + + this.list.setFocus([]); + this.list.setSelection([]); } private onConfigurationChange(event: IConfigurationChangeEvent): void { diff --git a/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts b/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts index 23e31480b122..b68ff49bc45f 100644 --- a/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts +++ b/src/vs/workbench/contrib/files/common/editors/fileEditorInput.ts @@ -111,7 +111,7 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput { setPreferredEncoding(encoding: string): void { this.preferredEncoding = encoding; - this.forceOpenAs = ForceOpenAs.Text; // encoding is a good hint to open the file as text + this.setForceOpenAsText(); // encoding is a good hint to open the file as text } getPreferredMode(): string | undefined { @@ -129,7 +129,7 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput { setPreferredMode(mode: string): void { this.preferredMode = mode; - this.forceOpenAs = ForceOpenAs.Text; // mode is a good hint to open the file as text + this.setForceOpenAsText(); // mode is a good hint to open the file as text } setForceOpenAsText(): void { @@ -165,21 +165,15 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput { } getDescription(verbosity: Verbosity = Verbosity.MEDIUM): string { - let description: string; switch (verbosity) { case Verbosity.SHORT: - description = this.shortDescription; - break; + return this.shortDescription; case Verbosity.LONG: - description = this.longDescription; - break; + return this.longDescription; case Verbosity.MEDIUM: default: - description = this.mediumDescription; - break; + return this.mediumDescription; } - - return description; } @memoize @@ -198,24 +192,16 @@ export class FileEditorInput extends EditorInput implements IFileEditorInput { } getTitle(verbosity: Verbosity): string { - let title: string; switch (verbosity) { case Verbosity.SHORT: - title = this.shortTitle; // already decorated by getName() - break; + return this.shortTitle; default: case Verbosity.MEDIUM: - title = this.mediumTitle; - title = this.decorateLabel(title); - break; + return this.decorateLabel(this.mediumTitle); case Verbosity.LONG: - title = this.longTitle; - title = this.decorateLabel(title); - break; + return this.decorateLabel(this.longTitle); } - - return title; } private decorateLabel(label: string): string { diff --git a/src/vs/workbench/contrib/issue/electron-browser/issue.contribution.ts b/src/vs/workbench/contrib/issue/electron-browser/issue.contribution.ts index acfebcc568a6..0dd6b7434bd0 100644 --- a/src/vs/workbench/contrib/issue/electron-browser/issue.contribution.ts +++ b/src/vs/workbench/contrib/issue/electron-browser/issue.contribution.ts @@ -5,7 +5,7 @@ import { Registry } from 'vs/platform/registry/common/platform'; import * as nls from 'vs/nls'; -import product from 'vs/platform/product/node/product'; +import product from 'vs/platform/product/common/product'; import { SyncActionDescriptor, ICommandAction, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; import { ReportPerformanceIssueUsingReporterAction, OpenProcessExplorer } from 'vs/workbench/contrib/issue/electron-browser/issueActions'; diff --git a/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts b/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts index 36b15836b902..cc515c8994cb 100644 --- a/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts +++ b/src/vs/workbench/contrib/localizations/browser/localizations.contribution.ts @@ -80,7 +80,7 @@ export class LocalizationWorkbenchContribution extends Disposable implements IWo [{ label: updateAndRestart ? localize('yes', "Yes") : localize('restart now', "Restart Now"), run: () => { - const updatePromise = updateAndRestart ? this.jsonEditingService.write(this.environmentService.localeResource, { key: 'locale', value: locale }, true) : Promise.resolve(undefined); + const updatePromise = updateAndRestart ? this.jsonEditingService.write(this.environmentService.localeResource, [{ key: 'locale', value: locale }], true) : Promise.resolve(undefined); updatePromise.then(() => this.windowsService.relaunch({}), e => this.notificationService.error(e)); } }], diff --git a/src/vs/workbench/contrib/localizations/browser/localizationsActions.ts b/src/vs/workbench/contrib/localizations/browser/localizationsActions.ts index 7192674a43d6..80052645f862 100644 --- a/src/vs/workbench/contrib/localizations/browser/localizationsActions.ts +++ b/src/vs/workbench/contrib/localizations/browser/localizationsActions.ts @@ -65,7 +65,7 @@ export class ConfigureLocaleAction extends Action { } if (selectedLanguage) { - await this.jsonEditingService.write(this.environmentService.localeResource, { key: 'locale', value: selectedLanguage.label }, true); + await this.jsonEditingService.write(this.environmentService.localeResource, [{ key: 'locale', value: selectedLanguage.label }], true); const restart = await this.dialogService.confirm({ type: 'info', message: localize('relaunchDisplayLanguageMessage', "A restart is required for the change in display language to take effect."), diff --git a/src/vs/workbench/contrib/markers/browser/markersPanel.ts b/src/vs/workbench/contrib/markers/browser/markersPanel.ts index 9f99d73b7477..1a1ccf254c51 100644 --- a/src/vs/workbench/contrib/markers/browser/markersPanel.ts +++ b/src/vs/workbench/contrib/markers/browser/markersPanel.ts @@ -117,7 +117,7 @@ export class MarkersPanel extends Panel implements IMarkerFilterController { this.rangeHighlightDecorations = this._register(this.instantiationService.createInstance(RangeHighlightDecorations)); // actions - this.collapseAllAction = this._register(new Action('vs.tree.collapse', localize('collapseAll', "Collapse All"), 'monaco-tree-action collapse-all', true, async () => this.collapseAll())); + this.collapseAllAction = this._register(new Action('vs.tree.collapse', localize('collapseAll', "Collapse All"), 'monaco-tree-action codicon-collapse-all', true, async () => this.collapseAll())); this.filterAction = this._register(this.instantiationService.createInstance(MarkersFilterAction, { filterText: this.panelState['filter'] || '', filterHistory: this.panelState['filterHistory'] || [], useFilesExclude: !!this.panelState['useFilesExclude'] })); } diff --git a/src/vs/workbench/contrib/markers/browser/markersPanelActions.ts b/src/vs/workbench/contrib/markers/browser/markersPanelActions.ts index 13b2fdbbb73a..9e58ba1ca7eb 100644 --- a/src/vs/workbench/contrib/markers/browser/markersPanelActions.ts +++ b/src/vs/workbench/contrib/markers/browser/markersPanelActions.ts @@ -213,7 +213,7 @@ export class MarkersFilterActionViewItem extends BaseActionViewItem { private createFilesExcludeCheckbox(container: HTMLElement): void { const filesExcludeFilter = this._register(new Checkbox({ - actionClassName: 'markers-panel-filter-filesExclude', + actionClassName: 'codicon codicon-exclude', title: this.action.useFilesExclude ? Messages.MARKERS_PANEL_ACTION_TOOLTIP_DO_NOT_USE_FILES_EXCLUDE : Messages.MARKERS_PANEL_ACTION_TOOLTIP_USE_FILES_EXCLUDE, isChecked: this.action.useFilesExclude })); diff --git a/src/vs/workbench/contrib/markers/browser/media/exclude-settings-dark.svg b/src/vs/workbench/contrib/markers/browser/media/exclude-settings-dark.svg deleted file mode 100644 index 0b1694dc2f18..000000000000 --- a/src/vs/workbench/contrib/markers/browser/media/exclude-settings-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/markers/browser/media/exclude-settings-hc.svg b/src/vs/workbench/contrib/markers/browser/media/exclude-settings-hc.svg deleted file mode 100644 index ba88235419ae..000000000000 --- a/src/vs/workbench/contrib/markers/browser/media/exclude-settings-hc.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/markers/browser/media/exclude-settings-light.svg b/src/vs/workbench/contrib/markers/browser/media/exclude-settings-light.svg deleted file mode 100644 index 114ec3f0fec6..000000000000 --- a/src/vs/workbench/contrib/markers/browser/media/exclude-settings-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/markers/browser/media/markers.css b/src/vs/workbench/contrib/markers/browser/media/markers.css index 286d8933813b..b7c60be8d875 100644 --- a/src/vs/workbench/contrib/markers/browser/media/markers.css +++ b/src/vs/workbench/contrib/markers/browser/media/markers.css @@ -56,19 +56,6 @@ display: none; } -.vs .markers-panel-action-filter > .markers-panel-filter-controls > .markers-panel-filter-filesExclude { - background: url('exclude-settings-light.svg') center center no-repeat; -} - -.vs-dark .markers-panel-action-filter > .markers-panel-filter-controls > .markers-panel-filter-filesExclude, -.hc-black .markers-panel-action-filter > .markers-panel-filter-controls > .markers-panel-filter-filesExclude { - background: url('exclude-settings-dark.svg') center center no-repeat; -} - -.hc-black .markers-panel-action-filter > .markers-panel-filter-controls > .markers-panel-filter-filesExclude { - background: url('exclude-settings-hc.svg') center center no-repeat; -} - .markers-panel .markers-panel-container { height: 100%; } diff --git a/src/vs/workbench/contrib/output/browser/media/clear-dark.svg b/src/vs/workbench/contrib/output/browser/media/clear-dark.svg deleted file mode 100644 index 04d64ab41ca3..000000000000 --- a/src/vs/workbench/contrib/output/browser/media/clear-dark.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/src/vs/workbench/contrib/output/browser/media/clear-hc.svg b/src/vs/workbench/contrib/output/browser/media/clear-hc.svg deleted file mode 100644 index 44a41edd3b3f..000000000000 --- a/src/vs/workbench/contrib/output/browser/media/clear-hc.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/src/vs/workbench/contrib/output/browser/media/clear-light.svg b/src/vs/workbench/contrib/output/browser/media/clear-light.svg deleted file mode 100644 index f6a51c856f00..000000000000 --- a/src/vs/workbench/contrib/output/browser/media/clear-light.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/src/vs/workbench/contrib/output/browser/media/locked-dark.svg b/src/vs/workbench/contrib/output/browser/media/locked-dark.svg deleted file mode 100644 index ebdc1c0078a1..000000000000 --- a/src/vs/workbench/contrib/output/browser/media/locked-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/output/browser/media/locked-hc.svg b/src/vs/workbench/contrib/output/browser/media/locked-hc.svg deleted file mode 100644 index 350dc4177107..000000000000 --- a/src/vs/workbench/contrib/output/browser/media/locked-hc.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/output/browser/media/locked-light.svg b/src/vs/workbench/contrib/output/browser/media/locked-light.svg deleted file mode 100644 index 03b035136882..000000000000 --- a/src/vs/workbench/contrib/output/browser/media/locked-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/output/browser/media/open-file-dark.svg b/src/vs/workbench/contrib/output/browser/media/open-file-dark.svg deleted file mode 100644 index ed302ae13984..000000000000 --- a/src/vs/workbench/contrib/output/browser/media/open-file-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/output/browser/media/open-file-hc.svg b/src/vs/workbench/contrib/output/browser/media/open-file-hc.svg deleted file mode 100644 index bba63494e6fb..000000000000 --- a/src/vs/workbench/contrib/output/browser/media/open-file-hc.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/output/browser/media/open-file-light.svg b/src/vs/workbench/contrib/output/browser/media/open-file-light.svg deleted file mode 100644 index 392a840c5ef6..000000000000 --- a/src/vs/workbench/contrib/output/browser/media/open-file-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/output/browser/media/output.css b/src/vs/workbench/contrib/output/browser/media/output.css deleted file mode 100644 index f61c5a15412d..000000000000 --- a/src/vs/workbench/contrib/output/browser/media/output.css +++ /dev/null @@ -1,52 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -.monaco-workbench .output-action.clear-output { - background: url('clear-light.svg') center center no-repeat; -} - -.vs-dark .monaco-workbench .output-action.clear-output { - background: url('clear-dark.svg') center center no-repeat; -} - -.hc-black .monaco-workbench .output-action.clear-output { - background: url('clear-hc.svg') center center no-repeat; -} - -.monaco-workbench .output-action.output-scroll-lock { - background: url('locked-light.svg') center center no-repeat; -} - -.vs-dark .monaco-workbench .output-action.output-scroll-lock { - background: url('locked-dark.svg') center center no-repeat; -} - -.hc-black .monaco-workbench .output-action.output-scroll-lock { - background: url('locked-hc.svg') center center no-repeat; -} - -.monaco-workbench .output-action.output-scroll-unlock { - background: url('unlocked-light.svg') center center no-repeat; -} - -.vs-dark .monaco-workbench .output-action.output-scroll-unlock { - background: url('unlocked-dark.svg') center center no-repeat; -} - -.hc-black .monaco-workbench .output-action.output-scroll-unlock { - background: url('unlocked-hc.svg') center center no-repeat; -} - -.monaco-workbench .output-action.open-log-file { - background: url('open-file-light.svg') center center no-repeat; -} - -.vs-dark .monaco-workbench .output-action.open-log-file { - background: url('open-file-dark.svg') center center no-repeat; -} - -.hc-black .monaco-workbench .output-action.open-log-file { - background: url('open-file-hc.svg') center center no-repeat; -} diff --git a/src/vs/workbench/contrib/output/browser/media/unlocked-dark.svg b/src/vs/workbench/contrib/output/browser/media/unlocked-dark.svg deleted file mode 100644 index 3b7947f9752f..000000000000 --- a/src/vs/workbench/contrib/output/browser/media/unlocked-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/output/browser/media/unlocked-hc.svg b/src/vs/workbench/contrib/output/browser/media/unlocked-hc.svg deleted file mode 100644 index 100cfc1385a7..000000000000 --- a/src/vs/workbench/contrib/output/browser/media/unlocked-hc.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/output/browser/media/unlocked-light.svg b/src/vs/workbench/contrib/output/browser/media/unlocked-light.svg deleted file mode 100644 index 0c3cb4b93b0b..000000000000 --- a/src/vs/workbench/contrib/output/browser/media/unlocked-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/output/browser/outputActions.ts b/src/vs/workbench/contrib/output/browser/outputActions.ts index b63fe7262a72..2099edf33bf5 100644 --- a/src/vs/workbench/contrib/output/browser/outputActions.ts +++ b/src/vs/workbench/contrib/output/browser/outputActions.ts @@ -45,7 +45,7 @@ export class ClearOutputAction extends Action { id: string, label: string, @IOutputService private readonly outputService: IOutputService ) { - super(id, label, 'output-action clear-output'); + super(id, label, 'output-action codicon-clear-all'); } public run(): Promise { @@ -67,7 +67,7 @@ export class ToggleOrSetOutputScrollLockAction extends Action { public static readonly LABEL = nls.localize({ key: 'toggleOutputScrollLock', comment: ['Turn on / off automatic output scrolling'] }, "Toggle Output Scroll Lock"); constructor(id: string, label: string, @IOutputService private readonly outputService: IOutputService) { - super(id, label, 'output-action output-scroll-unlock'); + super(id, label, 'output-action codicon-unlock'); this._register(this.outputService.onActiveOutputChannel(channel => { const activeChannel = this.outputService.getActiveChannel(); if (activeChannel) { @@ -94,10 +94,10 @@ export class ToggleOrSetOutputScrollLockAction extends Action { private setClassAndLabel(locked: boolean) { if (locked) { - this.class = 'output-action output-scroll-lock'; + this.class = 'output-action codicon-lock'; this.label = nls.localize('outputScrollOn', "Turn Auto Scrolling On"); } else { - this.class = 'output-action output-scroll-unlock'; + this.class = 'output-action codicon-unlock'; this.label = nls.localize('outputScrollOff', "Turn Auto Scrolling Off"); } } @@ -185,7 +185,7 @@ export class OpenLogOutputFile extends Action { @IEditorService private readonly editorService: IEditorService, @IInstantiationService private readonly instantiationService: IInstantiationService ) { - super(OpenLogOutputFile.ID, OpenLogOutputFile.LABEL, 'output-action open-log-file'); + super(OpenLogOutputFile.ID, OpenLogOutputFile.LABEL, 'output-action codicon-go-to-file'); this._register(this.outputService.onActiveOutputChannel(this.update, this)); this.update(); } diff --git a/src/vs/workbench/contrib/output/browser/outputPanel.ts b/src/vs/workbench/contrib/output/browser/outputPanel.ts index ab5723a513da..92907b8e6893 100644 --- a/src/vs/workbench/contrib/output/browser/outputPanel.ts +++ b/src/vs/workbench/contrib/output/browser/outputPanel.ts @@ -3,7 +3,6 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import 'vs/css!./media/output'; import * as nls from 'vs/nls'; import { Action, IAction } from 'vs/base/common/actions'; import { IActionViewItem } from 'vs/base/browser/ui/actionbar/actionbar'; diff --git a/src/vs/workbench/contrib/performance/electron-browser/perfviewEditor.ts b/src/vs/workbench/contrib/performance/electron-browser/perfviewEditor.ts index f1c18dab7a09..8039bd2521eb 100644 --- a/src/vs/workbench/contrib/performance/electron-browser/perfviewEditor.ts +++ b/src/vs/workbench/contrib/performance/electron-browser/perfviewEditor.ts @@ -20,8 +20,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { writeTransientState } from 'vs/workbench/contrib/codeEditor/browser/toggleWordWrap'; import { mergeSort } from 'vs/base/common/arrays'; -import product from 'vs/platform/product/node/product'; -import pkg from 'vs/platform/product/node/package'; +import product from 'vs/platform/product/common/product'; export class PerfviewContrib { @@ -127,7 +126,7 @@ class PerfModelContentProvider implements ITextModelContentProvider { private _addSummary(md: MarkdownBuilder, metrics: IStartupMetrics): void { md.heading(2, 'System Info'); - md.li(`${product.nameShort}: ${pkg.version} (${product.commit || '0000000'})`); + md.li(`${product.nameShort}: ${product.version} (${product.commit || '0000000'})`); md.li(`OS: ${metrics.platform}(${metrics.release})`); if (metrics.cpus) { md.li(`CPUs: ${metrics.cpus.model}(${metrics.cpus.count} x ${metrics.cpus.speed})`); diff --git a/src/vs/workbench/contrib/performance/electron-browser/startupProfiler.ts b/src/vs/workbench/contrib/performance/electron-browser/startupProfiler.ts index 83155827dc6b..eabe8ac98832 100644 --- a/src/vs/workbench/contrib/performance/electron-browser/startupProfiler.ts +++ b/src/vs/workbench/contrib/performance/electron-browser/startupProfiler.ts @@ -10,7 +10,7 @@ import { localize } from 'vs/nls'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; -import product from 'vs/platform/product/node/product'; +import product from 'vs/platform/product/common/product'; import { IWindowsService } from 'vs/platform/windows/common/windows'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { PerfviewInput } from 'vs/workbench/contrib/performance/electron-browser/perfviewEditor'; diff --git a/src/vs/workbench/contrib/performance/electron-browser/startupTimings.ts b/src/vs/workbench/contrib/performance/electron-browser/startupTimings.ts index 2a595ecc954e..ac2128dafc7d 100644 --- a/src/vs/workbench/contrib/performance/electron-browser/startupTimings.ts +++ b/src/vs/workbench/contrib/performance/electron-browser/startupTimings.ts @@ -10,7 +10,7 @@ import { onUnexpectedError } from 'vs/base/common/errors'; import { isCodeEditor } from 'vs/editor/browser/editorBrowser'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { ILifecycleService, StartupKind } from 'vs/platform/lifecycle/common/lifecycle'; -import product from 'vs/platform/product/node/product'; +import product from 'vs/platform/product/common/product'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IUpdateService } from 'vs/platform/update/common/update'; import { IWindowsService } from 'vs/platform/windows/common/windows'; diff --git a/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts b/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts index 26d4db2fe49a..115dbf5a6ef3 100644 --- a/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts +++ b/src/vs/workbench/contrib/preferences/browser/preferencesSearch.ts @@ -24,7 +24,7 @@ import { ExtensionType } from 'vs/platform/extensions/common/extensions'; import { nullRange } from 'vs/workbench/services/preferences/common/preferencesModels'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IStringDictionary } from 'vs/base/common/collections'; -import { IProductService } from 'vs/platform/product/common/product'; +import { IProductService } from 'vs/platform/product/common/productService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; export interface IEndpointDetails { diff --git a/src/vs/workbench/contrib/preferences/browser/settingsLayout.ts b/src/vs/workbench/contrib/preferences/browser/settingsLayout.ts index fa1afa47b15a..833b9a17e1e5 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsLayout.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsLayout.ts @@ -195,6 +195,11 @@ export const tocData: ITOCEntry = { id: 'application/telemetry', label: localize('telemetry', "Telemetry"), settings: ['telemetry.*'] + }, + { + id: 'application/sync', + label: localize('sync', "Sync"), + settings: ['userConfiguration.*'] } ] } @@ -218,3 +223,5 @@ export const knownAcronyms = new Set(); export const knownTermMappings = new Map(); knownTermMappings.set('power shell', 'PowerShell'); knownTermMappings.set('powershell', 'PowerShell'); +knownTermMappings.set('javascript', 'JavaScript'); +knownTermMappings.set('typescript', 'TypeScript'); diff --git a/src/vs/workbench/contrib/relauncher/common/relauncher.contribution.ts b/src/vs/workbench/contrib/relauncher/common/relauncher.contribution.ts index 019ecca7bb03..c86df42212da 100644 --- a/src/vs/workbench/contrib/relauncher/common/relauncher.contribution.ts +++ b/src/vs/workbench/contrib/relauncher/common/relauncher.contribution.ts @@ -171,7 +171,7 @@ export class WorkspaceChangeExtHostRelauncher extends Disposable implements IWor if (environmentService.configuration.remoteAuthority) { windowService.reloadWindow(); // TODO@aeschli, workaround - } else { + } else if (isNative) { extensionService.restartExtensionHost(); } }, 10)); diff --git a/src/vs/workbench/contrib/remote/browser/remote.ts b/src/vs/workbench/contrib/remote/browser/remote.ts index 8da04bcf7608..a6ca519382f2 100644 --- a/src/vs/workbench/contrib/remote/browser/remote.ts +++ b/src/vs/workbench/contrib/remote/browser/remote.ts @@ -38,6 +38,15 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IWorkbenchActionRegistry, Extensions as WorkbenchActionExtensions } from 'vs/workbench/common/actions'; import { SyncActionDescriptor } from 'vs/platform/actions/common/actions'; +import { IProgress, IProgressStep, IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; +import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { ReconnectionWaitEvent, PersistentConnectionEventType } from 'vs/platform/remote/common/remoteAgentConnection'; +import Severity from 'vs/base/common/severity'; +import { ReloadWindowAction } from 'vs/workbench/browser/actions/windowActions'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; interface HelpInformation { extensionDescription: IExtensionDescription; @@ -126,7 +135,7 @@ class HelpDataSource implements IAsyncDataSource { } getChildren(element: any) { - if (element instanceof HelpModel) { + if (element instanceof HelpModel && element.items) { return element.items; } @@ -201,7 +210,7 @@ class IssueReporterItem implements IHelpItem { } class HelpModel { - items: IHelpItem[]; + items: IHelpItem[] | undefined; constructor( viewModel: IViewModel, @@ -445,3 +454,189 @@ Registry.as(WorkbenchActionExtensions.WorkbenchActions 'View: Show Remote Explorer', nls.localize('view', "View") ); + + +class ProgressReporter { + private _currentProgress: IProgress | null = null; + private lastReport: string | null = null; + + constructor(currentProgress: IProgress | null) { + this._currentProgress = currentProgress; + } + + set currentProgress(progress: IProgress) { + this._currentProgress = progress; + } + + report(message?: string) { + if (message) { + this.lastReport = message; + } + + if (this.lastReport && this._currentProgress) { + this._currentProgress.report({ message: this.lastReport }); + } + } +} + +class RemoteAgentConnectionStatusListener implements IWorkbenchContribution { + constructor( + @IRemoteAgentService remoteAgentService: IRemoteAgentService, + @IProgressService progressService: IProgressService, + @IDialogService dialogService: IDialogService, + @ICommandService commandService: ICommandService, + @IContextKeyService contextKeyService: IContextKeyService + ) { + const connection = remoteAgentService.getConnection(); + if (connection) { + let currentProgressPromiseResolve: (() => void) | null = null; + let progressReporter: ProgressReporter | null = null; + let lastLocation: ProgressLocation | null = null; + let currentTimer: ReconnectionTimer | null = null; + let reconnectWaitEvent: ReconnectionWaitEvent | null = null; + let disposableListener: IDisposable | null = null; + + function showProgress(location: ProgressLocation, buttons?: string[]) { + if (currentProgressPromiseResolve) { + currentProgressPromiseResolve(); + } + + const promise = new Promise((resolve) => currentProgressPromiseResolve = resolve); + lastLocation = location; + + if (location === ProgressLocation.Dialog) { + // Show dialog + progressService!.withProgress( + { location: ProgressLocation.Dialog, buttons }, + (progress) => { if (progressReporter) { progressReporter.currentProgress = progress; } return promise; }, + (choice?) => { + // Handle choice from dialog + if (choice === 0 && buttons && reconnectWaitEvent) { + reconnectWaitEvent.skipWait(); + } else { + showProgress(ProgressLocation.Notification, buttons); + } + + progressReporter!.report(); + }); + } else { + // Show notification + progressService!.withProgress( + { location: ProgressLocation.Notification, buttons }, + (progress) => { if (progressReporter) { progressReporter.currentProgress = progress; } return promise; }, + (choice?) => { + // Handle choice from notification + if (choice === 0 && buttons && reconnectWaitEvent) { + reconnectWaitEvent.skipWait(); + } else { + hideProgress(); + } + }); + } + } + + function hideProgress() { + if (currentProgressPromiseResolve) { + currentProgressPromiseResolve(); + } + + currentProgressPromiseResolve = null; + } + + connection.onDidStateChange((e) => { + if (currentTimer) { + currentTimer.dispose(); + currentTimer = null; + } + + if (disposableListener) { + disposableListener.dispose(); + disposableListener = null; + } + switch (e.type) { + case PersistentConnectionEventType.ConnectionLost: + if (!currentProgressPromiseResolve) { + progressReporter = new ProgressReporter(null); + showProgress(ProgressLocation.Dialog, [nls.localize('reconnectNow', "Reconnect Now")]); + } + + progressReporter!.report(nls.localize('connectionLost', "Connection Lost")); + break; + case PersistentConnectionEventType.ReconnectionWait: + hideProgress(); + reconnectWaitEvent = e; + showProgress(lastLocation || ProgressLocation.Notification, [nls.localize('reconnectNow', "Reconnect Now")]); + currentTimer = new ReconnectionTimer(progressReporter!, Date.now() + 1000 * e.durationSeconds); + break; + case PersistentConnectionEventType.ReconnectionRunning: + hideProgress(); + showProgress(lastLocation || ProgressLocation.Notification); + progressReporter!.report(nls.localize('reconnectionRunning', "Attempting to reconnect...")); + + // Register to listen for quick input is opened + disposableListener = contextKeyService.onDidChangeContext((contextKeyChangeEvent) => { + const reconnectInteraction = new Set(['inQuickOpen']); + if (contextKeyChangeEvent.affectsSome(reconnectInteraction)) { + // Need to move from dialog if being shown and user needs to type in a prompt + if (lastLocation === ProgressLocation.Dialog && progressReporter !== null) { + hideProgress(); + showProgress(ProgressLocation.Notification); + progressReporter.report(); + } + } + }); + + break; + case PersistentConnectionEventType.ReconnectionPermanentFailure: + hideProgress(); + progressReporter = null; + + dialogService.show(Severity.Error, nls.localize('reconnectionPermanentFailure', "Cannot reconnect. Please reload the window."), [nls.localize('reloadWindow', "Reload Window"), nls.localize('cancel', "Cancel")], { cancelId: 1 }).then(result => { + // Reload the window + if (result.choice === 0) { + commandService.executeCommand(ReloadWindowAction.ID); + } + }); + break; + case PersistentConnectionEventType.ConnectionGain: + hideProgress(); + progressReporter = null; + break; + } + }); + } + } +} + +class ReconnectionTimer implements IDisposable { + private readonly _progressReporter: ProgressReporter; + private readonly _completionTime: number; + private readonly _token: any; + + constructor(progressReporter: ProgressReporter, completionTime: number) { + this._progressReporter = progressReporter; + this._completionTime = completionTime; + this._token = setInterval(() => this._render(), 1000); + this._render(); + } + + public dispose(): void { + clearInterval(this._token); + } + + private _render() { + const remainingTimeMs = this._completionTime - Date.now(); + if (remainingTimeMs < 0) { + return; + } + const remainingTime = Math.ceil(remainingTimeMs / 1000); + if (remainingTime === 1) { + this._progressReporter.report(nls.localize('reconnectionWaitOne', "Attempting to reconnect in {0} second...", remainingTime)); + } else { + this._progressReporter.report(nls.localize('reconnectionWaitMany', "Attempting to reconnect in {0} seconds...", remainingTime)); + } + } +} + +const workbenchContributionsRegistry = Registry.as(WorkbenchExtensions.Workbench); +workbenchContributionsRegistry.registerWorkbenchContribution(RemoteAgentConnectionStatusListener, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/contrib/remote/common/remote.contribution.ts b/src/vs/workbench/contrib/remote/common/remote.contribution.ts index 5258b62995f8..15873391af01 100644 --- a/src/vs/workbench/contrib/remote/common/remote.contribution.ts +++ b/src/vs/workbench/contrib/remote/common/remote.contribution.ts @@ -11,7 +11,7 @@ import { OperatingSystem, isWeb } from 'vs/base/common/platform'; import { Schemas } from 'vs/base/common/network'; import { IRemoteAgentService, RemoteExtensionLogFileName } from 'vs/workbench/services/remote/common/remoteAgentService'; import { ILogService } from 'vs/platform/log/common/log'; -import { LogLevelSetterChannelClient } from 'vs/platform/log/common/logIpc'; +import { LoggerChannelClient } from 'vs/platform/log/common/logIpc'; import { IOutputChannelRegistry, Extensions as OutputExt, } from 'vs/workbench/contrib/output/common/output'; import { localize } from 'vs/nls'; import { joinPath } from 'vs/base/common/resources'; @@ -79,9 +79,9 @@ class RemoteChannelsContribution extends Disposable implements IWorkbenchContrib super(); const connection = remoteAgentService.getConnection(); if (connection) { - const logLevelClient = new LogLevelSetterChannelClient(connection.getChannel('loglevel')); - logLevelClient.setLevel(logService.getLevel()); - this._register(logService.onDidChangeLogLevel(level => logLevelClient.setLevel(level))); + const loggerClient = new LoggerChannelClient(connection.getChannel('logger')); + loggerClient.setLevel(logService.getLevel()); + this._register(logService.onDidChangeLogLevel(level => loggerClient.setLevel(level))); } } } diff --git a/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts b/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts index 8838e4077202..7819769cee00 100644 --- a/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts +++ b/src/vs/workbench/contrib/remote/electron-browser/remote.contribution.ts @@ -6,12 +6,13 @@ import * as nls from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; import { STATUS_BAR_HOST_NAME_BACKGROUND, STATUS_BAR_HOST_NAME_FOREGROUND } from 'vs/workbench/common/theme'; - import { themeColorFromId } from 'vs/platform/theme/common/themeService'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; -import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; - -import { MenuId, IMenuService, MenuItemAction, IMenu, MenuRegistry } from 'vs/platform/actions/common/actions'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { isMacintosh } from 'vs/base/common/platform'; +import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; +import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; +import { MenuId, IMenuService, MenuItemAction, IMenu, MenuRegistry, SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchContributionsExtensions } from 'vs/workbench/common/contributions'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { StatusbarAlignment, IStatusbarService, IStatusbarEntryAccessor, IStatusbarEntry } from 'vs/platform/statusbar/common/statusbar'; @@ -23,22 +24,22 @@ import { IExtensionService } from 'vs/workbench/services/extensions/common/exten import { IQuickInputService, IQuickPickItem, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput'; import { ILogService } from 'vs/platform/log/common/log'; import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { DialogChannel } from 'vs/platform/dialogs/node/dialogIpc'; +import { DialogChannel } from 'vs/platform/dialogs/electron-browser/dialogIpc'; import { DownloadServiceChannel } from 'vs/platform/download/common/downloadIpc'; -import { LogLevelSetterChannel } from 'vs/platform/log/common/logIpc'; +import { LoggerChannel } from 'vs/platform/log/common/logIpc'; import { ipcRenderer as ipc } from 'electron'; import { IDiagnosticInfoOptions, IRemoteDiagnosticInfo } from 'vs/platform/diagnostics/common/diagnostics'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { IProgressService, IProgress, IProgressStep, ProgressLocation } from 'vs/platform/progress/common/progress'; -import { PersistentConnectionEventType, ReconnectionWaitEvent } from 'vs/platform/remote/common/remoteAgentConnection'; +import { PersistentConnectionEventType } from 'vs/platform/remote/common/remoteAgentConnection'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from 'vs/platform/configuration/common/configurationRegistry'; -import Severity from 'vs/base/common/severity'; -import { ReloadWindowAction } from 'vs/workbench/browser/actions/windowActions'; +import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; +import { OpenFileFolderAction, OpenFileAction, OpenFolderAction } from 'vs/workbench/browser/actions/workspaceActions'; import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { IWindowsService } from 'vs/platform/windows/common/windows'; -import { RemoteConnectionState, Deprecated_RemoteAuthorityContext } from 'vs/workbench/browser/contextkeys'; +import { RemoteConnectionState, Deprecated_RemoteAuthorityContext, RemoteFileDialogContext } from 'vs/workbench/browser/contextkeys'; import { IDownloadService } from 'vs/platform/download/common/download'; +import { OpenLocalFileFolderCommand, OpenLocalFileCommand, OpenLocalFolderCommand, SaveLocalFileCommand } from 'vs/workbench/services/dialogs/browser/remoteFileDialog'; const WINDOW_ACTIONS_COMMAND_ID = 'remote.showActions'; const CLOSE_REMOTE_COMMAND_ID = 'remote.closeRemote'; @@ -228,7 +229,7 @@ class RemoteChannelsContribution implements IWorkbenchContribution { if (connection) { connection.registerChannel('dialog', new DialogChannel(dialogService)); connection.registerChannel('download', new DownloadServiceChannel(downloadService)); - connection.registerChannel('loglevel', new LogLevelSetterChannel(logService)); + connection.registerChannel('logger', new LoggerChannel(logService)); } } } @@ -261,29 +262,6 @@ class RemoteAgentDiagnosticListener implements IWorkbenchContribution { } } -class ProgressReporter { - private _currentProgress: IProgress | null = null; - private lastReport: string | null = null; - - constructor(currentProgress: IProgress | null) { - this._currentProgress = currentProgress; - } - - set currentProgress(progress: IProgress) { - this._currentProgress = progress; - } - - report(message?: string) { - if (message) { - this.lastReport = message; - } - - if (this.lastReport && this._currentProgress) { - this._currentProgress.report({ message: this.lastReport }); - } - } -} - class RemoteExtensionHostEnvironmentUpdater implements IWorkbenchContribution { constructor( @IRemoteAgentService remoteAgentService: IRemoteAgentService, @@ -304,165 +282,6 @@ class RemoteExtensionHostEnvironmentUpdater implements IWorkbenchContribution { } } -class RemoteAgentConnectionStatusListener implements IWorkbenchContribution { - constructor( - @IRemoteAgentService remoteAgentService: IRemoteAgentService, - @IProgressService progressService: IProgressService, - @IDialogService dialogService: IDialogService, - @ICommandService commandService: ICommandService, - @IContextKeyService contextKeyService: IContextKeyService - ) { - const connection = remoteAgentService.getConnection(); - if (connection) { - let currentProgressPromiseResolve: (() => void) | null = null; - let progressReporter: ProgressReporter | null = null; - let lastLocation: ProgressLocation | null = null; - let currentTimer: ReconnectionTimer | null = null; - let reconnectWaitEvent: ReconnectionWaitEvent | null = null; - let disposableListener: IDisposable | null = null; - - function showProgress(location: ProgressLocation, buttons?: string[]) { - if (currentProgressPromiseResolve) { - currentProgressPromiseResolve(); - } - - const promise = new Promise((resolve) => currentProgressPromiseResolve = resolve); - lastLocation = location; - - if (location === ProgressLocation.Dialog) { - // Show dialog - progressService!.withProgress( - { location: ProgressLocation.Dialog, buttons }, - (progress) => { if (progressReporter) { progressReporter.currentProgress = progress; } return promise; }, - (choice?) => { - // Handle choice from dialog - if (choice === 0 && buttons && reconnectWaitEvent) { - reconnectWaitEvent.skipWait(); - } else { - showProgress(ProgressLocation.Notification, buttons); - } - - progressReporter!.report(); - }); - } else { - // Show notification - progressService!.withProgress( - { location: ProgressLocation.Notification, buttons }, - (progress) => { if (progressReporter) { progressReporter.currentProgress = progress; } return promise; }, - (choice?) => { - // Handle choice from notification - if (choice === 0 && buttons && reconnectWaitEvent) { - reconnectWaitEvent.skipWait(); - } else { - hideProgress(); - } - }); - } - } - - function hideProgress() { - if (currentProgressPromiseResolve) { - currentProgressPromiseResolve(); - } - - currentProgressPromiseResolve = null; - } - - connection.onDidStateChange((e) => { - if (currentTimer) { - currentTimer.dispose(); - currentTimer = null; - } - - if (disposableListener) { - disposableListener.dispose(); - disposableListener = null; - } - switch (e.type) { - case PersistentConnectionEventType.ConnectionLost: - if (!currentProgressPromiseResolve) { - progressReporter = new ProgressReporter(null); - showProgress(ProgressLocation.Dialog, [nls.localize('reconnectNow', "Reconnect Now")]); - } - - progressReporter!.report(nls.localize('connectionLost', "Connection Lost")); - break; - case PersistentConnectionEventType.ReconnectionWait: - hideProgress(); - reconnectWaitEvent = e; - showProgress(lastLocation || ProgressLocation.Notification, [nls.localize('reconnectNow', "Reconnect Now")]); - currentTimer = new ReconnectionTimer(progressReporter!, Date.now() + 1000 * e.durationSeconds); - break; - case PersistentConnectionEventType.ReconnectionRunning: - hideProgress(); - showProgress(lastLocation || ProgressLocation.Notification); - progressReporter!.report(nls.localize('reconnectionRunning', "Attempting to reconnect...")); - - // Register to listen for quick input is opened - disposableListener = contextKeyService.onDidChangeContext((contextKeyChangeEvent) => { - const reconnectInteraction = new Set(['inQuickOpen']); - if (contextKeyChangeEvent.affectsSome(reconnectInteraction)) { - // Need to move from dialog if being shown and user needs to type in a prompt - if (lastLocation === ProgressLocation.Dialog && progressReporter !== null) { - hideProgress(); - showProgress(ProgressLocation.Notification); - progressReporter.report(); - } - } - }); - - break; - case PersistentConnectionEventType.ReconnectionPermanentFailure: - hideProgress(); - progressReporter = null; - - dialogService.show(Severity.Error, nls.localize('reconnectionPermanentFailure', "Cannot reconnect. Please reload the window."), [nls.localize('reloadWindow', "Reload Window"), nls.localize('cancel', "Cancel")], { cancelId: 1 }).then(result => { - // Reload the window - if (result.choice === 0) { - commandService.executeCommand(ReloadWindowAction.ID); - } - }); - break; - case PersistentConnectionEventType.ConnectionGain: - hideProgress(); - progressReporter = null; - break; - } - }); - } - } -} - -class ReconnectionTimer implements IDisposable { - private readonly _progressReporter: ProgressReporter; - private readonly _completionTime: number; - private readonly _token: NodeJS.Timeout; - - constructor(progressReporter: ProgressReporter, completionTime: number) { - this._progressReporter = progressReporter; - this._completionTime = completionTime; - this._token = setInterval(() => this._render(), 1000); - this._render(); - } - - public dispose(): void { - clearInterval(this._token); - } - - private _render() { - const remainingTimeMs = this._completionTime - Date.now(); - if (remainingTimeMs < 0) { - return; - } - const remainingTime = Math.ceil(remainingTimeMs / 1000); - if (remainingTime === 1) { - this._progressReporter.report(nls.localize('reconnectionWaitOne', "Attempting to reconnect in {0} second...", remainingTime)); - } else { - this._progressReporter.report(nls.localize('reconnectionWaitMany', "Attempting to reconnect in {0} seconds...", remainingTime)); - } - } -} - class RemoteTelemetryEnablementUpdater extends Disposable implements IWorkbenchContribution { constructor( @IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService, @@ -523,7 +342,6 @@ class RemoteEmptyWorkbenchPresentation extends Disposable implements IWorkbenchC const workbenchContributionsRegistry = Registry.as(WorkbenchContributionsExtensions.Workbench); workbenchContributionsRegistry.registerWorkbenchContribution(RemoteChannelsContribution, LifecyclePhase.Starting); workbenchContributionsRegistry.registerWorkbenchContribution(RemoteAgentDiagnosticListener, LifecyclePhase.Eventually); -workbenchContributionsRegistry.registerWorkbenchContribution(RemoteAgentConnectionStatusListener, LifecyclePhase.Eventually); workbenchContributionsRegistry.registerWorkbenchContribution(RemoteExtensionHostEnvironmentUpdater, LifecyclePhase.Eventually); workbenchContributionsRegistry.registerWorkbenchContribution(RemoteWindowActiveIndicator, LifecyclePhase.Starting); workbenchContributionsRegistry.registerWorkbenchContribution(RemoteTelemetryEnablementUpdater, LifecyclePhase.Ready); @@ -558,3 +376,46 @@ Registry.as(ConfigurationExtensions.Configuration) } } }); + +const registry = Registry.as(ActionExtensions.WorkbenchActions); +const fileCategory = nls.localize('file', "File"); + +if (isMacintosh) { + registry.registerWorkbenchAction(new SyncActionDescriptor(OpenFileFolderAction, OpenFileFolderAction.ID, OpenFileFolderAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_O }), 'File: Open...', fileCategory); + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: OpenLocalFileFolderCommand.ID, + weight: KeybindingWeight.WorkbenchContrib, + primary: KeyMod.CtrlCmd | KeyCode.KEY_O, + when: RemoteFileDialogContext, + description: { description: OpenLocalFileFolderCommand.LABEL, args: [] }, + handler: OpenLocalFileFolderCommand.handler() + }); +} else { + registry.registerWorkbenchAction(new SyncActionDescriptor(OpenFileAction, OpenFileAction.ID, OpenFileAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_O }), 'File: Open File...', fileCategory); + registry.registerWorkbenchAction(new SyncActionDescriptor(OpenFolderAction, OpenFolderAction.ID, OpenFolderAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_O) }), 'File: Open Folder...', fileCategory); + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: OpenLocalFileCommand.ID, + weight: KeybindingWeight.WorkbenchContrib, + primary: KeyMod.CtrlCmd | KeyCode.KEY_O, + when: RemoteFileDialogContext, + description: { description: OpenLocalFileCommand.LABEL, args: [] }, + handler: OpenLocalFileCommand.handler() + }); + KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: OpenLocalFolderCommand.ID, + weight: KeybindingWeight.WorkbenchContrib, + primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_O), + when: RemoteFileDialogContext, + description: { description: OpenLocalFolderCommand.LABEL, args: [] }, + handler: OpenLocalFolderCommand.handler() + }); +} + +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: SaveLocalFileCommand.ID, + weight: KeybindingWeight.WorkbenchContrib, + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_S, + when: RemoteFileDialogContext, + description: { description: SaveLocalFileCommand.LABEL, args: [] }, + handler: SaveLocalFileCommand.handler() +}); diff --git a/src/vs/workbench/contrib/search/browser/media/searchview.css b/src/vs/workbench/contrib/search/browser/media/searchview.css index a7e2f0d36c81..b125d79b9184 100644 --- a/src/vs/workbench/contrib/search/browser/media/searchview.css +++ b/src/vs/workbench/contrib/search/browser/media/searchview.css @@ -86,7 +86,7 @@ height: 25px; } -.search-view .search-widget .replace-container .monaco-action-bar .action-item .icon { +.search-view .search-widget .replace-container .monaco-action-bar .action-item .codicon { background-repeat: no-repeat; width: 20px; height: 25px; diff --git a/src/vs/workbench/contrib/search/browser/searchWidget.ts b/src/vs/workbench/contrib/search/browser/searchWidget.ts index 5bef9b769f0e..a98e4f2d9c86 100644 --- a/src/vs/workbench/contrib/search/browser/searchWidget.ts +++ b/src/vs/workbench/contrib/search/browser/searchWidget.ts @@ -364,11 +364,12 @@ export class SearchWidget extends Widget { } })); - let controls = document.createElement('div'); + const controls = document.createElement('div'); controls.className = 'controls'; controls.style.display = 'block'; controls.appendChild(this._preserveCase.domNode); replaceBox.appendChild(controls); + this.replaceInput.paddingRight = this._preserveCase.width(); this._register(attachInputBoxStyler(this.replaceInput, this.themeService)); this.onkeydown(this.replaceInput.inputElement, (keyboardEvent) => this.onReplaceInputKeyDown(keyboardEvent)); diff --git a/src/vs/workbench/contrib/surveys/electron-browser/languageSurveys.contribution.ts b/src/vs/workbench/contrib/surveys/browser/languageSurveys.contribution.ts similarity index 87% rename from src/vs/workbench/contrib/surveys/electron-browser/languageSurveys.contribution.ts rename to src/vs/workbench/contrib/surveys/browser/languageSurveys.contribution.ts index 832bef10b522..191e0e2777e8 100644 --- a/src/vs/workbench/contrib/surveys/electron-browser/languageSurveys.contribution.ts +++ b/src/vs/workbench/contrib/surveys/browser/languageSurveys.contribution.ts @@ -10,14 +10,13 @@ import { IWorkbenchContributionsRegistry, IWorkbenchContribution, Extensions as import { Registry } from 'vs/platform/registry/common/platform'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import pkg from 'vs/platform/product/node/package'; -import product from 'vs/platform/product/node/product'; -import { ISurveyData } from 'vs/platform/product/common/product'; +import { ISurveyData, IProductService } from 'vs/platform/product/common/productService'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { Severity, INotificationService } from 'vs/platform/notification/common/notification'; import { ITextFileService, StateChange } from 'vs/workbench/services/textfile/common/textfiles'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { URI } from 'vs/base/common/uri'; +import { platform } from 'vs/base/common/process'; class LanguageSurvey { @@ -28,7 +27,8 @@ class LanguageSurvey { telemetryService: ITelemetryService, modelService: IModelService, textFileService: ITextFileService, - openerService: IOpenerService + openerService: IOpenerService, + productService: IProductService ) { const SESSION_COUNT_KEY = `${data.surveyId}.sessionCount`; const LAST_SESSION_DATE_KEY = `${data.surveyId}.lastSessionDate`; @@ -82,7 +82,7 @@ class LanguageSurvey { storageService.store(IS_CANDIDATE_KEY, isCandidate, StorageScope.GLOBAL); if (!isCandidate) { - storageService.store(SKIP_VERSION_KEY, pkg.version, StorageScope.GLOBAL); + storageService.store(SKIP_VERSION_KEY, productService.version, StorageScope.GLOBAL); return; } @@ -97,9 +97,9 @@ class LanguageSurvey { run: () => { telemetryService.publicLog(`${data.surveyId}.survey/takeShortSurvey`); telemetryService.getTelemetryInfo().then(info => { - openerService.open(URI.parse(`${data.surveyUrl}?o=${encodeURIComponent(process.platform)}&v=${encodeURIComponent(pkg.version)}&m=${encodeURIComponent(info.machineId)}`)); + openerService.open(URI.parse(`${data.surveyUrl}?o=${encodeURIComponent(platform)}&v=${encodeURIComponent(productService.version)}&m=${encodeURIComponent(info.machineId)}`)); storageService.store(IS_CANDIDATE_KEY, false, StorageScope.GLOBAL); - storageService.store(SKIP_VERSION_KEY, pkg.version, StorageScope.GLOBAL); + storageService.store(SKIP_VERSION_KEY, productService.version, StorageScope.GLOBAL); }); } }, { @@ -114,7 +114,7 @@ class LanguageSurvey { run: () => { telemetryService.publicLog(`${data.surveyId}.survey/dontShowAgain`); storageService.store(IS_CANDIDATE_KEY, false, StorageScope.GLOBAL); - storageService.store(SKIP_VERSION_KEY, pkg.version, StorageScope.GLOBAL); + storageService.store(SKIP_VERSION_KEY, productService.version, StorageScope.GLOBAL); } }], { sticky: true } @@ -130,15 +130,20 @@ class LanguageSurveysContribution implements IWorkbenchContribution { @ITelemetryService telemetryService: ITelemetryService, @IModelService modelService: IModelService, @ITextFileService textFileService: ITextFileService, - @IOpenerService openerService: IOpenerService + @IOpenerService openerService: IOpenerService, + @IProductService productService: IProductService ) { - product.surveys! + if (!productService.surveys) { + return; + } + + productService.surveys .filter(surveyData => surveyData.surveyId && surveyData.editCount && surveyData.languageId && surveyData.surveyUrl && surveyData.userProbability) - .map(surveyData => new LanguageSurvey(surveyData, storageService, notificationService, telemetryService, modelService, textFileService, openerService)); + .map(surveyData => new LanguageSurvey(surveyData, storageService, notificationService, telemetryService, modelService, textFileService, openerService, productService)); } } -if (language === 'en' && product.surveys && product.surveys.length) { +if (language === 'en') { const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); workbenchRegistry.registerWorkbenchContribution(LanguageSurveysContribution, LifecyclePhase.Restored); } diff --git a/src/vs/workbench/contrib/surveys/electron-browser/nps.contribution.ts b/src/vs/workbench/contrib/surveys/browser/nps.contribution.ts similarity index 81% rename from src/vs/workbench/contrib/surveys/electron-browser/nps.contribution.ts rename to src/vs/workbench/contrib/surveys/browser/nps.contribution.ts index da0aeb9732a2..8aaa798801b0 100644 --- a/src/vs/workbench/contrib/surveys/electron-browser/nps.contribution.ts +++ b/src/vs/workbench/contrib/surveys/browser/nps.contribution.ts @@ -9,12 +9,12 @@ import { IWorkbenchContributionsRegistry, IWorkbenchContribution, Extensions as import { Registry } from 'vs/platform/registry/common/platform'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; -import pkg from 'vs/platform/product/node/package'; -import product from 'vs/platform/product/node/product'; +import { IProductService } from 'vs/platform/product/common/productService'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; import { Severity, INotificationService } from 'vs/platform/notification/common/notification'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { URI } from 'vs/base/common/uri'; +import { platform } from 'vs/base/common/process'; const PROBABILITY = 0.15; const SESSION_COUNT_KEY = 'nps/sessionCount'; @@ -28,8 +28,13 @@ class NPSContribution implements IWorkbenchContribution { @IStorageService storageService: IStorageService, @INotificationService notificationService: INotificationService, @ITelemetryService telemetryService: ITelemetryService, - @IOpenerService openerService: IOpenerService + @IOpenerService openerService: IOpenerService, + @IProductService productService: IProductService ) { + if (!productService.npsSurveyUrl) { + return; + } + const skipVersion = storageService.get(SKIP_VERSION_KEY, StorageScope.GLOBAL, ''); if (skipVersion) { return; @@ -56,7 +61,7 @@ class NPSContribution implements IWorkbenchContribution { storageService.store(IS_CANDIDATE_KEY, isCandidate, StorageScope.GLOBAL); if (!isCandidate) { - storageService.store(SKIP_VERSION_KEY, pkg.version, StorageScope.GLOBAL); + storageService.store(SKIP_VERSION_KEY, productService.version, StorageScope.GLOBAL); return; } @@ -67,9 +72,9 @@ class NPSContribution implements IWorkbenchContribution { label: nls.localize('takeSurvey', "Take Survey"), run: () => { telemetryService.getTelemetryInfo().then(info => { - openerService.open(URI.parse(`${product.npsSurveyUrl}?o=${encodeURIComponent(process.platform)}&v=${encodeURIComponent(pkg.version)}&m=${encodeURIComponent(info.machineId)}`)); + openerService.open(URI.parse(`${productService.npsSurveyUrl}?o=${encodeURIComponent(platform)}&v=${encodeURIComponent(productService.version)}&m=${encodeURIComponent(info.machineId)}`)); storageService.store(IS_CANDIDATE_KEY, false, StorageScope.GLOBAL); - storageService.store(SKIP_VERSION_KEY, pkg.version, StorageScope.GLOBAL); + storageService.store(SKIP_VERSION_KEY, productService.version, StorageScope.GLOBAL); }); } }, { @@ -79,7 +84,7 @@ class NPSContribution implements IWorkbenchContribution { label: nls.localize('neverAgain', "Don't Show Again"), run: () => { storageService.store(IS_CANDIDATE_KEY, false, StorageScope.GLOBAL); - storageService.store(SKIP_VERSION_KEY, pkg.version, StorageScope.GLOBAL); + storageService.store(SKIP_VERSION_KEY, productService.version, StorageScope.GLOBAL); } }], { sticky: true } @@ -87,7 +92,7 @@ class NPSContribution implements IWorkbenchContribution { } } -if (language === 'en' && product.npsSurveyUrl) { +if (language === 'en') { const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); workbenchRegistry.registerWorkbenchContribution(NPSContribution, LifecyclePhase.Restored); } diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index 251e8914146f..aba32f894794 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -188,6 +188,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer protected _taskSystemInfos: Map; protected _workspaceTasksPromise?: Promise>; + protected _areJsonTasksSupportedPromise: Promise = Promise.resolve(false); protected _taskSystem?: ITaskSystem; protected _taskSystemListener?: IDisposable; @@ -1416,6 +1417,10 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } } + public setJsonTasksSupported(areSupported: Promise) { + this._areJsonTasksSupportedPromise = areSupported; + } + private computeWorkspaceFolderTasks(workspaceFolder: IWorkspaceFolder, runSource: TaskRunSource = TaskRunSource.User): Promise { return (this.executionEngine === ExecutionEngine.Process ? this.computeLegacyConfiguration(workspaceFolder) @@ -1424,7 +1429,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer if (!workspaceFolderConfiguration || !workspaceFolderConfiguration.config || workspaceFolderConfiguration.hasErrors) { return Promise.resolve({ workspaceFolder, set: undefined, configurations: undefined, hasErrors: workspaceFolderConfiguration ? workspaceFolderConfiguration.hasErrors : false }); } - return ProblemMatcherRegistry.onReady().then((): WorkspaceFolderTaskResult => { + return ProblemMatcherRegistry.onReady().then(async (): Promise => { let taskSystemInfo: TaskSystemInfo | undefined = this._taskSystemInfos.get(workspaceFolder.uri.scheme); let problemReporter = new ProblemReporter(this._outputChannel); let parseResult = TaskConfig.parse(workspaceFolder, taskSystemInfo ? taskSystemInfo.platform : Platform.platform, workspaceFolderConfiguration.config!, problemReporter); @@ -1446,7 +1451,10 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer customizedTasks.byIdentifier[task.configures._key] = task; } } - return { workspaceFolder, set: { tasks: parseResult.custom }, configurations: customizedTasks, hasErrors }; + if (!(await this._areJsonTasksSupportedPromise) && (parseResult.custom.length > 0)) { + console.warn('Custom workspace tasks are not supported.'); + } + return { workspaceFolder, set: { tasks: await this._areJsonTasksSupportedPromise ? parseResult.custom : [] }, configurations: customizedTasks, hasErrors }; }); }); } diff --git a/src/vs/workbench/contrib/tasks/common/taskService.ts b/src/vs/workbench/contrib/tasks/common/taskService.ts index e0caef8d1608..ec76f2afb204 100644 --- a/src/vs/workbench/contrib/tasks/common/taskService.ts +++ b/src/vs/workbench/contrib/tasks/common/taskService.ts @@ -83,6 +83,7 @@ export interface ITaskService { registerTaskProvider(taskProvider: ITaskProvider, type: string): IDisposable; registerTaskSystem(scheme: string, taskSystemInfo: TaskSystemInfo): void; + setJsonTasksSupported(areSuppored: Promise): void; extensionCallbackTaskComplete(task: Task, result: number | undefined): Promise; } diff --git a/src/vs/workbench/contrib/terminal/browser/media/kill-dark.svg b/src/vs/workbench/contrib/terminal/browser/media/kill-dark.svg deleted file mode 100644 index 9831c96a166d..000000000000 --- a/src/vs/workbench/contrib/terminal/browser/media/kill-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/terminal/browser/media/kill-hc.svg b/src/vs/workbench/contrib/terminal/browser/media/kill-hc.svg deleted file mode 100644 index 656f3bd7a42a..000000000000 --- a/src/vs/workbench/contrib/terminal/browser/media/kill-hc.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/terminal/browser/media/kill-light.svg b/src/vs/workbench/contrib/terminal/browser/media/kill-light.svg deleted file mode 100644 index d5ac851860b2..000000000000 --- a/src/vs/workbench/contrib/terminal/browser/media/kill-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/terminal/browser/media/new-dark.svg b/src/vs/workbench/contrib/terminal/browser/media/new-dark.svg deleted file mode 100644 index 4d9389336b93..000000000000 --- a/src/vs/workbench/contrib/terminal/browser/media/new-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/terminal/browser/media/new-hc.svg b/src/vs/workbench/contrib/terminal/browser/media/new-hc.svg deleted file mode 100644 index fb50c6c28499..000000000000 --- a/src/vs/workbench/contrib/terminal/browser/media/new-hc.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/terminal/browser/media/new-light.svg b/src/vs/workbench/contrib/terminal/browser/media/new-light.svg deleted file mode 100644 index 01a9de7d5abc..000000000000 --- a/src/vs/workbench/contrib/terminal/browser/media/new-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/terminal/browser/media/split-editor-horizontal-dark.svg b/src/vs/workbench/contrib/terminal/browser/media/split-editor-horizontal-dark.svg deleted file mode 100644 index 8c22a7c5bfed..000000000000 --- a/src/vs/workbench/contrib/terminal/browser/media/split-editor-horizontal-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/terminal/browser/media/split-editor-horizontal-hc.svg b/src/vs/workbench/contrib/terminal/browser/media/split-editor-horizontal-hc.svg deleted file mode 100644 index 82c19d0c8fc2..000000000000 --- a/src/vs/workbench/contrib/terminal/browser/media/split-editor-horizontal-hc.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/terminal/browser/media/split-editor-horizontal-light.svg b/src/vs/workbench/contrib/terminal/browser/media/split-editor-horizontal-light.svg deleted file mode 100644 index 2d53ab6d3c25..000000000000 --- a/src/vs/workbench/contrib/terminal/browser/media/split-editor-horizontal-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/terminal/browser/media/split-editor-vertical-dark.svg b/src/vs/workbench/contrib/terminal/browser/media/split-editor-vertical-dark.svg deleted file mode 100644 index 419c21be4f62..000000000000 --- a/src/vs/workbench/contrib/terminal/browser/media/split-editor-vertical-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/terminal/browser/media/split-editor-vertical-hc.svg b/src/vs/workbench/contrib/terminal/browser/media/split-editor-vertical-hc.svg deleted file mode 100644 index 7565fd3c1681..000000000000 --- a/src/vs/workbench/contrib/terminal/browser/media/split-editor-vertical-hc.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/terminal/browser/media/split-editor-vertical-light.svg b/src/vs/workbench/contrib/terminal/browser/media/split-editor-vertical-light.svg deleted file mode 100644 index 7e95763b463e..000000000000 --- a/src/vs/workbench/contrib/terminal/browser/media/split-editor-vertical-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/terminal/browser/media/terminal.css b/src/vs/workbench/contrib/terminal/browser/media/terminal.css index e84bdf08544d..5ea5fc9e9174 100644 --- a/src/vs/workbench/contrib/terminal/browser/media/terminal.css +++ b/src/vs/workbench/contrib/terminal/browser/media/terminal.css @@ -144,24 +144,6 @@ opacity: 0 !important; } -/* Light theme */ -.monaco-workbench .terminal-action.kill { background: url('kill-light.svg') center center no-repeat; } -.monaco-workbench .terminal-action.new { background: url('new-light.svg') center center no-repeat; } -.monaco-workbench .terminal-action.split { background: url('split-editor-horizontal-light.svg') center center no-repeat; } -.monaco-workbench .panel.right .terminal-action.split { background: url('split-editor-vertical-light.svg') center center no-repeat; } - -/* Dark theme */ -.vs-dark .monaco-workbench .terminal-action.kill, .hc-black .monaco-workbench .terminal-action.kill { background: url('kill-dark.svg') center center no-repeat; } -.vs-dark .monaco-workbench .terminal-action.new, .hc-black .monaco-workbench .terminal-action.new { background: url('new-dark.svg') center center no-repeat; } -.vs-dark .monaco-workbench .terminal-action.split, .hc-black .monaco-workbench .terminal-action.split { background: url('split-editor-horizontal-dark.svg') center center no-repeat; } -.vs-dark .monaco-workbench .panel.right .terminal-action.split, .hc-black .monaco-workbench .panel.right .terminal-action.split { background: url('split-editor-vertical-dark.svg') center center no-repeat; } - -/* HC theme */ -.hc-black .monaco-workbench .terminal-action.kill, .hc-black .monaco-workbench .terminal-action.kill { background: url('kill-hc.svg') center center no-repeat; } -.hc-black .monaco-workbench .terminal-action.new, .hc-black .monaco-workbench .terminal-action.new { background: url('new-hc.svg') center center no-repeat; } -.hc-black .monaco-workbench .terminal-action.split, .hc-black .monaco-workbench .terminal-action.split { background: url('split-editor-horizontal-hc.svg') center center no-repeat; } -.hc-black .monaco-workbench .panel.right .terminal-action.split, .hc-black .monaco-workbench .panel.right .terminal-action.split { background: url('split-editor-vertical-hc.svg') center center no-repeat; } - .vs-dark .monaco-workbench.mac .panel.integrated-terminal .terminal-outer-container:not(.alt-active) .terminal:not(.enable-mouse-events), .hc-black .monaco-workbench.mac .panel.integrated-terminal .terminal-outer-container:not(.alt-active) .terminal:not(.enable-mouse-events) { cursor: -webkit-image-set(url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAAL0lEQVQoz2NgCD3x//9/BhBYBWdhgFVAiVW4JBFKGIa4AqD0//9D3pt4I4tAdAMAHTQ/j5Zom30AAAAASUVORK5CYII=') 1x, url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAQAAADZc7J/AAAAz0lEQVRIx2NgYGBY/R8I/vx5eelX3n82IJ9FxGf6tksvf/8FiTMQAcAGQMDvSwu09abffY8QYSAScNk45G198eX//yev73/4///701eh//kZSARckrNBRvz//+8+6ZohwCzjGNjdgQxkAg7B9WADeBjIBqtJCbhRA0YNoIkBSNmaPEMoNmA0FkYNoFKhapJ6FGyAH3nauaSmPfwI0v/3OukVi0CIZ+F25KrtYcx/CTIy0e+rC7R1Z4KMICVTQQ14feVXIbR695u14+Ir4gwAAD49E54wc1kWAAAAAElFTkSuQmCC') 2x) 5 8, text; diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index 9aecc9a02690..bb719e489332 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -20,7 +20,7 @@ import * as panel from 'vs/workbench/browser/panel'; import { getQuickNavigateHandler } from 'vs/workbench/browser/parts/quickopen/quickopen'; import { Extensions as QuickOpenExtensions, IQuickOpenRegistry, QuickOpenHandlerDescriptor } from 'vs/workbench/browser/quickopen'; import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; -import { ClearSelectionTerminalAction, ClearTerminalAction, CopyTerminalSelectionAction, CreateNewInActiveWorkspaceTerminalAction, CreateNewTerminalAction, DeleteToLineStartTerminalAction, DeleteWordLeftTerminalAction, DeleteWordRightTerminalAction, FindNext, FindPrevious, FocusActiveTerminalAction, FocusNextPaneTerminalAction, FocusNextTerminalAction, FocusPreviousPaneTerminalAction, FocusPreviousTerminalAction, FocusTerminalFindWidgetAction, HideTerminalFindWidgetAction, KillTerminalAction, MoveToLineEndTerminalAction, MoveToLineStartTerminalAction, QuickOpenActionTermContributor, QuickOpenTermAction, RenameTerminalAction, ResizePaneDownTerminalAction, ResizePaneLeftTerminalAction, ResizePaneRightTerminalAction, ResizePaneUpTerminalAction, RunActiveFileInTerminalAction, RunSelectedTextInTerminalAction, ScrollDownPageTerminalAction, ScrollDownTerminalAction, ScrollToBottomTerminalAction, ScrollToNextCommandAction, ScrollToPreviousCommandAction, ScrollToTopTerminalAction, ScrollUpPageTerminalAction, ScrollUpTerminalAction, SelectAllTerminalAction, SelectDefaultShellWindowsTerminalAction, SelectToNextCommandAction, SelectToNextLineAction, SelectToPreviousCommandAction, SelectToPreviousLineAction, SendSequenceTerminalCommand, SplitInActiveWorkspaceTerminalAction, SplitTerminalAction, TerminalPasteAction, TERMINAL_PICKER_PREFIX, ToggleCaseSensitiveCommand, ToggleEscapeSequenceLoggingAction, ToggleRegexCommand, ToggleTerminalAction, ToggleWholeWordCommand, NavigationModeFocusPreviousTerminalAction, NavigationModeFocusNextTerminalAction, NavigationModeExitTerminalAction, ManageWorkspaceShellPermissionsTerminalCommand } from 'vs/workbench/contrib/terminal/browser/terminalActions'; +import { ClearSelectionTerminalAction, ClearTerminalAction, CopyTerminalSelectionAction, CreateNewInActiveWorkspaceTerminalAction, CreateNewTerminalAction, DeleteToLineStartTerminalAction, DeleteWordLeftTerminalAction, DeleteWordRightTerminalAction, FindNext, FindPrevious, FocusActiveTerminalAction, FocusNextPaneTerminalAction, FocusNextTerminalAction, FocusPreviousPaneTerminalAction, FocusPreviousTerminalAction, FocusTerminalFindWidgetAction, HideTerminalFindWidgetAction, KillTerminalAction, MoveToLineEndTerminalAction, MoveToLineStartTerminalAction, QuickOpenActionTermContributor, QuickOpenTermAction, RenameTerminalAction, ResizePaneDownTerminalAction, ResizePaneLeftTerminalAction, ResizePaneRightTerminalAction, ResizePaneUpTerminalAction, RunActiveFileInTerminalAction, RunSelectedTextInTerminalAction, ScrollDownPageTerminalAction, ScrollDownTerminalAction, ScrollToBottomTerminalAction, ScrollToNextCommandAction, ScrollToPreviousCommandAction, ScrollToTopTerminalAction, ScrollUpPageTerminalAction, ScrollUpTerminalAction, SelectAllTerminalAction, SelectDefaultShellWindowsTerminalAction, SelectToNextCommandAction, SelectToNextLineAction, SelectToPreviousCommandAction, SelectToPreviousLineAction, SendSequenceTerminalCommand, SplitInActiveWorkspaceTerminalAction, SplitTerminalAction, TerminalPasteAction, TERMINAL_PICKER_PREFIX, ToggleCaseSensitiveCommand, ToggleEscapeSequenceLoggingAction, ToggleRegexCommand, ToggleTerminalAction, ToggleWholeWordCommand, NavigationModeFocusPreviousTerminalAction, NavigationModeFocusNextTerminalAction, NavigationModeExitTerminalAction, ManageWorkspaceShellPermissionsTerminalCommand, CreateNewWithCwdTerminalCommand } from 'vs/workbench/contrib/terminal/browser/terminalActions'; import { TerminalPanel } from 'vs/workbench/contrib/terminal/browser/terminalPanel'; import { TerminalPickerHandler } from 'vs/workbench/contrib/terminal/browser/terminalQuickOpen'; 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_PANEL_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'; @@ -550,27 +550,43 @@ actionRegistry.registerWorkbenchAction(new SyncActionDescriptor(FindPrevious, Fi mac: { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_G, secondary: [KeyMod.Shift | KeyCode.F3, KeyCode.Enter] }, }, KEYBINDING_CONTEXT_TERMINAL_FIND_WIDGET_FOCUSED), 'Terminal: Find previous'); - -const sendSequenceTerminalCommand = new SendSequenceTerminalCommand({ +(new SendSequenceTerminalCommand({ id: SendSequenceTerminalCommand.ID, precondition: undefined, description: { - description: `Send Custom Sequence To Terminal`, + description: SendSequenceTerminalCommand.LABEL, args: [{ name: 'args', schema: { - 'type': 'object', - 'required': ['text'], - 'properties': { - 'text': { - 'type': 'string' + type: 'object', + required: ['text'], + properties: { + text: { type: 'string' } + }, + } + }] + } +})).register(); +(new CreateNewWithCwdTerminalCommand({ + id: CreateNewWithCwdTerminalCommand.ID, + precondition: undefined, + description: { + description: CreateNewWithCwdTerminalCommand.LABEL, + args: [{ + name: 'args', + schema: { + type: 'object', + required: ['cwd'], + properties: { + cwd: { + description: CreateNewWithCwdTerminalCommand.CWD_ARG_LABEL, + type: 'string' } }, } }] } -}); -sendSequenceTerminalCommand.register(); +})).register(); setupTerminalCommands(); setupTerminalMenu(); diff --git a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts index af21f393f333..7a71e2c2c54f 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalActions.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalActions.ts @@ -105,7 +105,7 @@ export class KillTerminalAction extends Action { id: string, label: string, @ITerminalService private readonly terminalService: ITerminalService ) { - super(id, label, 'terminal-action kill'); + super(id, label, 'terminal-action codicon-trash'); } public run(event?: any): Promise { @@ -298,6 +298,32 @@ export class SendSequenceTerminalCommand extends Command { } } +export class CreateNewWithCwdTerminalCommand extends Command { + 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"); + public static readonly CWD_ARG_LABEL = nls.localize('workbench.action.terminal.newWithCwd.cwd', "The directory to start the terminal at"); + + public runCommand(accessor: ServicesAccessor, args: { cwd: string } | undefined): Promise { + const terminalService = accessor.get(ITerminalService); + 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; + + let cwd: string | undefined; + if (args && args.cwd) { + cwd = configurationResolverService.resolve(lastActiveWorkspaceRoot, args.cwd); + } + const instance = terminalService.createTerminal({ cwd }); + if (!instance) { + return Promise.resolve(undefined); + } + terminalService.setActiveInstance(instance); + return terminalService.showPanel(true); + } +} + export class CreateNewTerminalAction extends Action { public static readonly ID = TERMINAL_COMMAND_ID.NEW; @@ -310,7 +336,7 @@ export class CreateNewTerminalAction extends Action { @ICommandService private readonly commandService: ICommandService, @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService ) { - super(id, label, 'terminal-action new'); + super(id, label, 'terminal-action codicon-add'); } public run(event?: any): Promise { @@ -386,7 +412,7 @@ export class SplitTerminalAction extends Action { @ICommandService private readonly commandService: ICommandService, @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService ) { - super(id, label, 'terminal-action split'); + super(id, label, 'terminal-action codicon-split-horizontal'); } public run(event?: any): Promise { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts b/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts index 970a4e44999b..c5544ead33c6 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalConfigHelper.ts @@ -19,7 +19,7 @@ import { ExtensionType } from 'vs/platform/extensions/common/extensions'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { InstallRecommendedExtensionAction } from 'vs/workbench/contrib/extensions/browser/extensionsActions'; -import { IProductService } from 'vs/platform/product/common/product'; +import { IProductService } from 'vs/platform/product/common/productService'; import { XTermCore } from 'vs/workbench/contrib/terminal/browser/xterm-private'; const MINIMUM_FONT_SIZE = 6; diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts index fd767e7f934f..8660142f7072 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts @@ -17,7 +17,7 @@ import { IConfigurationResolverService } from 'vs/workbench/services/configurati import { Schemas } from 'vs/base/common/network'; import { getRemoteAuthority } from 'vs/platform/remote/common/remoteHosts'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { IProductService } from 'vs/platform/product/common/product'; +import { IProductService } from 'vs/platform/product/common/productService'; import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/terminal'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index 1447f3474b7a..e423624da30f 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -423,6 +423,7 @@ export const enum TERMINAL_COMMAND_ID { MOVE_TO_LINE_START = 'workbench.action.terminal.moveToLineStart', MOVE_TO_LINE_END = 'workbench.action.terminal.moveToLineEnd', NEW = 'workbench.action.terminal.new', + NEW_WITH_CWD = 'workbench.action.terminal.newWithCwd', NEW_LOCAL = 'workbench.action.terminal.newLocal', NEW_IN_ACTIVE_WORKSPACE = 'workbench.action.terminal.newInActiveWorkspace', SPLIT = 'workbench.action.terminal.split', diff --git a/src/vs/workbench/contrib/update/browser/releaseNotesEditor.ts b/src/vs/workbench/contrib/update/browser/releaseNotesEditor.ts index 417d15893486..6b419e253785 100644 --- a/src/vs/workbench/contrib/update/browser/releaseNotesEditor.ts +++ b/src/vs/workbench/contrib/update/browser/releaseNotesEditor.ts @@ -18,10 +18,10 @@ import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { IRequestService, asText } from 'vs/platform/request/common/request'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IProductService } from 'vs/platform/product/common/product'; +import { IProductService } from 'vs/platform/product/common/productService'; import { IWebviewEditorService } from 'vs/workbench/contrib/webview/browser/webviewEditorService'; import { IEditorService, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; -import { WebviewEditorInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput'; +import { WebviewInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput'; import { KeybindingParser } from 'vs/base/common/keybindingParser'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; @@ -32,7 +32,7 @@ export class ReleaseNotesManager { private readonly _releaseNotesCache = new Map>(); - private _currentReleaseNotes: WebviewEditorInput | undefined = undefined; + private _currentReleaseNotes: WebviewInput | undefined = undefined; private _lastText: string | undefined; public constructor( diff --git a/src/vs/workbench/contrib/update/browser/update.ts b/src/vs/workbench/contrib/update/browser/update.ts index ab5222038aa4..bdcddbf3191d 100644 --- a/src/vs/workbench/contrib/update/browser/update.ts +++ b/src/vs/workbench/contrib/update/browser/update.ts @@ -28,7 +28,7 @@ import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { FalseContext } from 'vs/platform/contextkey/common/contextkeys'; import { ShowCurrentReleaseNotesActionId } from 'vs/workbench/contrib/update/common/update'; import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows'; -import { IProductService } from 'vs/platform/product/common/product'; +import { IProductService } from 'vs/platform/product/common/productService'; const CONTEXT_UPDATE_STATE = new RawContextKey('updateState', StateType.Uninitialized); @@ -400,7 +400,7 @@ export class UpdateContribution extends Disposable implements IWorkbenchContribu private registerGlobalActivityActions(): void { CommandsRegistry.registerCommand('update.check', () => this.updateService.checkForUpdates({ windowId: this.environmentService.configuration.windowId })); MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { - group: '5_update', + group: '6_update', command: { id: 'update.check', title: nls.localize('checkForUpdates', "Check for Updates...") @@ -410,7 +410,7 @@ export class UpdateContribution extends Disposable implements IWorkbenchContribu CommandsRegistry.registerCommand('update.checking', () => { }); MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { - group: '5_update', + group: '6_update', command: { id: 'update.checking', title: nls.localize('checkingForUpdates', "Checking for Updates..."), @@ -421,7 +421,7 @@ export class UpdateContribution extends Disposable implements IWorkbenchContribu CommandsRegistry.registerCommand('update.downloadNow', () => this.updateService.downloadUpdate()); MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { - group: '5_update', + group: '6_update', command: { id: 'update.downloadNow', title: nls.localize('download update', "Download Update") @@ -431,7 +431,7 @@ export class UpdateContribution extends Disposable implements IWorkbenchContribu CommandsRegistry.registerCommand('update.downloading', () => { }); MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { - group: '5_update', + group: '6_update', command: { id: 'update.downloading', title: nls.localize('DownloadingUpdate', "Downloading Update..."), @@ -442,7 +442,7 @@ export class UpdateContribution extends Disposable implements IWorkbenchContribu CommandsRegistry.registerCommand('update.install', () => this.updateService.applyUpdate()); MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { - group: '5_update', + group: '6_update', command: { id: 'update.install', title: nls.localize('installUpdate...', "Install Update...") @@ -452,7 +452,7 @@ export class UpdateContribution extends Disposable implements IWorkbenchContribu CommandsRegistry.registerCommand('update.updating', () => { }); MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { - group: '5_update', + group: '6_update', command: { id: 'update.updating', title: nls.localize('installingUpdate', "Installing Update..."), @@ -463,7 +463,7 @@ export class UpdateContribution extends Disposable implements IWorkbenchContribu CommandsRegistry.registerCommand('update.restart', () => this.updateService.quitAndInstall()); MenuRegistry.appendMenuItem(MenuId.GlobalActivity, { - group: '5_update', + group: '6_update', command: { id: 'update.restart', title: nls.localize('restartToUpdate', "Restart to Update") diff --git a/src/vs/workbench/contrib/update/electron-browser/update.ts b/src/vs/workbench/contrib/update/electron-browser/update.ts index 9a54595b4a0a..4b461e2fd699 100644 --- a/src/vs/workbench/contrib/update/electron-browser/update.ts +++ b/src/vs/workbench/contrib/update/electron-browser/update.ts @@ -5,7 +5,7 @@ import * as nls from 'vs/nls'; import severity from 'vs/base/common/severity'; -import product from 'vs/platform/product/node/product'; +import product from 'vs/platform/product/common/product'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { INotificationService } from 'vs/platform/notification/common/notification'; diff --git a/src/vs/workbench/contrib/url/common/externalUriResolver.ts b/src/vs/workbench/contrib/url/common/externalUriResolver.ts new file mode 100644 index 000000000000..1a86f56aa289 --- /dev/null +++ b/src/vs/workbench/contrib/url/common/externalUriResolver.ts @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable } from 'vs/base/common/lifecycle'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; + +export class ExternalUriResolverContribution extends Disposable implements IWorkbenchContribution { + constructor( + @IOpenerService _openerService: IOpenerService, + @IWorkbenchEnvironmentService _workbenchEnvironmentService: IWorkbenchEnvironmentService, + ) { + super(); + + if (_workbenchEnvironmentService.options && _workbenchEnvironmentService.options.resolveExternalUri) { + this._register(_openerService.registerExternalUriResolver({ + resolveExternalUri: async (resource) => { + return _workbenchEnvironmentService.options!.resolveExternalUri!(resource); + } + })); + } + } +} diff --git a/src/vs/workbench/contrib/url/common/trustedDomains.ts b/src/vs/workbench/contrib/url/common/trustedDomains.ts new file mode 100644 index 000000000000..c66f207570e6 --- /dev/null +++ b/src/vs/workbench/contrib/url/common/trustedDomains.ts @@ -0,0 +1,106 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { localize } from 'vs/nls'; +import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; +import { IProductService } from 'vs/platform/product/common/productService'; +import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; + +const TRUSTED_DOMAINS_URI = URI.parse('trustedDomains:/Trusted Domains'); + +export const configureTrustedDomainSettingsCommand = { + id: 'workbench.action.configureTrustedDomain', + description: { + description: localize('trustedDomain.configureTrustedDomain', 'Configure Trusted Domains'), + args: [] + }, + handler: async (accessor: ServicesAccessor) => { + const editorService = accessor.get(IEditorService); + editorService.openEditor({ resource: TRUSTED_DOMAINS_URI, mode: 'jsonc' }); + return; + } +}; + +export async function configureOpenerTrustedDomainsHandler( + trustedDomains: string[], + domainToConfigure: string, + quickInputService: IQuickInputService, + storageService: IStorageService, + editorService: IEditorService +) { + const parsedDomainToConfigure = URI.parse(domainToConfigure); + const toplevelDomainSegements = domainToConfigure.split('.'); + const domainEnd = toplevelDomainSegements.slice(toplevelDomainSegements.length - 2).join('.'); + const topLevelDomain = parsedDomainToConfigure.scheme + '://' + '*.' + domainEnd; + + const trustDomainAndOpenLinkItem: IQuickPickItem = { + type: 'item', + label: localize('trustedDomain.trustDomainAndOpenLink', 'Trust {0} and open link', domainToConfigure), + id: domainToConfigure, + picked: true + }; + const trustSubDomainAndOpenLinkItem: IQuickPickItem = { + type: 'item', + label: localize('trustedDomain.trustSubDomainAndOpenLink', 'Trust all domains ending in {0} and open link', domainEnd), + id: topLevelDomain + }; + const openAllLinksItem: IQuickPickItem = { + type: 'item', + label: localize('trustedDomain.trustAllAndOpenLink', 'Disable Link Protection and open link'), + id: '*', + picked: trustedDomains.indexOf('*') !== -1 + }; + const configureTrustedDomainItem: IQuickPickItem = { + type: 'item', + label: localize('trustedDomain.configureTrustedDomains', 'Configure Trusted Domains'), + id: 'configure' + }; + + const pickedResult = await quickInputService.pick( + [trustDomainAndOpenLinkItem, trustSubDomainAndOpenLinkItem, openAllLinksItem, configureTrustedDomainItem], + { + activeItem: trustDomainAndOpenLinkItem + } + ); + + if (pickedResult) { + if (pickedResult.id === 'configure') { + editorService.openEditor({ + resource: TRUSTED_DOMAINS_URI, + mode: 'jsonc' + }); + return trustedDomains; + } + if (pickedResult.id && trustedDomains.indexOf(pickedResult.id) === -1) { + storageService.store( + 'http.linkProtectionTrustedDomains', + JSON.stringify([...trustedDomains, pickedResult.id]), + StorageScope.GLOBAL + ); + + return [...trustedDomains, pickedResult.id]; + } + } + + return []; +} + +export function readTrustedDomains(storageService: IStorageService, productService: IProductService) { + let trustedDomains: string[] = productService.linkProtectionTrustedDomains + ? [...productService.linkProtectionTrustedDomains] + : []; + + try { + const trustedDomainsSrc = storageService.get('http.linkProtectionTrustedDomains', StorageScope.GLOBAL); + if (trustedDomainsSrc) { + trustedDomains = JSON.parse(trustedDomainsSrc); + } + } catch (err) { } + + return trustedDomains; +} diff --git a/src/vs/workbench/contrib/url/common/trustedDomainsFileSystemProvider.ts b/src/vs/workbench/contrib/url/common/trustedDomainsFileSystemProvider.ts new file mode 100644 index 000000000000..0be5c4830e4f --- /dev/null +++ b/src/vs/workbench/contrib/url/common/trustedDomainsFileSystemProvider.ts @@ -0,0 +1,126 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Event } from 'vs/base/common/event'; +import { parse } from 'vs/base/common/json'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { URI } from 'vs/base/common/uri'; +import { + FileDeleteOptions, + FileOverwriteOptions, + FileSystemProviderCapabilities, + FileType, + FileWriteOptions, + IFileService, + IFileSystemProvider, + IStat, + IWatchOptions +} from 'vs/platform/files/common/files'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { VSBuffer } from 'vs/base/common/buffer'; + +const TRUSTED_DOMAINS_SCHEMA = 'trustedDomains'; + +const TRUSTED_DOMAINS_STAT: IStat = { + type: FileType.File, + ctime: Date.now(), + mtime: Date.now(), + size: 0 +}; + +const CONFIG_HELP_TEXT = `// You can run "Configure Trusted Domains" command to edit trusted domains settings in this JSON file. +// The setting is updated upon saving this file. +// Links that match one of the entries can be opened without link protection. +// +// Example entries include: +// - "microsoft.com" +// - "*.microsoft.com": Match all domains ending in "microsoft.com" +// - "*": Match all domains +// +// By default, VS Code whitelists certain localhost and domains such as "code.visualstudio.com" +`; +const CONFIG_PLACEHOLDER_TEXT = `[ + // "microsoft.com" +] +`; + +function computeTrustedDomainContent(trustedDomains: string[]) { + if (trustedDomains.length === 0) { + return CONFIG_HELP_TEXT + CONFIG_PLACEHOLDER_TEXT; + } + + return CONFIG_HELP_TEXT + JSON.stringify(trustedDomains, null, 2); +} + +export class TrustedDomainsFileSystemProvider implements IFileSystemProvider, IWorkbenchContribution { + readonly capabilities = FileSystemProviderCapabilities.FileReadWrite; + + readonly onDidChangeCapabilities = Event.None; + readonly onDidChangeFile = Event.None; + + constructor( + @IFileService private readonly fileService: IFileService, + @IStorageService private readonly storageService: IStorageService + ) { + this.fileService.registerProvider(TRUSTED_DOMAINS_SCHEMA, this); + } + + stat(resource: URI): Promise { + return Promise.resolve(TRUSTED_DOMAINS_STAT); + } + + readFile(resource: URI): Promise { + let trustedDomains: string[] = []; + + try { + const trustedDomainsSrc = this.storageService.get('http.linkProtectionTrustedDomains', StorageScope.GLOBAL); + if (trustedDomainsSrc) { + trustedDomains = JSON.parse(trustedDomainsSrc); + } + } catch (err) { } + + + const trustedDomainsContent = computeTrustedDomainContent(trustedDomains); + const buffer = VSBuffer.fromString(trustedDomainsContent).buffer; + return Promise.resolve(buffer); + } + + writeFile(resource: URI, content: Uint8Array, opts: FileWriteOptions): Promise { + let trustedDomainsd = []; + + try { + trustedDomainsd = parse(content.toString()); + } catch (err) { } + + this.storageService.store( + 'http.linkProtectionTrustedDomains', + JSON.stringify(trustedDomainsd), + StorageScope.GLOBAL + ); + + return Promise.resolve(); + } + + watch(resource: URI, opts: IWatchOptions): IDisposable { + return { + dispose() { + return; + } + }; + } + mkdir(resource: URI): Promise { + return Promise.resolve(undefined!); + } + readdir(resource: URI): Promise<[string, FileType][]> { + return Promise.resolve(undefined!); + } + delete(resource: URI, opts: FileDeleteOptions): Promise { + return Promise.resolve(undefined!); + } + rename(from: URI, to: URI, opts: FileOverwriteOptions): Promise { + return Promise.resolve(undefined!); + } +} diff --git a/src/vs/workbench/contrib/url/common/trustedDomainsValidator.ts b/src/vs/workbench/contrib/url/common/trustedDomainsValidator.ts new file mode 100644 index 000000000000..28901eb85c09 --- /dev/null +++ b/src/vs/workbench/contrib/url/common/trustedDomainsValidator.ts @@ -0,0 +1,147 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Schemas } from 'vs/base/common/network'; +import Severity from 'vs/base/common/severity'; +import { equalsIgnoreCase } from 'vs/base/common/strings'; +import { URI } from 'vs/base/common/uri'; +import { localize } from 'vs/nls'; +import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { IProductService } from 'vs/platform/product/common/productService'; +import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { configureOpenerTrustedDomainsHandler, readTrustedDomains } from 'vs/workbench/contrib/url/common/trustedDomains'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; + +export class OpenerValidatorContributions implements IWorkbenchContribution { + constructor( + @IOpenerService private readonly _openerService: IOpenerService, + @IStorageService private readonly _storageService: IStorageService, + @IDialogService private readonly _dialogService: IDialogService, + @IProductService private readonly _productService: IProductService, + @IQuickInputService private readonly _quickInputService: IQuickInputService, + @IEditorService private readonly _editorService: IEditorService + ) { + this._openerService.registerValidator({ shouldOpen: r => this.validateLink(r) }); + } + + async validateLink(resource: URI): Promise { + const { scheme, authority } = resource; + + if (!equalsIgnoreCase(scheme, Schemas.http) && !equalsIgnoreCase(scheme, Schemas.https)) { + return true; + } + + const domainToOpen = `${scheme}://${authority}`; + const trustedDomains = readTrustedDomains(this._storageService, this._productService); + + if (isURLDomainTrusted(resource, trustedDomains)) { + return true; + } else { + const { choice } = await this._dialogService.show( + Severity.Info, + localize( + 'openExternalLinkAt', + 'Do you want {0} to open the external website?\n{1}', + this._productService.nameShort, + resource.toString(true) + ), + [ + localize('openLink', 'Open Link'), + localize('cancel', 'Cancel'), + localize('configureTrustedDomains', 'Configure Trusted Domains') + ], + { + cancelId: 1 + } + ); + + // Open Link + if (choice === 0) { + return true; + } + // Configure Trusted Domains + else if (choice === 2) { + const pickedDomains = await configureOpenerTrustedDomainsHandler( + trustedDomains, + domainToOpen, + this._quickInputService, + this._storageService, + this._editorService + ); + // Trust all domains + if (pickedDomains.indexOf('*') !== -1) { + return true; + } + // Trust current domain + if (pickedDomains.indexOf(domainToOpen) !== -1) { + return true; + } + return false; + } + + return false; + } + } +} + +const rLocalhost = /^localhost(:\d+)?$/i; +const r127 = /^127.0.0.1(:\d+)?$/; + +function isLocalhostAuthority(authority: string) { + return rLocalhost.test(authority) || r127.test(authority); +} + +/** + * Check whether a domain like https://www.microsoft.com matches + * the list of trusted domains. + * + * - Schemes must match + * - There's no subdomain matching. For example https://microsoft.com doesn't match https://www.microsoft.com + * - Star matches all subdomains. For example https://*.microsoft.com matches https://www.microsoft.com and https://foo.bar.microsoft.com + */ +export function isURLDomainTrusted(url: URI, trustedDomains: string[]) { + if (isLocalhostAuthority(url.authority)) { + return true; + } + + const domain = `${url.scheme}://${url.authority}`; + + for (let i = 0; i < trustedDomains.length; i++) { + if (trustedDomains[i] === '*') { + return true; + } + + if (trustedDomains[i] === domain) { + return true; + } + + if (trustedDomains[i].indexOf('*') !== -1) { + const parsedTrustedDomain = URI.parse(trustedDomains[i]); + if (url.scheme === parsedTrustedDomain.scheme) { + let reversedAuthoritySegments = url.authority.split('.').reverse(); + const reversedTrustedDomainAuthoritySegments = parsedTrustedDomain.authority.split('.').reverse(); + if ( + reversedTrustedDomainAuthoritySegments.length < reversedAuthoritySegments.length && + reversedTrustedDomainAuthoritySegments[reversedTrustedDomainAuthoritySegments.length - 1] === '*' + ) { + reversedAuthoritySegments = reversedAuthoritySegments.slice(0, reversedTrustedDomainAuthoritySegments.length); + } + + if ( + reversedAuthoritySegments.every((val, i) => { + return reversedTrustedDomainAuthoritySegments[i] === '*' || val === reversedTrustedDomainAuthoritySegments[i]; + }) + ) { + return true; + } + } + } + } + + return false; +} diff --git a/src/vs/workbench/contrib/url/common/url.contribution.ts b/src/vs/workbench/contrib/url/common/url.contribution.ts index 3e0466dba1bd..1e65e36da2f1 100644 --- a/src/vs/workbench/contrib/url/common/url.contribution.ts +++ b/src/vs/workbench/contrib/url/common/url.contribution.ts @@ -3,29 +3,21 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { localize } from 'vs/nls'; -import { SyncActionDescriptor, MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; -import { Registry } from 'vs/platform/registry/common/platform'; -import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; -import { IURLService } from 'vs/platform/url/common/url'; -import { IQuickInputService, IQuickPickItem, IQuickPickSeparator } from 'vs/platform/quickinput/common/quickInput'; -import { URI } from 'vs/base/common/uri'; import { Action } from 'vs/base/common/actions'; -import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { URI } from 'vs/base/common/uri'; +import { localize } from 'vs/nls'; +import { MenuId, MenuRegistry, SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; -import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { - IWorkbenchContribution, - IWorkbenchContributionsRegistry, - Extensions as WorkbenchExtensions -} from 'vs/workbench/common/contributions'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; -import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { IProductService } from 'vs/platform/product/common/product'; -import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { equalsIgnoreCase } from 'vs/base/common/strings'; -import { Schemas } from 'vs/base/common/network'; -import Severity from 'vs/base/common/severity'; +import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IURLService } from 'vs/platform/url/common/url'; +import { Extensions as ActionExtensions, IWorkbenchActionRegistry } from 'vs/workbench/common/actions'; +import { Extensions as WorkbenchExtensions, IWorkbenchContributionsRegistry } from 'vs/workbench/common/contributions'; +import { ExternalUriResolverContribution } from 'vs/workbench/contrib/url/common/externalUriResolver'; +import { configureTrustedDomainSettingsCommand } from 'vs/workbench/contrib/url/common/trustedDomains'; +import { TrustedDomainsFileSystemProvider } from 'vs/workbench/contrib/url/common/trustedDomainsFileSystemProvider'; +import { OpenerValidatorContributions } from 'vs/workbench/contrib/url/common/trustedDomainsValidator'; export class OpenUrlAction extends Action { static readonly ID = 'workbench.action.url.openUrl'; @@ -54,227 +46,27 @@ Registry.as(ActionExtensions.WorkbenchActions).registe localize('developer', 'Developer') ); -const configureTrustedDomainsHandler = async ( - quickInputService: IQuickInputService, - storageService: IStorageService, - linkProtectionTrustedDomains: string[], - domainToConfigure?: string -) => { - try { - const trustedDomainsSrc = storageService.get('http.linkProtectionTrustedDomains', StorageScope.GLOBAL); - if (trustedDomainsSrc) { - linkProtectionTrustedDomains = JSON.parse(trustedDomainsSrc); - } - } catch (err) { } - - const domainQuickPickItems: IQuickPickItem[] = linkProtectionTrustedDomains - .filter(d => d !== '*') - .map(d => { - return { - type: 'item', - label: d, - id: d, - picked: true - }; - }); - - const specialQuickPickItems: IQuickPickItem[] = [ - { - type: 'item', - label: localize('openAllLinksWithoutPrompt', 'Open all links without prompt'), - id: '*', - picked: linkProtectionTrustedDomains.indexOf('*') !== -1 - } - ]; - - let domainToConfigureItem: IQuickPickItem | undefined = undefined; - if (domainToConfigure && linkProtectionTrustedDomains.indexOf(domainToConfigure) === -1) { - domainToConfigureItem = { - type: 'item', - label: domainToConfigure, - id: domainToConfigure, - picked: true, - description: localize('trustDomainAndOpenLink', 'Trust domain and open link') - }; - specialQuickPickItems.push(domainToConfigureItem); - } - - const quickPickItems: (IQuickPickItem | IQuickPickSeparator)[] = - domainQuickPickItems.length === 0 - ? specialQuickPickItems - : [...specialQuickPickItems, { type: 'separator' }, ...domainQuickPickItems]; - - const pickedResult = await quickInputService.pick(quickPickItems, { - canPickMany: true, - activeItem: domainToConfigureItem - }); - - if (pickedResult) { - const pickedDomains: string[] = pickedResult.map(r => r.id!); - storageService.store('http.linkProtectionTrustedDomains', JSON.stringify(pickedDomains), StorageScope.GLOBAL); - - return pickedDomains; - } - - return []; -}; - -const configureTrustedDomainCommand = { - id: 'workbench.action.configureLinkProtectionTrustedDomains', - description: { - description: localize('configureLinkProtectionTrustedDomains', 'Configure Trusted Domains for Link Protection'), - args: [{ name: 'domainToConfigure', schema: { type: 'string' } }] - }, - handler: (accessor: ServicesAccessor, domainToConfigure?: string) => { - const quickInputService = accessor.get(IQuickInputService); - const storageService = accessor.get(IStorageService); - const productService = accessor.get(IProductService); - - const trustedDomains = productService.linkProtectionTrustedDomains - ? [...productService.linkProtectionTrustedDomains] - : []; - - return configureTrustedDomainsHandler(quickInputService, storageService, trustedDomains, domainToConfigure); - } -}; +/** + * Trusted Domains Contribution + */ -CommandsRegistry.registerCommand(configureTrustedDomainCommand); +CommandsRegistry.registerCommand(configureTrustedDomainSettingsCommand); MenuRegistry.appendMenuItem(MenuId.CommandPalette, { command: { - id: configureTrustedDomainCommand.id, - title: configureTrustedDomainCommand.description.description + id: configureTrustedDomainSettingsCommand.id, + title: configureTrustedDomainSettingsCommand.description.description } }); -class OpenerValidatorContributions implements IWorkbenchContribution { - constructor( - @IOpenerService private readonly _openerService: IOpenerService, - @IStorageService private readonly _storageService: IStorageService, - @IDialogService private readonly _dialogService: IDialogService, - @IProductService private readonly _productService: IProductService, - @IQuickInputService private readonly _quickInputService: IQuickInputService - ) { - this._openerService.registerValidator({ shouldOpen: r => this.validateLink(r) }); - } - - async validateLink(resource: URI): Promise { - const { scheme, authority } = resource; - - if (!equalsIgnoreCase(scheme, Schemas.http) && !equalsIgnoreCase(scheme, Schemas.https)) { - return true; - } - - let trustedDomains: string[] = this._productService.linkProtectionTrustedDomains - ? [...this._productService.linkProtectionTrustedDomains] - : []; - - try { - const trustedDomainsSrc = this._storageService.get('http.linkProtectionTrustedDomains', StorageScope.GLOBAL); - if (trustedDomainsSrc) { - trustedDomains = JSON.parse(trustedDomainsSrc); - } - } catch (err) { } - - const domainToOpen = `${scheme}://${authority}`; - - if (isURLDomainTrusted(resource, trustedDomains)) { - return true; - } else { - const { choice } = await this._dialogService.show( - Severity.Info, - localize( - 'openExternalLinkAt', - 'Do you want {0} to open the external website?\n{1}', - this._productService.nameShort, - resource.toString(true) - ), - [ - localize('openLink', 'Open Link'), - localize('cancel', 'Cancel'), - localize('configureTrustedDomains', 'Configure Trusted Domains') - ], - { - cancelId: 1 - } - ); - - // Open Link - if (choice === 0) { - return true; - } - // Configure Trusted Domains - else if (choice === 2) { - const pickedDomains = await configureTrustedDomainsHandler( - this._quickInputService, - this._storageService, - trustedDomains, - domainToOpen - ); - if (pickedDomains.indexOf(domainToOpen) !== -1) { - return true; - } - return false; - } - - return false; - } - } -} - Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution( OpenerValidatorContributions, LifecyclePhase.Restored ); - -const rLocalhost = /^localhost(:\d+)?$/i; -const r127 = /^127.0.0.1(:\d+)?$/; - -function isLocalhostAuthority(authority: string) { - return rLocalhost.test(authority) || r127.test(authority); -} - -/** - * Check whether a domain like https://www.microsoft.com matches - * the list of trusted domains. - * - * - Schemes must match - * - There's no subdomain matching. For example https://microsoft.com doesn't match https://www.microsoft.com - * - Star matches all. For example https://*.microsoft.com matches https://www.microsoft.com - */ -export function isURLDomainTrusted(url: URI, trustedDomains: string[]) { - if (isLocalhostAuthority(url.authority)) { - return true; - } - - const domain = `${url.scheme}://${url.authority}`; - - for (let i = 0; i < trustedDomains.length; i++) { - if (trustedDomains[i] === '*') { - return true; - } - - if (trustedDomains[i] === domain) { - return true; - } - - if (trustedDomains[i].indexOf('*') !== -1) { - const parsedTrustedDomain = URI.parse(trustedDomains[i]); - if (url.scheme === parsedTrustedDomain.scheme) { - const authoritySegments = url.authority.split('.'); - const trustedDomainAuthoritySegments = parsedTrustedDomain.authority.split('.'); - - if (authoritySegments.length === trustedDomainAuthoritySegments.length) { - if ( - authoritySegments.every( - (val, i) => trustedDomainAuthoritySegments[i] === '*' || val === trustedDomainAuthoritySegments[i] - ) - ) { - return true; - } - } - } - } - } - - return false; -} +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution( + TrustedDomainsFileSystemProvider, + LifecyclePhase.Ready +); +Registry.as(WorkbenchExtensions.Workbench).registerWorkbenchContribution( + ExternalUriResolverContribution, + LifecyclePhase.Ready +); diff --git a/src/vs/workbench/contrib/userData/browser/media/sync-push-dark.svg b/src/vs/workbench/contrib/userData/browser/media/sync-push-dark.svg new file mode 100644 index 000000000000..72695bb2e5b1 --- /dev/null +++ b/src/vs/workbench/contrib/userData/browser/media/sync-push-dark.svg @@ -0,0 +1,3 @@ + diff --git a/src/vs/workbench/contrib/userData/browser/media/sync-push-light.svg b/src/vs/workbench/contrib/userData/browser/media/sync-push-light.svg new file mode 100644 index 000000000000..82cbddecbc01 --- /dev/null +++ b/src/vs/workbench/contrib/userData/browser/media/sync-push-light.svg @@ -0,0 +1,3 @@ + diff --git a/src/vs/workbench/contrib/userData/browser/userData.contribution.ts b/src/vs/workbench/contrib/userData/browser/userData.contribution.ts new file mode 100644 index 000000000000..4fcefbf7a3d7 --- /dev/null +++ b/src/vs/workbench/contrib/userData/browser/userData.contribution.ts @@ -0,0 +1,250 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { IUserDataSyncService, SyncStatus, USER_DATA_PREVIEW_SCHEME, IUserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSync'; +import { localize } from 'vs/nls'; +import { Disposable, MutableDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { CommandsRegistry } from 'vs/platform/commands/common/commands'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; +import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope } from 'vs/platform/configuration/common/configurationRegistry'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { MenuRegistry, MenuId, IMenuItem } from 'vs/platform/actions/common/actions'; +import { RawContextKey, IContextKeyService, IContextKey, ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; +import { IActivityService, IBadge, NumberBadge, ProgressBadge } from 'vs/workbench/services/activity/common/activity'; +import { GLOBAL_ACTIVITY_ID } from 'vs/workbench/common/activity'; +import { timeout } from 'vs/base/common/async'; +import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; +import { URI } from 'vs/base/common/uri'; +import { registerAndGetAmdImageURL } from 'vs/base/common/amd'; +import { ResourceContextKey } from 'vs/workbench/common/resources'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; +import { Event } from 'vs/base/common/event'; +import { IHistoryService } from 'vs/workbench/services/history/common/history'; +import { IFileService } from 'vs/platform/files/common/files'; +import { InMemoryFileSystemProvider } from 'vs/workbench/services/userData/common/inMemoryUserDataProvider'; + +const CONTEXT_SYNC_STATE = new RawContextKey('syncStatus', SyncStatus.Uninitialized); + +Registry.as(ConfigurationExtensions.Configuration) + .registerConfiguration({ + id: 'userConfiguration', + order: 30, + title: localize('userConfiguration', "User Configuration"), + type: 'object', + properties: { + 'userConfiguration.enableSync': { + type: 'boolean', + description: localize('userConfiguration.enableSync', "When enabled, synchronises User Configuration: Settings, Keybindings, Extensions & Snippets."), + default: true, + scope: ConfigurationScope.APPLICATION + } + } + }); + +class UserDataSyncContribution extends Disposable implements IWorkbenchContribution { + + constructor( + @IFileService fileService: IFileService, + @IConfigurationService private readonly configurationService: IConfigurationService, + @IUserDataSyncService private readonly userDataSyncService: IUserDataSyncService, + @IUserDataSyncStoreService private readonly userDataSyncStoreService: IUserDataSyncStoreService, + ) { + super(); + this._register(fileService.registerProvider(USER_DATA_PREVIEW_SCHEME, new InMemoryFileSystemProvider())); + this.sync(true); + this._register(Event.any( + Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('userConfiguration.enableSync') && this.configurationService.getValue('userConfiguration.enableSync')), + this.userDataSyncStoreService.onDidChangeEnablement) + (() => this.sync(true))); + + // Sync immediately if there is a local change. + this._register(Event.debounce(this.userDataSyncService.onDidChangeLocal, () => undefined, 500)(() => this.sync(false))); + } + + private async sync(loop: boolean): Promise { + if (this.configurationService.getValue('userConfiguration.enableSync') && this.userDataSyncStoreService.enabled) { + try { + await this.userDataSyncService.sync(); + } catch (e) { + // Ignore errors + } + if (loop) { + await timeout(1000 * 5); // Loop sync for every 5s. + this.sync(loop); + } + } + } + +} + +const SYNC_PUSH_LIGHT_ICON_URI = URI.parse(registerAndGetAmdImageURL(`vs/workbench/contrib/userData/browser/media/sync-push-light.svg`)); +const SYNC_PUSH_DARK_ICON_URI = URI.parse(registerAndGetAmdImageURL(`vs/workbench/contrib/userData/browser/media/sync-push-dark.svg`)); +class SyncActionsContribution extends Disposable implements IWorkbenchContribution { + + private readonly syncEnablementContext: IContextKey; + private readonly badgeDisposable = this._register(new MutableDisposable()); + private readonly conflictsWarningDisposable = this._register(new MutableDisposable()); + + constructor( + @IUserDataSyncService private readonly userDataSyncService: IUserDataSyncService, + @IContextKeyService contextKeyService: IContextKeyService, + @IActivityService private readonly activityService: IActivityService, + @INotificationService private readonly notificationService: INotificationService, + @IConfigurationService private readonly configurationService: IConfigurationService, + @IEditorService private readonly editorService: IEditorService, + @ITextFileService private readonly textFileService: ITextFileService, + @IHistoryService private readonly historyService: IHistoryService, + ) { + super(); + this.syncEnablementContext = CONTEXT_SYNC_STATE.bindTo(contextKeyService); + this.onDidChangeStatus(userDataSyncService.status); + this._register(Event.debounce(userDataSyncService.onDidChangeStatus, () => undefined, 500)(status => this.onDidChangeStatus(userDataSyncService.status))); + this.registerActions(); + } + + private onDidChangeStatus(status: SyncStatus) { + this.syncEnablementContext.set(status); + + let badge: IBadge | undefined = undefined; + let clazz: string | undefined; + + if (status === SyncStatus.HasConflicts) { + badge = new NumberBadge(1, () => localize('resolve conflicts', "Resolve Conflicts")); + } else if (status === SyncStatus.Syncing) { + badge = new ProgressBadge(() => localize('syncing', "Synchronising User Configuration...")); + clazz = 'progress-badge'; + } + + this.badgeDisposable.clear(); + + if (badge) { + this.badgeDisposable.value = this.activityService.showActivity(GLOBAL_ACTIVITY_ID, badge, clazz); + } + + if (status === SyncStatus.HasConflicts) { + if (!this.conflictsWarningDisposable.value) { + const handle = this.notificationService.prompt(Severity.Warning, localize('conflicts detected', "Unable to sync due to conflicts. Please resolve them to continue."), + [ + { + label: localize('resolve', "Resolve Conflicts"), + run: () => this.handleConflicts() + } + ]); + this.conflictsWarningDisposable.value = toDisposable(() => handle.close()); + handle.onDidClose(() => this.conflictsWarningDisposable.clear()); + } + } else { + this.conflictsWarningDisposable.clear(); + } + } + + private async continueSync(): Promise { + // Get the preview editor + const editorInput = this.editorService.editors.filter(input => { + const resource = input.getResource(); + return resource && resource.scheme === USER_DATA_PREVIEW_SCHEME; + })[0]; + // Save the preview + if (editorInput && editorInput.isDirty()) { + await this.textFileService.save(editorInput.getResource()!); + } + try { + // Continue Sync + await this.userDataSyncService.continueSync(); + } catch (error) { + this.notificationService.error(error); + return; + } + // Close the preview editor + if (editorInput) { + editorInput.dispose(); + } + } + + private async handleConflicts(): Promise { + const resource = this.userDataSyncService.conflicts; + if (resource) { + const resourceInput = { + resource, + options: { + preserveFocus: false, + pinned: false, + revealIfVisible: true, + }, + mode: 'jsonc' + }; + this.editorService.openEditor(resourceInput).then(() => this.historyService.remove(resourceInput)); + } + } + + private registerActions(): void { + + const startSyncMenuItem: IMenuItem = { + group: '5_sync', + command: { + id: 'workbench.userData.actions.syncStart', + title: localize('start sync', "Sync: Start") + }, + when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), ContextKeyExpr.not('config.userConfiguration.enableSync')), + }; + CommandsRegistry.registerCommand(startSyncMenuItem.command.id, () => this.configurationService.updateValue('userConfiguration.enableSync', true)); + MenuRegistry.appendMenuItem(MenuId.GlobalActivity, startSyncMenuItem); + MenuRegistry.appendMenuItem(MenuId.CommandPalette, startSyncMenuItem); + + const stopSyncMenuItem: IMenuItem = { + group: '5_sync', + command: { + id: 'workbench.userData.actions.stopSync', + title: localize('stop sync', "Sync: Stop") + }, + when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), ContextKeyExpr.has('config.userConfiguration.enableSync')), + }; + CommandsRegistry.registerCommand(stopSyncMenuItem.command.id, () => this.configurationService.updateValue('userConfiguration.enableSync', false)); + MenuRegistry.appendMenuItem(MenuId.GlobalActivity, stopSyncMenuItem); + MenuRegistry.appendMenuItem(MenuId.CommandPalette, stopSyncMenuItem); + + const resolveConflictsMenuItem: IMenuItem = { + group: '5_sync', + command: { + id: 'sync.resolveConflicts', + title: localize('resolveConflicts', "Sync: Resolve Conflicts"), + }, + when: CONTEXT_SYNC_STATE.isEqualTo(SyncStatus.HasConflicts), + }; + CommandsRegistry.registerCommand(resolveConflictsMenuItem.command.id, () => this.handleConflicts()); + MenuRegistry.appendMenuItem(MenuId.GlobalActivity, resolveConflictsMenuItem); + MenuRegistry.appendMenuItem(MenuId.CommandPalette, resolveConflictsMenuItem); + + const continueSyncCommandId = 'workbench.userData.actions.continueSync'; + CommandsRegistry.registerCommand(continueSyncCommandId, () => this.continueSync()); + MenuRegistry.appendMenuItem(MenuId.CommandPalette, { + command: { + id: continueSyncCommandId, + title: localize('continue sync', "Sync: Continue") + }, + when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.isEqualTo(SyncStatus.HasConflicts)), + }); + MenuRegistry.appendMenuItem(MenuId.EditorTitle, { + command: { + id: continueSyncCommandId, + title: localize('continue sync', "Sync: Continue"), + iconLocation: { + light: SYNC_PUSH_LIGHT_ICON_URI, + dark: SYNC_PUSH_DARK_ICON_URI + } + }, + group: 'navigation', + order: 1, + when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.isEqualTo(SyncStatus.HasConflicts), ResourceContextKey.Scheme.isEqualTo(USER_DATA_PREVIEW_SCHEME)), + }); + } +} + +const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); +workbenchRegistry.registerWorkbenchContribution(SyncActionsContribution, LifecyclePhase.Starting); +workbenchRegistry.registerWorkbenchContribution(UserDataSyncContribution, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/contrib/watermark/browser/watermark.ts b/src/vs/workbench/contrib/watermark/browser/watermark.ts index 60f19e5bf98b..b283c86d16ff 100644 --- a/src/vs/workbench/contrib/watermark/browser/watermark.ts +++ b/src/vs/workbench/contrib/watermark/browser/watermark.ts @@ -77,7 +77,7 @@ const folderEntries = [ const WORKBENCH_TIPS_ENABLED_KEY = 'workbench.tips.enabled'; export class WatermarkContribution extends Disposable implements IWorkbenchContribution { - private watermark: HTMLElement; + private watermark: HTMLElement | undefined; private watermarkDisposable = this._register(new DisposableStore()); private enabled: boolean; private workbenchState: WorkbenchState; diff --git a/src/vs/workbench/contrib/webview/browser/webview.contribution.ts b/src/vs/workbench/contrib/webview/browser/webview.contribution.ts index ce06cf2b3140..8640bd7e247c 100644 --- a/src/vs/workbench/contrib/webview/browser/webview.contribution.ts +++ b/src/vs/workbench/contrib/webview/browser/webview.contribution.ts @@ -18,14 +18,14 @@ import { WebviewEditorInputFactory } from 'vs/workbench/contrib/webview/browser/ import { KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE, webviewDeveloperCategory, KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_FOCUSED } from 'vs/workbench/contrib/webview/browser/webview'; import { HideWebViewEditorFindCommand, ReloadWebviewAction, ShowWebViewEditorFindWidgetCommand, WebViewEditorFindNextCommand, WebViewEditorFindPreviousCommand } from '../browser/webviewCommands'; import { WebviewEditor } from '../browser/webviewEditor'; -import { WebviewEditorInput } from '../browser/webviewEditorInput'; +import { WebviewInput } from '../browser/webviewEditorInput'; import { IWebviewEditorService, WebviewEditorService } from '../browser/webviewEditorService'; (Registry.as(EditorExtensions.Editors)).registerEditor(new EditorDescriptor( WebviewEditor, WebviewEditor.ID, localize('webview.editor.label', "webview editor")), - [new SyncDescriptor(WebviewEditorInput)]); + [new SyncDescriptor(WebviewInput)]); Registry.as(EditorInputExtensions.EditorInputFactories).registerEditorInputFactory( WebviewEditorInputFactory.ID, diff --git a/src/vs/workbench/contrib/webview/browser/webviewEditor.ts b/src/vs/workbench/contrib/webview/browser/webviewEditor.ts index 576dc0dc4e8e..fb779c4e98de 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewEditor.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewEditor.ts @@ -14,7 +14,7 @@ import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IWindowService } from 'vs/platform/windows/common/windows'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { EditorOptions, EditorInput } from 'vs/workbench/common/editor'; -import { WebviewEditorInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput'; +import { WebviewInput } from 'vs/workbench/contrib/webview/browser/webviewEditorInput'; import { KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_VISIBLE, Webview, WebviewEditorOverlay } from 'vs/workbench/contrib/webview/browser/webview'; import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -27,6 +27,7 @@ export class WebviewEditor extends BaseEditor { private _findWidgetVisible: IContextKey; private _editorFrame?: HTMLElement; private _content?: HTMLElement; + private _dimension?: DOM.Dimension; private readonly _webviewFocusTrackerDisposables = this._register(new DisposableStore()); private readonly _onFocusWindowHandler = this._register(new MutableDisposable()); @@ -89,7 +90,8 @@ export class WebviewEditor extends BaseEditor { } public layout(dimension: DOM.Dimension): void { - if (this.input && this.input instanceof WebviewEditorInput) { + this._dimension = dimension; + if (this.input && this.input instanceof WebviewInput) { this.synchronizeWebviewContainerDimensions(this.input.webview, dimension); this.input.webview.layout(); } @@ -109,27 +111,27 @@ export class WebviewEditor extends BaseEditor { } public withWebview(f: (element: Webview) => void): void { - if (this.input && this.input instanceof WebviewEditorInput) { + if (this.input && this.input instanceof WebviewInput) { f(this.input.webview); } } protected setEditorVisible(visible: boolean, group: IEditorGroup | undefined): void { - const webview = this.input && (this.input as WebviewEditorInput).webview; + const webview = this.input && (this.input as WebviewInput).webview; if (webview) { if (visible) { webview.claim(this); } else { webview.release(this); } - this.claimWebview(this.input as WebviewEditorInput); + this.claimWebview(this.input as WebviewInput); } super.setEditorVisible(visible, group); } public clearInput() { - if (this.input && this.input instanceof WebviewEditorInput) { + if (this.input && this.input instanceof WebviewInput) { this.input.webview.release(this); } @@ -141,7 +143,7 @@ export class WebviewEditor extends BaseEditor { return; } - if (this.input && this.input instanceof WebviewEditorInput) { + if (this.input && this.input instanceof WebviewInput) { this.input.webview.release(this); } @@ -151,16 +153,19 @@ export class WebviewEditor extends BaseEditor { return; } - if (input instanceof WebviewEditorInput) { + if (input instanceof WebviewInput) { if (this.group) { input.updateGroup(this.group.id); } this.claimWebview(input); + if (this._dimension) { + this.layout(this._dimension); + } } } - private claimWebview(input: WebviewEditorInput): void { + private claimWebview(input: WebviewInput): void { input.webview.claim(this); if (input.webview.options.enableFindWidget) { diff --git a/src/vs/workbench/contrib/webview/browser/webviewEditorInput.ts b/src/vs/workbench/contrib/webview/browser/webviewEditorInput.ts index 973de6d98333..879bdae79c38 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewEditorInput.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewEditorInput.ts @@ -52,7 +52,7 @@ class WebviewIconsManager { } } -export class WebviewEditorInput extends EditorInput { +export class WebviewInput extends EditorInput { public static typeId = 'workbench.editors.webviewInput'; @@ -82,7 +82,7 @@ export class WebviewEditorInput extends EditorInput { } public getTypeId(): string { - return WebviewEditorInput.typeId; + return WebviewInput.typeId; } public getResource(): URI { @@ -119,7 +119,7 @@ export class WebviewEditorInput extends EditorInput { public set iconPath(value: { light: URI, dark: URI } | undefined) { this._iconPath = value; - WebviewEditorInput.iconsManager.setIcons(this.id, value); + WebviewInput.iconsManager.setIcons(this.id, value); } public matches(other: IEditorInput): boolean { @@ -143,7 +143,7 @@ export class WebviewEditorInput extends EditorInput { } } -export class RevivedWebviewEditorInput extends WebviewEditorInput { +export class RevivedWebviewEditorInput extends WebviewInput { private _revived: boolean = false; constructor( @@ -154,7 +154,7 @@ export class RevivedWebviewEditorInput extends WebviewEditorInput { readonly location: URI; readonly id: ExtensionIdentifier }, - private readonly reviver: (input: WebviewEditorInput) => Promise, + private readonly reviver: (input: WebviewInput) => Promise, webview: Unowned ) { super(id, viewType, name, extension, webview); diff --git a/src/vs/workbench/contrib/webview/browser/webviewEditorInputFactory.ts b/src/vs/workbench/contrib/webview/browser/webviewEditorInputFactory.ts index 903ea0589c27..4f789df57dea 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewEditorInputFactory.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewEditorInputFactory.ts @@ -8,7 +8,7 @@ import { generateUuid } from 'vs/base/common/uuid'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IEditorInputFactory } from 'vs/workbench/common/editor'; -import { WebviewEditorInput } from './webviewEditorInput'; +import { WebviewInput } from './webviewEditorInput'; import { IWebviewEditorService, WebviewInputOptions } from './webviewEditorService'; interface SerializedIconPath { @@ -29,13 +29,13 @@ interface SerializedWebview { export class WebviewEditorInputFactory implements IEditorInputFactory { - public static readonly ID = WebviewEditorInput.typeId; + public static readonly ID = WebviewInput.typeId; public constructor( @IWebviewEditorService private readonly _webviewService: IWebviewEditorService ) { } - public serialize(input: WebviewEditorInput): string | undefined { + public serialize(input: WebviewInput): string | undefined { if (!this._webviewService.shouldPersist(input)) { return undefined; } @@ -51,7 +51,7 @@ export class WebviewEditorInputFactory implements IEditorInputFactory { public deserialize( _instantiationService: IInstantiationService, serializedEditorInput: string - ): WebviewEditorInput { + ): WebviewInput { const data = this.fromJson(serializedEditorInput); return this._webviewService.reviveWebview(generateUuid(), data.viewType, data.title, data.iconPath, data.state, data.options, data.extensionLocation ? { location: data.extensionLocation, @@ -70,7 +70,7 @@ export class WebviewEditorInputFactory implements IEditorInputFactory { }; } - protected toJson(input: WebviewEditorInput): SerializedWebview { + protected toJson(input: WebviewInput): SerializedWebview { return { viewType: input.viewType, title: input.getName(), diff --git a/src/vs/workbench/contrib/webview/browser/webviewEditorService.ts b/src/vs/workbench/contrib/webview/browser/webviewEditorService.ts index f973e6d867ee..d4c223849fcb 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewEditorService.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewEditorService.ts @@ -13,7 +13,7 @@ import { GroupIdentifier } from 'vs/workbench/common/editor'; import { IWebviewService, WebviewOptions, WebviewContentOptions } from 'vs/workbench/contrib/webview/browser/webview'; import { IEditorGroup, IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService'; import { ACTIVE_GROUP_TYPE, IEditorService, SIDE_GROUP_TYPE } from 'vs/workbench/services/editor/common/editorService'; -import { RevivedWebviewEditorInput, WebviewEditorInput } from './webviewEditorInput'; +import { RevivedWebviewEditorInput, WebviewInput } from './webviewEditorInput'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { EditorActivation } from 'vs/platform/editor/common/editor'; @@ -38,7 +38,7 @@ export interface IWebviewEditorService { location: URI, id: ExtensionIdentifier }, - ): WebviewEditorInput; + ): WebviewInput; reviveWebview( id: string, @@ -52,10 +52,10 @@ export interface IWebviewEditorService { readonly id?: ExtensionIdentifier }, group: number | undefined - ): WebviewEditorInput; + ): WebviewInput; revealWebview( - webview: WebviewEditorInput, + webview: WebviewInput, group: IEditorGroup, preserveFocus: boolean ): void; @@ -65,21 +65,21 @@ export interface IWebviewEditorService { ): IDisposable; shouldPersist( - input: WebviewEditorInput + input: WebviewInput ): boolean; resolveWebview( - webview: WebviewEditorInput, + webview: WebviewInput, ): Promise; } export interface WebviewResolve { canResolve( - webview: WebviewEditorInput, + webview: WebviewInput, ): boolean; resolveWebview( - webview: WebviewEditorInput, + webview: WebviewInput, ): Promise; } @@ -99,7 +99,7 @@ export function areWebviewInputOptionsEqual(a: WebviewInputOptions, b: WebviewIn && (a.portMapping === b.portMapping || (Array.isArray(a.portMapping) && Array.isArray(b.portMapping) && equals(a.portMapping, b.portMapping, (a, b) => a.extensionHostPort === b.extensionHostPort && a.webviewPort === b.webviewPort))); } -function canRevive(reviver: WebviewResolve, webview: WebviewEditorInput): boolean { +function canRevive(reviver: WebviewResolve, webview: WebviewInput): boolean { if (webview.isDisposed()) { return false; } @@ -107,9 +107,9 @@ function canRevive(reviver: WebviewResolve, webview: WebviewEditorInput): boolea } class RevivalPool { - private _awaitingRevival: Array<{ input: WebviewEditorInput, resolve: () => void }> = []; + private _awaitingRevival: Array<{ input: WebviewInput, resolve: () => void }> = []; - public add(input: WebviewEditorInput, resolve: () => void) { + public add(input: WebviewInput, resolve: () => void) { this._awaitingRevival.push({ input, resolve }); } @@ -147,10 +147,10 @@ export class WebviewEditorService implements IWebviewEditorService { location: URI, id: ExtensionIdentifier }, - ): WebviewEditorInput { + ): WebviewInput { const webview = this.createWebiew(id, extension, options); - const webviewInput = this._instantiationService.createInstance(WebviewEditorInput, id, viewType, title, extension, new UnownedDisposable(webview), undefined); + const webviewInput = this._instantiationService.createInstance(WebviewInput, id, viewType, title, extension, new UnownedDisposable(webview), undefined); this._editorService.openEditor(webviewInput, { pinned: true, preserveFocus: showOptions.preserveFocus, @@ -162,7 +162,7 @@ export class WebviewEditorService implements IWebviewEditorService { } public revealWebview( - webview: WebviewEditorInput, + webview: WebviewInput, group: IEditorGroup, preserveFocus: boolean ): void { @@ -193,11 +193,11 @@ export class WebviewEditorService implements IWebviewEditorService { readonly id: ExtensionIdentifier }, group: number | undefined, - ): WebviewEditorInput { + ): WebviewInput { const webview = this.createWebiew(id, extension, options); webview.state = state; - const webviewInput = new RevivedWebviewEditorInput(id, viewType, title, extension, async (webview: WebviewEditorInput): Promise => { + const webviewInput = new RevivedWebviewEditorInput(id, viewType, title, extension, async (webview: WebviewInput): Promise => { const didRevive = await this.tryRevive(webview); if (didRevive) { return Promise.resolve(undefined); @@ -230,7 +230,7 @@ export class WebviewEditorService implements IWebviewEditorService { } public shouldPersist( - webview: WebviewEditorInput + webview: WebviewInput ): boolean { // Has no state, don't persist if (!webview.webview.state) { @@ -247,7 +247,7 @@ export class WebviewEditorService implements IWebviewEditorService { } private async tryRevive( - webview: WebviewEditorInput + webview: WebviewInput ): Promise { for (const reviver of values(this._revivers)) { if (canRevive(reviver, webview)) { @@ -259,7 +259,7 @@ export class WebviewEditorService implements IWebviewEditorService { } public async resolveWebview( - webview: WebviewEditorInput, + webview: WebviewInput, ): Promise { const didRevive = await this.tryRevive(webview); if (!didRevive) { diff --git a/src/vs/workbench/contrib/webview/browser/webviewElement.ts b/src/vs/workbench/contrib/webview/browser/webviewElement.ts index ac92123a9ac6..5b5ccd27198c 100644 --- a/src/vs/workbench/contrib/webview/browser/webviewElement.ts +++ b/src/vs/workbench/contrib/webview/browser/webviewElement.ts @@ -6,11 +6,11 @@ import { addClass, addDisposableListener } from 'vs/base/browser/dom'; import { Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; +import { isWeb } from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; import { IFileService } from 'vs/platform/files/common/files'; -import product from 'vs/platform/product/browser/product'; import { ITunnelService } from 'vs/platform/remote/common/tunnel'; import { ITheme, IThemeService } from 'vs/platform/theme/common/themeService'; import { Webview, WebviewContentOptions, WebviewOptions } from 'vs/workbench/contrib/webview/browser/webview'; @@ -19,7 +19,6 @@ import { WebviewPortMappingManager } from 'vs/workbench/contrib/webview/common/p import { loadLocalResource } from 'vs/workbench/contrib/webview/common/resourceLoader'; import { getWebviewThemeData } from 'vs/workbench/contrib/webview/common/themeing'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { isWeb } from 'vs/base/common/platform'; interface WebviewContent { readonly html: string; @@ -48,8 +47,8 @@ export class IFrameWebview extends Disposable implements Webview { @IConfigurationService private readonly _configurationService: IConfigurationService, ) { super(); - if (!this.useExternalEndpoint && (!environmentService.options || typeof environmentService.options.webviewEndpoint !== 'string')) { - throw new Error('To use iframe based webviews, you must configure `environmentService.webviewEndpoint`'); + if (!this.useExternalEndpoint && (!environmentService.options || typeof environmentService.webviewExternalEndpoint !== 'string')) { + throw new Error('To use iframe based webviews, you must configure `environmentService.webviewExternalEndpoint`'); } this._portMappingManager = this._register(new WebviewPortMappingManager( @@ -66,7 +65,7 @@ export class IFrameWebview extends Disposable implements Webview { this.element = document.createElement('iframe'); this.element.sandbox.add('allow-scripts', 'allow-same-origin'); - this.element.setAttribute('src', `${this.endpoint}/index.html?id=${this.id}`); + this.element.setAttribute('src', `${this.externalEndpoint}/index.html?id=${this.id}`); this.element.style.border = 'none'; this.element.style.width = '100%'; this.element.style.height = '100%'; @@ -144,24 +143,14 @@ export class IFrameWebview extends Disposable implements Webview { this._register(themeService.onThemeChange(this.style, this)); } - private get endpoint(): string { - const baseEndpoint = this.externalEndpoint || this.environmentService.options!.webviewEndpoint!; - const endpoint = baseEndpoint.replace('{{uuid}}', this.id); + private get externalEndpoint(): string { + const endpoint = this.environmentService.webviewExternalEndpoint!.replace('{{uuid}}', this.id); if (endpoint[endpoint.length - 1] === '/') { return endpoint.slice(0, endpoint.length - 1); } return endpoint; } - private get externalEndpoint(): string | undefined { - const useExternalEndpoint = this.useExternalEndpoint; - if (!useExternalEndpoint) { - return undefined; - } - const commit = product.quality && product.commit ? product.commit : '211fa02efe8c041fd7baa8ec3dce199d5185aa44'; - return `https://{{uuid}}.vscode-webview-test.com/${commit}`; - } - private get useExternalEndpoint(): boolean { return isWeb || this._configurationService.getValue('webview.experimental.useExternalEndpoint'); } @@ -196,7 +185,7 @@ export class IFrameWebview extends Disposable implements Webview { private preprocessHtml(value: string): string { return value.replace(/(["'])vscode-resource:([^\s'"]+?)(["'])/gi, (_, startQuote, path, endQuote) => - `${startQuote}${this.endpoint}/vscode-resource${path}${endQuote}`); + `${startQuote}${this.externalEndpoint}/vscode-resource${path}${endQuote}`); } public update(html: string, options: WebviewContentOptions, retainContextWhenHidden: boolean) { @@ -216,7 +205,7 @@ export class IFrameWebview extends Disposable implements Webview { contents: this.content.html, options: this.content.options, state: this.content.state, - endpoint: this.endpoint, + endpoint: this.externalEndpoint, }); } diff --git a/src/vs/workbench/contrib/webview/electron-browser/pre/electron-index.js b/src/vs/workbench/contrib/webview/electron-browser/pre/electron-index.js index 07f7d132d556..59edaba961a5 100644 --- a/src/vs/workbench/contrib/webview/electron-browser/pre/electron-index.js +++ b/src/vs/workbench/contrib/webview/electron-browser/pre/electron-index.js @@ -13,15 +13,6 @@ return; } hasRegistered = true; - - // @ts-ignore - require('electron').webFrame.registerURLSchemeAsPrivileged('vscode-resource', { - secure: true, - bypassCSP: false, - allowServiceWorkers: false, - supportFetchAPI: true, - corsEnabled: true - }); }; }()); diff --git a/src/vs/workbench/contrib/welcome/gettingStarted/electron-browser/gettingStarted.contribution.ts b/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.contribution.ts similarity index 100% rename from src/vs/workbench/contrib/welcome/gettingStarted/electron-browser/gettingStarted.contribution.ts rename to src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.contribution.ts diff --git a/src/vs/workbench/contrib/welcome/gettingStarted/electron-browser/gettingStarted.ts b/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.ts similarity index 87% rename from src/vs/workbench/contrib/welcome/gettingStarted/electron-browser/gettingStarted.ts rename to src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.ts index bae4f7007507..2e0973255407 100644 --- a/src/vs/workbench/contrib/welcome/gettingStarted/electron-browser/gettingStarted.ts +++ b/src/vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.ts @@ -8,9 +8,9 @@ import { IStorageService, StorageScope } from 'vs/platform/storage/common/storag import { ITelemetryService, ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import * as platform from 'vs/base/common/platform'; -import product from 'vs/platform/product/node/product'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { URI } from 'vs/base/common/uri'; +import { IProductService } from 'vs/platform/product/common/productService'; export class GettingStarted implements IWorkbenchContribution { @@ -23,23 +23,20 @@ export class GettingStarted implements IWorkbenchContribution { @IStorageService private readonly storageService: IStorageService, @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, @ITelemetryService private readonly telemetryService: ITelemetryService, - @IOpenerService private readonly openerService: IOpenerService + @IOpenerService private readonly openerService: IOpenerService, + @IProductService productService: IProductService ) { - this.appName = product.nameLong; + this.appName = productService.nameLong; + this.welcomePageURL = productService.welcomePage; - if (!product.welcomePage) { + if ( + !productService.welcomePage || + environmentService.skipGettingStarted || + environmentService.isExtensionDevelopment + ) { return; } - if (environmentService.skipGettingStarted) { - return; - } - - if (environmentService.isExtensionDevelopment) { - return; - } - - this.welcomePageURL = product.welcomePage; this.handleWelcome(); } diff --git a/src/vs/workbench/contrib/welcome/gettingStarted/electron-browser/telemetryOptOut.ts b/src/vs/workbench/contrib/welcome/gettingStarted/browser/telemetryOptOut.ts similarity index 90% rename from src/vs/workbench/contrib/welcome/gettingStarted/electron-browser/telemetryOptOut.ts rename to src/vs/workbench/contrib/welcome/gettingStarted/browser/telemetryOptOut.ts index 5ffcaf7a6c1b..22b945913561 100644 --- a/src/vs/workbench/contrib/welcome/gettingStarted/electron-browser/telemetryOptOut.ts +++ b/src/vs/workbench/contrib/welcome/gettingStarted/browser/telemetryOptOut.ts @@ -6,7 +6,6 @@ import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import product from 'vs/platform/product/node/product'; import { IOpenerService } from 'vs/platform/opener/common/opener'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { URI } from 'vs/base/common/uri'; @@ -18,6 +17,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { language, locale } from 'vs/base/common/platform'; import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { IProductService } from 'vs/platform/product/common/productService'; export class TelemetryOptOut implements IWorkbenchContribution { @@ -33,9 +33,10 @@ export class TelemetryOptOut implements IWorkbenchContribution { @ITelemetryService private readonly telemetryService: ITelemetryService, @IExperimentService private readonly experimentService: IExperimentService, @IConfigurationService private readonly configurationService: IConfigurationService, - @IExtensionGalleryService private readonly galleryService: IExtensionGalleryService + @IExtensionGalleryService private readonly galleryService: IExtensionGalleryService, + @IProductService productService: IProductService ) { - if (!product.telemetryOptOutUrl || storageService.get(TelemetryOptOut.TELEMETRY_OPT_OUT_SHOWN, StorageScope.GLOBAL)) { + if (!productService.telemetryOptOutUrl || storageService.get(TelemetryOptOut.TELEMETRY_OPT_OUT_SHOWN, StorageScope.GLOBAL)) { return; } const experimentId = 'telemetryOptOut'; @@ -49,17 +50,17 @@ export class TelemetryOptOut implements IWorkbenchContribution { } storageService.store(TelemetryOptOut.TELEMETRY_OPT_OUT_SHOWN, true, StorageScope.GLOBAL); - this.privacyUrl = product.privacyStatementUrl || product.telemetryOptOutUrl; + this.privacyUrl = productService.privacyStatementUrl || productService.telemetryOptOutUrl; if (experimentState && experimentState.state === ExperimentState.Run && telemetryService.isOptedIn) { this.runExperiment(experimentId); return; } - const telemetryOptOutUrl = product.telemetryOptOutUrl; + const telemetryOptOutUrl = productService.telemetryOptOutUrl; if (telemetryOptOutUrl) { - const optOutNotice = localize('telemetryOptOut.optOutNotice', "Help improve Azure Data Studio by allowing Microsoft to collect usage data. Read our [privacy statement]({0}) and learn how to [opt out]({1}).", this.privacyUrl, product.telemetryOptOutUrl); // {{SQL CARBON EDIT}} VScode to ads - const optInNotice = localize('telemetryOptOut.optInNotice', "Help improve Azure Data Studio by allowing Microsoft to collect usage data. Read our [privacy statement]({0}) and learn how to [opt in]({1}).", this.privacyUrl, product.telemetryOptOutUrl); // {{SQL CARBON EDIT}} VScode to ads + const optOutNotice = localize('telemetryOptOut.optOutNotice', "Help improve Azure Data Studio by allowing Microsoft to collect usage data. Read our [privacy statement]({0}) and learn how to [opt out]({1}).", this.privacyUrl, productService.telemetryOptOutUrl); // {{SQL CARBON EDIT}} VScode to ads + const optInNotice = localize('telemetryOptOut.optInNotice', "Help improve Azure Data Studio by allowing Microsoft to collect usage data. Read our [privacy statement]({0}) and learn how to [opt in]({1}).", this.privacyUrl, productService.telemetryOptOutUrl); // {{SQL CARBON EDIT}} VScode to ads notificationService.prompt( Severity.Info, diff --git a/src/vs/workbench/contrib/welcome/page/browser/vs_code_welcome_page.ts b/src/vs/workbench/contrib/welcome/page/browser/vs_code_welcome_page.ts index 0d2074a62498..4e70f372bcd2 100644 --- a/src/vs/workbench/contrib/welcome/page/browser/vs_code_welcome_page.ts +++ b/src/vs/workbench/contrib/welcome/page/browser/vs_code_welcome_page.ts @@ -6,9 +6,6 @@ import { escape } from 'vs/base/common/strings'; import { localize } from 'vs/nls'; -export function used() { -} - export default () => `
diff --git a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts index f10967ff25e2..14df4ed1cc35 100644 --- a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts +++ b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./welcomePage'; +import 'vs/workbench/contrib/welcome/page/browser/vs_code_welcome_page'; import { URI } from 'vs/base/common/uri'; import * as strings from 'vs/base/common/strings'; import { ICommandService } from 'vs/platform/commands/common/commands'; @@ -24,7 +25,6 @@ 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 { IExtensionEnablementService, EnablementState, IExtensionTipsService } from 'vs/workbench/services/extensionManagement/common/extensionManagement'; -import { used, setProductQuality } from 'sql/workbench/contrib/welcome/page/browser/az_data_welcome_page'; // {{SQL CARBON EDIT}} - Redirect to ADS welcome page import { ILifecycleService, StartupKind } from 'vs/platform/lifecycle/common/lifecycle'; import { Disposable } from 'vs/base/common/lifecycle'; import { splitName } from 'vs/base/common/labels'; @@ -43,8 +43,7 @@ import { joinPath } from 'vs/base/common/resources'; import { IRecentlyOpened, isRecentWorkspace, IRecentWorkspace, IRecentFolder, isRecentFolder } from 'vs/platform/history/common/history'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; // {{SQL CARBON EDIT}} - -used(); +import { setProductQuality } from 'sql/workbench/contrib/welcome/page/browser/az_data_welcome_page'; // {{SQL CARBON EDIT}} const configurationKey = 'workbench.startupEditor'; const oldConfigurationKey = 'workbench.welcome.enabled'; diff --git a/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/editorWalkThrough.ts b/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/editorWalkThrough.ts index 6aaaf9dfe9da..191176302e9e 100644 --- a/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/editorWalkThrough.ts +++ b/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/editorWalkThrough.ts @@ -3,6 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import 'vs/workbench/contrib/welcome/walkThrough/browser/editor/vs_code_editor_walkthrough'; import { localize } from 'vs/nls'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { Action } from 'vs/base/common/actions'; @@ -17,7 +18,10 @@ const inputOptions: WalkThroughInputOptions = { typeId, name: localize('editorWalkThrough.title', "Interactive Playground"), resource: URI.parse(require.toUrl('./vs_code_editor_walkthrough.md')) - .with({ scheme: Schemas.walkThrough }), + .with({ + scheme: Schemas.walkThrough, + query: JSON.stringify({ moduleId: 'vs/workbench/contrib/welcome/walkThrough/browser/editor/vs_code_editor_walkthrough' }) + }), telemetryFrom: 'walkThrough' }; diff --git a/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/vs_code_editor_walkthrough.md b/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/vs_code_editor_walkthrough.ts similarity index 83% rename from src/vs/workbench/contrib/welcome/walkThrough/browser/editor/vs_code_editor_walkthrough.md rename to src/vs/workbench/contrib/welcome/walkThrough/browser/editor/vs_code_editor_walkthrough.ts index 1f306f79ef21..b791a8bf9510 100644 --- a/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/vs_code_editor_walkthrough.md +++ b/src/vs/workbench/contrib/welcome/walkThrough/browser/editor/vs_code_editor_walkthrough.ts @@ -1,3 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export default () => ` ## Interactive Editor Playground The core editor in VS Code is packed with features. This page highlights a number of them and lets you interactively try them out through the use of a number of embedded editors. For full details on the editor features for VS Code and more head over to our [documentation](command:workbench.action.openDocumentationUrl). @@ -16,34 +22,34 @@ The core editor in VS Code is packed with features. This page highlights a numb ### Multi-Cursor Editing Using multiple cursors allows you to edit multiple parts of the document at once, greatly improving your productivity. Try the following actions in the code block below: -1. Box Selection - press any combination of kb(cursorColumnSelectDown), kb(cursorColumnSelectRight), kb(cursorColumnSelectUp), kb(cursorColumnSelectLeft) to select a block of text. You can also press `⇧⌥``Shift+Alt` while selecting text with the mouse or drag-select using the middle mouse button. +1. Box Selection - press any combination of kb(cursorColumnSelectDown), kb(cursorColumnSelectRight), kb(cursorColumnSelectUp), kb(cursorColumnSelectLeft) to select a block of text. You can also press |⇧⌥||Shift+Alt| while selecting text with the mouse or drag-select using the middle mouse button. 2. Add a cursor - press kb(editor.action.insertCursorAbove) to add a new cursor above, or kb(editor.action.insertCursorBelow) to add a new cursor below. You can also use your mouse with +Click to add a cursor anywhere. -3. Create cursors on all occurrences of a string - select one instance of a string e.g. `background-color` and press kb(editor.action.selectHighlights). Now you can replace all instances by simply typing. +3. Create cursors on all occurrences of a string - select one instance of a string e.g. |background-color| and press kb(editor.action.selectHighlights). Now you can replace all instances by simply typing. That is the tip of the iceberg for multi-cursor editing. Have a look at the selection menu and our handy [keyboard reference guide](command:workbench.action.keybindingsReference) for additional actions. -```css +|||css #p1 {background-color: #ff0000;} /* red in HEX format */ #p2 {background-color: hsl(120, 100%, 50%);} /* green in HSL format */ #p3 {background-color: rgba(0, 4, 255, 0.733);} /* blue with alpha channel in RGBA format */ -``` +||| -> **CSS Tip:** you may have noticed in the example above we also provide color swatches inline for CSS, additionally if you hover over an element such as `#p1` we will show how this is represented in HTML. These swatches also act as color pickers that allow you to easily change a color value. A simple example of some language-specific editor features. +> **CSS Tip:** you may have noticed in the example above we also provide color swatches inline for CSS, additionally if you hover over an element such as |#p1| we will show how this is represented in HTML. These swatches also act as color pickers that allow you to easily change a color value. A simple example of some language-specific editor features. ### IntelliSense Visual Studio Code comes with the powerful IntelliSense for JavaScript and TypeScript pre-installed. In the below example, position the text cursor in front of the error underline, right after the dot and press kb(editor.action.triggerSuggest) to invoke IntelliSense. Notice how the suggestion comes from the Request API. -```js +|||js const express = require('express'); const app = express(); app.get('/', (req, res) => { - res.send(`Hello ${req.}`); + res.send(|Hello \${req.}|); }); app.listen(3000); -``` +||| >**Tip:** while we ship JavaScript and TypeScript support out of the box other languages can be upgraded with better IntelliSense through one of the many [extensions](command:workbench.extensions.action.showPopularExtensions). @@ -54,22 +60,22 @@ Since it's very common to work with the entire text in a line we provide a set o 2. Move an entire line or selection of lines up or down with kb(editor.action.moveLinesUpAction) and kb(editor.action.moveLinesDownAction) respectively. 3. Delete the entire line with kb(editor.action.deleteLines). -```json +|||json { - "name": "John", - "age": 31, - "city": "New York" + "name": "John", + "age": 31, + "city": "New York" } -``` +||| >**Tip:** Another very common task is to comment out a block of code - you can toggle commenting by pressing kb(editor.action.commentLine). ### Rename Refactoring -It's easy to rename a symbol such as a function name or variable name. Hit kb(editor.action.rename) while in the symbol `Book` to rename all instances - this will occur across all files in a project. You can also see refactoring in the right-click context menu. +It's easy to rename a symbol such as a function name or variable name. Hit kb(editor.action.rename) while in the symbol |Book| to rename all instances - this will occur across all files in a project. You can also see refactoring in the right-click context menu. -```js +|||js // Reference the function new Book("War of the Worlds", "H G Wells"); new Book("The Martian", "Andy Weir"); @@ -84,91 +90,91 @@ function Book(title, author) { this.title = title; this.author = author; } -``` +||| -> **JSDoc Tip:** VS Code's IntelliSense uses JSDoc comments to provide richer suggestions. The types and documentation from JSDoc comments show up when you hover over a reference to `Book` or in IntelliSense when you create a new instance of `Book`. +> **JSDoc Tip:** VS Code's IntelliSense uses JSDoc comments to provide richer suggestions. The types and documentation from JSDoc comments show up when you hover over a reference to |Book| or in IntelliSense when you create a new instance of |Book|. ### Formatting Keeping your code looking great is hard without a good formatter. Luckily it's easy to format content, either for the entire document with kb(editor.action.formatDocument) or for the current selection with kb(editor.action.formatSelection). Both of these options are also available through the right-click context menu. -```js +|||js const cars = ["🚗", "🚙", "🚕"]; for (const car of cars){ - // Drive the car - console.log(`This is the car ${car}`); + // Drive the car + console.log(|This is the car \${car}|); } -``` +||| ->**Tip:** Additional formatters are available in the [extension gallery](command:workbench.extensions.action.showPopularExtensions). Formatting support can also be configured via [settings](command:workbench.action.openGlobalSettings) e.g. enabling `editor.formatOnSave`. +>**Tip:** Additional formatters are available in the [extension gallery](command:workbench.extensions.action.showPopularExtensions). Formatting support can also be configured via [settings](command:workbench.action.openGlobalSettings) e.g. enabling |editor.formatOnSave|. ### Code Folding In a large file it can often be useful to collapse sections of code to increase readability. To do this, you can simply press kb(editor.fold) to fold or press kb(editor.unfold) to unfold the ranges at the current cursor position. Folding can also be done with the +/- icons in the left gutter. To fold all sections use kb(editor.foldAll) or to unfold all use kb(editor.unfoldAll). -```html +|||html
-
-
    -
  • -
  • -
-
-
-

-
+
+
    +
  • +
  • +
+
+
+

+
-``` +||| >**Tip:** Folding is based on indentation and as a result can apply to all languages. Simply indent your code to create a foldable section you can fold a certain number of levels with shortcuts like kb(editor.foldLevel1) through to kb(editor.foldLevel5). ### Errors and Warnings Errors and warnings are highlighted as you edit your code with squiggles. In the sample below you can see a number of syntax errors. By pressing kb(editor.action.marker.nextInFiles) you can navigate across them in sequence and see the detailed error message. As you correct them the squiggles and scrollbar indicators will update. -```js +|||js // This code has a few syntax errors Console.log(add(1, 1.5)); function Add(a : Number, b : Number) : Int { - return a + b; + return a + b; } -``` +||| ### Snippets -You can greatly accelerate your editing through the use of snippets. Simply start typing `try` and select `trycatch` from the suggestion list and press kb(insertSnippet) to create a `try`->`catch` block. Your cursor will be placed on the text `error` for easy editing. If more than one parameter exists then press kb(jumpToNextSnippetPlaceholder) to jump to it. +You can greatly accelerate your editing through the use of snippets. Simply start typing |try| and select |trycatch| from the suggestion list and press kb(insertSnippet) to create a |try|->|catch| block. Your cursor will be placed on the text |error| for easy editing. If more than one parameter exists then press kb(jumpToNextSnippetPlaceholder) to jump to it. -```js +|||js -``` +||| >**Tip:** the [extension gallery](command:workbench.extensions.action.showPopularExtensions) includes snippets for almost every framework and language imaginable. You can also create your own [user-defined snippets](command:workbench.action.openSnippets). ### Emmet -Emmet takes the snippets idea to a whole new level: you can type CSS-like expressions that can be dynamically parsed, and produce output depending on what you type in the abbreviation. Try it by selecting `Emmet: Expand Abbreviation` from the `Edit` menu with the cursor at the end of a valid Emmet abbreviation or snippet and the expansion will occur. +Emmet takes the snippets idea to a whole new level: you can type CSS-like expressions that can be dynamically parsed, and produce output depending on what you type in the abbreviation. Try it by selecting |Emmet: Expand Abbreviation| from the |Edit| menu with the cursor at the end of a valid Emmet abbreviation or snippet and the expansion will occur. -```html +|||html ul>li.item$*5 -``` +||| ->**Tip:** The [Emmet cheat sheet](http://docs.emmet.io/cheat-sheet/) is a great source of Emmet syntax suggestions. To expand Emmet abbreviations and snippets using the `tab` key use the `emmet.triggerExpansionOnTab` [setting](command:workbench.action.openGlobalSettings). Check out the docs on [Emmet in VS Code](https://code.visualstudio.com/docs/editor/emmet) to learn more. +>**Tip:** The [Emmet cheat sheet](http://docs.emmet.io/cheat-sheet/) is a great source of Emmet syntax suggestions. To expand Emmet abbreviations and snippets using the |tab| key use the |emmet.triggerExpansionOnTab| [setting](command:workbench.action.openGlobalSettings). Check out the docs on [Emmet in VS Code](https://code.visualstudio.com/docs/editor/emmet) to learn more. ### JavaScript Type Checking -Sometimes type checking your JavaScript code can help you spot mistakes you might have not caught otherwise. You can run the TypeScript type checker against your existing JavaScript code by simply adding a `// @ts-check` comment to the top of your file. +Sometimes type checking your JavaScript code can help you spot mistakes you might have not caught otherwise. You can run the TypeScript type checker against your existing JavaScript code by simply adding a |// @ts-check| comment to the top of your file. -```js +|||js // @ts-nocheck let easy = true; easy = 42; -``` +||| ->**Tip:** You can also enable the checks workspace or application wide by adding `"javascript.implicitProjectConfig.checkJs": true` to your workspace or user settings and explicitly ignoring files or lines using `// @ts-nocheck` and `// @ts-ignore`. Check out the docs on [JavaScript in VS Code](https://code.visualstudio.com/docs/languages/javascript) to learn more. +>**Tip:** You can also enable the checks workspace or application wide by adding |"javascript.implicitProjectConfig.checkJs": true| to your workspace or user settings and explicitly ignoring files or lines using |// @ts-nocheck| and |// @ts-ignore|. Check out the docs on [JavaScript in VS Code](https://code.visualstudio.com/docs/languages/javascript) to learn more. ## Thanks! @@ -180,3 +186,5 @@ Well if you have got this far then you will have touched on some of the editing That's all for now, Happy Coding! 🎉 + +`.replace(/\|/g, '`'); diff --git a/src/vs/workbench/contrib/welcome/walkThrough/common/walkThroughContentProvider.ts b/src/vs/workbench/contrib/welcome/walkThrough/common/walkThroughContentProvider.ts index e86280e45ee5..ee761b57bccb 100644 --- a/src/vs/workbench/contrib/welcome/walkThrough/common/walkThroughContentProvider.ts +++ b/src/vs/workbench/contrib/welcome/walkThrough/common/walkThroughContentProvider.ts @@ -6,46 +6,58 @@ import { URI } from 'vs/base/common/uri'; import { ITextModelService, ITextModelContentProvider } from 'vs/editor/common/services/resolverService'; import { IModelService } from 'vs/editor/common/services/modelService'; -import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; -import { ITextModel, DefaultEndOfLine, EndOfLinePreference, ITextBufferFactory } from 'vs/editor/common/model'; +import { ITextModel, DefaultEndOfLine, EndOfLinePreference } from 'vs/editor/common/model'; import { IModeService } from 'vs/editor/common/services/modeService'; import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import * as marked from 'vs/base/common/marked/marked'; import { Schemas } from 'vs/base/common/network'; import { Range } from 'vs/editor/common/core/range'; +import { createTextBufferFactory } from 'vs/editor/common/model/textModel'; + +function requireToContent(resource: URI): Promise { + if (!resource.query) { + throw new Error('Welcome: invalid resource'); + } + + const query = JSON.parse(resource.query); + if (!query.moduleId) { + throw new Error('Welcome: invalid resource'); + } + + const content: Promise = new Promise((resolve, reject) => { + require([query.moduleId], content => { + try { + resolve(content.default()); + } catch (err) { + reject(err); + } + }); + }); + + return content; +} export class WalkThroughContentProvider implements ITextModelContentProvider, IWorkbenchContribution { constructor( @ITextModelService private readonly textModelResolverService: ITextModelService, - @ITextFileService private readonly textFileService: ITextFileService, @IModeService private readonly modeService: IModeService, @IModelService private readonly modelService: IModelService, ) { this.textModelResolverService.registerTextModelContentProvider(Schemas.walkThrough, this); } - public provideTextContent(resource: URI): Promise { - const query = resource.query ? JSON.parse(resource.query) : {}; - const content: Promise = (query.moduleId ? new Promise((resolve, reject) => { - require([query.moduleId], content => { - try { - resolve(content.default()); - } catch (err) { - reject(err); - } - }); - }) : this.textFileService.readStream(URI.file(resource.fsPath)).then(content => content.value)); - return content.then(content => { - let codeEditorModel = this.modelService.getModel(resource); - if (!codeEditorModel) { - codeEditorModel = this.modelService.createModel(content, this.modeService.createByFilepathOrFirstLine(resource), resource); - } else { - this.modelService.updateModel(codeEditorModel, content); - } + public async provideTextContent(resource: URI): Promise { + const content = await requireToContent(resource); - return codeEditorModel; - }); + let codeEditorModel = this.modelService.getModel(resource); + if (!codeEditorModel) { + codeEditorModel = this.modelService.createModel(content, this.modeService.createByFilepathOrFirstLine(resource), resource); + } else { + this.modelService.updateModel(codeEditorModel, content); + } + + return codeEditorModel; } } @@ -53,45 +65,44 @@ export class WalkThroughSnippetContentProvider implements ITextModelContentProvi constructor( @ITextModelService private readonly textModelResolverService: ITextModelService, - @ITextFileService private readonly textFileService: ITextFileService, @IModeService private readonly modeService: IModeService, @IModelService private readonly modelService: IModelService, ) { this.textModelResolverService.registerTextModelContentProvider(Schemas.walkThroughSnippet, this); } - public provideTextContent(resource: URI): Promise { - return this.textFileService.readStream(URI.file(resource.fsPath)).then(content => { - let codeEditorModel = this.modelService.getModel(resource); - if (!codeEditorModel) { - const j = parseInt(resource.fragment); - - let codeSnippet = ''; - let languageName = ''; - let i = 0; - const renderer = new marked.Renderer(); - renderer.code = (code, lang) => { - if (i++ === j) { - codeSnippet = code; - languageName = lang; - } - return ''; - }; - - const textBuffer = content.value.create(DefaultEndOfLine.LF); - const lineCount = textBuffer.getLineCount(); - const range = new Range(1, 1, lineCount, textBuffer.getLineLength(lineCount) + 1); - const markdown = textBuffer.getValueInRange(range, EndOfLinePreference.TextDefined); - marked(markdown, { renderer }); - - const languageId = this.modeService.getModeIdForLanguageName(languageName) || ''; - const languageSelection = this.modeService.create(languageId); - codeEditorModel = this.modelService.createModel(codeSnippet, languageSelection, resource); - } else { - this.modelService.updateModel(codeEditorModel, content.value); - } + public async provideTextContent(resource: URI): Promise { + const factory = createTextBufferFactory(await requireToContent(resource)); - return codeEditorModel; - }); + let codeEditorModel = this.modelService.getModel(resource); + if (!codeEditorModel) { + const j = parseInt(resource.fragment); + + let codeSnippet = ''; + let languageName = ''; + let i = 0; + const renderer = new marked.Renderer(); + renderer.code = (code, lang) => { + if (i++ === j) { + codeSnippet = code; + languageName = lang; + } + return ''; + }; + + const textBuffer = factory.create(DefaultEndOfLine.LF); + const lineCount = textBuffer.getLineCount(); + const range = new Range(1, 1, lineCount, textBuffer.getLineLength(lineCount) + 1); + const markdown = textBuffer.getValueInRange(range, EndOfLinePreference.TextDefined); + marked(markdown, { renderer }); + + const languageId = this.modeService.getModeIdForLanguageName(languageName) || ''; + const languageSelection = this.modeService.create(languageId); + codeEditorModel = this.modelService.createModel(codeSnippet, languageSelection, resource); + } else { + this.modelService.updateModel(codeEditorModel, factory); + } + + return codeEditorModel; } } diff --git a/src/vs/workbench/electron-browser/actions/helpActions.ts b/src/vs/workbench/electron-browser/actions/helpActions.ts deleted file mode 100644 index b3d74d1d048d..000000000000 --- a/src/vs/workbench/electron-browser/actions/helpActions.ts +++ /dev/null @@ -1,236 +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 { Action } from 'vs/base/common/actions'; -import * as nls from 'vs/nls'; -import product from 'vs/platform/product/node/product'; -import { isMacintosh, isLinux, language } from 'vs/base/common/platform'; -import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { URI } from 'vs/base/common/uri'; - -export class KeybindingsReferenceAction extends Action { - - static readonly ID = 'workbench.action.keybindingsReference'; - static readonly LABEL = nls.localize('keybindingsReference', "Keyboard Shortcuts Reference"); - - private static readonly URL = isLinux ? product.keyboardShortcutsUrlLinux : isMacintosh ? product.keyboardShortcutsUrlMac : product.keyboardShortcutsUrlWin; - static readonly AVAILABLE = !!KeybindingsReferenceAction.URL; - - constructor( - id: string, - label: string, - @IOpenerService private readonly openerService: IOpenerService - ) { - super(id, label); - } - - run(): Promise { - if (KeybindingsReferenceAction.URL) { - this.openerService.open(URI.parse(KeybindingsReferenceAction.URL)); - } - - return Promise.resolve(); - } -} - -export class OpenDocumentationUrlAction extends Action { - - static readonly ID = 'workbench.action.openDocumentationUrl'; - static readonly LABEL = nls.localize('openDocumentationUrl', "Documentation"); - - private static readonly URL = product.documentationUrl; - static readonly AVAILABLE = !!OpenDocumentationUrlAction.URL; - - constructor( - id: string, - label: string, - @IOpenerService private readonly openerService: IOpenerService - ) { - super(id, label); - } - - run(): Promise { - if (OpenDocumentationUrlAction.URL) { - this.openerService.open(URI.parse(OpenDocumentationUrlAction.URL)); - } - - return Promise.resolve(); - } -} - -export class OpenIntroductoryVideosUrlAction extends Action { - - static readonly ID = 'workbench.action.openIntroductoryVideosUrl'; - static readonly LABEL = nls.localize('openIntroductoryVideosUrl', "Introductory Videos"); - - private static readonly URL = product.introductoryVideosUrl; - static readonly AVAILABLE = !!OpenIntroductoryVideosUrlAction.URL; - - constructor( - id: string, - label: string, - @IOpenerService private readonly openerService: IOpenerService - ) { - super(id, label); - } - - run(): Promise { - if (OpenIntroductoryVideosUrlAction.URL) { - this.openerService.open(URI.parse(OpenIntroductoryVideosUrlAction.URL)); - } - - return Promise.resolve(); - } -} - -export class OpenTipsAndTricksUrlAction extends Action { - - static readonly ID = 'workbench.action.openTipsAndTricksUrl'; - static readonly LABEL = nls.localize('openTipsAndTricksUrl', "Tips and Tricks"); - - private static readonly URL = product.tipsAndTricksUrl; - static readonly AVAILABLE = !!OpenTipsAndTricksUrlAction.URL; - - constructor( - id: string, - label: string, - @IOpenerService private readonly openerService: IOpenerService - ) { - super(id, label); - } - - run(): Promise { - if (OpenTipsAndTricksUrlAction.URL) { - this.openerService.open(URI.parse(OpenTipsAndTricksUrlAction.URL)); - } - - return Promise.resolve(); - } -} - -export class OpenNewsletterSignupUrlAction extends Action { - - static readonly ID = 'workbench.action.openNewsletterSignupUrl'; - static readonly LABEL = nls.localize('newsletterSignup', "Signup for the VS Code Newsletter"); - static readonly AVAILABLE = !!product.newsletterSignupUrl; - - constructor( - id: string, - label: string, - @IOpenerService private readonly openerService: IOpenerService, - @ITelemetryService private readonly telemetryService: ITelemetryService - ) { - super(id, label); - } - - async run(): Promise { - const info = await this.telemetryService.getTelemetryInfo(); - - this.openerService.open(URI.parse(`${product.newsletterSignupUrl}?machineId=${encodeURIComponent(info.machineId)}`)); - } -} - -export class OpenTwitterUrlAction extends Action { - - static readonly ID = 'workbench.action.openTwitterUrl'; - static readonly LABEL = nls.localize('openTwitterUrl', "Join Us on Twitter", product.applicationName); - static readonly AVAILABLE = !!product.twitterUrl; - - constructor( - id: string, - label: string, - @IOpenerService private readonly openerService: IOpenerService - ) { - super(id, label); - } - - run(): Promise { - if (product.twitterUrl) { - this.openerService.open(URI.parse(product.twitterUrl)); - } - - return Promise.resolve(); - } -} - -export class OpenRequestFeatureUrlAction extends Action { - - static readonly ID = 'workbench.action.openRequestFeatureUrl'; - static readonly LABEL = nls.localize('openUserVoiceUrl', "Search Feature Requests"); - static readonly AVAILABLE = !!product.requestFeatureUrl; - - constructor( - id: string, - label: string, - @IOpenerService private readonly openerService: IOpenerService - ) { - super(id, label); - } - - run(): Promise { - if (product.requestFeatureUrl) { - this.openerService.open(URI.parse(product.requestFeatureUrl)); - } - - return Promise.resolve(); - } -} - -export class OpenLicenseUrlAction extends Action { - - static readonly ID = 'workbench.action.openLicenseUrl'; - static readonly LABEL = nls.localize('openLicenseUrl', "View License"); - static readonly AVAILABLE = !!product.licenseUrl; - - constructor( - id: string, - label: string, - @IOpenerService private readonly openerService: IOpenerService - ) { - super(id, label); - } - - run(): Promise { - if (product.licenseUrl) { - if (language) { - const queryArgChar = product.licenseUrl.indexOf('?') > 0 ? '&' : '?'; - this.openerService.open(URI.parse(`${product.licenseUrl}${queryArgChar}lang=${language}`)); - } else { - this.openerService.open(URI.parse(product.licenseUrl)); - } - } - - return Promise.resolve(); - } -} - -export class OpenPrivacyStatementUrlAction extends Action { - - static readonly ID = 'workbench.action.openPrivacyStatementUrl'; - static readonly LABEL = nls.localize('openPrivacyStatement', "Privacy Statement"); - static readonly AVAILABE = !!product.privacyStatementUrl; - - constructor( - id: string, - label: string, - @IOpenerService private readonly openerService: IOpenerService - ) { - super(id, label); - } - - run(): Promise { - if (product.privacyStatementUrl) { - if (language) { - const queryArgChar = product.privacyStatementUrl.indexOf('?') > 0 ? '&' : '?'; - this.openerService.open(URI.parse(`${product.privacyStatementUrl}${queryArgChar}lang=${language}`)); - } else { - this.openerService.open(URI.parse(product.privacyStatementUrl)); - } - } - - return Promise.resolve(); - } -} diff --git a/src/vs/workbench/electron-browser/actions/workspaceActions.ts b/src/vs/workbench/electron-browser/actions/workspaceActions.ts new file mode 100644 index 000000000000..097be36376da --- /dev/null +++ b/src/vs/workbench/electron-browser/actions/workspaceActions.ts @@ -0,0 +1,97 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Action } from 'vs/base/common/actions'; +import * as nls from 'vs/nls'; +import { IWindowService } from 'vs/platform/windows/common/windows'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; +import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; +import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { INotificationService } from 'vs/platform/notification/common/notification'; + +export class SaveWorkspaceAsAction extends Action { + + static readonly ID = 'workbench.action.saveWorkspaceAs'; + static LABEL = nls.localize('saveWorkspaceAsAction', "Save Workspace As..."); + + constructor( + id: string, + label: string, + @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, + @IWorkspaceEditingService private readonly workspaceEditingService: IWorkspaceEditingService + + ) { + super(id, label); + } + + async run(): Promise { + const configPathUri = await this.workspaceEditingService.pickNewWorkspacePath(); + if (configPathUri) { + switch (this.contextService.getWorkbenchState()) { + case WorkbenchState.EMPTY: + case WorkbenchState.FOLDER: + const folders = this.contextService.getWorkspace().folders.map(folder => ({ uri: folder.uri })); + return this.workspaceEditingService.createAndEnterWorkspace(folders, configPathUri); + case WorkbenchState.WORKSPACE: + return this.workspaceEditingService.saveAndEnterWorkspace(configPathUri); + } + } + } +} + +export class DuplicateWorkspaceInNewWindowAction extends Action { + + static readonly ID = 'workbench.action.duplicateWorkspaceInNewWindow'; + static readonly LABEL = nls.localize('duplicateWorkspaceInNewWindow', "Duplicate Workspace in New Window"); + + constructor( + id: string, + label: string, + @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, + @IWorkspaceEditingService private readonly workspaceEditingService: IWorkspaceEditingService, + @IWindowService private readonly windowService: IWindowService, + @IWorkspacesService private readonly workspacesService: IWorkspacesService, + @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService + ) { + super(id, label); + } + + async run(): Promise { + const folders = this.workspaceContextService.getWorkspace().folders; + const remoteAuthority = this.environmentService.configuration.remoteAuthority; + + const newWorkspace = await this.workspacesService.createUntitledWorkspace(folders, remoteAuthority); + await this.workspaceEditingService.copyWorkspaceSettings(newWorkspace); + + return this.windowService.openWindow([{ workspaceUri: newWorkspace.configPath }], { forceNewWindow: true }); + } +} + +export class CloseWorkspaceAction extends Action { + + static readonly ID = 'workbench.action.closeFolder'; + static LABEL = nls.localize('closeWorkspace', "Close Workspace"); + + constructor( + id: string, + label: string, + @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, + @INotificationService private readonly notificationService: INotificationService, + @IWindowService private readonly windowService: IWindowService + ) { + super(id, label); + } + + run(): Promise { + if (this.contextService.getWorkbenchState() === WorkbenchState.EMPTY) { + this.notificationService.info(nls.localize('noWorkspaceOpened', "There is currently no workspace opened in this instance to close.")); + + return Promise.resolve(undefined); + } + + return this.windowService.closeWorkspace(); + } +} diff --git a/src/vs/workbench/electron-browser/desktop.contribution.ts b/src/vs/workbench/electron-browser/desktop.contribution.ts index 605cfa64a7e5..aa3da69af5b8 100644 --- a/src/vs/workbench/electron-browser/desktop.contribution.ts +++ b/src/vs/workbench/electron-browser/desktop.contribution.ts @@ -11,15 +11,13 @@ import { IConfigurationRegistry, Extensions as ConfigurationExtensions, Configur import { IWorkbenchActionRegistry, Extensions } from 'vs/workbench/common/actions'; import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes'; import { isWindows, isLinux, isMacintosh } from 'vs/base/common/platform'; -import { KeybindingsReferenceAction, OpenDocumentationUrlAction, OpenIntroductoryVideosUrlAction, OpenTipsAndTricksUrlAction, OpenTwitterUrlAction, OpenRequestFeatureUrlAction, OpenPrivacyStatementUrlAction, OpenLicenseUrlAction, OpenNewsletterSignupUrlAction } from 'vs/workbench/electron-browser/actions/helpActions'; import { ToggleSharedProcessAction, ToggleDevToolsAction } from 'vs/workbench/electron-browser/actions/developerActions'; import { ZoomResetAction, ZoomOutAction, ZoomInAction, CloseCurrentWindowAction, SwitchWindow, NewWindowAction, QuickSwitchWindow, ReloadWindowWithExtensionsDisabledAction, NewWindowTabHandler, ShowPreviousWindowTabHandler, ShowNextWindowTabHandler, MoveWindowTabToNewWindowHandler, MergeWindowTabsHandlerHandler, ToggleWindowTabsBarHandler } from 'vs/workbench/electron-browser/actions/windowActions'; -import { AddRootFolderAction, GlobalRemoveRootFolderAction, SaveWorkspaceAsAction, DuplicateWorkspaceInNewWindowAction, CloseWorkspaceAction } from 'vs/workbench/browser/actions/workspaceActions'; +import { SaveWorkspaceAsAction, DuplicateWorkspaceInNewWindowAction, CloseWorkspaceAction } from 'vs/workbench/electron-browser/actions/workspaceActions'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { KeybindingsRegistry, KeybindingWeight } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { CommandsRegistry } from 'vs/platform/commands/common/commands'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; -import { ADD_ROOT_FOLDER_COMMAND_ID } from 'vs/workbench/browser/actions/workspaceCommands'; import { SupportsWorkspacesContext, IsMacContext, HasMacNativeTabsContext, IsDevelopmentContext, WorkbenchStateContext, WorkspaceFolderCountContext } from 'vs/workbench/browser/contextkeys'; import { NoEditorsVisibleContext, SingleEditorGroupsContext } from 'vs/workbench/common/editor'; import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows'; @@ -34,7 +32,7 @@ import { InstallVSIXAction } from 'vs/workbench/contrib/extensions/browser/exten (function registerFileActions(): void { const fileCategory = nls.localize('file', "File"); - registry.registerWorkbenchAction(new SyncActionDescriptor(CloseWorkspaceAction, CloseWorkspaceAction.ID, CloseWorkspaceAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_F) }), 'File: Close Workspace', fileCategory); + registry.registerWorkbenchAction(new SyncActionDescriptor(CloseWorkspaceAction, CloseWorkspaceAction.ID, CloseWorkspaceAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_F) }), 'File: Close Workspace', fileCategory, SupportsWorkspacesContext); })(); // Actions: View @@ -81,10 +79,8 @@ import { InstallVSIXAction } from 'vs/workbench/contrib/extensions/browser/exten (function registerWorkspaceActions(): void { const workspacesCategory = nls.localize('workspaces', "Workspaces"); - registry.registerWorkbenchAction(new SyncActionDescriptor(AddRootFolderAction, AddRootFolderAction.ID, AddRootFolderAction.LABEL), 'Workspaces: Add Folder to Workspace...', workspacesCategory, SupportsWorkspacesContext); - registry.registerWorkbenchAction(new SyncActionDescriptor(GlobalRemoveRootFolderAction, GlobalRemoveRootFolderAction.ID, GlobalRemoveRootFolderAction.LABEL), 'Workspaces: Remove Folder from Workspace...', workspacesCategory); registry.registerWorkbenchAction(new SyncActionDescriptor(SaveWorkspaceAsAction, SaveWorkspaceAsAction.ID, SaveWorkspaceAsAction.LABEL), 'Workspaces: Save Workspace As...', workspacesCategory, SupportsWorkspacesContext); - registry.registerWorkbenchAction(new SyncActionDescriptor(DuplicateWorkspaceInNewWindowAction, DuplicateWorkspaceInNewWindowAction.ID, DuplicateWorkspaceInNewWindowAction.LABEL), 'Workspaces: Duplicate Workspace in New Window', workspacesCategory); + registry.registerWorkbenchAction(new SyncActionDescriptor(DuplicateWorkspaceInNewWindowAction, DuplicateWorkspaceInNewWindowAction.ID, DuplicateWorkspaceInNewWindowAction.LABEL), 'Workspaces: Duplicate Workspace in New Window', workspacesCategory, SupportsWorkspacesContext); })(); // Actions: macOS Native Tabs @@ -123,47 +119,6 @@ import { InstallVSIXAction } from 'vs/workbench/contrib/extensions/browser/exten mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_I } }); })(); - - // Actions: help - (function registerHelpActions(): void { - const helpCategory = nls.localize('help', "Help"); - - if (KeybindingsReferenceAction.AVAILABLE) { - registry.registerWorkbenchAction(new SyncActionDescriptor(KeybindingsReferenceAction, KeybindingsReferenceAction.ID, KeybindingsReferenceAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_R) }), 'Help: Keyboard Shortcuts Reference', helpCategory); - } - - if (OpenDocumentationUrlAction.AVAILABLE) { - registry.registerWorkbenchAction(new SyncActionDescriptor(OpenDocumentationUrlAction, OpenDocumentationUrlAction.ID, OpenDocumentationUrlAction.LABEL), 'Help: Documentation', helpCategory); - } - - if (OpenIntroductoryVideosUrlAction.AVAILABLE) { - registry.registerWorkbenchAction(new SyncActionDescriptor(OpenIntroductoryVideosUrlAction, OpenIntroductoryVideosUrlAction.ID, OpenIntroductoryVideosUrlAction.LABEL), 'Help: Introductory Videos', helpCategory); - } - - if (OpenTipsAndTricksUrlAction.AVAILABLE) { - registry.registerWorkbenchAction(new SyncActionDescriptor(OpenTipsAndTricksUrlAction, OpenTipsAndTricksUrlAction.ID, OpenTipsAndTricksUrlAction.LABEL), 'Help: Tips and Tricks', helpCategory); - } - - if (OpenNewsletterSignupUrlAction.AVAILABLE) { - registry.registerWorkbenchAction(new SyncActionDescriptor(OpenNewsletterSignupUrlAction, OpenNewsletterSignupUrlAction.ID, OpenNewsletterSignupUrlAction.LABEL), 'Help: Tips and Tricks', helpCategory); - } - - if (OpenTwitterUrlAction.AVAILABLE) { - registry.registerWorkbenchAction(new SyncActionDescriptor(OpenTwitterUrlAction, OpenTwitterUrlAction.ID, OpenTwitterUrlAction.LABEL), 'Help: Join Us on Twitter', helpCategory); - } - - if (OpenRequestFeatureUrlAction.AVAILABLE) { - registry.registerWorkbenchAction(new SyncActionDescriptor(OpenRequestFeatureUrlAction, OpenRequestFeatureUrlAction.ID, OpenRequestFeatureUrlAction.LABEL), 'Help: Search Feature Requests', helpCategory); - } - - if (OpenLicenseUrlAction.AVAILABLE) { - registry.registerWorkbenchAction(new SyncActionDescriptor(OpenLicenseUrlAction, OpenLicenseUrlAction.ID, OpenLicenseUrlAction.LABEL), 'Help: View License', helpCategory); - } - - if (OpenPrivacyStatementUrlAction.AVAILABE) { - registry.registerWorkbenchAction(new SyncActionDescriptor(OpenPrivacyStatementUrlAction, OpenPrivacyStatementUrlAction.ID, OpenPrivacyStatementUrlAction.LABEL), 'Help: Privacy Statement', helpCategory); - } - })(); })(); // Menu @@ -186,16 +141,6 @@ import { InstallVSIXAction } from 'vs/workbench/contrib/extensions/browser/exten } }); - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { - group: '3_workspace', - command: { - id: ADD_ROOT_FOLDER_COMMAND_ID, - title: nls.localize({ key: 'miAddFolderToWorkspace', comment: ['&& denotes a mnemonic'] }, "A&&dd Folder to Workspace...") - }, - order: 1, - when: SupportsWorkspacesContext - }); - MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { group: '3_workspace', command: { @@ -224,7 +169,7 @@ import { InstallVSIXAction } from 'vs/workbench/contrib/extensions/browser/exten title: nls.localize({ key: 'miCloseWorkspace', comment: ['&& denotes a mnemonic'] }, "Close &&Workspace") }, order: 3, - when: WorkbenchStateContext.isEqualTo('workspace') + when: ContextKeyExpr.and(WorkbenchStateContext.isEqualTo('workspace'), SupportsWorkspacesContext) }); MenuRegistry.appendMenuItem(MenuId.MenubarFileMenu, { @@ -275,87 +220,6 @@ import { InstallVSIXAction } from 'vs/workbench/contrib/extensions/browser/exten order: 3 }); - // Help - - if (OpenDocumentationUrlAction.AVAILABLE) { - MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { - group: '1_welcome', - command: { - id: OpenDocumentationUrlAction.ID, - title: nls.localize({ key: 'miDocumentation', comment: ['&& denotes a mnemonic'] }, "&&Documentation") - }, - order: 3 - }); - } - - /* // {{SQL CARBON EDIT}} - Disable unused menu item - MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { - group: '1_welcome', - command: { - id: 'update.showCurrentReleaseNotes', - title: nls.localize({ key: 'miReleaseNotes', comment: ['&& denotes a mnemonic'] }, "&&Release Notes") - }, - order: 4 - }); - - // Reference - if (KeybindingsReferenceAction.AVAILABLE) { - MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { - group: '2_reference', - command: { - id: KeybindingsReferenceAction.ID, - title: nls.localize({ key: 'miKeyboardShortcuts', comment: ['&& denotes a mnemonic'] }, "&&Keyboard Shortcuts Reference") - }, - order: 1 - }); - } - - if (OpenIntroductoryVideosUrlAction.AVAILABLE) { - MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { - group: '2_reference', - command: { - id: OpenIntroductoryVideosUrlAction.ID, - title: nls.localize({ key: 'miIntroductoryVideos', comment: ['&& denotes a mnemonic'] }, "Introductory &&Videos") - }, - order: 2 - }); - } - - if (OpenTipsAndTricksUrlAction.AVAILABLE) { - MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { - group: '2_reference', - command: { - id: OpenTipsAndTricksUrlAction.ID, - title: nls.localize({ key: 'miTipsAndTricks', comment: ['&& denotes a mnemonic'] }, "Tips and Tri&&cks") - }, - order: 3 - }); - } - - // Feedback - if (OpenTwitterUrlAction.AVAILABLE) { - MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { - group: '3_feedback', - command: { - id: OpenTwitterUrlAction.ID, - title: nls.localize({ key: 'miTwitter', comment: ['&& denotes a mnemonic'] }, "&&Join Us on Twitter") - }, - order: 1 - }); - } - - if (OpenRequestFeatureUrlAction.AVAILABLE) { - MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { - group: '3_feedback', - command: { - id: OpenRequestFeatureUrlAction.ID, - title: nls.localize({ key: 'miUserVoice', comment: ['&& denotes a mnemonic'] }, "&&Search Feature Requests") - }, - order: 2 - }); - } - */ - MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { group: '3_feedback', command: { @@ -365,29 +229,6 @@ import { InstallVSIXAction } from 'vs/workbench/contrib/extensions/browser/exten order: 3 }); - // Legal - if (OpenLicenseUrlAction.AVAILABLE) { - MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { - group: '4_legal', - command: { - id: OpenLicenseUrlAction.ID, - title: nls.localize({ key: 'miLicense', comment: ['&& denotes a mnemonic'] }, "View &&License") - }, - order: 1 - }); - } - - if (OpenPrivacyStatementUrlAction.AVAILABE) { - MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { - group: '4_legal', - command: { - id: OpenPrivacyStatementUrlAction.ID, - title: nls.localize({ key: 'miPrivacyStatement', comment: ['&& denotes a mnemonic'] }, "Privac&&y Statement") - }, - order: 2 - }); - } - // Tools MenuRegistry.appendMenuItem(MenuId.MenubarHelpMenu, { group: '5_tools', diff --git a/src/vs/workbench/electron-browser/desktop.main.ts b/src/vs/workbench/electron-browser/desktop.main.ts index 10fab9a16a39..dd0daa47d910 100644 --- a/src/vs/workbench/electron-browser/desktop.main.ts +++ b/src/vs/workbench/electron-browser/desktop.main.ts @@ -23,9 +23,9 @@ import { KeyboardMapperFactory } from 'vs/workbench/services/keybinding/electron import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; import { webFrame } from 'electron'; import { ISingleFolderWorkspaceIdentifier, IWorkspaceInitializationPayload, ISingleFolderWorkspaceInitializationPayload, reviveWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; -import { ConsoleLogService, MultiplexLogService, ILogService } from 'vs/platform/log/common/log'; +import { ConsoleLogService, MultiplexLogService, ILogService, ConsoleLogInMainService } from 'vs/platform/log/common/log'; import { StorageService } from 'vs/platform/storage/node/storageService'; -import { LogLevelSetterChannelClient, FollowerLogService } from 'vs/platform/log/common/logIpc'; +import { LoggerChannelClient, FollowerLogService } from 'vs/platform/log/common/logIpc'; import { Schemas } from 'vs/base/common/network'; import { sanitizeFilePath } from 'vs/base/common/extpath'; import { GlobalStorageDatabaseChannelClient } from 'vs/platform/storage/node/storageIpc'; @@ -51,8 +51,8 @@ import { SignService } from 'vs/platform/sign/node/signService'; import { ISignService } from 'vs/platform/sign/common/sign'; import { FileUserDataProvider } from 'vs/workbench/services/userData/common/fileUserDataProvider'; import { basename } from 'vs/base/common/resources'; -import { IProductService } from 'vs/platform/product/common/product'; -import product from 'vs/platform/product/node/product'; +import { IProductService } from 'vs/platform/product/common/productService'; +import product from 'vs/platform/product/common/product'; class CodeRendererMain extends Disposable { @@ -345,12 +345,25 @@ class CodeRendererMain extends Disposable { } private createLogService(mainProcessService: IMainProcessService, environmentService: IWorkbenchEnvironmentService): ILogService { - const spdlogService = new SpdLogService(`renderer${this.environmentService.configuration.windowId}`, environmentService.logsPath, this.environmentService.configuration.logLevel); - const consoleLogService = new ConsoleLogService(this.environmentService.configuration.logLevel); - const logService = new MultiplexLogService([consoleLogService, spdlogService]); - const logLevelClient = new LogLevelSetterChannelClient(mainProcessService.getChannel('loglevel')); + const loggerClient = new LoggerChannelClient(mainProcessService.getChannel('logger')); + + // Extension development test CLI: forward everything to main side + const loggers: ILogService[] = []; + if (environmentService.isExtensionDevelopment && !!environmentService.extensionTestsLocationURI) { + loggers.push( + new ConsoleLogInMainService(loggerClient, this.environmentService.configuration.logLevel) + ); + } + + // Normal logger: spdylog and console + else { + loggers.push( + new ConsoleLogService(this.environmentService.configuration.logLevel), + new SpdLogService(`renderer${this.environmentService.configuration.windowId}`, environmentService.logsPath, this.environmentService.configuration.logLevel) + ); + } - return new FollowerLogService(logLevelClient, logService); + return new FollowerLogService(loggerClient, new MultiplexLogService(loggers)); } } diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts index 563181826280..4b605b3b8b4e 100644 --- a/src/vs/workbench/electron-browser/window.ts +++ b/src/vs/workbench/electron-browser/window.ts @@ -32,9 +32,8 @@ import { IDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecyc import { LifecyclePhase, ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; import { IWorkspaceFolderCreationData } from 'vs/platform/workspaces/common/workspaces'; import { IIntegrityService } from 'vs/workbench/services/integrity/common/integrity'; -import { isRootUser, isWindows, isMacintosh, isLinux, isWeb } from 'vs/base/common/platform'; -import product from 'vs/platform/product/node/product'; -import pkg from 'vs/platform/product/node/package'; +import { isRootUser, isWindows, isMacintosh, isLinux } from 'vs/base/common/platform'; +import product from 'vs/platform/product/common/product'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { EditorServiceImpl } from 'vs/workbench/browser/parts/editor/editor'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; @@ -460,7 +459,7 @@ export class ElectronWindow extends Disposable { productName, submitURL: isWindows ? hockeyAppConfig[process.arch === 'ia32' ? 'win32-ia32' : 'win32-x64'] : isLinux ? hockeyAppConfig[`linux-x64`] : hockeyAppConfig.darwin, extra: { - vscode_version: pkg.version, + vscode_version: product.version, vscode_commit: product.commit } }; @@ -615,7 +614,7 @@ class NativeMenubarControl extends MenubarControl { environmentService, accessibilityService); - if (isMacintosh && !isWeb) { + if (isMacintosh) { this.menus['Preferences'] = this._register(this.menuService.createMenu(MenuId.MenubarPreferencesMenu, this.contextKeyService)); this.topLevelTitles['Preferences'] = nls.localize('mPreferences', "Preferences"); } diff --git a/src/vs/workbench/services/configuration/browser/configuration.ts b/src/vs/workbench/services/configuration/browser/configuration.ts index 35888ab8f27b..1c9ab98412b4 100644 --- a/src/vs/workbench/services/configuration/browser/configuration.ts +++ b/src/vs/workbench/services/configuration/browser/configuration.ts @@ -337,7 +337,7 @@ export class WorkspaceConfiguration extends Disposable { setFolders(folders: IStoredWorkspaceFolder[], jsonEditingService: JSONEditingService): Promise { if (this._workspaceIdentifier) { - return jsonEditingService.write(this._workspaceIdentifier.configPath, { key: 'folders', value: folders }, true) + return jsonEditingService.write(this._workspaceIdentifier.configPath, [{ key: 'folders', value: folders }], true) .then(() => this.reload()); } return Promise.resolve(); diff --git a/src/vs/workbench/services/configuration/common/jsonEditing.ts b/src/vs/workbench/services/configuration/common/jsonEditing.ts index 4f1240d55560..e6994e1a353c 100644 --- a/src/vs/workbench/services/configuration/common/jsonEditing.ts +++ b/src/vs/workbench/services/configuration/common/jsonEditing.ts @@ -36,5 +36,5 @@ export interface IJSONEditingService { _serviceBrand: undefined; - write(resource: URI, value: IJSONValue, save: boolean): Promise; + write(resource: URI, values: IJSONValue[], save: boolean): Promise; } diff --git a/src/vs/workbench/services/configuration/common/jsonEditingService.ts b/src/vs/workbench/services/configuration/common/jsonEditingService.ts index 77e425fe91e4..ad91a72bb72b 100644 --- a/src/vs/workbench/services/configuration/common/jsonEditingService.ts +++ b/src/vs/workbench/services/configuration/common/jsonEditingService.ts @@ -35,20 +35,24 @@ export class JSONEditingService implements IJSONEditingService { this.queue = new Queue(); } - write(resource: URI, value: IJSONValue, save: boolean): Promise { - return Promise.resolve(this.queue.queue(() => this.doWriteConfiguration(resource, value, save))); // queue up writes to prevent race conditions + write(resource: URI, values: IJSONValue[], save: boolean): Promise { + return Promise.resolve(this.queue.queue(() => this.doWriteConfiguration(resource, values, save))); // queue up writes to prevent race conditions } - private async doWriteConfiguration(resource: URI, value: IJSONValue, save: boolean): Promise { + private async doWriteConfiguration(resource: URI, values: IJSONValue[], save: boolean): Promise { const reference = await this.resolveAndValidate(resource, save); - await this.writeToBuffer(reference.object.textEditorModel, value); + await this.writeToBuffer(reference.object.textEditorModel, values); reference.dispose(); } - private async writeToBuffer(model: ITextModel, value: IJSONValue): Promise { - const edit = this.getEdits(model, value)[0]; - if (this.applyEditsToBuffer(edit, model)) { + private async writeToBuffer(model: ITextModel, values: IJSONValue[]): Promise { + let hasEdits: boolean = false; + for (const value of values) { + const edit = this.getEdits(model, value)[0]; + hasEdits = this.applyEditsToBuffer(edit, model); + } + if (hasEdits) { return this.textFileService.save(model.uri); } } @@ -133,4 +137,4 @@ export class JSONEditingService implements IJSONEditingService { } } -registerSingleton(IJSONEditingService, JSONEditingService, true); \ No newline at end of file +registerSingleton(IJSONEditingService, JSONEditingService, true); diff --git a/src/vs/workbench/services/configuration/node/configurationExportHelper.ts b/src/vs/workbench/services/configuration/node/configurationExportHelper.ts index 04d9c2eee889..eccadbcfedb5 100644 --- a/src/vs/workbench/services/configuration/node/configurationExportHelper.ts +++ b/src/vs/workbench/services/configuration/node/configurationExportHelper.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { writeFile } from 'vs/base/node/pfs'; -import product from 'vs/platform/product/node/product'; +import product from 'vs/platform/product/common/product'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { Registry } from 'vs/platform/registry/common/platform'; import { IConfigurationNode, IConfigurationRegistry, Extensions, IConfigurationPropertySchema } from 'vs/platform/configuration/common/configurationRegistry'; 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 a0574238d0a0..a0e4c658a0cc 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 @@ -1161,21 +1161,21 @@ suite.skip('WorkspaceConfigurationService-Multiroot', () => { // {{SQL CARBON ED test('application settings are not read from workspace', () => { fs.writeFileSync(globalSettingsFile, '{ "configurationService.workspace.applicationSetting": "userValue" }'); - return jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, { key: 'settings', value: { 'configurationService.workspace.applicationSetting': 'workspaceValue' } }, true) + return jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ key: 'settings', value: { 'configurationService.workspace.applicationSetting': 'workspaceValue' } }], true) .then(() => testObject.reloadConfiguration()) .then(() => assert.equal(testObject.getValue('configurationService.workspace.applicationSetting'), 'userValue')); }); test('machine settings are not read from workspace', () => { fs.writeFileSync(globalSettingsFile, '{ "configurationService.workspace.machineSetting": "userValue" }'); - return jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, { key: 'settings', value: { 'configurationService.workspace.machineSetting': 'workspaceValue' } }, true) + return jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ key: 'settings', value: { 'configurationService.workspace.machineSetting': 'workspaceValue' } }], true) .then(() => testObject.reloadConfiguration()) .then(() => assert.equal(testObject.getValue('configurationService.workspace.machineSetting'), 'userValue')); }); test('workspace settings override user settings after defaults are registered ', () => { fs.writeFileSync(globalSettingsFile, '{ "configurationService.workspace.newSetting": "userValue" }'); - return jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, { key: 'settings', value: { 'configurationService.workspace.newSetting': 'workspaceValue' } }, true) + return jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ key: 'settings', value: { 'configurationService.workspace.newSetting': 'workspaceValue' } }], true) .then(() => testObject.reloadConfiguration()) .then(() => { configurationRegistry.registerConfiguration({ @@ -1194,7 +1194,7 @@ suite.skip('WorkspaceConfigurationService-Multiroot', () => { // {{SQL CARBON ED test('workspace settings override user settings after defaults are registered for machine overridable settings ', () => { fs.writeFileSync(globalSettingsFile, '{ "configurationService.workspace.newMachineOverridableSetting": "userValue" }'); - return jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, { key: 'settings', value: { 'configurationService.workspace.newMachineOverridableSetting': 'workspaceValue' } }, true) + return jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ key: 'settings', value: { 'configurationService.workspace.newMachineOverridableSetting': 'workspaceValue' } }], true) .then(() => testObject.reloadConfiguration()) .then(() => { configurationRegistry.registerConfiguration({ @@ -1268,7 +1268,7 @@ suite.skip('WorkspaceConfigurationService-Multiroot', () => { // {{SQL CARBON ED test('resource setting in folder is read after it is registered later', () => { fs.writeFileSync(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json').fsPath, '{ "configurationService.workspace.testNewResourceSetting2": "workspaceFolderValue" }'); - return jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, { key: 'settings', value: { 'configurationService.workspace.testNewResourceSetting2': 'workspaceValue' } }, true) + return jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ key: 'settings', value: { 'configurationService.workspace.testNewResourceSetting2': 'workspaceValue' } }], true) .then(() => testObject.reloadConfiguration()) .then(() => { configurationRegistry.registerConfiguration({ @@ -1288,7 +1288,7 @@ suite.skip('WorkspaceConfigurationService-Multiroot', () => { // {{SQL CARBON ED test('machine overridable setting in folder is read after it is registered later', () => { fs.writeFileSync(workspaceContextService.getWorkspace().folders[0].toResource('.vscode/settings.json').fsPath, '{ "configurationService.workspace.testNewMachineOverridableSetting2": "workspaceFolderValue" }'); - return jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, { key: 'settings', value: { 'configurationService.workspace.testNewMachineOverridableSetting2': 'workspaceValue' } }, true) + return jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ key: 'settings', value: { 'configurationService.workspace.testNewMachineOverridableSetting2': 'workspaceValue' } }], true) .then(() => testObject.reloadConfiguration()) .then(() => { configurationRegistry.registerConfiguration({ @@ -1331,7 +1331,7 @@ suite.skip('WorkspaceConfigurationService-Multiroot', () => { // {{SQL CARBON ED assert.equal(actual.workspaceFolder, undefined); assert.equal(actual.value, 'userValue'); - return jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, { key: 'settings', value: { 'configurationService.workspace.testResourceSetting': 'workspaceValue' } }, true) + return jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ key: 'settings', value: { 'configurationService.workspace.testResourceSetting': 'workspaceValue' } }], true) .then(() => testObject.reloadConfiguration()) .then(() => { actual = testObject.inspect('configurationService.workspace.testResourceSetting'); @@ -1373,7 +1373,7 @@ suite.skip('WorkspaceConfigurationService-Multiroot', () => { // {{SQL CARBON ED } ] }; - return jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, { key: 'launch', value: expectedLaunchConfiguration }, true) + return jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ key: 'launch', value: expectedLaunchConfiguration }], true) .then(() => testObject.reloadConfiguration()) .then(() => { const actual = testObject.getValue('launch'); @@ -1398,7 +1398,7 @@ suite.skip('WorkspaceConfigurationService-Multiroot', () => { // {{SQL CARBON ED } ] }; - return jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, { key: 'launch', value: expectedLaunchConfiguration }, true) + return jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ key: 'launch', value: expectedLaunchConfiguration }], true) .then(() => testObject.reloadConfiguration()) .then(() => { const actual = testObject.inspect('launch').workspace; @@ -1496,7 +1496,7 @@ suite.skip('WorkspaceConfigurationService-Multiroot', () => { // {{SQL CARBON ED }); test('task configurations are not read from workspace', () => { - return jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, { key: 'tasks', value: { 'version': '1.0' } }, true) + return jsonEditingServce.write(workspaceContextService.getWorkspace().configuration!, [{ key: 'tasks', value: { 'version': '1.0' } }], true) .then(() => testObject.reloadConfiguration()) .then(() => { const actual = testObject.inspect('tasks.version'); diff --git a/src/vs/workbench/services/credentials/browser/credentialsService.ts b/src/vs/workbench/services/credentials/browser/credentialsService.ts index 30db14a5ea2f..795e61ab289b 100644 --- a/src/vs/workbench/services/credentials/browser/credentialsService.ts +++ b/src/vs/workbench/services/credentials/browser/credentialsService.ts @@ -10,7 +10,9 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ export interface ICredentialsProvider { getPassword(service: string, account: string): Promise; setPassword(service: string, account: string, password: string): Promise; + deletePassword(service: string, account: string): Promise; + findPassword(service: string): Promise; findCredentials(service: string): Promise>; } diff --git a/src/vs/platform/dialogs/browser/dialogService.ts b/src/vs/workbench/services/dialogs/browser/dialogService.ts similarity index 82% rename from src/vs/platform/dialogs/browser/dialogService.ts rename to src/vs/workbench/services/dialogs/browser/dialogService.ts index 975de2e60af5..5509c5d3e039 100644 --- a/src/vs/platform/dialogs/browser/dialogService.ts +++ b/src/vs/workbench/services/dialogs/browser/dialogService.ts @@ -15,8 +15,12 @@ import { DisposableStore } from 'vs/base/common/lifecycle'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { EventHelper } from 'vs/base/browser/dom'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IProductService } from 'vs/platform/product/common/productService'; +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; export class DialogService implements IDialogService { + _serviceBrand: undefined; private allowableCommands = ['copy', 'cut']; @@ -25,7 +29,9 @@ export class DialogService implements IDialogService { @ILogService private readonly logService: ILogService, @ILayoutService private readonly layoutService: ILayoutService, @IThemeService private readonly themeService: IThemeService, - @IKeybindingService private readonly keybindingService: IKeybindingService + @IKeybindingService private readonly keybindingService: IKeybindingService, + @IProductService private readonly productService: IProductService, + @IClipboardService private readonly clipboardService: IClipboardService ) { } async confirm(confirmation: IConfirmation): Promise { @@ -113,4 +119,22 @@ export class DialogService implements IDialogService { checkboxChecked: result.checkboxChecked }; } + + async about(): Promise { + const detail = nls.localize('aboutDetail', + "Version: {0}\nCommit: {1}\nDate: {2}\nBrowser: {3}", + this.productService.version || 'Unknown', + this.productService.commit || 'Unknown', + this.productService.date || 'Unknown', + navigator.userAgent + ); + + const { choice } = await this.show(Severity.Info, this.productService.nameLong, [nls.localize('copy', "Copy"), nls.localize('ok', "OK")], { detail }); + + if (choice === 0) { + this.clipboardService.writeText(detail); + } + } } + +registerSingleton(IDialogService, DialogService, true); diff --git a/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts b/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts index fb56e8531dac..798daf5773c7 100644 --- a/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts +++ b/src/vs/workbench/services/dialogs/browser/remoteFileDialog.ts @@ -23,7 +23,6 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ import { IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { equalsIgnoreCase, format, startsWithIgnoreCase } from 'vs/base/common/strings'; -import { OpenLocalFileCommand, OpenLocalFileFolderCommand, OpenLocalFolderCommand, SaveLocalFileCommand } from 'vs/workbench/browser/actions/workspaceActions'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IRemoteAgentEnvironment } from 'vs/platform/remote/common/remoteAgentEnvironment'; import { isValidBasename } from 'vs/base/common/extpath'; @@ -32,6 +31,60 @@ import { Emitter } from 'vs/base/common/event'; import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { createCancelablePromise, CancelablePromise } from 'vs/base/common/async'; import { CancellationToken } from 'vs/base/common/cancellation'; +import { ICommandHandler } from 'vs/platform/commands/common/commands'; +import { ITextFileService, ISaveOptions } from 'vs/workbench/services/textfile/common/textfiles'; +import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { toResource } from 'vs/workbench/common/editor'; + +export namespace OpenLocalFileCommand { + export const ID = 'workbench.action.files.openLocalFile'; + export const LABEL = nls.localize('openLocalFile', "Open Local File..."); + export function handler(): ICommandHandler { + return accessor => { + const dialogService = accessor.get(IFileDialogService); + return dialogService.pickFileAndOpen({ forceNewWindow: false, availableFileSystems: [Schemas.file] }); + }; + } +} + +export namespace SaveLocalFileCommand { + export const ID = 'workbench.action.files.saveLocalFile'; + export const LABEL = nls.localize('saveLocalFile', "Save Local File..."); + export function handler(): ICommandHandler { + return accessor => { + const textFileService = accessor.get(ITextFileService); + const editorService = accessor.get(IEditorService); + let resource: URI | undefined = toResource(editorService.activeEditor); + const options: ISaveOptions = { force: true, availableFileSystems: [Schemas.file] }; + if (resource) { + return textFileService.saveAs(resource, undefined, options); + } + return Promise.resolve(undefined); + }; + } +} + +export namespace OpenLocalFolderCommand { + export const ID = 'workbench.action.files.openLocalFolder'; + export const LABEL = nls.localize('openLocalFolder', "Open Local Folder..."); + export function handler(): ICommandHandler { + return accessor => { + const dialogService = accessor.get(IFileDialogService); + return dialogService.pickFolderAndOpen({ forceNewWindow: false, availableFileSystems: [Schemas.file] }); + }; + } +} + +export namespace OpenLocalFileFolderCommand { + export const ID = 'workbench.action.files.openLocalFileFolder'; + export const LABEL = nls.localize('openLocalFileFolder', "Open Local..."); + export function handler(): ICommandHandler { + return accessor => { + const dialogService = accessor.get(IFileDialogService); + return dialogService.pickFileFolderAndOpen({ forceNewWindow: false, availableFileSystems: [Schemas.file] }); + }; + } +} interface FileQuickPickItem extends IQuickPickItem { uri: URI; diff --git a/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts b/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts index 0f34cfaaa119..982ba620c0a8 100644 --- a/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts +++ b/src/vs/workbench/services/dialogs/electron-browser/dialogService.ts @@ -4,21 +4,25 @@ *--------------------------------------------------------------------------------------------*/ import * as nls from 'vs/nls'; -import product from 'vs/platform/product/node/product'; +import * as os from 'os'; +import product from 'vs/platform/product/common/product'; import Severity from 'vs/base/common/severity'; import { isLinux, isWindows } from 'vs/base/common/platform'; import { IWindowService } from 'vs/platform/windows/common/windows'; import { mnemonicButtonLabel } from 'vs/base/common/labels'; import { IDialogService, IConfirmation, IConfirmationResult, IDialogOptions, IShowResult } from 'vs/platform/dialogs/common/dialogs'; -import { DialogService as HTMLDialogService } from 'vs/platform/dialogs/browser/dialogService'; +import { DialogService as HTMLDialogService } from 'vs/workbench/services/dialogs/browser/dialogService'; import { ILogService } from 'vs/platform/log/common/log'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; -import { DialogChannel } from 'vs/platform/dialogs/node/dialogIpc'; +import { DialogChannel } from 'vs/platform/dialogs/electron-browser/dialogIpc'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ILayoutService } from 'vs/platform/layout/browser/layoutService'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; +import { IProductService } from 'vs/platform/product/common/productService'; +import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; +import { IElectronService } from 'vs/platform/electron/node/electron'; interface IMassagedMessageBoxOptions { @@ -36,6 +40,7 @@ interface IMassagedMessageBoxOptions { } export class DialogService implements IDialogService { + _serviceBrand: undefined; private impl: IDialogService; @@ -47,25 +52,33 @@ export class DialogService implements IDialogService { @IThemeService themeService: IThemeService, @IWindowService windowService: IWindowService, @ISharedProcessService sharedProcessService: ISharedProcessService, - @IKeybindingService keybindingService: IKeybindingService + @IKeybindingService keybindingService: IKeybindingService, + @IProductService productService: IProductService, + @IClipboardService clipboardService: IClipboardService, + @IElectronService electronService: IElectronService ) { // Use HTML based dialogs if (configurationService.getValue('workbench.dialogs.customEnabled') === true) { - this.impl = new HTMLDialogService(logService, layoutService, themeService, keybindingService); + this.impl = new HTMLDialogService(logService, layoutService, themeService, keybindingService, productService, clipboardService); } // Electron dialog service else { - this.impl = new NativeDialogService(windowService, logService, sharedProcessService); + this.impl = new NativeDialogService(windowService, logService, sharedProcessService, electronService, clipboardService); } } confirm(confirmation: IConfirmation): Promise { return this.impl.confirm(confirmation); } + show(severity: Severity, message: string, buttons: string[], options?: IDialogOptions | undefined): Promise { return this.impl.show(severity, message, buttons, options); } + + about(): Promise { + return this.impl.about(); + } } class NativeDialogService implements IDialogService { @@ -75,7 +88,9 @@ class NativeDialogService implements IDialogService { constructor( @IWindowService private readonly windowService: IWindowService, @ILogService private readonly logService: ILogService, - @ISharedProcessService sharedProcessService: ISharedProcessService + @ISharedProcessService sharedProcessService: ISharedProcessService, + @IElectronService private readonly electronService: IElectronService, + @IClipboardService private readonly clipboardService: IClipboardService ) { sharedProcessService.registerChannel('dialog', new DialogChannel(this)); } @@ -189,6 +204,51 @@ class NativeDialogService implements IDialogService { return { options, buttonIndexMap }; } + + async about(): Promise { + let version = product.version; + if (product.target) { + version = `${version} (${product.target} setup)`; + } + + const isSnap = process.platform === 'linux' && process.env.SNAP && process.env.SNAP_REVISION; + const detail = nls.localize('aboutDetail', // {{SQL CARBON EDIT}} update about dialog + "Version: {0}\nCommit: {1}\nDate: {2}\nVS Code: {8}\nElectron: {3}\nChrome: {4}\nNode.js: {5}\nV8: {6}\nOS: {7}", + version, + product.commit || 'Unknown', + product.date || 'Unknown', + process.versions['electron'], + process.versions['chrome'], + process.versions['node'], + process.versions['v8'], + `${os.type()} ${os.arch()} ${os.release()}${isSnap ? ' snap' : ''}`, + product.vscodeVersion + ); + + const ok = nls.localize('okButton', "OK"); + const copy = mnemonicButtonLabel(nls.localize({ key: 'copy', comment: ['&& denotes a mnemonic'] }, "&&Copy")); + let buttons: string[]; + if (isLinux) { + buttons = [copy, ok]; + } else { + buttons = [ok, copy]; + } + + const result = await this.electronService.showMessageBox({ + title: product.nameLong, + type: 'info', + message: product.nameLong, + detail: `\n${detail}`, + buttons, + noLink: true, + defaultId: buttons.indexOf(ok), + cancelId: buttons.indexOf(ok) + }); + + if (buttons[result.response] === copy) { + this.clipboardService.writeText(detail); + } + } } registerSingleton(IDialogService, DialogService, true); diff --git a/src/vs/workbench/services/environment/browser/environmentService.ts b/src/vs/workbench/services/environment/browser/environmentService.ts index f706be96316e..db17209808dc 100644 --- a/src/vs/workbench/services/environment/browser/environmentService.ts +++ b/src/vs/workbench/services/environment/browser/environmentService.ts @@ -3,18 +3,19 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IWindowConfiguration, IPath, IPathsToWaitFor } from 'vs/platform/windows/common/windows'; -import { IExtensionHostDebugParams, IDebugParams, BACKUPS } from 'vs/platform/environment/common/environment'; -import { URI } from 'vs/base/common/uri'; -import { IProcessEnvironment } from 'vs/base/common/platform'; -import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { Schemas } from 'vs/base/common/network'; import { ExportData } from 'vs/base/common/performance'; -import { LogLevel } from 'vs/platform/log/common/log'; +import { IProcessEnvironment } from 'vs/base/common/platform'; import { joinPath } from 'vs/base/common/resources'; -import { Schemas } from 'vs/base/common/network'; +import { URI } from 'vs/base/common/uri'; +import { generateUuid } from 'vs/base/common/uuid'; +import { BACKUPS, IDebugParams, IExtensionHostDebugParams } from 'vs/platform/environment/common/environment'; +import { LogLevel } from 'vs/platform/log/common/log'; +import { IPath, IPathsToWaitFor, IWindowConfiguration } from 'vs/platform/windows/common/windows'; +import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IWorkbenchConstructionOptions } from 'vs/workbench/workbench.web.api'; -import { generateUuid } from 'vs/base/common/uuid'; +import product from 'vs/platform/product/common/product'; export class BrowserWindowConfiguration implements IWindowConfiguration { @@ -178,12 +179,19 @@ export class BrowserWorkbenchEnvironmentService implements IWorkbenchEnvironment galleryMachineIdResource?: URI; readonly logFile: URI; + get webviewExternalEndpoint(): string { + // TODO: get fallback from product.json + return (this.options.webviewEndpoint || 'https://{{uuid}}.vscode-webview-test.com/{{commit}}') + .replace('{{commit}}', product.commit || '211fa02efe8c041fd7baa8ec3dce199d5185aa44'); + } + get webviewResourceRoot(): string { - return this.options.webviewEndpoint ? `${this.options.webviewEndpoint}/vscode-resource{{resource}}` : 'vscode-resource:{{resource}}'; + return `${this.webviewExternalEndpoint}/vscode-resource{{resource}}`; } get webviewCspSource(): string { - return this.options.webviewEndpoint ? this.options.webviewEndpoint : 'vscode-resource:'; + return this.webviewExternalEndpoint + .replace('{{uuid}}', '*'); } } diff --git a/src/vs/workbench/services/environment/common/environmentService.ts b/src/vs/workbench/services/environment/common/environmentService.ts index 4682637295aa..a1ab511ae934 100644 --- a/src/vs/workbench/services/environment/common/environmentService.ts +++ b/src/vs/workbench/services/environment/common/environmentService.ts @@ -24,6 +24,7 @@ export interface IWorkbenchEnvironmentService extends IEnvironmentService { readonly debugSearch: IDebugParams; + readonly webviewExternalEndpoint: string; readonly webviewResourceRoot: string; readonly webviewCspSource: string; diff --git a/src/vs/workbench/services/environment/node/environmentService.ts b/src/vs/workbench/services/environment/node/environmentService.ts index e59ba5d06b3e..0aa16f30544c 100644 --- a/src/vs/workbench/services/environment/node/environmentService.ts +++ b/src/vs/workbench/services/environment/node/environmentService.ts @@ -12,11 +12,17 @@ import { Schemas } from 'vs/base/common/network'; import { toBackupWorkspaceResource } from 'vs/workbench/services/backup/common/backup'; import { join } from 'vs/base/common/path'; import { IDebugParams } from 'vs/platform/environment/common/environment'; +import product from 'vs/platform/product/common/product'; export class WorkbenchEnvironmentService extends EnvironmentService implements IWorkbenchEnvironmentService { _serviceBrand: undefined; + get webviewExternalEndpoint(): string { + const baseEndpoint = 'https://{{uuid}}.vscode-webview-test.com/{{commit}}'; + return baseEndpoint.replace('{{commit}}', product.commit || '211fa02efe8c041fd7baa8ec3dce199d5185aa44'); + } + readonly webviewResourceRoot = 'vscode-resource:{{resource}}'; readonly webviewCspSource = 'vscode-resource:'; diff --git a/src/vs/workbench/services/extensionManagement/common/extensionEnablementService.ts b/src/vs/workbench/services/extensionManagement/common/extensionEnablementService.ts index 51afdf20863d..1ede8267a2ec 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionEnablementService.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionEnablementService.ts @@ -17,7 +17,7 @@ import { ExtensionType, IExtension } from 'vs/platform/extensions/common/extensi import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { isUIExtension } from 'vs/workbench/services/extensions/common/extensionsUtil'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { IProductService } from 'vs/platform/product/common/product'; +import { IProductService } from 'vs/platform/product/common/productService'; const DISABLED_EXTENSIONS_STORAGE_PATH = 'extensionsIdentifiers/disabled'; const ENABLED_EXTENSIONS_STORAGE_PATH = 'extensionsIdentifiers/enabled'; @@ -377,4 +377,4 @@ class StorageManager extends Disposable { } } -registerSingleton(IExtensionEnablementService, ExtensionEnablementService, true); \ No newline at end of file +registerSingleton(IExtensionEnablementService, ExtensionEnablementService, true); diff --git a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts index b905413cddae..6d231747112c 100644 --- a/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts +++ b/src/vs/workbench/services/extensionManagement/common/extensionManagementService.ts @@ -16,7 +16,7 @@ import { CancellationToken } from 'vs/base/common/cancellation'; import { areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { localize } from 'vs/nls'; import { isUIExtension } from 'vs/workbench/services/extensions/common/extensionsUtil'; -import { IProductService } from 'vs/platform/product/common/product'; +import { IProductService } from 'vs/platform/product/common/productService'; import { Schemas } from 'vs/base/common/network'; import { IDownloadService } from 'vs/platform/download/common/download'; diff --git a/src/vs/workbench/services/extensionManagement/electron-browser/extensionManagementServerService.ts b/src/vs/workbench/services/extensionManagement/electron-browser/extensionManagementServerService.ts index 3d02ce9ffbae..1902a29fcb10 100644 --- a/src/vs/workbench/services/extensionManagement/electron-browser/extensionManagementServerService.ts +++ b/src/vs/workbench/services/extensionManagement/electron-browser/extensionManagementServerService.ts @@ -17,7 +17,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ILogService } from 'vs/platform/log/common/log'; import { RemoteExtensionManagementChannelClient } from 'vs/workbench/services/extensions/electron-browser/remoteExtensionManagementIpc'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IProductService } from 'vs/platform/product/common/product'; +import { IProductService } from 'vs/platform/product/common/productService'; import { ILabelService } from 'vs/platform/label/common/label'; const localExtensionManagementServerAuthority: string = 'vscode-local'; @@ -63,4 +63,4 @@ export class ExtensionManagementServerService implements IExtensionManagementSer } } -registerSingleton(IExtensionManagementServerService, ExtensionManagementServerService); \ No newline at end of file +registerSingleton(IExtensionManagementServerService, ExtensionManagementServerService); diff --git a/src/vs/workbench/services/extensions/browser/extensionService.ts b/src/vs/workbench/services/extensions/browser/extensionService.ts index dcb29119b857..bc43981dbdb3 100644 --- a/src/vs/workbench/services/extensions/browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/browser/extensionService.ts @@ -12,7 +12,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IFileService } from 'vs/platform/files/common/files'; -import { IProductService } from 'vs/platform/product/common/product'; +import { IProductService } from 'vs/platform/product/common/productService'; import { AbstractExtensionService } from 'vs/workbench/services/extensions/common/abstractExtensionService'; import { ExtensionHostProcessManager } from 'vs/workbench/services/extensions/common/extensionHostProcessManager'; import { RemoteExtensionHostClient, IInitDataProvider } from 'vs/workbench/services/extensions/common/remoteExtensionHostClient'; diff --git a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts index 66b906eb235e..6d6e83958808 100644 --- a/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts +++ b/src/vs/workbench/services/extensions/browser/webWorkerExtensionHostStarter.ts @@ -9,7 +9,7 @@ import { DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; import { VSBuffer } from 'vs/base/common/buffer'; import { createMessageOfType, MessageType, isMessageOfType } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; -import { IInitData } from 'vs/workbench/api/common/extHost.protocol'; +import { IInitData, UIKind } from 'vs/workbench/api/common/extHost.protocol'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { ILabelService } from 'vs/platform/label/common/label'; @@ -18,7 +18,7 @@ import { IExtensionDescription } from 'vs/platform/extensions/common/extensions' import * as platform from 'vs/base/common/platform'; import { URI } from 'vs/base/common/uri'; import { IExtensionHostStarter } from 'vs/workbench/services/extensions/common/extensions'; -import { IProductService } from 'vs/platform/product/common/product'; +import { IProductService } from 'vs/platform/product/common/productService'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; export class WebWorkerExtensionHostStarter implements IExtensionHostStarter { @@ -151,6 +151,7 @@ export class WebWorkerExtensionHostStarter implements IExtensionHostStarter { authority: this._environmentService.configuration.remoteAuthority, isRemote: false }, + uiKind: platform.isWeb ? UIKind.Web : UIKind.Desktop }; } } diff --git a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts index 9226810981d2..8ab041733419 100644 --- a/src/vs/workbench/services/extensions/common/abstractExtensionService.ts +++ b/src/vs/workbench/services/extensions/common/abstractExtensionService.ts @@ -23,7 +23,7 @@ import { ExtensionHostProcessManager } from 'vs/workbench/services/extensions/co import { ExtensionIdentifier, IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { IFileService } from 'vs/platform/files/common/files'; import { parseExtensionDevOptions } from 'vs/workbench/services/extensions/common/extensionDevOptions'; -import { IProductService } from 'vs/platform/product/common/product'; +import { IProductService } from 'vs/platform/product/common/productService'; const hasOwnProperty = Object.hasOwnProperty; const NO_OP_VOID_PROMISE = Promise.resolve(undefined); diff --git a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts index d4d1459b75cf..3aff7c62a1a2 100644 --- a/src/vs/workbench/services/extensions/common/extensionsRegistry.ts +++ b/src/vs/workbench/services/extensions/common/extensionsRegistry.ts @@ -267,6 +267,11 @@ export const schema = { body: 'onView:${5:viewId}', description: nls.localize('vscode.extension.activationEvents.onView', 'An activation event emitted whenever the specified view is expanded.'), }, + { + label: 'onIdentity', + body: 'onIdentity:${8:identity}', + description: nls.localize('vscode.extension.activationEvents.onIdentity', 'An activation event emitted whenever the specified user identity.'), + }, { label: 'onUri', body: 'onUri', diff --git a/src/vs/workbench/services/extensions/common/extensionsUtil.ts b/src/vs/workbench/services/extensions/common/extensionsUtil.ts index d54748d0e07a..043f5e2837f0 100644 --- a/src/vs/workbench/services/extensions/common/extensionsUtil.ts +++ b/src/vs/workbench/services/extensions/common/extensionsUtil.ts @@ -8,7 +8,7 @@ import { IExtensionManifest } from 'vs/platform/extensions/common/extensions'; import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; import { getGalleryExtensionId, areSameExtensions } from 'vs/platform/extensionManagement/common/extensionManagementUtil'; import { isNonEmptyArray } from 'vs/base/common/arrays'; -import { IProductService } from 'vs/platform/product/common/product'; +import { IProductService } from 'vs/platform/product/common/productService'; export function isWebExtension(manifest: IExtensionManifest, configurationService: IConfigurationService): boolean { const extensionKind = getExtensionKind(manifest, configurationService); diff --git a/src/vs/workbench/services/extensions/common/remoteConsoleUtil.ts b/src/vs/workbench/services/extensions/common/remoteConsoleUtil.ts new file mode 100644 index 000000000000..96c5da0760c6 --- /dev/null +++ b/src/vs/workbench/services/extensions/common/remoteConsoleUtil.ts @@ -0,0 +1,32 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IRemoteConsoleLog, parse } from 'vs/base/common/console'; +import { ILogService } from 'vs/platform/log/common/log'; + +export function logRemoteEntry(logService: ILogService, entry: IRemoteConsoleLog): void { + const args = parse(entry).args; + const firstArg = args.shift(); + if (typeof firstArg !== 'string') { + return; + } + + if (!entry.severity) { + entry.severity = 'info'; + } + + switch (entry.severity) { + case 'log': + case 'info': + logService.info(firstArg, ...args); + break; + case 'warn': + logService.warn(firstArg, ...args); + break; + case 'error': + logService.error(firstArg, ...args); + break; + } +} diff --git a/src/vs/workbench/services/extensions/common/remoteExtensionHostClient.ts b/src/vs/workbench/services/extensions/common/remoteExtensionHostClient.ts index 637b7c066080..1026b8259e51 100644 --- a/src/vs/workbench/services/extensions/common/remoteExtensionHostClient.ts +++ b/src/vs/workbench/services/extensions/common/remoteExtensionHostClient.ts @@ -11,7 +11,7 @@ import { ILogService } from 'vs/platform/log/common/log'; import { connectRemoteAgentExtensionHost, IRemoteExtensionHostStartParams, IConnectionOptions, ISocketFactory } from 'vs/platform/remote/common/remoteAgentConnection'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; -import { IInitData } from 'vs/workbench/api/common/extHost.protocol'; +import { IInitData, UIKind } from 'vs/workbench/api/common/extHost.protocol'; import { MessageType, createMessageOfType, isMessageOfType } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; import { IExtensionHostStarter } from 'vs/workbench/services/extensions/common/extensions'; import { parseExtensionDevOptions } from 'vs/workbench/services/extensions/common/extensionDevOptions'; @@ -25,7 +25,7 @@ import { PersistentProtocol } from 'vs/base/parts/ipc/common/ipc.net'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; import { VSBuffer } from 'vs/base/common/buffer'; import { IExtensionHostDebugService } from 'vs/platform/debug/common/extensionHostDebug'; -import { IProductService } from 'vs/platform/product/common/product'; +import { IProductService } from 'vs/platform/product/common/productService'; import { ISignService } from 'vs/platform/sign/common/sign'; export interface IInitDataProvider { @@ -216,6 +216,7 @@ export class RemoteExtensionHostClient extends Disposable implements IExtensionH logLevel: this._logService.getLevel(), logsLocation: remoteExtensionHostData.extensionHostLogsPath, autoStart: true, + uiKind: platform.isWeb ? UIKind.Web : UIKind.Desktop }; return r; }); diff --git a/src/vs/workbench/services/extensions/common/staticExtensions.ts b/src/vs/workbench/services/extensions/common/staticExtensions.ts index c12d8c97530f..7ba961cd9453 100644 --- a/src/vs/workbench/services/extensions/common/staticExtensions.ts +++ b/src/vs/workbench/services/extensions/common/staticExtensions.ts @@ -5,7 +5,6 @@ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { IExtensionDescription, ExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; -import { URI } from 'vs/base/common/uri'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; @@ -27,7 +26,7 @@ export class StaticExtensionsService implements IStaticExtensionsService { this._descriptions = staticExtensions.map(data => { identifier: new ExtensionIdentifier(`${data.packageJSON.publisher}.${data.packageJSON.name}`), - extensionLocation: URI.revive(data.extensionLocation), + extensionLocation: data.extensionLocation, ...data.packageJSON, }); } diff --git a/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts b/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts index d7a847e09b44..6cd22d2073b8 100644 --- a/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts +++ b/src/vs/workbench/services/extensions/electron-browser/cachedExtensionScanner.ts @@ -17,8 +17,7 @@ import * as pfs from 'vs/base/node/pfs'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IExtensionEnablementService } 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 pkg from 'vs/platform/product/node/package'; -import product from 'vs/platform/product/node/product'; +import product from 'vs/platform/product/common/product'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { IWindowService } from 'vs/platform/windows/common/windows'; import { ExtensionScanner, ExtensionScannerInput, IExtensionReference, IExtensionResolver, IRelaxedExtensionDescription } from 'vs/workbench/services/extensions/node/extensionPoints'; @@ -68,7 +67,7 @@ export class CachedExtensionScanner { public async scanSingleExtension(path: string, isBuiltin: boolean, log: ILog): Promise { const translations = await this.translationConfig; - const version = pkg.version; + const version = product.version; const commit = product.commit; const devMode = !!process.env['VSCODE_DEV']; const locale = platform.language; @@ -243,7 +242,7 @@ export class CachedExtensionScanner { translations: Translations ): Promise<{ system: IExtensionDescription[], user: IExtensionDescription[], development: IExtensionDescription[] }> { - const version = pkg.version; + const version = product.version; const commit = product.commit; const devMode = !!process.env['VSCODE_DEV']; const locale = platform.language; diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts index 8a02af398267..bd49669f7365 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionHost.ts @@ -13,9 +13,9 @@ import { Emitter, Event } from 'vs/base/common/event'; import { toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import * as objects from 'vs/base/common/objects'; import * as platform from 'vs/base/common/platform'; -import pkg from 'vs/platform/product/node/package'; import { URI } from 'vs/base/common/uri'; -import { IRemoteConsoleLog, log, parse } from 'vs/base/common/console'; +import { IRemoteConsoleLog, log } from 'vs/base/common/console'; +import { logRemoteEntry } from 'vs/workbench/services/extensions/common/remoteConsoleUtil'; import { findFreePort, randomPort } from 'vs/base/node/ports'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; import { PersistentProtocol } from 'vs/base/parts/ipc/common/ipc.net'; @@ -24,12 +24,12 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ import { ILabelService } from 'vs/platform/label/common/label'; import { ILifecycleService, WillShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle'; import { ILogService } from 'vs/platform/log/common/log'; -import product from 'vs/platform/product/node/product'; +import product from 'vs/platform/product/common/product'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows'; +import { IWindowService } from 'vs/platform/windows/common/windows'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; -import { IInitData } from 'vs/workbench/api/common/extHost.protocol'; +import { IInitData, UIKind } from 'vs/workbench/api/common/extHost.protocol'; import { MessageType, createMessageOfType, isMessageOfType } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; import { withNullAsUndefined } from 'vs/base/common/types'; import { IExtensionDescription } from 'vs/platform/extensions/common/extensions'; @@ -68,7 +68,6 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { private readonly _extensionHostLogsLocation: URI, @IWorkspaceContextService private readonly _contextService: IWorkspaceContextService, @INotificationService private readonly _notificationService: INotificationService, - @IWindowsService private readonly _windowsService: IWindowsService, @IWindowService private readonly _windowService: IWindowService, @ILifecycleService private readonly _lifecycleService: ILifecycleService, @IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService, @@ -388,7 +387,7 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { const workspace = this._contextService.getWorkspace(); const r: IInitData = { commit: product.commit, - version: pkg.version, + version: product.version, vscodeVersion: product.vscodeVersion, // {{SQL CARBON EDIT}} add vscode version parentPid: process.pid, environment: { @@ -421,7 +420,8 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { telemetryInfo, logLevel: this._logService.getLevel(), logsLocation: this._extensionHostLogsLocation, - autoStart: this._autoStart + autoStart: this._autoStart, + uiKind: UIKind.Desktop }; return r; }); @@ -436,7 +436,7 @@ export class ExtensionHostProcessWorker implements IExtensionHostStarter { // Log on main side if running tests from cli if (this._isExtensionDevTestFromCli) { - this._windowsService.log(entry.severity, parse(entry).args); + logRemoteEntry(this._logService, entry); } // Broadcast to other windows if we are in development mode diff --git a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts index 42115130ace6..3fe1fcdbccd9 100644 --- a/src/vs/workbench/services/extensions/electron-browser/extensionService.ts +++ b/src/vs/workbench/services/extensions/electron-browser/extensionService.ts @@ -32,7 +32,7 @@ import { ExtensionIdentifier, IExtension, ExtensionType, IExtensionDescription } import { Schemas } from 'vs/base/common/network'; import { IFileService } from 'vs/platform/files/common/files'; import { PersistentConnectionEventType } from 'vs/platform/remote/common/remoteAgentConnection'; -import { IProductService } from 'vs/platform/product/common/product'; +import { IProductService } from 'vs/platform/product/common/productService'; import { Logger } from 'vs/workbench/services/extensions/common/extensionPoints'; import { flatten } from 'vs/base/common/arrays'; import { IStaticExtensionsService } from 'vs/workbench/services/extensions/common/staticExtensions'; diff --git a/src/vs/workbench/services/extensions/electron-browser/remoteExtensionManagementIpc.ts b/src/vs/workbench/services/extensions/electron-browser/remoteExtensionManagementIpc.ts index b85e5295b58b..3e74d33c9cc8 100644 --- a/src/vs/workbench/services/extensions/electron-browser/remoteExtensionManagementIpc.ts +++ b/src/vs/workbench/services/extensions/electron-browser/remoteExtensionManagementIpc.ts @@ -16,7 +16,7 @@ import { isNonEmptyArray } from 'vs/base/common/arrays'; import { values } from 'vs/base/common/map'; import { CancellationToken } from 'vs/base/common/cancellation'; import { localize } from 'vs/nls'; -import { IProductService } from 'vs/platform/product/common/product'; +import { IProductService } from 'vs/platform/product/common/productService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ExtensionManagementChannelClient } from 'vs/platform/extensionManagement/common/extensionManagementIpc'; diff --git a/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts b/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts index 4c9fa6268741..042f465e43c6 100644 --- a/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts +++ b/src/vs/workbench/services/extensions/node/extensionHostProcessSetup.ts @@ -11,7 +11,7 @@ import { Event } from 'vs/base/common/event'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; import { PersistentProtocol, ProtocolConstants, BufferedEmitter } from 'vs/base/parts/ipc/common/ipc.net'; import { NodeSocket, WebSocketNodeSocket } from 'vs/base/parts/ipc/node/ipc.net'; -import product from 'vs/platform/product/node/product'; +import product from 'vs/platform/product/common/product'; import { IInitData } from 'vs/workbench/api/common/extHost.protocol'; import { MessageType, createMessageOfType, isMessageOfType, IExtHostSocketMessage, IExtHostReadyMessage } from 'vs/workbench/services/extensions/common/extensionHostProtocol'; import { ExtensionHostMain, IExitFn } from 'vs/workbench/services/extensions/common/extensionHostMain'; diff --git a/src/vs/workbench/services/extensions/worker/extHost.services.ts b/src/vs/workbench/services/extensions/worker/extHost.services.ts index 4b6481be30d0..de6124dc481b 100644 --- a/src/vs/workbench/services/extensions/worker/extHost.services.ts +++ b/src/vs/workbench/services/extensions/worker/extHost.services.ts @@ -11,7 +11,7 @@ import { IExtHostConfiguration, ExtHostConfiguration } from 'vs/workbench/api/co import { IExtHostCommands, ExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; import { IExtHostDocumentsAndEditors, ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; import { IExtHostTerminalService, WorkerExtHostTerminalService } from 'vs/workbench/api/common/extHostTerminalService'; -import { IExtHostTask } from 'vs/workbench/api/common/extHostTask'; +import { IExtHostTask, WorkerExtHostTask } from 'vs/workbench/api/common/extHostTask'; import { IExtHostDebugService } from 'vs/workbench/api/common/extHostDebugService'; import { IExtHostSearch } from 'vs/workbench/api/common/extHostSearch'; import { IExtensionStoragePaths } from 'vs/workbench/api/common/extHostStoragePaths'; @@ -49,7 +49,7 @@ function NotImplementedProxy(name: ServiceIdentifier): { new(): T } { }; } registerSingleton(IExtHostTerminalService, WorkerExtHostTerminalService); -registerSingleton(IExtHostTask, class extends NotImplementedProxy(IExtHostTask) { }); +registerSingleton(IExtHostTask, WorkerExtHostTask); registerSingleton(IExtHostDebugService, class extends NotImplementedProxy(IExtHostDebugService) { }); registerSingleton(IExtHostSearch, class extends NotImplementedProxy(IExtHostSearch) { }); registerSingleton(IExtensionStoragePaths, class extends NotImplementedProxy(IExtensionStoragePaths) { diff --git a/src/vs/workbench/services/integrity/node/integrityService.ts b/src/vs/workbench/services/integrity/node/integrityService.ts index 756340e270ad..90414ab78668 100644 --- a/src/vs/workbench/services/integrity/node/integrityService.ts +++ b/src/vs/workbench/services/integrity/node/integrityService.ts @@ -10,7 +10,7 @@ import Severity from 'vs/base/common/severity'; import { URI } from 'vs/base/common/uri'; import { ChecksumPair, IIntegrityService, IntegrityTestResult } from 'vs/workbench/services/integrity/common/integrity'; import { ILifecycleService, LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; -import product from 'vs/platform/product/node/product'; +import product from 'vs/platform/product/common/product'; import { INotificationService } from 'vs/platform/notification/common/notification'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; diff --git a/src/vs/workbench/services/preferences/browser/preferencesService.ts b/src/vs/workbench/services/preferences/browser/preferencesService.ts index c6676efd0a18..aaa710d096b4 100644 --- a/src/vs/workbench/services/preferences/browser/preferencesService.ts +++ b/src/vs/workbench/services/preferences/browser/preferencesService.ts @@ -557,7 +557,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic return this.textFileService.read(workspaceConfig) .then(content => { if (Object.keys(parse(content.value)).indexOf('settings') === -1) { - return this.jsonEditingService.write(resource, { key: 'settings', value: {} }, true).then(undefined, () => { }); + return this.jsonEditingService.write(resource, [{ key: 'settings', value: {} }], true).then(undefined, () => { }); } return undefined; }); diff --git a/src/vs/workbench/services/remote/browser/remoteAgentServiceImpl.ts b/src/vs/workbench/services/remote/browser/remoteAgentServiceImpl.ts index e124d390aedd..e31c39fb9803 100644 --- a/src/vs/workbench/services/remote/browser/remoteAgentServiceImpl.ts +++ b/src/vs/workbench/services/remote/browser/remoteAgentServiceImpl.ts @@ -7,7 +7,7 @@ import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/ import { IRemoteAgentConnection, IRemoteAgentService } from 'vs/workbench/services/remote/common/remoteAgentService'; import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { AbstractRemoteAgentService, RemoteAgentConnection } from 'vs/workbench/services/remote/common/abstractRemoteAgentService'; -import { IProductService } from 'vs/platform/product/common/product'; +import { IProductService } from 'vs/platform/product/common/productService'; import { IWebSocketFactory, BrowserSocketFactory } from 'vs/platform/remote/browser/browserSocketFactory'; import { ISignService } from 'vs/platform/sign/common/sign'; import { ISocketFactory } from 'vs/platform/remote/common/remoteAgentConnection'; diff --git a/src/vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl.ts b/src/vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl.ts index 8ede3c2029e6..881997341c18 100644 --- a/src/vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl.ts +++ b/src/vs/workbench/services/remote/electron-browser/remoteAgentServiceImpl.ts @@ -7,7 +7,7 @@ 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/node/product'; +import product from 'vs/platform/product/common/product'; import { nodeSocketFactory } from 'vs/platform/remote/node/nodeSocketFactory'; import { AbstractRemoteAgentService, RemoteAgentConnection } from 'vs/workbench/services/remote/common/abstractRemoteAgentService'; import { ISignService } from 'vs/platform/sign/common/sign'; diff --git a/src/vs/workbench/services/remote/node/tunnelService.ts b/src/vs/workbench/services/remote/node/tunnelService.ts index 055dbb7817c7..e7e94e847f22 100644 --- a/src/vs/workbench/services/remote/node/tunnelService.ts +++ b/src/vs/workbench/services/remote/node/tunnelService.ts @@ -8,7 +8,7 @@ import { Barrier } from 'vs/base/common/async'; import { Disposable } from 'vs/base/common/lifecycle'; import { NodeSocket } from 'vs/base/parts/ipc/node/ipc.net'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import product from 'vs/platform/product/node/product'; +import product from 'vs/platform/product/common/product'; import { connectRemoteAgentTunnel, IConnectionOptions } from 'vs/platform/remote/common/remoteAgentConnection'; import { IRemoteAuthorityResolverService } from 'vs/platform/remote/common/remoteAuthorityResolver'; import { ITunnelService, RemoteTunnel } from 'vs/platform/remote/common/tunnel'; diff --git a/src/vs/workbench/services/search/common/search.ts b/src/vs/workbench/services/search/common/search.ts index 5279cf20d5df..7a6f63299c29 100644 --- a/src/vs/workbench/services/search/common/search.ts +++ b/src/vs/workbench/services/search/common/search.ts @@ -252,7 +252,10 @@ export class TextSearchMatch implements ITextSearchMatch { constructor(text: string, range: ISearchRange | ISearchRange[], previewOptions?: ITextSearchPreviewOptions) { this.ranges = range; - if (previewOptions && previewOptions.matchLines === 1 && (!Array.isArray(range) || range.length === 1)) { + // Trim preview if this is one match and a single-line match with a preview requested. + // Otherwise send the full text, like for replace or for showing multiple previews. + // TODO this is fishy. + if (previewOptions && previewOptions.matchLines === 1 && (!Array.isArray(range) || range.length === 1) && isSingleLineRange(range)) { const oneRange = Array.isArray(range) ? range[0] : range; // 1 line preview requested @@ -273,7 +276,6 @@ export class TextSearchMatch implements ITextSearchMatch { } else { const firstMatchLine = Array.isArray(range) ? range[0].startLineNumber : range.startLineNumber; - // n line, no preview requested, or multiple matches in the preview this.preview = { text, matches: mapArrayOrNot(range, r => new SearchRange(r.startLineNumber - firstMatchLine, r.startColumn, r.endLineNumber - firstMatchLine, r.endColumn)) @@ -282,6 +284,12 @@ export class TextSearchMatch implements ITextSearchMatch { } } +function isSingleLineRange(range: ISearchRange | ISearchRange[]): boolean { + return Array.isArray(range) ? + range[0].startLineNumber === range[0].endLineNumber : + range.startLineNumber === range.endLineNumber; +} + export class SearchRange implements ISearchRange { startLineNumber: number; startColumn: number; diff --git a/src/vs/workbench/services/search/node/ripgrepTextSearchEngine.ts b/src/vs/workbench/services/search/node/ripgrepTextSearchEngine.ts index c42ab60aa13a..d54eb867e231 100644 --- a/src/vs/workbench/services/search/node/ripgrepTextSearchEngine.ts +++ b/src/vs/workbench/services/search/node/ripgrepTextSearchEngine.ts @@ -125,7 +125,7 @@ export function rgErrorMsgForDisplay(msg: string): Maybe { const firstLine = lines[0].trim(); if (lines.some(l => startsWith(l, 'regex parse error'))) { - return new SearchError('Regex parse error', SearchErrorCode.regexParseError); + return new SearchError(buildRegexParseError(lines), SearchErrorCode.regexParseError); } const match = firstLine.match(/grep config error: unknown encoding: (.*)/); @@ -150,6 +150,21 @@ export function rgErrorMsgForDisplay(msg: string): Maybe { return undefined; } +export function buildRegexParseError(lines: string[]): string { + let errorMessage: string[] = ['Regex parse error']; + let pcre2ErrorLine = lines.filter(l => (startsWith(l, 'PCRE2:'))); + if (pcre2ErrorLine.length >= 1) { + let pcre2ErrorMessage = pcre2ErrorLine[0].replace('PCRE2:', ''); + if (pcre2ErrorMessage.indexOf(':') !== -1 && pcre2ErrorMessage.split(':').length >= 2) { + let pcre2ActualErrorMessage = pcre2ErrorMessage.split(':')[1]; + errorMessage.push(':' + pcre2ActualErrorMessage); + } + } + + return errorMessage.join(''); +} + + export class RipgrepParser extends EventEmitter { private remainder = ''; private isDone = false; diff --git a/src/vs/workbench/services/search/test/common/search.test.ts b/src/vs/workbench/services/search/test/common/search.test.ts index 5bad58b1fc2b..9968bca6144c 100644 --- a/src/vs/workbench/services/search/test/common/search.test.ts +++ b/src/vs/workbench/services/search/test/common/search.test.ts @@ -12,7 +12,7 @@ suite('TextSearchResult', () => { charsPerLine: 100 }; - function assertPreviewRangeText(text: string, result: TextSearchMatch): void { + function assertOneLinePreviewRangeText(text: string, result: TextSearchMatch): void { assert.equal( result.preview.text.substring((result.preview.matches).startColumn, (result.preview.matches).endColumn), text); @@ -22,49 +22,49 @@ suite('TextSearchResult', () => { const range = new OneLineRange(5, 0, 0); const result = new TextSearchMatch('', range); assert.deepEqual(result.ranges, range); - assertPreviewRangeText('', result); + assertOneLinePreviewRangeText('', result); }); test('empty with preview options', () => { const range = new OneLineRange(5, 0, 0); const result = new TextSearchMatch('', range, previewOptions1); assert.deepEqual(result.ranges, range); - assertPreviewRangeText('', result); + assertOneLinePreviewRangeText('', result); }); test('short without preview options', () => { const range = new OneLineRange(5, 4, 7); const result = new TextSearchMatch('foo bar', range); assert.deepEqual(result.ranges, range); - assertPreviewRangeText('bar', result); + assertOneLinePreviewRangeText('bar', result); }); test('short with preview options', () => { const range = new OneLineRange(5, 4, 7); const result = new TextSearchMatch('foo bar', range, previewOptions1); assert.deepEqual(result.ranges, range); - assertPreviewRangeText('bar', result); + assertOneLinePreviewRangeText('bar', result); }); test('leading', () => { const range = new OneLineRange(5, 25, 28); const result = new TextSearchMatch('long text very long text foo', range, previewOptions1); assert.deepEqual(result.ranges, range); - assertPreviewRangeText('foo', result); + assertOneLinePreviewRangeText('foo', result); }); test('trailing', () => { const range = new OneLineRange(5, 0, 3); const result = new TextSearchMatch('foo long text very long text long text very long text long text very long text long text very long text long text very long text', range, previewOptions1); assert.deepEqual(result.ranges, range); - assertPreviewRangeText('foo', result); + assertOneLinePreviewRangeText('foo', result); }); test('middle', () => { const range = new OneLineRange(5, 30, 33); const result = new TextSearchMatch('long text very long text long foo text very long text long text very long text long text very long text long text very long text', range, previewOptions1); assert.deepEqual(result.ranges, range); - assertPreviewRangeText('foo', result); + assertOneLinePreviewRangeText('foo', result); }); test('truncating match', () => { @@ -76,7 +76,7 @@ suite('TextSearchResult', () => { const range = new OneLineRange(0, 4, 7); const result = new TextSearchMatch('foo bar', range, previewOptions); assert.deepEqual(result.ranges, range); - assertPreviewRangeText('b', result); + assertOneLinePreviewRangeText('b', result); }); test('one line of multiline match', () => { @@ -88,7 +88,11 @@ suite('TextSearchResult', () => { const range = new SearchRange(5, 4, 6, 3); const result = new TextSearchMatch('foo bar\nfoo bar', range, previewOptions); assert.deepEqual(result.ranges, range); - assertPreviewRangeText('bar', result); + assert.equal(result.preview.text, 'foo bar\nfoo bar'); + assert.equal((result.preview.matches).startLineNumber, 0); + assert.equal((result.preview.matches).startColumn, 4); + assert.equal((result.preview.matches).endLineNumber, 1); + assert.equal((result.preview.matches).endColumn, 3); }); // test('all lines of multiline match', () => { @@ -102,4 +106,4 @@ suite('TextSearchResult', () => { // assert.deepEqual(result.range, range); // assertPreviewRangeText('bar\nfoo', result); // }); -}); \ No newline at end of file +}); diff --git a/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts b/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts index f300ea3b2d9b..7b96bc286f2b 100644 --- a/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts +++ b/src/vs/workbench/services/search/test/node/textSearch.integrationTest.ts @@ -376,7 +376,7 @@ suite('Search-integration', function () { }); }); - test('invalid regex', () => { + test('invalid regex case 1', () => { const config: ITextQuery = { type: QueryType.Text, folderQueries: ROOT_FOLDER_QUERY, @@ -387,11 +387,30 @@ suite('Search-integration', function () { throw new Error('expected fail'); }, err => { const searchError = deserializeSearchError(err.message); - assert.equal(searchError.message, 'Regex parse error'); + let regexParseErrorForUnclosedParenthesis = 'Regex parse error: unmatched closing parenthesis'; + assert.equal(searchError.message, regexParseErrorForUnclosedParenthesis); assert.equal(searchError.code, SearchErrorCode.regexParseError); }); }); + test('invalid regex case 2', () => { + const config: ITextQuery = { + type: QueryType.Text, + folderQueries: ROOT_FOLDER_QUERY, + contentPattern: { pattern: '(? { + throw new Error('expected fail'); + }, err => { + const searchError = deserializeSearchError(err.message); + let regexParseErrorForLookAround = 'Regex parse error: lookbehind assertion is not fixed length'; + assert.equal(searchError.message, regexParseErrorForLookAround); + assert.equal(searchError.code, SearchErrorCode.regexParseError); + }); + }); + + test('invalid glob', () => { const config: ITextQuery = { type: QueryType.Text, diff --git a/src/vs/workbench/services/telemetry/browser/telemetryService.ts b/src/vs/workbench/services/telemetry/browser/telemetryService.ts index f4e28399c12b..9d0c92ab2d52 100644 --- a/src/vs/workbench/services/telemetry/browser/telemetryService.ts +++ b/src/vs/workbench/services/telemetry/browser/telemetryService.ts @@ -14,7 +14,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ClassifiedEvent, StrictPropertyCheck, GDPRClassification } from 'vs/platform/telemetry/common/gdprTypings'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { resolveWorkbenchCommonProperties } from 'vs/platform/telemetry/browser/workbenchCommonProperties'; -import { IProductService } from 'vs/platform/product/common/product'; +import { IProductService } from 'vs/platform/product/common/productService'; import { ApplicationInsights } from '@microsoft/applicationinsights-web'; export class WebTelemetryAppender implements ITelemetryAppender { diff --git a/src/vs/workbench/services/telemetry/electron-browser/telemetryService.ts b/src/vs/workbench/services/telemetry/electron-browser/telemetryService.ts index 04746c129605..49781cbd4ebf 100644 --- a/src/vs/workbench/services/telemetry/electron-browser/telemetryService.ts +++ b/src/vs/workbench/services/telemetry/electron-browser/telemetryService.ts @@ -8,7 +8,7 @@ import { NullTelemetryService, combinedAppender, LogAppender } from 'vs/platform import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { Disposable } from 'vs/base/common/lifecycle'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { IProductService } from 'vs/platform/product/common/product'; +import { IProductService } from 'vs/platform/product/common/productService'; import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; import { TelemetryAppenderClient } from 'vs/platform/telemetry/node/telemetryIpc'; import { ILogService } from 'vs/platform/log/common/log'; diff --git a/src/vs/workbench/services/textfile/common/textFileService.ts b/src/vs/workbench/services/textfile/common/textFileService.ts index f5554411334d..2c4cc605e422 100644 --- a/src/vs/workbench/services/textfile/common/textFileService.ts +++ b/src/vs/workbench/services/textfile/common/textFileService.ts @@ -578,26 +578,7 @@ export abstract class TextFileService extends Disposable implements ITextFileSer if (resourcesToConfirm.length === 0) { return ConfirmResult.DONT_SAVE; } - - const message = resourcesToConfirm.length === 1 ? nls.localize('saveChangesMessage', "Do you want to save the changes you made to {0}?", basename(resourcesToConfirm[0])) - : getConfirmMessage(nls.localize('saveChangesMessages', "Do you want to save the changes to the following {0} files?", resourcesToConfirm.length), resourcesToConfirm); - - const buttons: string[] = [ - resourcesToConfirm.length > 1 ? nls.localize({ key: 'saveAll', comment: ['&& denotes a mnemonic'] }, "&&Save All") : nls.localize({ key: 'save', comment: ['&& denotes a mnemonic'] }, "&&Save"), - nls.localize({ key: 'dontSave', comment: ['&& denotes a mnemonic'] }, "Do&&n't Save"), - nls.localize('cancel', "Cancel") - ]; - - const { choice } = await this.dialogService.show(Severity.Warning, message, buttons, { - cancelId: 2, - detail: nls.localize('saveChangesDetail', "Your changes will be lost if you don't save them.") - }); - - switch (choice) { - case 0: return ConfirmResult.SAVE; - case 1: return ConfirmResult.DONT_SAVE; - default: return ConfirmResult.CANCEL; - } + return promptSave(this.dialogService, resourcesToConfirm); } async confirmOverwrite(resource: URI): Promise { @@ -1063,3 +1044,27 @@ export abstract class TextFileService extends Disposable implements ITextFileSer super.dispose(); } } + +export async function promptSave(dialogService: IDialogService, resourcesToConfirm: readonly URI[]) { + const message = resourcesToConfirm.length === 1 + ? nls.localize('saveChangesMessage', "Do you want to save the changes you made to {0}?", basename(resourcesToConfirm[0])) + : getConfirmMessage(nls.localize('saveChangesMessages', "Do you want to save the changes to the following {0} files?", resourcesToConfirm.length), resourcesToConfirm); + + const buttons: string[] = [ + resourcesToConfirm.length > 1 ? nls.localize({ key: 'saveAll', comment: ['&& denotes a mnemonic'] }, "&&Save All") : nls.localize({ key: 'save', comment: ['&& denotes a mnemonic'] }, "&&Save"), + nls.localize({ key: 'dontSave', comment: ['&& denotes a mnemonic'] }, "Do&&n't Save"), + nls.localize('cancel', "Cancel") + ]; + + const { choice } = await dialogService.show(Severity.Warning, message, buttons, { + cancelId: 2, + detail: nls.localize('saveChangesDetail', "Your changes will be lost if you don't save them.") + }); + + switch (choice) { + case 0: return ConfirmResult.SAVE; + case 1: return ConfirmResult.DONT_SAVE; + default: return ConfirmResult.CANCEL; + } +} + diff --git a/src/vs/workbench/services/textfile/common/textResourcePropertiesService.ts b/src/vs/workbench/services/textfile/common/textResourcePropertiesService.ts index 2fd4ef389d0d..ef0a5ed015bd 100644 --- a/src/vs/workbench/services/textfile/common/textResourcePropertiesService.ts +++ b/src/vs/workbench/services/textfile/common/textResourcePropertiesService.ts @@ -29,7 +29,7 @@ export class TextResourcePropertiesService implements ITextResourcePropertiesSer remoteAgentService.getEnvironment().then(remoteEnv => this.remoteEnvironment = remoteEnv); } - getEOL(resource: URI, language?: string): string { + getEOL(resource?: URI, language?: string): string { const filesConfiguration = this.configurationService.getValue<{ eol: string }>('files', { overrideIdentifier: language, resource }); if (filesConfiguration && filesConfiguration.eol && filesConfiguration.eol !== 'auto') { return filesConfiguration.eol; @@ -38,12 +38,12 @@ export class TextResourcePropertiesService implements ITextResourcePropertiesSer return os === OperatingSystem.Linux || os === OperatingSystem.Macintosh ? '\n' : '\r\n'; } - private getOS(resource: URI): OperatingSystem { + private getOS(resource?: URI): OperatingSystem { let os = OS; const remoteAuthority = this.environmentService.configuration.remoteAuthority; if (remoteAuthority) { - if (resource.scheme !== Schemas.file) { + if (resource && resource.scheme !== Schemas.file) { const osCacheKey = `resource.authority.os.${remoteAuthority}`; os = this.remoteEnvironment ? this.remoteEnvironment.os : /* Get it from cache */ this.storageService.getNumber(osCacheKey, StorageScope.WORKSPACE, OS); this.storageService.store(osCacheKey, os, StorageScope.WORKSPACE); diff --git a/src/vs/workbench/services/textfile/node/textFileService.ts b/src/vs/workbench/services/textfile/node/textFileService.ts index 0046c4ff53ef..c92e5524d738 100644 --- a/src/vs/workbench/services/textfile/node/textFileService.ts +++ b/src/vs/workbench/services/textfile/node/textFileService.ts @@ -14,7 +14,7 @@ import { Schemas } from 'vs/base/common/network'; import { exists, stat, chmod, rimraf, MAX_FILE_SIZE, MAX_HEAP_SIZE } from 'vs/base/node/pfs'; import { join, dirname } from 'vs/base/common/path'; import { isMacintosh } from 'vs/base/common/platform'; -import product from 'vs/platform/product/node/product'; +import product from 'vs/platform/product/common/product'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; import { UTF8, UTF8_with_bom, UTF16be, UTF16le, encodingExists, encodeStream, UTF8_BOM, toDecodeStream, IDecodeStreamResult, detectEncodingByBOMFromBuffer, isUTFEncoding } from 'vs/base/node/encoding'; diff --git a/src/vs/workbench/services/url/electron-browser/urlService.ts b/src/vs/workbench/services/url/electron-browser/urlService.ts index 5130f7d30720..9250a4f9d191 100644 --- a/src/vs/workbench/services/url/electron-browser/urlService.ts +++ b/src/vs/workbench/services/url/electron-browser/urlService.ts @@ -9,7 +9,7 @@ import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProces import { URLServiceChannelClient, URLHandlerChannel } from 'vs/platform/url/common/urlIpc'; import { URLService } from 'vs/platform/url/node/urlService'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import product from 'vs/platform/product/node/product'; +import product from 'vs/platform/product/common/product'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IWindowService } from 'vs/platform/windows/common/windows'; diff --git a/src/vs/workbench/services/userData/common/inMemoryUserDataProvider.ts b/src/vs/workbench/services/userData/common/inMemoryUserDataProvider.ts index 359ede672819..59609811118c 100644 --- a/src/vs/workbench/services/userData/common/inMemoryUserDataProvider.ts +++ b/src/vs/workbench/services/userData/common/inMemoryUserDataProvider.ts @@ -50,7 +50,7 @@ class Directory implements IStat { export type Entry = File | Directory; -export class InMemoryUserDataProvider extends Disposable implements IFileSystemProvider { +export class InMemoryFileSystemProvider extends Disposable implements IFileSystemProvider { readonly capabilities: FileSystemProviderCapabilities = FileSystemProviderCapabilities.FileReadWrite; readonly onDidChangeCapabilities: Event = Event.None; diff --git a/src/vs/workbench/services/userData/common/settingsMergeService.ts b/src/vs/workbench/services/userData/common/settingsMergeService.ts new file mode 100644 index 000000000000..40214dabbf37 --- /dev/null +++ b/src/vs/workbench/services/userData/common/settingsMergeService.ts @@ -0,0 +1,206 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as objects from 'vs/base/common/objects'; +import { values } from 'vs/base/common/map'; +import { parse, findNodeAtLocation, parseTree } from 'vs/base/common/json'; +import { EditOperation } from 'vs/editor/common/core/editOperation'; +import { IModeService } from 'vs/editor/common/services/modeService'; +import { ITextModel } from 'vs/editor/common/model'; +import { setProperty } from 'vs/base/common/jsonEdit'; +import { Range } from 'vs/editor/common/core/range'; +import { Selection } from 'vs/editor/common/core/selection'; +import { IModelService } from 'vs/editor/common/services/modelService'; +import { Position } from 'vs/editor/common/core/position'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { ISettingsMergeService } from 'vs/platform/userDataSync/common/userDataSync'; + +class SettingsMergeService implements ISettingsMergeService { + + _serviceBrand: undefined; + + constructor( + @IModelService private readonly modelService: IModelService, + @IModeService private readonly modeService: IModeService + ) { } + + async merge(localContent: string, remoteContent: string, baseContent: string | null): Promise { + const local = parse(localContent); + const remote = parse(remoteContent); + const base = baseContent ? parse(baseContent) : null; + const { changes, conflicts } = this.getChanges(local, remote, base); + + if (!changes.length && !conflicts.length) { + return localContent; + } + + const settingsPreviewModel = this.modelService.createModel(localContent, this.modeService.create('jsonc')); + for (const change of changes) { + this.editSetting(settingsPreviewModel, change.key, change.value); + } + for (const key of conflicts) { + const tree = parseTree(settingsPreviewModel.getValue()); + const valueNode = findNodeAtLocation(tree, [key]); + const eol = settingsPreviewModel.getEOL(); + const remoteEdit = setProperty(`{${eol}\t${eol}}`, [key], remote[key], { tabSize: 4, insertSpaces: false, eol: eol })[0]; + const remoteContent = remoteEdit ? `${remoteEdit.content.substring(remoteEdit.offset + remoteEdit.length + 1)},${eol}` : ''; + if (valueNode) { + // Updated in Local and Remote with different value + const keyPosition = settingsPreviewModel.getPositionAt(valueNode.parent!.offset); + const valuePosition = settingsPreviewModel.getPositionAt(valueNode.offset + valueNode.length); + const editOperations = [ + EditOperation.insert(new Position(keyPosition.lineNumber - 1, settingsPreviewModel.getLineMaxColumn(keyPosition.lineNumber - 1)), `${eol}<<<<<<< local`), + EditOperation.insert(new Position(valuePosition.lineNumber, settingsPreviewModel.getLineMaxColumn(valuePosition.lineNumber)), `${eol}=======${eol}${remoteContent}>>>>>>> remote`) + ]; + settingsPreviewModel.pushEditOperations([new Selection(keyPosition.lineNumber, keyPosition.column, keyPosition.lineNumber, keyPosition.column)], editOperations, () => []); + } else { + // Removed in Local, but updated in Remote + const position = new Position(settingsPreviewModel.getLineCount() - 1, settingsPreviewModel.getLineMaxColumn(settingsPreviewModel.getLineCount() - 1)); + const editOperations = [ + EditOperation.insert(position, `${eol}<<<<<<< local${eol}=======${eol}${remoteContent}>>>>>>> remote`) + ]; + settingsPreviewModel.pushEditOperations([new Selection(position.lineNumber, position.column, position.lineNumber, position.column)], editOperations, () => []); + } + } + return settingsPreviewModel.getValue(); + } + + private editSetting(model: ITextModel, key: string, value: any | undefined): void { + const insertSpaces = false; + const tabSize = 4; + const eol = model.getEOL(); + const edit = setProperty(model.getValue(), [key], value, { tabSize, insertSpaces, eol })[0]; + if (edit) { + const startPosition = model.getPositionAt(edit.offset); + const endPosition = model.getPositionAt(edit.offset + edit.length); + const range = new Range(startPosition.lineNumber, startPosition.column, endPosition.lineNumber, endPosition.column); + let currentText = model.getValueInRange(range); + if (edit.content !== currentText) { + const editOperation = currentText ? EditOperation.replace(range, edit.content) : EditOperation.insert(startPosition, edit.content); + model.pushEditOperations([new Selection(startPosition.lineNumber, startPosition.column, startPosition.lineNumber, startPosition.column)], [editOperation], () => []); + } + } + } + + private getChanges(local: { [key: string]: any }, remote: { [key: string]: any }, base: { [key: string]: any } | null): { changes: { key: string; value: any | undefined; }[], conflicts: string[] } { + const localToRemote = this.compare(local, remote); + if (localToRemote.added.size === 0 && localToRemote.removed.size === 0 && localToRemote.updated.size === 0) { + // No changes found between local and remote. + return { changes: [], conflicts: [] }; + } + + const changes: { key: string, value: any | undefined }[] = []; + const conflicts: Set = new Set(); + const baseToLocal = base ? this.compare(base, local) : { added: Object.keys(local).reduce((r, k) => { r.add(k); return r; }, new Set()), removed: new Set(), updated: new Set() }; + const baseToRemote = base ? this.compare(base, remote) : { added: Object.keys(remote).reduce((r, k) => { r.add(k); return r; }, new Set()), removed: new Set(), updated: new Set() }; + + // Removed settings in Local + for (const key of baseToLocal.removed.keys()) { + // Got updated in remote + if (baseToRemote.updated.has(key)) { + conflicts.add(key); + } + } + + // Removed settings in Remote + for (const key of baseToRemote.removed.keys()) { + if (conflicts.has(key)) { + continue; + } + // Got updated in local + if (baseToLocal.updated.has(key)) { + conflicts.add(key); + } else { + changes.push({ key, value: undefined }); + } + } + + // Added settings in Local + for (const key of baseToLocal.added.keys()) { + if (conflicts.has(key)) { + continue; + } + // Got added in remote + if (baseToRemote.added.has(key)) { + // Has different value + if (localToRemote.updated.has(key)) { + conflicts.add(key); + } + } + } + + // Added settings in remote + for (const key of baseToRemote.added.keys()) { + if (conflicts.has(key)) { + continue; + } + // Got added in local + if (baseToLocal.added.has(key)) { + // Has different value + if (localToRemote.updated.has(key)) { + conflicts.add(key); + } + } else { + changes.push({ key, value: remote[key] }); + } + } + + // Updated settings in Local + for (const key of baseToLocal.updated.keys()) { + if (conflicts.has(key)) { + continue; + } + // Got updated in remote + if (baseToRemote.updated.has(key)) { + // Has different value + if (localToRemote.updated.has(key)) { + conflicts.add(key); + } + } + } + + // Updated settings in Remote + for (const key of baseToRemote.updated.keys()) { + if (conflicts.has(key)) { + continue; + } + // Got updated in local + if (baseToLocal.updated.has(key)) { + // Has different value + if (localToRemote.updated.has(key)) { + conflicts.add(key); + } + } else { + changes.push({ key, value: remote[key] }); + } + } + + return { changes, conflicts: values(conflicts) }; + } + + private compare(from: { [key: string]: any }, to: { [key: string]: any }): { added: Set, removed: Set, updated: Set } { + const fromKeys = Object.keys(from); + const toKeys = Object.keys(to); + const added = toKeys.filter(key => fromKeys.indexOf(key) === -1).reduce((r, key) => { r.add(key); return r; }, new Set()); + const removed = fromKeys.filter(key => toKeys.indexOf(key) === -1).reduce((r, key) => { r.add(key); return r; }, new Set()); + const updated: Set = new Set(); + + for (const key of fromKeys) { + if (removed.has(key)) { + continue; + } + const value1 = from[key]; + const value2 = to[key]; + if (!objects.equals(value1, value2)) { + updated.add(key); + } + } + + return { added, removed, updated }; + } + +} + +registerSingleton(ISettingsMergeService, SettingsMergeService); diff --git a/src/vs/workbench/services/userDataSync/common/userDataSyncStores.ts b/src/vs/workbench/services/userDataSync/common/userDataSyncStores.ts new file mode 100644 index 000000000000..929afc5a5f9e --- /dev/null +++ b/src/vs/workbench/services/userDataSync/common/userDataSyncStores.ts @@ -0,0 +1,90 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Event, Emitter } from 'vs/base/common/event'; +import { IUserDataSyncStore } from 'vs/platform/userDataSync/common/userDataSync'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { values } from 'vs/base/common/map'; +import { Registry } from 'vs/platform/registry/common/platform'; + + +export namespace Extensions { + export const UserDataSyncStoresRegistry = 'workbench.registry.userData.syncStores'; +} + +export interface IUserDataSyncStoresRegistry { + /** + * An event that is triggerred when a user data sync store is registered. + */ + readonly onDidRegister: Event; + + /** + * An event that is triggerred when a user data sync store is deregistered. + */ + readonly onDidDeregister: Event; + + /** + * All registered user data sync stores + */ + readonly all: IUserDataSyncStore[]; + + /** + * Registers a user data sync store + * + * @param userDataSyncStore to register + */ + registerUserDataSyncStore(userDataSyncStore: IUserDataSyncStore): void; + + /** + * Deregisters the user data sync store with given id + */ + deregisterUserDataSyncStore(id: string): void; + + /** + * Returns the user data sync store with given id. + * + * @returns the user data sync store with given id. + */ + get(id: string): IUserDataSyncStore | undefined; +} + +class UserDataSyncStoresRegistryImpl extends Disposable implements IUserDataSyncStoresRegistry { + + private readonly _onDidRegister = this._register(new Emitter()); + readonly onDidRegister: Event = this._onDidRegister.event; + + private readonly _onDidDeregister = this._register(new Emitter()); + readonly onDidDeregister: Event = this._onDidDeregister.event; + + private userDataSyncStores: Map = new Map(); + + get all(): IUserDataSyncStore[] { + return values(this.userDataSyncStores); + } + + registerUserDataSyncStore(userDataSyncStore: IUserDataSyncStore): void { + const existing = this.userDataSyncStores.get(userDataSyncStore.id); + if (existing) { + return; + } + + this.userDataSyncStores.set(userDataSyncStore.id, userDataSyncStore); + this._onDidRegister.fire(userDataSyncStore); + } + + deregisterUserDataSyncStore(id: string): void { + const existing = this.userDataSyncStores.get(id); + if (existing) { + this.userDataSyncStores.delete(id); + this._onDidDeregister.fire(id); + } + } + + get(id: string): IUserDataSyncStore | undefined { + return this.userDataSyncStores.get(id); + } +} + +Registry.add(Extensions.UserDataSyncStoresRegistry, new UserDataSyncStoresRegistryImpl()); diff --git a/src/vs/workbench/services/workspace/browser/workspaceEditingService.ts b/src/vs/workbench/services/workspace/browser/workspaceEditingService.ts index 855e4a431240..44df7d5001e2 100644 --- a/src/vs/workbench/services/workspace/browser/workspaceEditingService.ts +++ b/src/vs/workbench/services/workspace/browser/workspaceEditingService.ts @@ -448,7 +448,7 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { } } - return this.jsonEditingService.write(toWorkspace.configPath, { key: 'settings', value: targetWorkspaceConfiguration }, true); + return this.jsonEditingService.write(toWorkspace.configPath, [{ key: 'settings', value: targetWorkspaceConfiguration }], true); } private getCurrentWorkspaceIdentifier(): IWorkspaceIdentifier | undefined { diff --git a/src/vs/workbench/services/workspace/browser/workspacesService.ts b/src/vs/workbench/services/workspace/browser/workspacesService.ts new file mode 100644 index 000000000000..94034b80a91e --- /dev/null +++ b/src/vs/workbench/services/workspace/browser/workspacesService.ts @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IWorkspacesService, IWorkspaceFolderCreationData, IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { URI } from 'vs/base/common/uri'; + +export class WorkspacesService implements IWorkspacesService { + + _serviceBrand: undefined; + + async createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[], remoteAuthority?: string): Promise { + throw new Error('Untitled workspaces are currently unsupported in Web'); + } + + async deleteUntitledWorkspace(workspace: IWorkspaceIdentifier): Promise { + throw new Error('Untitled workspaces are currently unsupported in Web'); + } + + async getWorkspaceIdentifier(workspacePath: URI): Promise { + throw new Error('Untitled workspaces are currently unsupported in Web'); + } +} + +registerSingleton(IWorkspacesService, WorkspacesService, true); diff --git a/src/vs/platform/workspaces/electron-browser/workspacesService.ts b/src/vs/workbench/services/workspace/electron-browser/workspacesService.ts similarity index 91% rename from src/vs/platform/workspaces/electron-browser/workspacesService.ts rename to src/vs/workbench/services/workspace/electron-browser/workspacesService.ts index 6107be9d0594..0249e060eee9 100644 --- a/src/vs/platform/workspaces/electron-browser/workspacesService.ts +++ b/src/vs/workbench/services/workspace/electron-browser/workspacesService.ts @@ -7,6 +7,7 @@ import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { IWorkspacesService, IWorkspaceIdentifier, IWorkspaceFolderCreationData, reviveWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; import { URI } from 'vs/base/common/uri'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; export class WorkspacesService implements IWorkspacesService { @@ -30,3 +31,5 @@ export class WorkspacesService implements IWorkspacesService { return this.channel.call('getWorkspaceIdentifier', configPath).then(reviveWorkspaceIdentifier); } } + +registerSingleton(IWorkspacesService, WorkspacesService, true); diff --git a/src/vs/workbench/test/contrib/linkProtection.test.ts b/src/vs/workbench/test/contrib/linkProtection.test.ts index 77e4076ac9d6..0facf78c49bd 100644 --- a/src/vs/workbench/test/contrib/linkProtection.test.ts +++ b/src/vs/workbench/test/contrib/linkProtection.test.ts @@ -5,7 +5,7 @@ import * as assert from 'assert'; -import { isURLDomainTrusted } from 'vs/workbench/contrib/url/common/url.contribution'; +import { isURLDomainTrusted } from 'vs/workbench/contrib/url/common/trustedDomainsValidator'; import { URI } from 'vs/base/common/uri'; suite('Link protection domain matching', () => { @@ -32,11 +32,11 @@ suite('Link protection domain matching', () => { test('* star', () => { assert.ok(isURLDomainTrusted(URI.parse('https://a.x.org'), ['https://*.x.org'])); + assert.ok(isURLDomainTrusted(URI.parse('https://a.b.x.org'), ['https://*.x.org'])); assert.ok(isURLDomainTrusted(URI.parse('https://a.x.org'), ['https://a.x.*'])); assert.ok(isURLDomainTrusted(URI.parse('https://a.x.org'), ['https://a.*.org'])); assert.ok(isURLDomainTrusted(URI.parse('https://a.x.org'), ['https://*.*.org'])); - - assert.ok(!isURLDomainTrusted(URI.parse('https://a.b.c.org'), ['https://*.*.org'])); - assert.ok(isURLDomainTrusted(URI.parse('https://a.b.c.org'), ['https://*.*.*.org'])); + assert.ok(isURLDomainTrusted(URI.parse('https://a.b.x.org'), ['https://*.b.*.org'])); + assert.ok(isURLDomainTrusted(URI.parse('https://a.a.b.x.org'), ['https://*.b.*.org'])); }); }); diff --git a/src/vs/workbench/test/electron-browser/api/extHostMessagerService.test.ts b/src/vs/workbench/test/electron-browser/api/extHostMessagerService.test.ts index 24bb5ec4c75b..0f3ff60ce4e3 100644 --- a/src/vs/workbench/test/electron-browser/api/extHostMessagerService.test.ts +++ b/src/vs/workbench/test/electron-browser/api/extHostMessagerService.test.ts @@ -20,6 +20,10 @@ const emptyDialogService = new class implements IDialogService { confirm(): never { throw new Error('not implemented'); } + + about(): never { + throw new Error('not implemented'); + } }; const emptyCommandService: ICommandService = { diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index 8d87c7a3c4f0..e274eeeb96ad 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -84,8 +84,8 @@ import { WorkbenchEnvironmentService } from 'vs/workbench/services/environment/n import { VSBuffer, VSBufferReadable } from 'vs/base/common/buffer'; import { NodeTextFileService } from 'vs/workbench/services/textfile/node/textFileService'; import { Schemas } from 'vs/base/common/network'; -import { IProductService } from 'vs/platform/product/common/product'; -import product from 'vs/platform/product/node/product'; +import { IProductService } from 'vs/platform/product/common/productService'; +import product from 'vs/platform/product/common/product'; export function createFileInput(instantiationService: IInstantiationService, resource: URI): FileEditorInput { return instantiationService.createInstance(FileEditorInput, resource, undefined, undefined); @@ -402,6 +402,10 @@ export class TestDialogService implements IDialogService { public show(_severity: Severity, _message: string, _buttons: string[], _options?: IDialogOptions): Promise { return Promise.resolve({ choice: 0 }); } + + public about(): Promise { + return Promise.resolve(); + } } export class TestFileDialogService implements IFileDialogService { @@ -1490,10 +1494,6 @@ export class TestWindowsService implements IWindowsService { return Promise.resolve(this.windowCount); } - log(_severity: string, _args: string[]): Promise { - return Promise.resolve(); - } - showItemInFolder(_path: URI): Promise { return Promise.resolve(); } @@ -1553,10 +1553,6 @@ export class TestWindowsService implements IWindowsService { throw new Error('not implemented'); } - openAboutDialog(): Promise { - return Promise.resolve(); - } - resolveProxy(windowId: number, url: string): Promise { return Promise.resolve(undefined); } diff --git a/src/vs/workbench/workbench.common.main.ts b/src/vs/workbench/workbench.common.main.ts index 2086891b4def..519e95e36392 100644 --- a/src/vs/workbench/workbench.common.main.ts +++ b/src/vs/workbench/workbench.common.main.ts @@ -16,11 +16,15 @@ import 'vs/workbench/browser/workbench.contribution'; //#region --- workbench actions -import 'vs/workbench/browser/actions/layoutActions'; -import 'vs/workbench/browser/actions/windowActions'; import 'vs/workbench/browser/actions/developerActions'; +import 'vs/workbench/browser/actions/helpActions'; +import 'vs/workbench/browser/actions/layoutActions'; import 'vs/workbench/browser/actions/listCommands'; import 'vs/workbench/browser/actions/navigationActions'; +import 'vs/workbench/browser/actions/windowActions'; +import 'vs/workbench/browser/actions/workspaceActions'; +import 'vs/workbench/browser/actions/workspaceCommands'; + import 'vs/workbench/browser/parts/quickopen/quickOpenActions'; import 'vs/workbench/browser/parts/quickinput/quickInputActions'; @@ -76,6 +80,7 @@ import 'vs/workbench/services/label/common/labelService'; import 'vs/workbench/services/extensionManagement/common/extensionEnablementService'; import 'vs/workbench/services/notification/common/notificationService'; import 'vs/workbench/services/extensions/common/staticExtensions'; +import 'vs/workbench/services/userData/common/settingsMergeService'; import 'vs/workbench/services/workspace/browser/workspaceEditingService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; @@ -102,6 +107,9 @@ import { IDownloadService } from 'vs/platform/download/common/download'; import { DownloadService } from 'vs/platform/download/common/downloadService'; import { OpenerService } from 'vs/editor/browser/services/openerService'; import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { IUserDataSyncStoreService, IUserDataSyncService } from 'vs/platform/userDataSync/common/userDataSync'; +import { UserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; +import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService'; registerSingleton(IExtensionGalleryService, ExtensionGalleryService, true); registerSingleton(IContextViewService, ContextViewService, true); @@ -115,6 +123,8 @@ registerSingleton(ITextResourceConfigurationService, TextResourceConfigurationSe registerSingleton(IMenuService, MenuService, true); registerSingleton(IDownloadService, DownloadService, true); registerSingleton(IOpenerService, OpenerService, true); +registerSingleton(IUserDataSyncStoreService, UserDataSyncStoreService); +registerSingleton(IUserDataSyncService, UserDataSyncService); //#endregion @@ -159,6 +169,7 @@ import 'vs/workbench/contrib/scm/browser/scmViewlet'; import 'vs/workbench/contrib/debug/browser/debug.contribution'; import 'vs/workbench/contrib/debug/browser/debugQuickOpen'; import 'vs/workbench/contrib/debug/browser/debugEditorContribution'; +import 'vs/workbench/contrib/debug/browser/breakpointEditorContribution'; import 'vs/workbench/contrib/debug/browser/repl'; import 'vs/workbench/contrib/debug/browser/debugViewlet'; */ @@ -228,8 +239,15 @@ import 'vs/workbench/contrib/update/browser/update.contribution'; // Watermark import 'vs/workbench/contrib/watermark/browser/watermark'; +// Surveys +import 'vs/workbench/contrib/surveys/browser/nps.contribution'; +import 'vs/workbench/contrib/surveys/browser/languageSurveys.contribution'; + // Welcome import 'vs/workbench/contrib/welcome/overlay/browser/welcomeOverlay'; +import 'vs/workbench/contrib/welcome/page/browser/welcomePage.contribution'; +import 'vs/workbench/contrib/welcome/walkThrough/browser/walkThrough.contribution'; +import 'vs/workbench/contrib/welcome/gettingStarted/browser/gettingStarted.contribution'; // Call Hierarchy import 'vs/workbench/contrib/callHierarchy/browser/callHierarchy.contribution'; @@ -243,4 +261,7 @@ import 'vs/workbench/contrib/experiments/browser/experiments.contribution'; // Send a Smile import 'vs/workbench/contrib/feedback/browser/feedback.contribution'; +// User Data +import 'vs/workbench/contrib/userData/browser/userData.contribution'; + //#endregion diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index 8d3373ec9ef8..ca68a19bd08a 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -49,6 +49,7 @@ import 'vs/workbench/services/remote/node/tunnelService'; import 'vs/workbench/services/backup/node/backupFileService'; import 'vs/workbench/services/credentials/node/credentialsService'; import 'vs/workbench/services/url/electron-browser/urlService'; +import 'vs/workbench/services/workspace/electron-browser/workspacesService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; @@ -66,10 +67,10 @@ import { IUpdateService } from 'vs/platform/update/common/update'; import { UpdateService } from 'vs/platform/update/electron-browser/updateService'; import { IIssueService } from 'vs/platform/issue/node/issue'; import { IssueService } from 'vs/platform/issue/electron-browser/issueService'; -import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; -import { WorkspacesService } from 'vs/platform/workspaces/electron-browser/workspacesService'; import { IMenubarService } from 'vs/platform/menubar/node/menubar'; import { MenubarService } from 'vs/platform/menubar/electron-browser/menubarService'; +import { IElectronService } from 'vs/platform/electron/node/electron'; +import { ElectronService } from 'vs/platform/electron/electron-browser/electronService'; registerSingleton(IClipboardService, ClipboardService, true); registerSingleton(IRequestService, RequestService, true); @@ -79,8 +80,8 @@ registerSingleton(ISharedProcessService, SharedProcessService, true); registerSingleton(IWindowsService, WindowsService); registerSingleton(IUpdateService, UpdateService); registerSingleton(IIssueService, IssueService); -registerSingleton(IWorkspacesService, WorkspacesService); registerSingleton(IMenubarService, MenubarService); +registerSingleton(IElectronService, ElectronService, true); //#endregion @@ -230,10 +231,6 @@ import 'vs/workbench/contrib/externalTerminal/node/externalTerminalService'; // Update import 'vs/workbench/contrib/update/electron-browser/update.contribution'; -// Surveys -import 'vs/workbench/contrib/surveys/electron-browser/nps.contribution'; -import 'vs/workbench/contrib/surveys/electron-browser/languageSurveys.contribution'; - // Performance import 'vs/workbench/contrib/performance/electron-browser/performance.contribution'; @@ -243,11 +240,6 @@ import 'vs/workbench/contrib/cli/node/cli.contribution'; // Themes Support import 'vs/workbench/contrib/themes/test/electron-browser/themes.test.contribution'; -// Welcome -import 'vs/workbench/contrib/welcome/walkThrough/browser/walkThrough.contribution'; -import 'vs/workbench/contrib/welcome/gettingStarted/electron-browser/gettingStarted.contribution'; -import 'vs/workbench/contrib/welcome/page/browser/welcomePage.contribution'; - // Issues import 'vs/workbench/contrib/issue/electron-browser/issue.contribution'; diff --git a/src/vs/workbench/workbench.web.api.ts b/src/vs/workbench/workbench.web.api.ts index af9b82b6f7ae..0c7e797d429b 100644 --- a/src/vs/workbench/workbench.web.api.ts +++ b/src/vs/workbench/workbench.web.api.ts @@ -6,15 +6,17 @@ import 'vs/workbench/workbench.web.main'; import { main } from 'vs/workbench/browser/web.main'; import { UriComponents, URI } from 'vs/base/common/uri'; -import { IFileSystemProvider } from 'vs/platform/files/common/files'; -import { IWebSocketFactory } from 'vs/platform/remote/browser/browserSocketFactory'; +import { IFileSystemProvider, FileSystemProviderCapabilities, IFileChange, FileChangeType } from 'vs/platform/files/common/files'; +import { IWebSocketFactory, IWebSocket } from 'vs/platform/remote/browser/browserSocketFactory'; import { ICredentialsProvider } from 'vs/workbench/services/credentials/browser/credentialsService'; import { IExtensionManifest } from 'vs/platform/extensions/common/extensions'; import { IURLCallbackProvider } from 'vs/workbench/services/url/browser/urlService'; import { LogLevel } from 'vs/platform/log/common/log'; -import { IUpdateProvider } from 'vs/workbench/services/update/browser/updateService'; +import { IUpdateProvider, IUpdate } from 'vs/workbench/services/update/browser/updateService'; +import { Event, Emitter } from 'vs/base/common/event'; +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; -export interface IWorkbenchConstructionOptions { +interface IWorkbenchConstructionOptions { /** * Experimental: the remote authority is the IP:PORT from where the workbench is served @@ -36,12 +38,12 @@ export interface IWorkbenchConstructionOptions { /** * Experimental: An optional folder that is set as workspace context for the workbench. */ - folderUri?: UriComponents; + folderUri?: URI; /** * Experimental: An optional workspace that is set as workspace context for the workbench. */ - workspaceUri?: UriComponents; + workspaceUri?: URI; /** * Experimental: The userDataProvider is used to handle user specific application @@ -57,7 +59,7 @@ export interface IWorkbenchConstructionOptions { /** * A provider for resource URIs. */ - resourceUriProvider?: (uri: URI) => UriComponents; + resourceUriProvider?: (uri: URI) => URI; /** * Experimental: Whether to enable the smoke test driver. @@ -72,7 +74,7 @@ export interface IWorkbenchConstructionOptions { /** * Experimental: Add static extensions that cannot be uninstalled but only be disabled. */ - staticExtensions?: { packageJSON: IExtensionManifest, extensionLocation: UriComponents }[]; + staticExtensions?: { packageJSON: IExtensionManifest, extensionLocation: URI }[]; /** * Experimental: Support for URL callbacks. @@ -88,6 +90,11 @@ export interface IWorkbenchConstructionOptions { * Experimental: Support for update reporting. */ updateProvider?: IUpdateProvider; + + /** + * Experimental: Resolves an external uri before it is opened. + */ + readonly resolveExternalUri?: (uri: URI) => Promise; } /** @@ -101,5 +108,42 @@ function create(domElement: HTMLElement, options: IWorkbenchConstructionOptions) } export { - create + + // Factory + create, + IWorkbenchConstructionOptions, + + // Basic Types + URI, + UriComponents, + Event, + Emitter, + IDisposable, + Disposable, + + // FileSystem + IFileSystemProvider, + FileSystemProviderCapabilities, + IFileChange, + FileChangeType, + + // WebSockets + IWebSocketFactory, + IWebSocket, + + // Credentials + ICredentialsProvider, + + // Static Extensions + IExtensionManifest, + + // Callbacks + IURLCallbackProvider, + + // LogLevel + LogLevel, + + // Updates + IUpdateProvider, + IUpdate }; diff --git a/src/vs/workbench/workbench.web.main.ts b/src/vs/workbench/workbench.web.main.ts index 52ab83b14fe8..94ea4b4db8e9 100644 --- a/src/vs/workbench/workbench.web.main.ts +++ b/src/vs/workbench/workbench.web.main.ts @@ -40,6 +40,8 @@ import 'vs/workbench/services/credentials/browser/credentialsService'; import 'vs/workbench/services/url/browser/urlService'; import 'vs/workbench/services/update/browser/updateService'; import 'vs/workbench/contrib/stats/browser/workspaceStatsService'; +import 'vs/workbench/services/workspace/browser/workspacesService'; +import 'vs/workbench/services/dialogs/browser/dialogService'; import 'vs/workbench/browser/web.simpleservices'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; @@ -52,8 +54,6 @@ import { BrowserAccessibilityService } from 'vs/platform/accessibility/common/ac import { IExtensionManagementService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { BrowserLifecycleService } from 'vs/platform/lifecycle/browser/lifecycleService'; import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; -import { IDialogService } from 'vs/platform/dialogs/common/dialogs'; -import { DialogService } from 'vs/platform/dialogs/browser/dialogService'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { ContextMenuService } from 'vs/platform/contextview/browser/contextMenuService'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; @@ -65,7 +65,6 @@ import { NoOpTunnelService } from 'vs/platform/remote/common/tunnelService'; registerSingleton(IRequestService, RequestService, true); registerSingleton(IExtensionManagementService, ExtensionManagementService); registerSingleton(IBackupFileService, BackupFileService); -registerSingleton(IDialogService, DialogService, true); registerSingleton(IClipboardService, BrowserClipboardService, true); registerSingleton(IAccessibilityService, BrowserAccessibilityService, true); registerSingleton(ILifecycleService, BrowserLifecycleService); diff --git a/test/automation/src/keybindings.ts b/test/automation/src/keybindings.ts index b5f9ff684aa0..55ac104f089f 100644 --- a/test/automation/src/keybindings.ts +++ b/test/automation/src/keybindings.ts @@ -24,7 +24,7 @@ export class KeybindingsEditor { await this.code.waitAndClick('.keybindings-list-container .monaco-list-row.keybinding-item'); await this.code.waitForElement('.keybindings-list-container .monaco-list-row.keybinding-item.focused.selected'); - await this.code.waitAndClick('.keybindings-list-container .monaco-list-row.keybinding-item .action-item .icon.add'); + await this.code.waitAndClick('.keybindings-list-container .monaco-list-row.keybinding-item .action-item .codicon.add'); await this.code.waitForActiveElement('.defineKeybindingWidget .monaco-inputbox input'); await this.code.dispatchKeybinding(keybinding); diff --git a/test/electron/index.js b/test/electron/index.js index 9e5e46c27869..c26a8fdc65cb 100644 --- a/test/electron/index.js +++ b/test/electron/index.js @@ -9,7 +9,7 @@ const { join } = require('path'); const path = require('path'); const mocha = require('mocha'); const events = require('events'); -const MochaJUnitReporter = require('mocha-junit-reporter'); +// const MochaJUnitReporter = require('mocha-junit-reporter'); const url = require('url'); const defaultReporterName = process.platform === 'win32' ? 'list' : 'spec'; @@ -133,12 +133,13 @@ app.on('ready', () => { if (argv.tfs) { new mocha.reporters.Spec(runner); - new MochaJUnitReporter(runner, { - reporterOptions: { - testsuitesTitle: `${argv.tfs} ${process.platform}`, - mochaFile: process.env.BUILD_ARTIFACTSTAGINGDIRECTORY ? path.join(process.env.BUILD_ARTIFACTSTAGINGDIRECTORY, `test-results/${process.platform}-${argv.tfs.toLowerCase().replace(/[^\w]/g, '-')}-results.xml`) : undefined - } - }); + // TODO@deepak the mocha Junit reporter seems to cause a hang when running with Electron 6 inside docker container + // new MochaJUnitReporter(runner, { + // reporterOptions: { + // testsuitesTitle: `${argv.tfs} ${process.platform}`, + // mochaFile: process.env.BUILD_ARTIFACTSTAGINGDIRECTORY ? path.join(process.env.BUILD_ARTIFACTSTAGINGDIRECTORY, `test-results/${process.platform}-${argv.tfs.toLowerCase().replace(/[^\w]/g, '-')}-results.xml`) : undefined + // } + // }); } else { const reporterPath = path.join(path.dirname(require.resolve('mocha')), 'lib', 'reporters', argv.reporter); let Reporter; diff --git a/yarn.lock b/yarn.lock index 434c1b0ba53d..e1f51cfd9d5a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -677,6 +677,14 @@ anymatch@^2.0.0: micromatch "^3.1.4" normalize-path "^2.1.1" +anymatch@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.0.tgz#e609350e50a9313b472789b2f14ef35808ee14d6" + integrity sha512-Ozz7l4ixzI7Oxj2+cw+p0tVUt27BpaJ+1+q1TCeANWxHpvyn2+Un+YamBdfKu0uh8xLodGhoa1v7595NhKDAuA== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + append-buffer@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/append-buffer/-/append-buffer-1.0.2.tgz#d8220cf466081525efea50614f3de6514dfa58f1" @@ -1066,6 +1074,11 @@ binary-extensions@^1.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.10.0.tgz#9aeb9a6c5e88638aad171e167f5900abe24835d0" integrity sha1-muuabF6IY4qtFx4Wf1kAq+JINdA= +binary-extensions@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.0.0.tgz#23c0df14f6a88077f5f986c0d167ec03c3d5537c" + integrity sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow== + binary-search-bounds@2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/binary-search-bounds/-/binary-search-bounds-2.0.3.tgz#5ff8616d6dd2ca5388bc85b2d6266e2b9da502dc" @@ -1196,6 +1209,13 @@ braces@^2.3.0, braces@^2.3.1, braces@^2.3.2: split-string "^3.0.2" to-regex "^3.0.1" +braces@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + brorand@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" @@ -1518,6 +1538,21 @@ cheerio@^1.0.0-rc.1: lodash "^4.15.0" parse5 "^3.0.1" +chokidar@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.1.0.tgz#ff23d077682a90eadd209bfa76eb10ed6d359668" + integrity sha512-6vZfo+7W0EOlbSo0nhVKMz4yyssrwiPbBZ8wj1lq8/+l4ZhGZ2U4Md7PspvmijXp1a26D3B7AHEBmIB7aVtaOQ== + dependencies: + anymatch "^3.1.0" + braces "^3.0.2" + glob-parent "^5.0.0" + is-binary-path "^2.1.0" + is-glob "^4.0.1" + normalize-path "^3.0.0" + readdirp "^3.1.1" + optionalDependencies: + fsevents "^2.0.6" + chokidar@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.0.tgz#5fcb70d0b28ebe0867eb0f09d5f6a08f29a1efa0" @@ -3081,6 +3116,13 @@ fill-range@^4.0.0: repeat-string "^1.6.1" to-regex-range "^2.1.0" +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + finalhandler@1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.0.tgz#ce0b6855b45853e791b2fcc680046d88253dd7f5" @@ -3353,6 +3395,11 @@ fsevents@^1.2.7: nan "^2.9.2" node-pre-gyp "^0.10.0" +fsevents@^2.0.6: + version "2.0.7" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.0.7.tgz#382c9b443c6cbac4c57187cdda23aa3bf1ccfc2a" + integrity sha512-a7YT0SV3RB+DjYcppwVDLtn13UQnmg0SWZS7ezZD0UjnLwXmy8Zm21GMVGLaFGimIqcvyMQaOJBrop8MyOp1kQ== + fstream@^1.0.2: version "1.0.11" resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171" @@ -3468,6 +3515,13 @@ glob-parent@^3.0.0, glob-parent@^3.1.0: is-glob "^3.1.0" path-dirname "^1.0.0" +glob-parent@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.0.0.tgz#1dc99f0f39b006d3e92c2c284068382f0c20e954" + integrity sha512-Z2RwiujPRGluePM6j699ktJYxmPpJKCfpGA13jz2hmFZC7gKetzrWvg5KN3+OsIFmydGyZ1AVwERCq1w/ZZwRg== + dependencies: + is-glob "^4.0.1" + glob-stream@^5.3.2: version "5.3.5" resolved "https://registry.yarnpkg.com/glob-stream/-/glob-stream-5.3.5.tgz#a55665a9a8ccdc41915a87c701e32d4e016fad22" @@ -3855,10 +3909,10 @@ gulp-symdest@^1.1.1: queue "^3.1.0" vinyl-fs "^2.4.3" -gulp-tsb@4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/gulp-tsb/-/gulp-tsb-4.0.2.tgz#8717a18f1ce032147e010028f0a59863bd552b96" - integrity sha512-xF88h0vFH8JkunSnmVrYfrR3LGTMAY+KTkHHF/S9BAOCsdaC83/hv4EmpcLxev7B+2yd3+xcitlsDFMBSo/gSw== +gulp-tsb@4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/gulp-tsb/-/gulp-tsb-4.0.4.tgz#c0486534e2f86cd4a11c2393593d9eae3a426ac4" + integrity sha512-BIIls2PpT3+JR1Svvd0SLjkBj2AmlHIRnum/ajf1XQUgbVA9wwsZuTjpVXU/K06KbTYfGsSSYAPRK2dICNAMZQ== dependencies: ansi-colors "^1.0.1" fancy-log "^1.3.2" @@ -4404,6 +4458,13 @@ is-binary-path@^1.0.0: dependencies: binary-extensions "^1.0.0" +is-binary-path@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + is-buffer@^1.0.2: version "1.1.4" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.4.tgz#cfc86ccd5dc5a52fa80489111c6920c457e2d98b" @@ -4520,6 +4581,13 @@ is-glob@^4.0.0: dependencies: is-extglob "^2.1.1" +is-glob@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" + integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== + dependencies: + is-extglob "^2.1.1" + is-my-ip-valid@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz#7b351b8e8edd4d3995d4d066680e664d94696824" @@ -4560,6 +4628,11 @@ is-number@^4.0.0: resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff" integrity sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ== +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + is-path-cwd@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-path-cwd/-/is-path-cwd-1.0.0.tgz#d225ec23132e89edd38fda767472e62e65f1106d" @@ -7292,6 +7365,13 @@ readdirp@^2.2.1: micromatch "^3.1.10" readable-stream "^2.0.2" +readdirp@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.1.2.tgz#fa85d2d14d4289920e4671dead96431add2ee78a" + integrity sha512-8rhl0xs2cxfVsqzreYCvs8EwBfn/DhVdqtoLmw19uI3SC5avYX9teCurlErfpPXGmYtMHReGaP2RsLnFvz/lnw== + dependencies: + picomatch "^2.0.4" + rechoir@^0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384" @@ -8645,6 +8725,13 @@ to-regex-range@^2.1.0: is-number "^3.0.0" repeat-string "^1.6.1" +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + to-regex@^3.0.1, to-regex@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/to-regex/-/to-regex-3.0.2.tgz#13cfdd9b336552f30b51f33a8ae1b42a7a7599ce" @@ -8994,11 +9081,6 @@ upath@^1.0.5, upath@^1.1.0: resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.0.tgz#35256597e46a581db4793d0ce47fa9aebfc9fabd" integrity sha512-bzpH/oBhoS/QI/YtbkqCg6VEiPYjSZtrHQM6/QnJS6OL9pKUFLqb3aFh4Scvwm45+7iAgiMkLhSbaZxUqmrprw== -upath@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/upath/-/upath-1.1.2.tgz#3db658600edaeeccbe6db5e684d67ee8c2acd068" - integrity sha512-kXpym8nmDmlCBr7nKdIx8P2jNBa+pBpIUFRnKJ4dr8htyYGJFokkr2ZvERRtUN+9SY+JqXouNgUPtv6JQva/2Q== - uri-js@^4.2.1, uri-js@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.2.2.tgz#94c540e1ff772956e2299507c010aea6c8838eb0" @@ -9266,45 +9348,11 @@ vsce@1.48.0: yauzl "^2.3.1" yazl "^2.2.2" -vscode-anymatch@3.0.3: - version "3.0.3" - resolved "https://registry.yarnpkg.com/vscode-anymatch/-/vscode-anymatch-3.0.3.tgz#5a79101e6df7e659a1f070367bc42f190eb4ae76" - integrity sha512-qQgfbzJJ5nNShh4jjC3BBekY4d8emcxHFgnqcXwsB/PUKvJPCg7AZYXM7hqS7EDnKrX9tsIFwFMihZ7yut92Qg== - dependencies: - normalize-path "^3.0.0" - picomatch "^2.0.4" - -vscode-chokidar@2.1.7: - version "2.1.7" - resolved "https://registry.yarnpkg.com/vscode-chokidar/-/vscode-chokidar-2.1.7.tgz#c5b31eb87402f4779bb4170915245bdcb6f7854b" - integrity sha512-uSNEQetPjAlgIAHmcF9E6M+KCw0f842rsEnJ64aamUAV6TO7gkXNCvLSzb4MuLsPU7ZQyCa++DrLQFjvciK5dg== - dependencies: - async-each "^1.0.1" - braces "^2.3.2" - glob-parent "^3.1.0" - inherits "^2.0.3" - is-binary-path "^1.0.0" - is-glob "^4.0.0" - normalize-path "^3.0.0" - path-is-absolute "^1.0.0" - readdirp "^2.2.1" - upath "^1.1.1" - vscode-anymatch "3.0.3" - optionalDependencies: - vscode-fsevents "1.2.12" - vscode-debugprotocol@1.36.0: version "1.36.0" resolved "https://registry.yarnpkg.com/vscode-debugprotocol/-/vscode-debugprotocol-1.36.0.tgz#88e6246045480a9cc643e819b597396eaa9d0f4b" integrity sha512-F0MfcUkF88TfNf4iQbcmC+K9rA+zsrQpEz1XpTKidy5sMq8sYsJGUadYDGmmktfjRX+S/ebjHgM+YV/2qm6lVQ== -vscode-fsevents@1.2.12: - version "1.2.12" - resolved "https://registry.yarnpkg.com/vscode-fsevents/-/vscode-fsevents-1.2.12.tgz#01a71a01f90ee95ca822c34427aba437a17c03a7" - integrity sha512-bH/jRdDpSesGpqiVLjp6gHLSKUOh7oNvppzZ17JIrdbRYCcDmV7dIWR5gQc27DFy0RD9JDT+t+ixMid94MkM1A== - dependencies: - nan "^2.14.0" - vscode-minimist@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/vscode-minimist/-/vscode-minimist-1.2.1.tgz#e63d3f4a9bf3680dcb8f9304eed612323fd6926a"