From bca7c8e6bda229d2d71415d1b9e941dcce387db0 Mon Sep 17 00:00:00 2001 From: Anthony Dresser Date: Fri, 27 Sep 2019 23:30:36 -0700 Subject: [PATCH] Merge from vscode f2d41726ba5a0e8abfe61b2c743022b1b6372010 (#7415) * Merge from vscode f2d41726ba5a0e8abfe61b2c743022b1b6372010 * add missing files --- build/lib/i18n.resources.json | 2 +- .../image-preview/media/loading-dark.svg | 31 ++ extensions/image-preview/media/loading-hc.svg | 31 ++ extensions/image-preview/media/loading.svg | 31 ++ extensions/image-preview/media/main.css | 24 +- extensions/image-preview/media/main.js | 24 +- extensions/image-preview/src/preview.ts | 1 + .../notebook/browser/notebook.contribution.ts | 2 +- src/vs/base/browser/dom.ts | 6 + src/vs/base/browser/ui/menu/menubar.ts | 4 +- .../ipc/node/{ipcChannelCreator.ts => ipc.ts} | 21 +- src/vs/base/parts/ipc/test/node/ipc.test.ts | 131 +++++++- src/vs/code/browser/workbench/workbench.ts | 46 ++- .../issue/issueReporterMain.ts | 2 +- .../sharedProcess/sharedProcessMain.ts | 16 +- src/vs/code/electron-main/app.ts | 19 +- src/vs/code/electron-main/main.ts | 18 +- src/vs/code/electron-main/windows.ts | 3 +- src/vs/editor/browser/editorExtensions.ts | 2 +- src/vs/platform/auth/common/auth.ts | 29 ++ src/vs/platform/auth/common/authTokenIpc.ts | 31 ++ .../platform/auth/common/authTokenService.ts | 76 +++++ .../credentials/common/credentials.ts | 19 ++ .../credentials/node/credentialsService.ts | 40 +++ .../electron-main/electronMainService.ts | 38 +-- src/vs/platform/electron/node/electron.ts | 14 +- .../instantiation/common/instantiation.ts | 20 +- .../launch/electron-main/launchMainService.ts | 60 ---- .../electron-browser/localizationsService.ts | 26 -- .../localizations/node/localizationsIpc.ts | 33 -- src/vs/platform/request/common/request.ts | 2 +- src/vs/platform/url/common/urlIpc.ts | 41 +-- .../userDataSync/common/extensionsSync.ts | 5 +- .../userDataSync/common/settingsSync.ts | 2 + .../userDataSync/common/userDataSync.ts | 5 - .../common/userDataSyncService.ts | 53 +++- .../common/userDataSyncStoreService.ts | 56 ++-- src/vs/platform/windows/common/windows.ts | 5 +- src/vs/platform/windows/node/window.ts | 14 + src/vs/platform/workspace/common/workspace.ts | 32 +- .../platform/workspaces/common/workspaces.ts | 202 +++++++++++-- .../workspaces/common/workspacesHistory.ts | 41 --- .../common/workspacesHistoryStorage.ts | 129 -------- .../workspacesHistoryMainService.ts | 6 +- .../workspaces/electron-main/workspacesIpc.ts | 58 ---- .../electron-main/workspacesMainService.ts | 12 +- .../electron-main/workspacesService.ts | 75 +++++ .../workspacesHistoryStorage.test.ts | 6 +- .../workbench/api/browser/mainThreadKeytar.ts | 2 +- .../api/browser/mainThreadWorkspace.ts | 2 +- src/vs/workbench/api/common/apiCommands.ts | 15 +- src/vs/workbench/api/node/extHostCLIServer.ts | 5 +- .../browser/actions/windowActions.ts | 17 +- .../browser/actions/workspaceActions.ts | 4 +- .../browser/actions/workspaceCommands.ts | 2 +- src/vs/workbench/browser/dnd.ts | 10 +- .../browser/parts/editor/editorActions.ts | 6 +- .../browser/parts/titlebar/menubarControl.ts | 15 +- src/vs/workbench/browser/style.ts | 19 +- src/vs/workbench/browser/web.main.ts | 23 +- .../files/browser/fileActions.contribution.ts | 7 +- .../contrib/files/browser/fileCommands.ts | 2 +- .../files/browser/views/explorerViewer.ts | 2 +- .../files/browser/views/openEditorsView.ts | 5 +- .../fileActions.contribution.ts | 8 +- .../contrib/logs/common/logs.contribution.ts | 2 +- .../preferences/browser/keybindingsEditor.ts | 8 +- .../preferences/browser/media/add-dark.svg | 3 - .../preferences/browser/media/add-light.svg | 3 - .../preferences/browser/media/check-dark.svg | 3 - .../preferences/browser/media/check-light.svg | 3 - .../preferences/browser/media/clear-dark.svg | 7 - .../preferences/browser/media/clear-light.svg | 7 - .../browser/media/configure-dark.svg | 6 - .../browser/media/configure-light.svg | 6 - .../preferences/browser/media/edit-dark.svg | 4 - .../preferences/browser/media/edit-light.svg | 4 - .../browser/media/keybindingsEditor.css | 49 +-- .../preferences/browser/media/preferences.css | 10 +- .../browser/media/record-keys-dark.svg | 3 - .../browser/media/record-keys-light.svg | 3 - .../preferences/browser/media/remove-dark.svg | 3 - .../browser/media/remove-light.svg | 3 - .../browser/media/settingsEditor2.css | 33 +- .../browser/media/settingsWidgets.css | 19 +- .../browser/media/sort-precedence-dark.svg | 3 - .../browser/media/sort-precedence-light.svg | 3 - .../preferences/browser/settingsTree.ts | 29 +- .../preferences/browser/settingsWidgets.ts | 4 +- .../tasks/browser/abstractTaskService.ts | 132 +++++--- .../browser/userDataSync.contribution.ts | 202 +------------ .../userDataSync/browser/userDataSync.ts | 283 ++++++++++++++++++ .../welcome/page/browser/welcomePage.ts | 7 +- .../actions/workspaceActions.ts | 2 +- src/vs/workbench/electron-browser/window.ts | 11 +- .../electron-browser/authTokenService.ts | 58 ++++ .../clipboard/browser/clipboardService.ts | 0 .../electron-browser/clipboardService.ts | 11 +- .../credentials/browser/credentialsService.ts | 2 +- .../credentials/node/credentialsService.ts | 2 +- .../electron-browser/electronService.ts | 2 +- .../services/history/browser/history.ts | 6 +- .../host/browser/browserHostService.ts | 70 +++-- .../issue/electron-browser/issueService.ts | 5 +- .../electron-browser/localizationsService.ts | 22 ++ .../electron-browser/menubarService.ts | 5 +- .../textfile/common/textFileEditorModel.ts | 4 +- .../services/update/browser/updateService.ts | 4 +- .../update/electron-browser/updateService.ts | 5 +- .../url/electron-browser/urlService.ts | 5 +- .../workspace/browser/workspacesService.ts | 31 -- .../common/workspacesHistoryService.ts | 25 -- .../workspacesHistoryService.ts | 39 --- .../electron-browser/workspacesService.ts | 48 --- .../abstractWorkspaceEditingService.ts | 2 +- .../browser/workspaceEditingService.ts | 4 +- .../browser/workspacesService.ts} | 50 +++- .../common/workspaceEditing.ts | 0 .../workspaceEditingService.ts | 8 +- .../electron-browser/workspacesService.ts | 24 ++ src/vs/workbench/workbench.desktop.main.ts | 32 +- src/vs/workbench/workbench.web.api.ts | 15 +- src/vs/workbench/workbench.web.main.ts | 12 +- 123 files changed, 1704 insertions(+), 1330 deletions(-) create mode 100644 extensions/image-preview/media/loading-dark.svg create mode 100644 extensions/image-preview/media/loading-hc.svg create mode 100644 extensions/image-preview/media/loading.svg rename src/vs/base/parts/ipc/node/{ipcChannelCreator.ts => ipc.ts} (83%) create mode 100644 src/vs/platform/auth/common/auth.ts create mode 100644 src/vs/platform/auth/common/authTokenIpc.ts create mode 100644 src/vs/platform/auth/common/authTokenService.ts create mode 100644 src/vs/platform/credentials/common/credentials.ts create mode 100644 src/vs/platform/credentials/node/credentialsService.ts delete mode 100644 src/vs/platform/localizations/electron-browser/localizationsService.ts delete mode 100644 src/vs/platform/localizations/node/localizationsIpc.ts create mode 100644 src/vs/platform/windows/node/window.ts delete mode 100644 src/vs/platform/workspaces/common/workspacesHistory.ts delete mode 100644 src/vs/platform/workspaces/common/workspacesHistoryStorage.ts delete mode 100644 src/vs/platform/workspaces/electron-main/workspacesIpc.ts create mode 100644 src/vs/platform/workspaces/electron-main/workspacesService.ts delete mode 100644 src/vs/workbench/contrib/preferences/browser/media/add-dark.svg delete mode 100644 src/vs/workbench/contrib/preferences/browser/media/add-light.svg delete mode 100644 src/vs/workbench/contrib/preferences/browser/media/check-dark.svg delete mode 100644 src/vs/workbench/contrib/preferences/browser/media/check-light.svg delete mode 100644 src/vs/workbench/contrib/preferences/browser/media/clear-dark.svg delete mode 100644 src/vs/workbench/contrib/preferences/browser/media/clear-light.svg delete mode 100644 src/vs/workbench/contrib/preferences/browser/media/configure-dark.svg delete mode 100644 src/vs/workbench/contrib/preferences/browser/media/configure-light.svg delete mode 100644 src/vs/workbench/contrib/preferences/browser/media/edit-dark.svg delete mode 100644 src/vs/workbench/contrib/preferences/browser/media/edit-light.svg delete mode 100644 src/vs/workbench/contrib/preferences/browser/media/record-keys-dark.svg delete mode 100644 src/vs/workbench/contrib/preferences/browser/media/record-keys-light.svg delete mode 100644 src/vs/workbench/contrib/preferences/browser/media/remove-dark.svg delete mode 100644 src/vs/workbench/contrib/preferences/browser/media/remove-light.svg delete mode 100644 src/vs/workbench/contrib/preferences/browser/media/sort-precedence-dark.svg delete mode 100644 src/vs/workbench/contrib/preferences/browser/media/sort-precedence-light.svg create mode 100644 src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts create mode 100644 src/vs/workbench/services/authToken/electron-browser/authTokenService.ts rename src/vs/{platform => workbench/services}/clipboard/browser/clipboardService.ts (100%) rename src/vs/{platform => workbench/services}/clipboard/electron-browser/clipboardService.ts (78%) rename src/vs/{platform => workbench/services}/issue/electron-browser/issueService.ts (78%) create mode 100644 src/vs/workbench/services/localizations/electron-browser/localizationsService.ts rename src/vs/{platform => workbench/services}/menubar/electron-browser/menubarService.ts (78%) rename src/vs/{platform => workbench/services}/update/electron-browser/updateService.ts (90%) delete mode 100644 src/vs/workbench/services/workspace/browser/workspacesService.ts delete mode 100644 src/vs/workbench/services/workspace/common/workspacesHistoryService.ts delete mode 100644 src/vs/workbench/services/workspace/electron-browser/workspacesHistoryService.ts delete mode 100644 src/vs/workbench/services/workspace/electron-browser/workspacesService.ts rename src/vs/workbench/services/{workspace => workspaces}/browser/abstractWorkspaceEditingService.ts (99%) rename src/vs/workbench/services/{workspace => workspaces}/browser/workspaceEditingService.ts (97%) rename src/vs/workbench/services/{workspace/browser/workspacesHistoryService.ts => workspaces/browser/workspacesService.ts} (68%) rename src/vs/workbench/services/{workspace => workspaces}/common/workspaceEditing.ts (100%) rename src/vs/workbench/services/{workspace => workspaces}/electron-browser/workspaceEditingService.ts (94%) create mode 100644 src/vs/workbench/services/workspaces/electron-browser/workspacesService.ts diff --git a/build/lib/i18n.resources.json b/build/lib/i18n.resources.json index 8f781cef8d..3f8c3cec51 100644 --- a/build/lib/i18n.resources.json +++ b/build/lib/i18n.resources.json @@ -271,7 +271,7 @@ "project": "vscode-workbench" }, { - "name": "vs/workbench/services/workspace", + "name": "vs/workbench/services/workspaces", "project": "vscode-workbench" }, { diff --git a/extensions/image-preview/media/loading-dark.svg b/extensions/image-preview/media/loading-dark.svg new file mode 100644 index 0000000000..7dc1ebd8cf --- /dev/null +++ b/extensions/image-preview/media/loading-dark.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + diff --git a/extensions/image-preview/media/loading-hc.svg b/extensions/image-preview/media/loading-hc.svg new file mode 100644 index 0000000000..c3633c0dda --- /dev/null +++ b/extensions/image-preview/media/loading-hc.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + diff --git a/extensions/image-preview/media/loading.svg b/extensions/image-preview/media/loading.svg new file mode 100644 index 0000000000..e762f06d5e --- /dev/null +++ b/extensions/image-preview/media/loading.svg @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + diff --git a/extensions/image-preview/media/main.css b/extensions/image-preview/media/main.css index a77d3f60cf..f2818ce866 100644 --- a/extensions/image-preview/media/main.css +++ b/extensions/image-preview/media/main.css @@ -62,11 +62,11 @@ body img { margin: auto; } -.container.zoom-in { +.container.ready.zoom-in { cursor: zoom-in; } -.container.zoom-out { +.container.ready.zoom-out { cursor: zoom-out; } @@ -76,3 +76,23 @@ body img { text-decoration: underline; margin-left: 5px; } + +.loading { + position: fixed; + width: 30px; + height: 30px; + left: 50%; + top: 50%; + margin-top: -15px; + margin-left: -15px; + background-image: url('./loading.svg'); + background-size: cover; +} + +.vscode-dark .loading { + background-image: url('./loading-dark.svg'); +} + +.vscode-high-contrast .loading { + background-image: url('./loading-hc.svg'); +} diff --git a/extensions/image-preview/media/main.js b/extensions/image-preview/media/main.js index 70ed69aad8..0f1bd15114 100644 --- a/extensions/image-preview/media/main.js +++ b/extensions/image-preview/media/main.js @@ -69,13 +69,14 @@ let scale = initialState.scale; let ctrlPressed = false; let altPressed = false; + let hasLoadedImage = false; // Elements const container = /** @type {HTMLElement} */(document.querySelector('body')); const image = document.createElement('img'); function updateScale(newScale) { - if (!image || !image.parentElement) { + if (!image || !hasLoadedImage || !image.parentElement) { return; } @@ -125,7 +126,7 @@ } function firstZoom() { - if (!image) { + if (!image || !hasLoadedImage) { return; } @@ -134,7 +135,7 @@ } window.addEventListener('keydown', (/** @type {KeyboardEvent} */ e) => { - if (!image) { + if (!image || !hasLoadedImage) { return; } ctrlPressed = e.ctrlKey; @@ -147,7 +148,7 @@ }); window.addEventListener('keyup', (/** @type {KeyboardEvent} */ e) => { - if (!image) { + if (!image || !hasLoadedImage) { return; } @@ -161,7 +162,7 @@ }); container.addEventListener('click', (/** @type {MouseEvent} */ e) => { - if (!image) { + if (!image || !hasLoadedImage) { return; } @@ -194,7 +195,7 @@ }); container.addEventListener('wheel', (/** @type {WheelEvent} */ e) => { - if (!image) { + if (!image || !hasLoadedImage) { return; } @@ -215,7 +216,7 @@ }); window.addEventListener('scroll', () => { - if (!image || !image.parentElement || scale === 'fit') { + if (!image || !hasLoadedImage || !image.parentElement || scale === 'fit') { return; } @@ -229,9 +230,11 @@ container.classList.add('zoom-in'); image.classList.add('scale-to-fit'); - image.style.visibility = 'hidden'; image.addEventListener('load', () => { + document.querySelector('.loading').remove(); + hasLoadedImage = true; + if (!image) { return; } @@ -241,7 +244,9 @@ value: `${image.naturalWidth}x${image.naturalHeight}`, }); - image.style.visibility = 'visible'; + container.classList.add('ready'); + document.body.append(image); + updateScale(scale); if (initialState.scale !== 'fit') { @@ -250,7 +255,6 @@ }); image.src = decodeURI(settings.src); - document.body.append(image); window.addEventListener('message', e => { switch (e.data.type) { diff --git a/extensions/image-preview/src/preview.ts b/extensions/image-preview/src/preview.ts index daf7bc520d..d309d7ffcb 100644 --- a/extensions/image-preview/src/preview.ts +++ b/extensions/image-preview/src/preview.ts @@ -96,6 +96,7 @@ export class Preview extends Disposable { +
`; diff --git a/src/sql/workbench/parts/notebook/browser/notebook.contribution.ts b/src/sql/workbench/parts/notebook/browser/notebook.contribution.ts index 884c26a8c3..02816baea8 100644 --- a/src/sql/workbench/parts/notebook/browser/notebook.contribution.ts +++ b/src/sql/workbench/parts/notebook/browser/notebook.contribution.ts @@ -21,7 +21,7 @@ import { registerComponentType } from 'sql/workbench/parts/notebook/browser/outp import { MimeRendererComponent } from 'sql/workbench/parts/notebook/browser/outputs/mimeRenderer.component'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { URI } from 'vs/base/common/uri'; -import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; +import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { NodeContextKey } from 'sql/workbench/parts/dataExplorer/browser/nodeContext'; import { MssqlNodeContext } from 'sql/workbench/parts/dataExplorer/browser/mssqlNodeContext'; diff --git a/src/vs/base/browser/dom.ts b/src/vs/base/browser/dom.ts index b17d05c9b2..e322f00ad8 100644 --- a/src/vs/base/browser/dom.ts +++ b/src/vs/base/browser/dom.ts @@ -781,6 +781,12 @@ export function createStyleSheet(container: HTMLElement = document.getElementsBy return style; } +export function createMetaElement(container: HTMLElement = document.getElementsByTagName('head')[0]): HTMLMetaElement { + let meta = document.createElement('meta'); + container.appendChild(meta); + return meta; +} + let _sharedStyleSheet: HTMLStyleElement | null = null; function getSharedStyleSheet(): HTMLStyleElement { if (!_sharedStyleSheet) { diff --git a/src/vs/base/browser/ui/menu/menubar.ts b/src/vs/base/browser/ui/menu/menubar.ts index a3a61c4c57..a02282c329 100644 --- a/src/vs/base/browser/ui/menu/menubar.ts +++ b/src/vs/base/browser/ui/menu/menubar.ts @@ -698,7 +698,7 @@ export class MenuBar extends Disposable { private focusPrevious(): void { - if (!this.focusedMenu) { + if (!this.focusedMenu || this.numMenusShown === 0) { return; } @@ -728,7 +728,7 @@ export class MenuBar extends Disposable { } private focusNext(): void { - if (!this.focusedMenu) { + if (!this.focusedMenu || this.numMenusShown === 0) { return; } diff --git a/src/vs/base/parts/ipc/node/ipcChannelCreator.ts b/src/vs/base/parts/ipc/node/ipc.ts similarity index 83% rename from src/vs/base/parts/ipc/node/ipcChannelCreator.ts rename to src/vs/base/parts/ipc/node/ipc.ts index 934d92e6fd..5ee86d2a26 100644 --- a/src/vs/base/parts/ipc/node/ipcChannelCreator.ts +++ b/src/vs/base/parts/ipc/node/ipc.ts @@ -4,16 +4,25 @@ *--------------------------------------------------------------------------------------------*/ import { Event } from 'vs/base/common/event'; -import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; +import { IServerChannel, IChannel } from 'vs/base/parts/ipc/common/ipc'; import { revive } from 'vs/base/common/marshalling'; import { isUndefinedOrNull } from 'vs/base/common/types'; import { isUpperAsciiLetter } from 'vs/base/common/strings'; -// -// Use both `createChannelReceiver` and `createChannelSender` -// for automated process <=> process communication over methods -// and events. -// +/** + * Use both `createChannelReceiver` and `createChannelSender` + * for automated process <=> process communication over methods + * and events. You do not need to spell out each method on both + * sides, a proxy will take care of this. + * + * Rules: + * - if marshalling is enabled, only `URI` and `RegExp` is converted + * automatically for you + * - events must follow the naming convention `onUppercase` + * - `CancellationToken` is currently not supported + * - if a context is provided, you can use `AddFirstParameterToFunctions` + * utility to signal this in the receiving side type + */ export interface IBaseChannelOptions { diff --git a/src/vs/base/parts/ipc/test/node/ipc.test.ts b/src/vs/base/parts/ipc/test/node/ipc.test.ts index 415c59f6bd..227659bf5f 100644 --- a/src/vs/base/parts/ipc/test/node/ipc.test.ts +++ b/src/vs/base/parts/ipc/test/node/ipc.test.ts @@ -5,11 +5,14 @@ import * as assert from 'assert'; import { IChannel, IServerChannel, IMessagePassingProtocol, IPCServer, ClientConnectionEvent, IPCClient } from 'vs/base/parts/ipc/common/ipc'; +import { createChannelReceiver, createChannelSender } from 'vs/base/parts/ipc/node/ipc'; import { Emitter, Event } from 'vs/base/common/event'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { canceled } from 'vs/base/common/errors'; import { timeout } from 'vs/base/common/async'; import { VSBuffer } from 'vs/base/common/buffer'; +import { URI } from 'vs/base/common/uri'; +import { isEqual } from 'vs/base/common/resources'; class QueueProtocol implements IMessagePassingProtocol { @@ -101,14 +104,16 @@ interface ITestService { neverComplete(): Promise; neverCompleteCT(cancellationToken: CancellationToken): Promise; buffersLength(buffers: Buffer[]): Promise; + marshall(uri: URI): Promise; + context(): Promise; - pong: Event; + onPong: Event; } class TestService implements ITestService { - private readonly _pong = new Emitter(); - readonly pong = this._pong.event; + private readonly _onPong = new Emitter(); + readonly onPong = this._onPong.event; marco(): Promise { return Promise.resolve('polo'); @@ -135,7 +140,15 @@ class TestService implements ITestService { } ping(msg: string): void { - this._pong.fire(msg); + this._onPong.fire(msg); + } + + marshall(uri: URI): Promise { + return Promise.resolve(uri); + } + + context(context?: unknown): Promise { + return Promise.resolve(context); } } @@ -156,7 +169,7 @@ class TestChannel implements IServerChannel { listen(_: unknown, event: string, arg?: any): Event { switch (event) { - case 'pong': return this.service.pong; + case 'onPong': return this.service.onPong; default: throw new Error('not implemented'); } } @@ -164,8 +177,8 @@ class TestChannel implements IServerChannel { class TestChannelClient implements ITestService { - get pong(): Event { - return this.channel.listen('pong'); + get onPong(): Event { + return this.channel.listen('onPong'); } constructor(private channel: IChannel) { } @@ -189,6 +202,14 @@ class TestChannelClient implements ITestService { buffersLength(buffers: Buffer[]): Promise { return this.channel.call('buffersLength', buffers); } + + marshall(uri: URI): Promise { + return this.channel.call('marshall', uri); + } + + context(): Promise { + return this.channel.call('context'); + } } suite('Base IPC', function () { @@ -281,7 +302,7 @@ suite('Base IPC', function () { test('listen to events', async function () { const messages: string[] = []; - ipcService.pong(msg => messages.push(msg)); + ipcService.onPong(msg => messages.push(msg)); await timeout(0); assert.deepEqual(messages, []); @@ -300,4 +321,98 @@ suite('Base IPC', function () { return assert.equal(r, 5); }); }); + + suite('one to one (proxy)', function () { + let server: IPCServer; + let client: IPCClient; + let service: TestService; + let ipcService: ITestService; + + setup(function () { + service = new TestService(); + const testServer = new TestIPCServer(); + server = testServer; + + server.registerChannel(TestChannelId, createChannelReceiver(service)); + + client = testServer.createConnection('client1'); + ipcService = createChannelSender(client.getChannel(TestChannelId)); + }); + + teardown(function () { + client.dispose(); + server.dispose(); + }); + + test('call success', async function () { + const r = await ipcService.marco(); + return assert.equal(r, 'polo'); + }); + + test('call error', async function () { + try { + await ipcService.error('nice error'); + return assert.fail('should not reach here'); + } catch (err) { + return assert.equal(err.message, 'nice error'); + } + }); + + test('listen to events', async function () { + const messages: string[] = []; + + ipcService.onPong(msg => messages.push(msg)); + await timeout(0); + + assert.deepEqual(messages, []); + service.ping('hello'); + await timeout(0); + + assert.deepEqual(messages, ['hello']); + service.ping('world'); + await timeout(0); + + assert.deepEqual(messages, ['hello', 'world']); + }); + + test('marshalling uri', async function () { + const uri = URI.file('foobar'); + const r = await ipcService.marshall(uri); + assert.ok(r instanceof URI); + return assert.ok(isEqual(r, uri)); + }); + + test('buffers in arrays', async function () { + const r = await ipcService.buffersLength([Buffer.allocUnsafe(2), Buffer.allocUnsafe(3)]); + return assert.equal(r, 5); + }); + }); + + suite('one to one (proxy, extra context)', function () { + let server: IPCServer; + let client: IPCClient; + let service: TestService; + let ipcService: ITestService; + + setup(function () { + service = new TestService(); + const testServer = new TestIPCServer(); + server = testServer; + + server.registerChannel(TestChannelId, createChannelReceiver(service)); + + client = testServer.createConnection('client1'); + ipcService = createChannelSender(client.getChannel(TestChannelId), { context: 'Super Context' }); + }); + + teardown(function () { + client.dispose(); + server.dispose(); + }); + + test('call extra context', async function () { + const r = await ipcService.context(); + return assert.equal(r, 'Super Context'); + }); + }); }); diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts index c5780cd125..fb11ab2f96 100644 --- a/src/vs/code/browser/workbench/workbench.ts +++ b/src/vs/code/browser/workbench/workbench.ts @@ -3,12 +3,13 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IWorkbenchConstructionOptions, create, URI, Event, Emitter, UriComponents, ICredentialsProvider, IURLCallbackProvider } from 'vs/workbench/workbench.web.api'; +import { IWorkbenchConstructionOptions, create, URI, Event, Emitter, UriComponents, ICredentialsProvider, IURLCallbackProvider, IWorkspaceProvider, IWorkspace } 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 { isFolderToOpen, isWorkspaceToOpen } from 'vs/platform/windows/common/windows'; interface ICredential { service: string; @@ -197,18 +198,43 @@ class PollingURLCallbackProvider extends Disposable implements IURLCallbackProvi } } -const options: IWorkbenchConstructionOptions = JSON.parse(document.getElementById('vscode-workbench-web-configuration')!.getAttribute('data-settings')!); +class WorkspaceProvider implements IWorkspaceProvider { + + constructor(public readonly workspace: IWorkspace) { } + + async open(workspace: IWorkspace, options?: { reuse?: boolean }): Promise { + let targetHref: string | undefined = undefined; + + // Empty + if (!workspace) { + targetHref = `${document.location.origin}${document.location.pathname}?ew=true`; + } + + // Folder + else if (isFolderToOpen(workspace)) { + targetHref = `${document.location.origin}${document.location.pathname}?folder=${workspace.folderUri.path}`; + } + + // Workspace + else if (isWorkspaceToOpen(workspace)) { + targetHref = `${document.location.origin}${document.location.pathname}?workspace=${workspace.workspaceUri.path}`; + } + + if (targetHref) { + if (options && options.reuse) { + window.location.href = targetHref; + } else { + window.open(targetHref); + } + } + } +} + +const options: IWorkbenchConstructionOptions & { folderUri?: UriComponents, workspaceUri?: UriComponents } = JSON.parse(document.getElementById('vscode-workbench-web-configuration')!.getAttribute('data-settings')!); +options.workspaceProvider = new WorkspaceProvider(options.folderUri ? { folderUri: URI.revive(options.folderUri) } : options.workspaceUri ? { workspaceUri: URI.revive(options.workspaceUri) } : undefined); 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); -} - if (Array.isArray(options.staticExtensions)) { options.staticExtensions.forEach(extension => { extension.extensionLocation = URI.revive(extension.extensionLocation); diff --git a/src/vs/code/electron-browser/issue/issueReporterMain.ts b/src/vs/code/electron-browser/issue/issueReporterMain.ts index 22ba15a708..5cfb0cf896 100644 --- a/src/vs/code/electron-browser/issue/issueReporterMain.ts +++ b/src/vs/code/electron-browser/issue/issueReporterMain.ts @@ -16,6 +16,7 @@ import { debounce } from 'vs/base/common/decorators'; import * as platform from 'vs/base/common/platform'; import { Disposable } from 'vs/base/common/lifecycle'; import { getDelayedChannel } from 'vs/base/parts/ipc/common/ipc'; +import { createChannelSender } from 'vs/base/parts/ipc/node/ipc'; import { connect as connectNet } from 'vs/base/parts/ipc/node/ipc.net'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IWindowConfiguration } from 'vs/platform/windows/common/windows'; @@ -40,7 +41,6 @@ import { withUndefinedAsNull } from 'vs/base/common/types'; import { SystemInfo, isRemoteDiagnosticError } from 'vs/platform/diagnostics/common/diagnostics'; import { SpdLogService } from 'vs/platform/log/node/spdlogService'; import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; -import { createChannelSender } from 'vs/base/parts/ipc/node/ipcChannelCreator'; const MAX_URL_LENGTH = 2045; diff --git a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts index f1e84761e2..6b2be413a4 100644 --- a/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-browser/sharedProcess/sharedProcessMain.ts @@ -32,11 +32,11 @@ import { ILogService, LogLevel, ILoggerService } from 'vs/platform/log/common/lo 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'; import { combinedDisposable, DisposableStore, toDisposable } from 'vs/base/common/lifecycle'; import { DownloadService } from 'vs/platform/download/common/downloadService'; import { IDownloadService } from 'vs/platform/download/common/download'; import { IChannel, IServerChannel, StaticRouter } from 'vs/base/parts/ipc/common/ipc'; +import { createChannelSender, createChannelReceiver } from 'vs/base/parts/ipc/node/ipc'; import { NodeCachedDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/nodeCachedDataCleaner'; import { LanguagePackCachedDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/languagePackCachedDataCleaner'; import { StorageDataCleaner } from 'vs/code/electron-browser/sharedProcess/contrib/storageDataCleaner'; @@ -55,10 +55,14 @@ import { UserDataSyncService, UserDataAutoSync } from 'vs/platform/userDataSync/ import { UserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; import { UserDataSyncChannel } from 'vs/platform/userDataSync/common/userDataSyncIpc'; import { SettingsMergeChannelClient } from 'vs/platform/userDataSync/common/settingsSyncIpc'; -import { createChannelSender } from 'vs/base/parts/ipc/node/ipcChannelCreator'; import { IElectronService } from 'vs/platform/electron/node/electron'; import { LoggerService } from 'vs/platform/log/node/loggerService'; import { UserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSyncLog'; +import { IAuthTokenService } from 'vs/platform/auth/common/auth'; +import { AuthTokenService } from 'vs/platform/auth/common/authTokenService'; +import { AuthTokenChannel } from 'vs/platform/auth/common/authTokenIpc'; +import { ICredentialsService } from 'vs/platform/credentials/common/credentials'; +import { KeytarCredentialsService } from 'vs/platform/credentials/node/credentialsService'; export interface ISharedProcessConfiguration { readonly machineId: string; @@ -177,6 +181,8 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat services.set(ILocalizationsService, new SyncDescriptor(LocalizationsService)); services.set(IDiagnosticsService, new SyncDescriptor(DiagnosticsService)); + services.set(ICredentialsService, new SyncDescriptor(KeytarCredentialsService)); + services.set(IAuthTokenService, new SyncDescriptor(AuthTokenService)); services.set(IUserDataSyncLogService, new SyncDescriptor(UserDataSyncLogService)); const settingsMergeChannel = server.getChannel('settingsMerge', activeWindowRouter); services.set(ISettingsMergeService, new SettingsMergeChannelClient(settingsMergeChannel)); @@ -193,13 +199,17 @@ async function main(server: Server, initData: ISharedProcessInitData, configurat server.registerChannel('extensions', channel); const localizationsService = accessor.get(ILocalizationsService); - const localizationsChannel = new LocalizationsChannel(localizationsService); + const localizationsChannel = createChannelReceiver(localizationsService); server.registerChannel('localizations', localizationsChannel); const diagnosticsService = accessor.get(IDiagnosticsService); const diagnosticsChannel = new DiagnosticsChannel(diagnosticsService); server.registerChannel('diagnostics', diagnosticsChannel); + const authTokenService = accessor.get(IAuthTokenService); + const authTokenChannel = new AuthTokenChannel(authTokenService); + server.registerChannel('authToken', authTokenChannel); + const userDataSyncService = accessor.get(IUserDataSyncService); const userDataSyncChannel = new UserDataSyncChannel(userDataSyncService); server.registerChannel('userDataSync', userDataSyncChannel); diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index e801a8b80c..0573e612f1 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -16,7 +16,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 { LaunchMainService, LaunchChannel, ILaunchMainService } from 'vs/platform/launch/electron-main/launchMainService'; +import { LaunchMainService, ILaunchMainService } from 'vs/platform/launch/electron-main/launchMainService'; 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'; @@ -25,21 +25,21 @@ import { IStateService } from 'vs/platform/state/node/state'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IURLService } from 'vs/platform/url/common/url'; -import { URLHandlerChannelClient, URLServiceChannel, URLHandlerRouter } from 'vs/platform/url/common/urlIpc'; +import { URLHandlerChannelClient, URLHandlerRouter } from 'vs/platform/url/common/urlIpc'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { NullTelemetryService, combinedAppender, LogAppender } from 'vs/platform/telemetry/common/telemetryUtils'; 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 { createChannelReceiver } from 'vs/base/parts/ipc/node/ipcChannelCreator'; +import { createChannelReceiver } from 'vs/base/parts/ipc/node/ipc'; 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 { URI } from 'vs/base/common/uri'; -import { WorkspacesChannel } from 'vs/platform/workspaces/electron-main/workspacesIpc'; -import { hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces'; +import { hasWorkspaceFileExtension, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; +import { WorkspacesService } from 'vs/platform/workspaces/electron-main/workspacesService'; 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'; @@ -457,6 +457,7 @@ export class CodeApplication extends Disposable { services.set(IIssueService, new SyncDescriptor(IssueMainService, [machineId, this.userEnv])); services.set(IElectronService, new SyncDescriptor(ElectronMainService)); + services.set(IWorkspacesService, new SyncDescriptor(WorkspacesService)); services.set(IMenubarService, new SyncDescriptor(MenubarMainService)); const storageMainService = new StorageMainService(this.logService, this.environmentService); @@ -530,7 +531,7 @@ export class CodeApplication extends Disposable { // Register more Main IPC services const launchMainService = accessor.get(ILaunchMainService); - const launchChannel = new LaunchChannel(launchMainService); + const launchChannel = createChannelReceiver(launchMainService, { disableMarshalling: true }); this.mainIpcServer.registerChannel('launch', launchChannel); // Register more Electron IPC services @@ -551,8 +552,8 @@ export class CodeApplication extends Disposable { const sharedProcessChannel = createChannelReceiver(sharedProcessMainService); electronIpcServer.registerChannel('sharedProcess', sharedProcessChannel); - const workspacesMainService = accessor.get(IWorkspacesMainService); - const workspacesChannel = new WorkspacesChannel(workspacesMainService, accessor.get(IWindowsMainService)); + const workspacesService = accessor.get(IWorkspacesService); + const workspacesChannel = createChannelReceiver(workspacesService); electronIpcServer.registerChannel('workspaces', workspacesChannel); const menubarService = accessor.get(IMenubarService); @@ -560,7 +561,7 @@ export class CodeApplication extends Disposable { electronIpcServer.registerChannel('menubar', menubarChannel); const urlService = accessor.get(IURLService); - const urlChannel = new URLServiceChannel(urlService); + const urlChannel = createChannelReceiver(urlService); electronIpcServer.registerChannel('url', urlChannel); const storageMainService = accessor.get(IStorageMainService); diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index 27be4a2323..ab8ad1c5a2 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -14,7 +14,8 @@ import { mkdirp } from 'vs/base/node/pfs'; import { validatePaths } from 'vs/code/node/paths'; 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/launchMainService'; +import { createChannelSender } from 'vs/base/parts/ipc/node/ipc'; +import { ILaunchMainService } from 'vs/platform/launch/electron-main/launchMainService'; import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { InstantiationService } from 'vs/platform/instantiation/common/instantiationService'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; @@ -272,8 +273,7 @@ class CodeMain { }, 10000); } - const channel = client.getChannel('launch'); - const launchClient = new LaunchChannelClient(channel); + const launchService = createChannelSender(client.getChannel('launch'), { disableMarshalling: true }); // Process Info if (environmentService.args.status) { @@ -282,8 +282,8 @@ class CodeMain { const sharedProcessClient = await connect(environmentService.sharedIPCHandle, 'main'); const diagnosticsChannel = sharedProcessClient.getChannel('diagnostics'); const diagnosticsService = new DiagnosticsService(diagnosticsChannel); - const mainProcessInfo = await launchClient.getMainProcessInfo(); - const remoteDiagnostics = await launchClient.getRemoteDiagnostics({ includeProcesses: true, includeWorkspaceMetadata: true }); + const mainProcessInfo = await launchService.getMainProcessInfo(); + const remoteDiagnostics = await launchService.getRemoteDiagnostics({ includeProcesses: true, includeWorkspaceMetadata: true }); const diagnostics = await diagnosticsService.getDiagnostics(mainProcessInfo, remoteDiagnostics); console.log(diagnostics); @@ -293,12 +293,12 @@ class CodeMain { // Windows: allow to set foreground if (platform.isWindows) { - await this.windowsAllowSetForegroundWindow(launchClient, logService); + await this.windowsAllowSetForegroundWindow(launchService, logService); } // Send environment over... logService.trace('Sending env to running instance...'); - await launchClient.start(environmentService.args, process.env as platform.IProcessEnvironment); + await launchService.start(environmentService.args, process.env as platform.IProcessEnvironment); // Cleanup await client.dispose(); @@ -360,9 +360,9 @@ class CodeMain { }); } - private async windowsAllowSetForegroundWindow(client: LaunchChannelClient, logService: ILogService): Promise { + private async windowsAllowSetForegroundWindow(launchService: ILaunchMainService, logService: ILogService): Promise { if (platform.isWindows) { - const processId = await client.getMainProcessId(); + const processId = await launchService.getMainProcessId(); logService.trace('Sending some foreground love to the running instance:', processId); diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 585f3c4f43..21ec680735 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -25,10 +25,9 @@ import { Event as CommonEvent, Emitter } from 'vs/base/common/event'; 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 { IRecent } from 'vs/platform/workspaces/common/workspacesHistory'; import { IWorkspacesHistoryMainService } from 'vs/platform/workspaces/electron-main/workspacesHistoryMainService'; import { IProcessEnvironment, isMacintosh, isWindows } from 'vs/base/common/platform'; -import { IWorkspaceIdentifier, WORKSPACE_FILTER, isSingleFolderWorkspaceIdentifier, hasWorkspaceFileExtension, IEnterWorkspaceResult } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspaceIdentifier, WORKSPACE_FILTER, isSingleFolderWorkspaceIdentifier, hasWorkspaceFileExtension, IEnterWorkspaceResult, IRecent } 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'; diff --git a/src/vs/editor/browser/editorExtensions.ts b/src/vs/editor/browser/editorExtensions.ts index 3dadbaedfd..ed6bc6cc43 100644 --- a/src/vs/editor/browser/editorExtensions.ts +++ b/src/vs/editor/browser/editorExtensions.ts @@ -241,7 +241,7 @@ export abstract class EditorAction extends EditorCommand { // --- Registration of commands and actions -export function registerLanguageCommand(id: string, handler: (accessor: ServicesAccessor, args: { [n: string]: any }) => any) { +export function registerLanguageCommand(id: string, handler: (accessor: ServicesAccessor, args: Args) => any) { CommandsRegistry.registerCommand(id, (accessor, args) => handler(accessor, args || {})); } diff --git a/src/vs/platform/auth/common/auth.ts b/src/vs/platform/auth/common/auth.ts new file mode 100644 index 0000000000..466357e293 --- /dev/null +++ b/src/vs/platform/auth/common/auth.ts @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { Event } from 'vs/base/common/event'; + +export const enum AuthTokenStatus { + Disabled = 'Disabled', + Inactive = 'Inactive', + Active = 'Active' +} + +export const IAuthTokenService = createDecorator('IAuthTokenService'); + +export interface IAuthTokenService { + _serviceBrand: undefined; + + readonly status: AuthTokenStatus; + readonly onDidChangeStatus: Event; + + getToken(): Promise; + updateToken(token: string): Promise; + refreshToken(): Promise; + deleteToken(): Promise; + +} + diff --git a/src/vs/platform/auth/common/authTokenIpc.ts b/src/vs/platform/auth/common/authTokenIpc.ts new file mode 100644 index 0000000000..e6c0a16250 --- /dev/null +++ b/src/vs/platform/auth/common/authTokenIpc.ts @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IServerChannel } from 'vs/base/parts/ipc/common/ipc'; +import { Event } from 'vs/base/common/event'; +import { IAuthTokenService } from 'vs/platform/auth/common/auth'; + +export class AuthTokenChannel implements IServerChannel { + + constructor(private readonly service: IAuthTokenService) { } + + listen(_: unknown, event: string): Event { + switch (event) { + case 'onDidChangeStatus': return this.service.onDidChangeStatus; + } + throw new Error(`Event not found: ${event}`); + } + + call(context: any, command: string, args?: any): Promise { + switch (command) { + case '_getInitialStatus': return Promise.resolve(this.service.status); + case 'getToken': return this.service.getToken(); + case 'updateToken': return this.service.updateToken(args[0]); + case 'refreshToken': return this.service.refreshToken(); + case 'deleteToken': return this.service.deleteToken(); + } + throw new Error('Invalid call'); + } +} diff --git a/src/vs/platform/auth/common/authTokenService.ts b/src/vs/platform/auth/common/authTokenService.ts new file mode 100644 index 0000000000..1cc3cf398f --- /dev/null +++ b/src/vs/platform/auth/common/authTokenService.ts @@ -0,0 +1,76 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Event, Emitter } from 'vs/base/common/event'; +import { IAuthTokenService, AuthTokenStatus } from 'vs/platform/auth/common/auth'; +import { ICredentialsService } from 'vs/platform/credentials/common/credentials'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { IProductService } from 'vs/platform/product/common/productService'; + +const SERVICE_NAME = 'VS Code'; +const ACCOUNT = 'MyAccount'; + +export class AuthTokenService extends Disposable implements IAuthTokenService { + _serviceBrand: undefined; + + private _status: AuthTokenStatus = AuthTokenStatus.Disabled; + get status(): AuthTokenStatus { return this._status; } + private _onDidChangeStatus: Emitter = this._register(new Emitter()); + readonly onDidChangeStatus: Event = this._onDidChangeStatus.event; + + constructor( + @ICredentialsService private readonly credentialsService: ICredentialsService, + @IProductService productService: IProductService, + ) { + super(); + if (productService.settingsSyncStoreUrl) { + this._status = AuthTokenStatus.Inactive; + this.getToken().then(token => { + if (token) { + this.setStatus(AuthTokenStatus.Active); + } + }); + } + } + + getToken(): Promise { + if (this.status === AuthTokenStatus.Disabled) { + throw new Error('Not enabled'); + } + return this.credentialsService.getPassword(SERVICE_NAME, ACCOUNT); + } + + async updateToken(token: string): Promise { + if (this.status === AuthTokenStatus.Disabled) { + throw new Error('Not enabled'); + } + await this.credentialsService.setPassword(SERVICE_NAME, ACCOUNT, token); + this.setStatus(AuthTokenStatus.Active); + } + + async refreshToken(): Promise { + if (this.status === AuthTokenStatus.Disabled) { + throw new Error('Not enabled'); + } + await this.deleteToken(); + } + + async deleteToken(): Promise { + if (this.status === AuthTokenStatus.Disabled) { + throw new Error('Not enabled'); + } + await this.credentialsService.deletePassword(SERVICE_NAME, ACCOUNT); + this.setStatus(AuthTokenStatus.Inactive); + } + + private setStatus(status: AuthTokenStatus): void { + if (this._status !== status) { + this._status = status; + this._onDidChangeStatus.fire(status); + } + } + +} + diff --git a/src/vs/platform/credentials/common/credentials.ts b/src/vs/platform/credentials/common/credentials.ts new file mode 100644 index 0000000000..f8f4dedc95 --- /dev/null +++ b/src/vs/platform/credentials/common/credentials.ts @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; + +export const ICredentialsService = createDecorator('ICredentialsService'); + +export interface ICredentialsService { + + _serviceBrand: undefined; + + 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/credentials/node/credentialsService.ts b/src/vs/platform/credentials/node/credentialsService.ts new file mode 100644 index 0000000000..e994c983b5 --- /dev/null +++ b/src/vs/platform/credentials/node/credentialsService.ts @@ -0,0 +1,40 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ICredentialsService } from 'vs/platform/credentials/common/credentials'; +import { IdleValue } from 'vs/base/common/async'; + +type KeytarModule = typeof import('keytar'); +export class KeytarCredentialsService implements ICredentialsService { + + _serviceBrand: undefined; + + private readonly _keytar = new IdleValue>(() => import('keytar')); + + async getPassword(service: string, account: string): Promise { + const keytar = await this._keytar.getValue(); + return keytar.getPassword(service, account); + } + + async setPassword(service: string, account: string, password: string): Promise { + const keytar = await this._keytar.getValue(); + return keytar.setPassword(service, account, password); + } + + async deletePassword(service: string, account: string): Promise { + const keytar = await this._keytar.getValue(); + return keytar.deletePassword(service, account); + } + + async findPassword(service: string): Promise { + const keytar = await this._keytar.getValue(); + return keytar.findPassword(service); + } + + async findCredentials(service: string): Promise> { + const keytar = await this._keytar.getValue(); + return keytar.findCredentials(service); + } +} diff --git a/src/vs/platform/electron/electron-main/electronMainService.ts b/src/vs/platform/electron/electron-main/electronMainService.ts index 23857547cb..6db4f453e4 100644 --- a/src/vs/platform/electron/electron-main/electronMainService.ts +++ b/src/vs/platform/electron/electron-main/electronMainService.ts @@ -6,17 +6,15 @@ import { Event } from 'vs/base/common/event'; import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows'; import { MessageBoxOptions, MessageBoxReturnValue, shell, OpenDevToolsOptions, SaveDialogOptions, SaveDialogReturnValue, OpenDialogOptions, OpenDialogReturnValue, CrashReporterStartOptions, crashReporter, Menu, BrowserWindow, app } from 'electron'; +import { INativeOpenInWindowOptions } from 'vs/platform/windows/node/window'; import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; -import { IOpenedWindow, OpenContext, IWindowOpenable, IOpenInWindowOptions, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows'; +import { IOpenedWindow, OpenContext, IWindowOpenable, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows'; import { INativeOpenDialogOptions } from 'vs/platform/dialogs/node/dialogs'; import { isMacintosh, IProcessEnvironment } from 'vs/base/common/platform'; import { IElectronService } from 'vs/platform/electron/node/electron'; import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; import { IEnvironmentService, ParsedArgs } from 'vs/platform/environment/common/environment'; import { AddFirstParameterToFunctions } from 'vs/base/common/types'; -import { IWorkspacesHistoryMainService } from 'vs/platform/workspaces/electron-main/workspacesHistoryMainService'; -import { IRecentlyOpened, IRecent } from 'vs/platform/workspaces/common/workspacesHistory'; -import { URI } from 'vs/base/common/uri'; export class ElectronMainService implements AddFirstParameterToFunctions /* only methods, not events */, number /* window ID */> { @@ -25,8 +23,7 @@ export class ElectronMainService implements AddFirstParameterToFunctions { + async openInWindow(windowId: number, toOpen: IWindowOpenable[], options: INativeOpenInWindowOptions = Object.create(null)): Promise { if (toOpen.length > 0) { this.windowsMainService.open({ context: OpenContext.API, @@ -332,33 +329,6 @@ export class ElectronMainService implements AddFirstParameterToFunctions { - const window = this.windowsMainService.getWindowById(windowId); - if (window) { - return this.workspacesHistoryMainService.getRecentlyOpened(window.config.workspace, window.config.folderUri, window.config.filesToOpenOrCreate); - } - - return this.workspacesHistoryMainService.getRecentlyOpened(); - } - - async addRecentlyOpened(windowId: number, recents: IRecent[]): Promise { - return this.workspacesHistoryMainService.addRecentlyOpened(recents); - } - - async removeFromRecentlyOpened(windowId: number, paths: URI[]): Promise { - return this.workspacesHistoryMainService.removeFromRecentlyOpened(paths); - } - - async clearRecentlyOpened(windowId: number): Promise { - return this.workspacesHistoryMainService.clearRecentlyOpened(); - } - - //#endregion - //#region Debug // TODO@Isidor move into debug IPC channel (https://github.com/microsoft/vscode/issues/81060) diff --git a/src/vs/platform/electron/node/electron.ts b/src/vs/platform/electron/node/electron.ts index 94de2856d0..c17b6b643b 100644 --- a/src/vs/platform/electron/node/electron.ts +++ b/src/vs/platform/electron/node/electron.ts @@ -6,13 +6,12 @@ import { Event } from 'vs/base/common/event'; import { MessageBoxOptions, MessageBoxReturnValue, OpenDevToolsOptions, SaveDialogOptions, OpenDialogOptions, OpenDialogReturnValue, SaveDialogReturnValue, CrashReporterStartOptions } from 'electron'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IWindowOpenable, IOpenInWindowOptions, IOpenEmptyWindowOptions, IOpenedWindow } from 'vs/platform/windows/common/windows'; +import { IWindowOpenable, IOpenEmptyWindowOptions, IOpenedWindow } from 'vs/platform/windows/common/windows'; import { INativeOpenDialogOptions } from 'vs/platform/dialogs/node/dialogs'; import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; -import { IRecentlyOpened, IRecent } from 'vs/platform/workspaces/common/workspacesHistory'; -import { URI } from 'vs/base/common/uri'; import { ParsedArgs } from 'vscode-minimist'; import { IProcessEnvironment } from 'vs/base/common/platform'; +import { INativeOpenInWindowOptions } from 'vs/platform/windows/node/window'; export const IElectronService = createDecorator('electronService'); @@ -35,7 +34,7 @@ export interface IElectronService { getActiveWindowId(): Promise; openEmptyWindow(options?: IOpenEmptyWindowOptions): Promise; - openInWindow(toOpen: IWindowOpenable[], options?: IOpenInWindowOptions): Promise; + openInWindow(toOpen: IWindowOpenable[], options?: INativeOpenInWindowOptions): Promise; toggleFullScreen(): Promise; @@ -89,13 +88,6 @@ export interface IElectronService { // Connectivity resolveProxy(url: string): Promise; - // Workspaces History - readonly onRecentlyOpenedChange: Event; - getRecentlyOpened(): Promise; - addRecentlyOpened(recents: IRecent[]): Promise; - removeFromRecentlyOpened(paths: URI[]): Promise; - clearRecentlyOpened(): Promise; - // Debug (TODO@Isidor move into debug IPC channel (https://github.com/microsoft/vscode/issues/81060) openExtensionDevelopmentHostWindow(args: ParsedArgs, env: IProcessEnvironment): Promise; } diff --git a/src/vs/platform/instantiation/common/instantiation.ts b/src/vs/platform/instantiation/common/instantiation.ts index 345127cc13..06b635015b 100644 --- a/src/vs/platform/instantiation/common/instantiation.ts +++ b/src/vs/platform/instantiation/common/instantiation.ts @@ -22,40 +22,42 @@ export namespace _util { // --- interfaces ------ +type BrandedService = { _serviceBrand: undefined }; + export interface IConstructorSignature0 { - new(...services: { _serviceBrand: undefined; }[]): T; + new(...services: BrandedService[]): T; } export interface IConstructorSignature1 { - new(first: A1, ...services: { _serviceBrand: undefined; }[]): T; + new(first: A1, ...services: BrandedService[]): T; } export interface IConstructorSignature2 { - new(first: A1, second: A2, ...services: { _serviceBrand: undefined; }[]): T; + new(first: A1, second: A2, ...services: BrandedService[]): T; } export interface IConstructorSignature3 { - new(first: A1, second: A2, third: A3, ...services: { _serviceBrand: undefined; }[]): T; + new(first: A1, second: A2, third: A3, ...services: BrandedService[]): T; } export interface IConstructorSignature4 { - new(first: A1, second: A2, third: A3, fourth: A4, ...services: { _serviceBrand: undefined; }[]): T; + new(first: A1, second: A2, third: A3, fourth: A4, ...services: BrandedService[]): T; } export interface IConstructorSignature5 { - new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, ...services: { _serviceBrand: undefined; }[]): T; + new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, ...services: BrandedService[]): T; } export interface IConstructorSignature6 { - new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, ...services: { _serviceBrand: undefined; }[]): T; + new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, ...services: BrandedService[]): T; } export interface IConstructorSignature7 { - new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, seventh: A7, ...services: { _serviceBrand: undefined; }[]): T; + new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, seventh: A7, ...services: BrandedService[]): T; } export interface IConstructorSignature8 { - new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, seventh: A7, eigth: A8, ...services: { _serviceBrand: undefined; }[]): T; + new(first: A1, second: A2, third: A3, fourth: A4, fifth: A5, sixth: A6, seventh: A7, eigth: A8, ...services: BrandedService[]): T; } export interface ServicesAccessor { diff --git a/src/vs/platform/launch/electron-main/launchMainService.ts b/src/vs/platform/launch/electron-main/launchMainService.ts index 5e2d83172a..e8e7e6b2ca 100644 --- a/src/vs/platform/launch/electron-main/launchMainService.ts +++ b/src/vs/platform/launch/electron-main/launchMainService.ts @@ -3,7 +3,6 @@ * 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 { ILogService } from 'vs/platform/log/common/log'; import { IURLService } from 'vs/platform/url/common/url'; import { IProcessEnvironment, isMacintosh } from 'vs/base/common/platform'; @@ -16,7 +15,6 @@ import { IWorkspacesMainService } from 'vs/platform/workspaces/electron-main/wor 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/launch'; @@ -60,64 +58,6 @@ export interface ILaunchMainService { getRemoteDiagnostics(options: IRemoteDiagnosticOptions): Promise<(IRemoteDiagnosticInfo | IRemoteDiagnosticError)[]>; } -export class LaunchChannel implements IServerChannel { - - constructor(private service: ILaunchMainService) { } - - listen(_: unknown, event: string): Event { - throw new Error(`Event not found: ${event}`); - } - - call(_: unknown, command: string, arg: any): Promise { - switch (command) { - case 'start': - const { args, userEnv } = arg as IStartArguments; - return this.service.start(args, userEnv); - - case 'get-main-process-id': - return this.service.getMainProcessId(); - - case 'get-main-process-info': - return this.service.getMainProcessInfo(); - - case 'get-logs-path': - return this.service.getLogsPath(); - - case 'get-remote-diagnostics': - return this.service.getRemoteDiagnostics(arg); - } - - throw new Error(`Call not found: ${command}`); - } -} - -export class LaunchChannelClient implements ILaunchMainService { - - _serviceBrand: undefined; - - constructor(private channel: IChannel) { } - - start(args: ParsedArgs, userEnv: IProcessEnvironment): Promise { - return this.channel.call('start', { args, userEnv }); - } - - getMainProcessId(): Promise { - return this.channel.call('get-main-process-id', null); - } - - getMainProcessInfo(): Promise { - return this.channel.call('get-main-process-info', null); - } - - getLogsPath(): Promise { - return this.channel.call('get-logs-path', null); - } - - getRemoteDiagnostics(options: IRemoteDiagnosticOptions): Promise { - return this.channel.call('get-remote-diagnostics', options); - } -} - export class LaunchMainService implements ILaunchMainService { _serviceBrand: undefined; diff --git a/src/vs/platform/localizations/electron-browser/localizationsService.ts b/src/vs/platform/localizations/electron-browser/localizationsService.ts deleted file mode 100644 index 674a9addf9..0000000000 --- a/src/vs/platform/localizations/electron-browser/localizationsService.ts +++ /dev/null @@ -1,26 +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 { IChannel } from 'vs/base/parts/ipc/common/ipc'; -import { Event } from 'vs/base/common/event'; -import { ILocalizationsService, LanguageType } from 'vs/platform/localizations/common/localizations'; -import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; - -export class LocalizationsService implements ILocalizationsService { - - _serviceBrand: undefined; - - private channel: IChannel; - - constructor(@ISharedProcessService sharedProcessService: ISharedProcessService) { - this.channel = sharedProcessService.getChannel('localizations'); - } - - get onDidLanguagesChange(): Event { return this.channel.listen('onDidLanguagesChange'); } - - getLanguageIds(type?: LanguageType): Promise { - return this.channel.call('getLanguageIds', type); - } -} diff --git a/src/vs/platform/localizations/node/localizationsIpc.ts b/src/vs/platform/localizations/node/localizationsIpc.ts deleted file mode 100644 index ba4665e824..0000000000 --- a/src/vs/platform/localizations/node/localizationsIpc.ts +++ /dev/null @@ -1,33 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { IServerChannel } from 'vs/base/parts/ipc/common/ipc'; -import { Event } from 'vs/base/common/event'; -import { ILocalizationsService } from 'vs/platform/localizations/common/localizations'; - -export class LocalizationsChannel implements IServerChannel { - - onDidLanguagesChange: Event; - - constructor(private service: ILocalizationsService) { - this.onDidLanguagesChange = Event.buffer(service.onDidLanguagesChange, true); - } - - listen(_: unknown, event: string): Event { - switch (event) { - case 'onDidLanguagesChange': return this.onDidLanguagesChange; - } - - throw new Error(`Event not found: ${event}`); - } - - call(_: unknown, command: string, arg?: any): Promise { - switch (command) { - case 'getLanguageIds': return this.service.getLanguageIds(arg); - } - - throw new Error(`Call not found: ${command}`); - } -} diff --git a/src/vs/platform/request/common/request.ts b/src/vs/platform/request/common/request.ts index 2db4ec2032..346dff23d1 100644 --- a/src/vs/platform/request/common/request.ts +++ b/src/vs/platform/request/common/request.ts @@ -21,7 +21,7 @@ export interface IRequestService { resolveProxy(url: string): Promise; } -function isSuccess(context: IRequestContext): boolean { +export function isSuccess(context: IRequestContext): boolean { return (context.res.statusCode && context.res.statusCode >= 200 && context.res.statusCode < 300) || context.res.statusCode === 1223; } diff --git a/src/vs/platform/url/common/urlIpc.ts b/src/vs/platform/url/common/urlIpc.ts index 0822f1053a..9c7f569a4b 100644 --- a/src/vs/platform/url/common/urlIpc.ts +++ b/src/vs/platform/url/common/urlIpc.ts @@ -4,49 +4,12 @@ *--------------------------------------------------------------------------------------------*/ import { IChannel, IServerChannel, IClientRouter, IConnectionHub, Client } from 'vs/base/parts/ipc/common/ipc'; -import { URI, UriComponents } from 'vs/base/common/uri'; -import { IDisposable } from 'vs/base/common/lifecycle'; +import { URI } from 'vs/base/common/uri'; import { Event } from 'vs/base/common/event'; -import { IURLService, IURLHandler } from 'vs/platform/url/common/url'; +import { IURLHandler } from 'vs/platform/url/common/url'; import { CancellationToken } from 'vs/base/common/cancellation'; import { first } from 'vs/base/common/arrays'; -export class URLServiceChannel implements IServerChannel { - - constructor(private service: IURLService) { } - - listen(_: unknown, event: string): Event { - throw new Error(`Event not found: ${event}`); - } - - call(_: unknown, command: string, arg?: any): Promise { - switch (command) { - case 'open': return this.service.open(URI.revive(arg)); - } - - throw new Error(`Call not found: ${command}`); - } -} - -export class URLServiceChannelClient implements IURLService { - - _serviceBrand: undefined; - - constructor(private channel: IChannel) { } - - open(url: URI): Promise { - return this.channel.call('open', url.toJSON()); - } - - registerHandler(handler: IURLHandler): IDisposable { - throw new Error('Not implemented.'); - } - - create(_options?: Partial): URI { - throw new Error('Method not implemented.'); - } -} - export class URLHandlerChannel implements IServerChannel { constructor(private handler: IURLHandler) { } diff --git a/src/vs/platform/userDataSync/common/extensionsSync.ts b/src/vs/platform/userDataSync/common/extensionsSync.ts index f796c4de3a..67b3d6673c 100644 --- a/src/vs/platform/userDataSync/common/extensionsSync.ts +++ b/src/vs/platform/userDataSync/common/extensionsSync.ts @@ -79,6 +79,7 @@ export class ExtensionsSynchroniser extends Disposable implements ISynchroniser return false; } + this.logService.trace('Extensions: Started synchronising extensions...'); this.setStatus(SyncStatus.Syncing); try { @@ -142,7 +143,9 @@ export class ExtensionsSynchroniser extends Disposable implements ISynchroniser remoteData = await this.writeToRemote(remote, remoteData.ref); } - if (remoteData.content) { + if (remoteData.content + && (!lastSyncData || lastSyncData.ref !== remoteData.ref) + ) { // update last sync this.logService.info('Extensions: Updating last synchronised extensions...'); await this.updateLastSyncValue(remoteData); diff --git a/src/vs/platform/userDataSync/common/settingsSync.ts b/src/vs/platform/userDataSync/common/settingsSync.ts index 0598b83210..2543e0e647 100644 --- a/src/vs/platform/userDataSync/common/settingsSync.ts +++ b/src/vs/platform/userDataSync/common/settingsSync.ts @@ -177,6 +177,8 @@ export class SettingsSynchroniser extends Disposable implements ISynchroniser { // Delete the preview await this.fileService.del(this.environmentService.settingsSyncPreviewResource); + } else { + this.logService.trace('Settings: No changes found during synchronising settings.'); } this.logService.trace('Settings: Finised synchronising settings.'); diff --git a/src/vs/platform/userDataSync/common/userDataSync.ts b/src/vs/platform/userDataSync/common/userDataSync.ts index a5cb324298..6911680df5 100644 --- a/src/vs/platform/userDataSync/common/userDataSync.ts +++ b/src/vs/platform/userDataSync/common/userDataSync.ts @@ -104,11 +104,6 @@ export interface IUserDataSyncStoreService { readonly enabled: boolean; - readonly loggedIn: boolean; - readonly onDidChangeLoggedIn: Event; - login(): Promise; - logout(): Promise; - read(key: string, oldValue: IUserData | null): Promise; write(key: string, content: string, ref: string | null): Promise; } diff --git a/src/vs/platform/userDataSync/common/userDataSyncService.ts b/src/vs/platform/userDataSync/common/userDataSyncService.ts index ea075a410e..df24380473 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncService.ts @@ -12,6 +12,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur import { timeout } from 'vs/base/common/async'; import { ExtensionsSynchroniser } from 'vs/platform/userDataSync/common/extensionsSync'; import { IExtensionIdentifier } from 'vs/platform/extensions/common/extensions'; +import { IAuthTokenService, AuthTokenStatus } from 'vs/platform/auth/common/auth'; export class UserDataSyncService extends Disposable implements IUserDataSyncService { @@ -35,6 +36,7 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ constructor( @IUserDataSyncStoreService private readonly userDataSyncStoreService: IUserDataSyncStoreService, @IInstantiationService private readonly instantiationService: IInstantiationService, + @IAuthTokenService private readonly authTokenService: IAuthTokenService, ) { super(); this.settingsSynchroniser = this._register(this.instantiationService.createInstance(SettingsSynchroniser)); @@ -43,12 +45,16 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ this.updateStatus(); this._register(Event.any(...this.synchronisers.map(s => Event.map(s.onDidChangeStatus, () => undefined)))(() => this.updateStatus())); this.onDidChangeLocal = Event.any(...this.synchronisers.map(s => s.onDidChangeLocal)); + this._register(authTokenService.onDidChangeStatus(() => this.onDidChangeAuthTokenStatus())); } async sync(_continue?: boolean): Promise { if (!this.userDataSyncStoreService.enabled) { throw new Error('Not enabled'); } + if (this.authTokenService.status === AuthTokenStatus.Inactive) { + return Promise.reject('Not Authenticated. Please sign in to start sync.'); + } for (const synchroniser of this.synchronisers) { if (!await synchroniser.sync(_continue)) { return false; @@ -105,36 +111,51 @@ export class UserDataSyncService extends Disposable implements IUserDataSyncServ return null; } + private onDidChangeAuthTokenStatus(): void { + if (this.authTokenService.status === AuthTokenStatus.Inactive) { + this.stop(); + } + } + } export class UserDataAutoSync extends Disposable { + private enabled: boolean = false; + constructor( @IConfigurationService private readonly configurationService: IConfigurationService, @IUserDataSyncService private readonly userDataSyncService: IUserDataSyncService, @IUserDataSyncStoreService userDataSyncStoreService: IUserDataSyncStoreService, @IUserDataSyncLogService private readonly userDataSyncLogService: IUserDataSyncLogService, + @IAuthTokenService private readonly authTokenService: IAuthTokenService, ) { super(); - if (userDataSyncStoreService.enabled) { - this.sync(true); - this._register(Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('configurationSync.enable'))(() => { - if (this.isSyncEnabled()) { - userDataSyncLogService.info('Syncing configuration started...'); - this.sync(true); - } else { - this.userDataSyncService.stop(); - userDataSyncLogService.info('Syncing configuration stopped.'); - } - })); + this.updateEnablement(); + this.sync(true); + this._register(Event.any(authTokenService.onDidChangeStatus, userDataSyncService.onDidChangeStatus)(() => this.updateEnablement())); + this._register(Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('configurationSync.enable'))(() => this.updateEnablement())); - // Sync immediately if there is a local change. - this._register(Event.debounce(this.userDataSyncService.onDidChangeLocal, () => undefined, 500)(() => this.sync(false))); + // Sync immediately if there is a local change. + this._register(Event.debounce(this.userDataSyncService.onDidChangeLocal, () => undefined, 500)(() => this.sync(false))); + } + + private updateEnablement(): void { + const enabled = this.isSyncEnabled(); + if (this.enabled !== enabled) { + this.enabled = enabled; + if (this.enabled) { + this.userDataSyncLogService.info('Syncing configuration started'); + this.sync(true); + } else { + this.userDataSyncService.stop(); + this.userDataSyncLogService.info('Syncing configuration stopped.'); + } } } private async sync(loop: boolean): Promise { - if (this.isSyncEnabled()) { + if (this.enabled) { try { await this.userDataSyncService.sync(); } catch (e) { @@ -148,7 +169,9 @@ export class UserDataAutoSync extends Disposable { } private isSyncEnabled(): boolean { - return this.configurationService.getValue('configurationSync.enable'); + return this.configurationService.getValue('configurationSync.enable') + && this.userDataSyncService.status !== SyncStatus.Uninitialized + && this.authTokenService.status !== AuthTokenStatus.Inactive; } } diff --git a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts index 5c2b429a83..769bb69835 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncStoreService.ts @@ -4,14 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import { Disposable, } from 'vs/base/common/lifecycle'; -import { IUserData, IUserDataSyncStoreService, UserDataSyncStoreErrorCode, UserDataSyncStoreError } from 'vs/platform/userDataSync/common/userDataSync'; +import { IUserData, IUserDataSyncStoreService, UserDataSyncStoreErrorCode, UserDataSyncStoreError, IUserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSync'; import { IProductService } from 'vs/platform/product/common/productService'; -import { Emitter, Event } from 'vs/base/common/event'; -import { IRequestService, asText } from 'vs/platform/request/common/request'; +import { IRequestService, asText, isSuccess } from 'vs/platform/request/common/request'; import { URI } from 'vs/base/common/uri'; import { joinPath } from 'vs/base/common/resources'; import { CancellationToken } from 'vs/base/common/cancellation'; -import { IHeaders } from 'vs/base/parts/request/common/request'; +import { IHeaders, IRequestOptions, IRequestContext } from 'vs/base/parts/request/common/request'; +import { IAuthTokenService, AuthTokenStatus } from 'vs/platform/auth/common/auth'; export class UserDataSyncStoreService extends Disposable implements IUserDataSyncStoreService { @@ -19,24 +19,15 @@ export class UserDataSyncStoreService extends Disposable implements IUserDataSyn get enabled(): boolean { return !!this.productService.settingsSyncStoreUrl; } - private _loggedIn: boolean = false; - get loggedIn(): boolean { return this._loggedIn; } - private readonly _onDidChangeLoggedIn: Emitter = this._register(new Emitter()); - readonly onDidChangeLoggedIn: Event = this._onDidChangeLoggedIn.event; - constructor( @IProductService private readonly productService: IProductService, @IRequestService private readonly requestService: IRequestService, + @IUserDataSyncLogService private readonly logService: IUserDataSyncLogService, + @IAuthTokenService private readonly authTokenService: IAuthTokenService, ) { super(); } - async login(): Promise { - } - - async logout(): Promise { - } - async read(key: string, oldValue: IUserData | null): Promise { if (!this.enabled) { return Promise.reject(new Error('No settings sync store url configured.')); @@ -48,13 +39,17 @@ export class UserDataSyncStoreService extends Disposable implements IUserDataSyn headers['If-None-Match'] = oldValue.ref; } - const context = await this.requestService.request({ type: 'GET', url, headers }, CancellationToken.None); + const context = await this.request({ type: 'GET', url, headers }, CancellationToken.None); if (context.res.statusCode === 304) { // There is no new value. Hence return the old value. return oldValue!; } + if (!isSuccess(context)) { + throw new Error('Server returned ' + context.res.statusCode); + } + const ref = context.res.headers['etag']; if (!ref) { throw new Error('Server did not return the ref'); @@ -74,13 +69,17 @@ export class UserDataSyncStoreService extends Disposable implements IUserDataSyn headers['If-Match'] = ref; } - const context = await this.requestService.request({ type: 'POST', url, data, headers }, CancellationToken.None); + const context = await this.request({ type: 'POST', url, data, headers }, CancellationToken.None); if (context.res.statusCode === 412) { // There is a new value. Throw Rejected Error throw new UserDataSyncStoreError('New data exists', UserDataSyncStoreErrorCode.Rejected); } + if (!isSuccess(context)) { + throw new Error('Server returned ' + context.res.statusCode); + } + const newRef = context.res.headers['etag']; if (!newRef) { throw new Error('Server did not return the ref'); @@ -88,4 +87,27 @@ export class UserDataSyncStoreService extends Disposable implements IUserDataSyn return newRef; } + private async request(options: IRequestOptions, token: CancellationToken): Promise { + if (this.authTokenService.status !== AuthTokenStatus.Disabled) { + const authToken = await this.authTokenService.getToken(); + if (!authToken) { + return Promise.reject(new Error('No Auth Token Available.')); + } + options.headers = options.headers || {}; + options.headers['authorization'] = `Bearer ${authToken}`; + } + + const context = await this.requestService.request(options, token); + + if (context.res.statusCode === 401) { + // Not Authorized + this.logService.info('Authroization Failed.'); + this.authTokenService.refreshToken(); + Promise.reject('Authroization Failed.'); + } + + return context; + + } + } diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index 91577a5fca..4d1450ed5c 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -22,11 +22,8 @@ export interface IOpenedWindow { export interface IOpenInWindowOptions { forceNewWindow?: boolean; forceReuseWindow?: boolean; - diffMode?: boolean; - addMode?: boolean; - gotoLineMode?: boolean; + noRecentEntry?: boolean; - waitMarkerFileURI?: URI; } export interface IOpenEmptyWindowOptions { diff --git a/src/vs/platform/windows/node/window.ts b/src/vs/platform/windows/node/window.ts new file mode 100644 index 0000000000..6de95f4e4e --- /dev/null +++ b/src/vs/platform/windows/node/window.ts @@ -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. + *--------------------------------------------------------------------------------------------*/ + +import { IOpenInWindowOptions } from 'vs/platform/windows/common/windows'; +import { URI } from 'vs/base/common/uri'; + +export interface INativeOpenInWindowOptions extends IOpenInWindowOptions { + diffMode?: boolean; + addMode?: boolean; + gotoLineMode?: boolean; + waitMarkerFileURI?: URI; +} diff --git a/src/vs/platform/workspace/common/workspace.ts b/src/vs/platform/workspace/common/workspace.ts index 47ea5ab022..d1a376d5fb 100644 --- a/src/vs/platform/workspace/common/workspace.ts +++ b/src/vs/platform/workspace/common/workspace.ts @@ -12,35 +12,23 @@ import { IWorkspaceIdentifier, IStoredWorkspaceFolder, isRawFileWorkspaceFolder, export const IWorkspaceContextService = createDecorator('contextService'); -export const enum WorkbenchState { - EMPTY = 1, - FOLDER, - WORKSPACE -} - -export interface IWorkspaceFoldersChangeEvent { - added: IWorkspaceFolder[]; - removed: IWorkspaceFolder[]; - changed: IWorkspaceFolder[]; -} - export interface IWorkspaceContextService { _serviceBrand: undefined; /** * An event which fires on workbench state changes. */ - onDidChangeWorkbenchState: Event; + readonly onDidChangeWorkbenchState: Event; /** * An event which fires on workspace name changes. */ - onDidChangeWorkspaceName: Event; + readonly onDidChangeWorkspaceName: Event; /** * An event which fires on workspace folders change. */ - onDidChangeWorkspaceFolders: Event; + readonly onDidChangeWorkspaceFolders: Event; /** * Provides access to the complete workspace object. @@ -79,6 +67,18 @@ export interface IWorkspaceContextService { isInsideWorkspace(resource: URI): boolean; } +export const enum WorkbenchState { + EMPTY = 1, + FOLDER, + WORKSPACE +} + +export interface IWorkspaceFoldersChangeEvent { + added: IWorkspaceFolder[]; + removed: IWorkspaceFolder[]; + changed: IWorkspaceFolder[]; +} + export namespace IWorkspace { export function isIWorkspace(thing: any): thing is IWorkspace { return thing && typeof thing === 'object' @@ -106,6 +106,7 @@ export interface IWorkspace { } export interface IWorkspaceFolderData { + /** * The associated URI for this workspace folder. */ @@ -264,5 +265,6 @@ export function toWorkspaceFolders(configuredFolders: IStoredWorkspaceFolder[], } } } + return result; } diff --git a/src/vs/platform/workspaces/common/workspaces.ts b/src/vs/platform/workspaces/common/workspaces.ts index 2714dff743..65a403464e 100644 --- a/src/vs/platform/workspaces/common/workspaces.ts +++ b/src/vs/platform/workspaces/common/workspaces.ts @@ -17,11 +17,67 @@ import { normalizeDriveLetter } from 'vs/base/common/labels'; import { toSlashes } from 'vs/base/common/extpath'; import { FormattingOptions } from 'vs/base/common/jsonFormatter'; import { getRemoteAuthority } from 'vs/platform/remote/common/remoteHosts'; +import { ILogService } from 'vs/platform/log/common/log'; +import { Event as CommonEvent } from 'vs/base/common/event'; 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'; +export const IWorkspacesService = createDecorator('workspacesService'); + +export interface IWorkspacesService { + + _serviceBrand: undefined; + + // Management + enterWorkspace(path: URI): Promise; + createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[], remoteAuthority?: string): Promise; + deleteUntitledWorkspace(workspace: IWorkspaceIdentifier): Promise; + getWorkspaceIdentifier(workspacePath: URI): Promise; + + // History + readonly onRecentlyOpenedChange: CommonEvent; + addRecentlyOpened(recents: IRecent[]): Promise; + removeFromRecentlyOpened(workspaces: URI[]): Promise; + clearRecentlyOpened(): Promise; + getRecentlyOpened(): Promise; +} + +export interface IRecentlyOpened { + workspaces: Array; + files: IRecentFile[]; +} + +export type IRecent = IRecentWorkspace | IRecentFolder | IRecentFile; + +export interface IRecentWorkspace { + workspace: IWorkspaceIdentifier; + label?: string; +} + +export interface IRecentFolder { + folderUri: ISingleFolderWorkspaceIdentifier; + label?: string; +} + +export interface IRecentFile { + fileUri: URI; + label?: string; +} + +export function isRecentWorkspace(curr: IRecent): curr is IRecentWorkspace { + return curr.hasOwnProperty('workspace'); +} + +export function isRecentFolder(curr: IRecent): curr is IRecentFolder { + return curr.hasOwnProperty('folderUri'); +} + +export function isRecentFile(curr: IRecent): curr is IRecentFile { + return curr.hasOwnProperty('fileUri'); +} + /** * A single folder workspace identifier is just the path to the folder. */ @@ -76,11 +132,6 @@ export interface IStoredWorkspace { remoteAuthority?: string; } -export interface IWorkspaceSavedEvent { - workspace: IWorkspaceIdentifier; - oldConfigPath: string; -} - export interface IWorkspaceFolderCreationData { uri: URI; name?: string; @@ -96,21 +147,6 @@ export interface IEnterWorkspaceResult { backupPath?: string; } -export const IWorkspacesService = createDecorator('workspacesService'); - -export interface IWorkspacesService { - - _serviceBrand: undefined; - - enterWorkspace(path: URI): Promise; - - createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[], remoteAuthority?: string): Promise; - - deleteUntitledWorkspace(workspace: IWorkspaceIdentifier): Promise; - - getWorkspaceIdentifier(workspacePath: URI): Promise; -} - export function isSingleFolderWorkspaceIdentifier(obj: any): obj is ISingleFolderWorkspaceIdentifier { return obj instanceof URI; } @@ -267,3 +303,129 @@ export function useSlashForPath(storedFolders: IStoredWorkspaceFolder[]): boolea } return true; } + +//#region Workspace Storage + +interface ISerializedRecentlyOpened { + workspaces3: Array; // workspace or URI.toString() // added in 1.32 + workspaceLabels?: Array; // added in 1.33 + files2: string[]; // files as URI.toString() // added in 1.32 + fileLabels?: Array; // added in 1.33 +} + +interface ILegacySerializedRecentlyOpened { + workspaces2: Array; // legacy, configPath as file path + workspaces: Array; // legacy (UriComponents was also supported for a few insider builds) + files: string[]; // files as paths +} + +interface ISerializedWorkspace { id: string; configURIPath: string; } +interface ILegacySerializedWorkspace { id: string; configPath: string; } + +function isLegacySerializedWorkspace(curr: any): curr is ILegacySerializedWorkspace { + return typeof curr === 'object' && typeof curr['id'] === 'string' && typeof curr['configPath'] === 'string'; +} + +function isUriComponents(curr: any): curr is UriComponents { + return curr && typeof curr['path'] === 'string' && typeof curr['scheme'] === 'string'; +} + +export type RecentlyOpenedStorageData = object; + +export function restoreRecentlyOpened(data: RecentlyOpenedStorageData | undefined, logService: ILogService): IRecentlyOpened { + const result: IRecentlyOpened = { workspaces: [], files: [] }; + if (data) { + const restoreGracefully = function (entries: T[], func: (entry: T, index: number) => void) { + for (let i = 0; i < entries.length; i++) { + try { + func(entries[i], i); + } catch (e) { + logService.warn(`Error restoring recent entry ${JSON.stringify(entries[i])}: ${e.toString()}. Skip entry.`); + } + } + }; + + const storedRecents = data as ISerializedRecentlyOpened & ILegacySerializedRecentlyOpened; + if (Array.isArray(storedRecents.workspaces3)) { + restoreGracefully(storedRecents.workspaces3, (workspace, i) => { + const label: string | undefined = (Array.isArray(storedRecents.workspaceLabels) && storedRecents.workspaceLabels[i]) || undefined; + if (typeof workspace === 'object' && typeof workspace.id === 'string' && typeof workspace.configURIPath === 'string') { + result.workspaces.push({ label, workspace: { id: workspace.id, configPath: URI.parse(workspace.configURIPath) } }); + } else if (typeof workspace === 'string') { + result.workspaces.push({ label, folderUri: URI.parse(workspace) }); + } + }); + } else if (Array.isArray(storedRecents.workspaces2)) { + restoreGracefully(storedRecents.workspaces2, workspace => { + if (typeof workspace === 'object' && typeof workspace.id === 'string' && typeof workspace.configPath === 'string') { + result.workspaces.push({ workspace: { id: workspace.id, configPath: URI.file(workspace.configPath) } }); + } else if (typeof workspace === 'string') { + result.workspaces.push({ folderUri: URI.parse(workspace) }); + } + }); + } else if (Array.isArray(storedRecents.workspaces)) { + // TODO@martin legacy support can be removed at some point (6 month?) + // format of 1.25 and before + restoreGracefully(storedRecents.workspaces, workspace => { + if (typeof workspace === 'string') { + result.workspaces.push({ folderUri: URI.file(workspace) }); + } else if (isLegacySerializedWorkspace(workspace)) { + result.workspaces.push({ workspace: { id: workspace.id, configPath: URI.file(workspace.configPath) } }); + } else if (isUriComponents(workspace)) { + // added by 1.26-insiders + result.workspaces.push({ folderUri: URI.revive(workspace) }); + } + }); + } + if (Array.isArray(storedRecents.files2)) { + restoreGracefully(storedRecents.files2, (file, i) => { + const label: string | undefined = (Array.isArray(storedRecents.fileLabels) && storedRecents.fileLabels[i]) || undefined; + if (typeof file === 'string') { + result.files.push({ label, fileUri: URI.parse(file) }); + } + }); + } else if (Array.isArray(storedRecents.files)) { + restoreGracefully(storedRecents.files, file => { + if (typeof file === 'string') { + result.files.push({ fileUri: URI.file(file) }); + } + }); + } + } + + return result; +} + +export function toStoreData(recents: IRecentlyOpened): RecentlyOpenedStorageData { + const serialized: ISerializedRecentlyOpened = { workspaces3: [], files2: [] }; + + let hasLabel = false; + const workspaceLabels: (string | null)[] = []; + for (const recent of recents.workspaces) { + if (isRecentFolder(recent)) { + serialized.workspaces3.push(recent.folderUri.toString()); + } else { + serialized.workspaces3.push({ id: recent.workspace.id, configURIPath: recent.workspace.configPath.toString() }); + } + workspaceLabels.push(recent.label || null); + hasLabel = hasLabel || !!recent.label; + } + if (hasLabel) { + serialized.workspaceLabels = workspaceLabels; + } + + hasLabel = false; + const fileLabels: (string | null)[] = []; + for (const recent of recents.files) { + serialized.files2.push(recent.fileUri.toString()); + fileLabels.push(recent.label || null); + hasLabel = hasLabel || !!recent.label; + } + if (hasLabel) { + serialized.fileLabels = fileLabels; + } + + return serialized; +} + +//#endregion diff --git a/src/vs/platform/workspaces/common/workspacesHistory.ts b/src/vs/platform/workspaces/common/workspacesHistory.ts deleted file mode 100644 index 7fbdfc505f..0000000000 --- a/src/vs/platform/workspaces/common/workspacesHistory.ts +++ /dev/null @@ -1,41 +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 { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; -import { URI } from 'vs/base/common/uri'; - -export interface IRecentlyOpened { - workspaces: Array; - files: IRecentFile[]; -} - -export type IRecent = IRecentWorkspace | IRecentFolder | IRecentFile; - -export interface IRecentWorkspace { - workspace: IWorkspaceIdentifier; - label?: string; -} - -export interface IRecentFolder { - folderUri: ISingleFolderWorkspaceIdentifier; - label?: string; -} - -export interface IRecentFile { - fileUri: URI; - label?: string; -} - -export function isRecentWorkspace(curr: IRecent): curr is IRecentWorkspace { - return curr.hasOwnProperty('workspace'); -} - -export function isRecentFolder(curr: IRecent): curr is IRecentFolder { - return curr.hasOwnProperty('folderUri'); -} - -export function isRecentFile(curr: IRecent): curr is IRecentFile { - return curr.hasOwnProperty('fileUri'); -} diff --git a/src/vs/platform/workspaces/common/workspacesHistoryStorage.ts b/src/vs/platform/workspaces/common/workspacesHistoryStorage.ts deleted file mode 100644 index 37ccd90371..0000000000 --- a/src/vs/platform/workspaces/common/workspacesHistoryStorage.ts +++ /dev/null @@ -1,129 +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 { UriComponents, URI } from 'vs/base/common/uri'; -import { IRecentlyOpened, isRecentFolder } from 'vs/platform/workspaces/common/workspacesHistory'; -import { ILogService } from 'vs/platform/log/common/log'; - -interface ISerializedRecentlyOpened { - workspaces3: Array; // workspace or URI.toString() // added in 1.32 - workspaceLabels?: Array; // added in 1.33 - files2: string[]; // files as URI.toString() // added in 1.32 - fileLabels?: Array; // added in 1.33 -} - -interface ILegacySerializedRecentlyOpened { - workspaces2: Array; // legacy, configPath as file path - workspaces: Array; // legacy (UriComponents was also supported for a few insider builds) - files: string[]; // files as paths -} - -interface ISerializedWorkspace { id: string; configURIPath: string; } -interface ILegacySerializedWorkspace { id: string; configPath: string; } - -function isLegacySerializedWorkspace(curr: any): curr is ILegacySerializedWorkspace { - return typeof curr === 'object' && typeof curr['id'] === 'string' && typeof curr['configPath'] === 'string'; -} - -function isUriComponents(curr: any): curr is UriComponents { - return curr && typeof curr['path'] === 'string' && typeof curr['scheme'] === 'string'; -} - -export type RecentlyOpenedStorageData = object; - -export function restoreRecentlyOpened(data: RecentlyOpenedStorageData | undefined, logService: ILogService): IRecentlyOpened { - const result: IRecentlyOpened = { workspaces: [], files: [] }; - if (data) { - const restoreGracefully = function (entries: T[], func: (entry: T, index: number) => void) { - for (let i = 0; i < entries.length; i++) { - try { - func(entries[i], i); - } catch (e) { - logService.warn(`Error restoring recent entry ${JSON.stringify(entries[i])}: ${e.toString()}. Skip entry.`); - } - } - }; - - const storedRecents = data as ISerializedRecentlyOpened & ILegacySerializedRecentlyOpened; - if (Array.isArray(storedRecents.workspaces3)) { - restoreGracefully(storedRecents.workspaces3, (workspace, i) => { - const label: string | undefined = (Array.isArray(storedRecents.workspaceLabels) && storedRecents.workspaceLabels[i]) || undefined; - if (typeof workspace === 'object' && typeof workspace.id === 'string' && typeof workspace.configURIPath === 'string') { - result.workspaces.push({ label, workspace: { id: workspace.id, configPath: URI.parse(workspace.configURIPath) } }); - } else if (typeof workspace === 'string') { - result.workspaces.push({ label, folderUri: URI.parse(workspace) }); - } - }); - } else if (Array.isArray(storedRecents.workspaces2)) { - restoreGracefully(storedRecents.workspaces2, workspace => { - if (typeof workspace === 'object' && typeof workspace.id === 'string' && typeof workspace.configPath === 'string') { - result.workspaces.push({ workspace: { id: workspace.id, configPath: URI.file(workspace.configPath) } }); - } else if (typeof workspace === 'string') { - result.workspaces.push({ folderUri: URI.parse(workspace) }); - } - }); - } else if (Array.isArray(storedRecents.workspaces)) { - // TODO@martin legacy support can be removed at some point (6 month?) - // format of 1.25 and before - restoreGracefully(storedRecents.workspaces, workspace => { - if (typeof workspace === 'string') { - result.workspaces.push({ folderUri: URI.file(workspace) }); - } else if (isLegacySerializedWorkspace(workspace)) { - result.workspaces.push({ workspace: { id: workspace.id, configPath: URI.file(workspace.configPath) } }); - } else if (isUriComponents(workspace)) { - // added by 1.26-insiders - result.workspaces.push({ folderUri: URI.revive(workspace) }); - } - }); - } - if (Array.isArray(storedRecents.files2)) { - restoreGracefully(storedRecents.files2, (file, i) => { - const label: string | undefined = (Array.isArray(storedRecents.fileLabels) && storedRecents.fileLabels[i]) || undefined; - if (typeof file === 'string') { - result.files.push({ label, fileUri: URI.parse(file) }); - } - }); - } else if (Array.isArray(storedRecents.files)) { - restoreGracefully(storedRecents.files, file => { - if (typeof file === 'string') { - result.files.push({ fileUri: URI.file(file) }); - } - }); - } - } - - return result; -} - -export function toStoreData(recents: IRecentlyOpened): RecentlyOpenedStorageData { - const serialized: ISerializedRecentlyOpened = { workspaces3: [], files2: [] }; - - let hasLabel = false; - const workspaceLabels: (string | null)[] = []; - for (const recent of recents.workspaces) { - if (isRecentFolder(recent)) { - serialized.workspaces3.push(recent.folderUri.toString()); - } else { - serialized.workspaces3.push({ id: recent.workspace.id, configURIPath: recent.workspace.configPath.toString() }); - } - workspaceLabels.push(recent.label || null); - hasLabel = hasLabel || !!recent.label; - } - if (hasLabel) { - serialized.workspaceLabels = workspaceLabels; - } - - hasLabel = false; - const fileLabels: (string | null)[] = []; - for (const recent of recents.files) { - serialized.files2.push(recent.fileUri.toString()); - fileLabels.push(recent.label || null); - hasLabel = hasLabel || !!recent.label; - } - if (hasLabel) { - serialized.fileLabels = fileLabels; - } - - return serialized; -} diff --git a/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts b/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts index a481af2c5b..1517db6b64 100644 --- a/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts +++ b/src/vs/platform/workspaces/electron-main/workspacesHistoryMainService.ts @@ -12,16 +12,14 @@ 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, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, isSingleFolderWorkspaceIdentifier, IRecentlyOpened, isRecentWorkspace, isRecentFolder, IRecent, isRecentFile, IRecentFolder, IRecentWorkspace, IRecentFile, toStoreData, restoreRecentlyOpened, RecentlyOpenedStorageData } 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/workspaces/common/workspacesHistory'; 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'; import { Schemas } from 'vs/base/common/network'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { getSimpleWorkspaceLabel } from 'vs/platform/label/common/label'; -import { toStoreData, restoreRecentlyOpened, RecentlyOpenedStorageData } from 'vs/platform/workspaces/common/workspacesHistoryStorage'; import { exists } from 'vs/base/node/pfs'; import { ILifecycleMainService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; @@ -32,7 +30,7 @@ export interface IWorkspacesHistoryMainService { _serviceBrand: undefined; - onRecentlyOpenedChange: CommonEvent; + readonly onRecentlyOpenedChange: CommonEvent; addRecentlyOpened(recents: IRecent[]): void; getRecentlyOpened(currentWorkspace?: IWorkspaceIdentifier, currentFolder?: ISingleFolderWorkspaceIdentifier, currentFiles?: IPath[]): IRecentlyOpened; diff --git a/src/vs/platform/workspaces/electron-main/workspacesIpc.ts b/src/vs/platform/workspaces/electron-main/workspacesIpc.ts deleted file mode 100644 index c74159e705..0000000000 --- a/src/vs/platform/workspaces/electron-main/workspacesIpc.ts +++ /dev/null @@ -1,58 +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 { IServerChannel } from 'vs/base/parts/ipc/common/ipc'; -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'; -import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows'; - -export class WorkspacesChannel implements IServerChannel { - - constructor( - private workspacesMainService: IWorkspacesMainService, - private windowsMainService: IWindowsMainService - ) { } - - listen(_: unknown, event: string): Event { - throw new Error(`Event not found: ${event}`); - } - - call(_: unknown, command: string, arg?: any): Promise { - switch (command) { - case 'createUntitledWorkspace': { - const rawFolders: IWorkspaceFolderCreationData[] = arg[0]; - const remoteAuthority: string = arg[1]; - let folders: IWorkspaceFolderCreationData[] | undefined = undefined; - if (Array.isArray(rawFolders)) { - folders = rawFolders.map(rawFolder => { - return { - uri: URI.revive(rawFolder.uri), // convert raw URI back to real URI - name: rawFolder.name - }; - }); - } - - return this.workspacesMainService.createUntitledWorkspace(folders, remoteAuthority); - } - case 'deleteUntitledWorkspace': { - const identifier: IWorkspaceIdentifier = arg; - return this.workspacesMainService.deleteUntitledWorkspace({ id: identifier.id, configPath: URI.revive(identifier.configPath) }); - } - case 'getWorkspaceIdentifier': { - return this.workspacesMainService.getWorkspaceIdentifier(URI.revive(arg)); - } - case 'enterWorkspace': { - const window = this.windowsMainService.getWindowById(arg[0]); - if (window) { - return this.windowsMainService.enterWorkspace(window, URI.revive(arg[1])); - } - } - } - - throw new Error(`Call not found: ${command}`); - } -} diff --git a/src/vs/platform/workspaces/electron-main/workspacesMainService.ts b/src/vs/platform/workspaces/electron-main/workspacesMainService.ts index e6bca89a36..d644c9375f 100644 --- a/src/vs/platform/workspaces/electron-main/workspacesMainService.ts +++ b/src/vs/platform/workspaces/electron-main/workspacesMainService.ts @@ -20,18 +20,13 @@ 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; + readonly onUntitledWorkspaceDeleted: Event; createUntitledWorkspaceSync(folders?: IWorkspaceFolderCreationData[]): IWorkspaceIdentifier; @@ -50,6 +45,11 @@ export interface IWorkspacesMainService { getWorkspaceIdentifier(workspacePath: URI): Promise; } +export interface IStoredWorkspace { + folders: IStoredWorkspaceFolder[]; + remoteAuthority?: string; +} + export class WorkspacesMainService extends Disposable implements IWorkspacesMainService { _serviceBrand: undefined; diff --git a/src/vs/platform/workspaces/electron-main/workspacesService.ts b/src/vs/platform/workspaces/electron-main/workspacesService.ts new file mode 100644 index 0000000000..8f3b205667 --- /dev/null +++ b/src/vs/platform/workspaces/electron-main/workspacesService.ts @@ -0,0 +1,75 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { AddFirstParameterToFunctions } from 'vs/base/common/types'; +import { IWorkspacesService, IEnterWorkspaceResult, IWorkspaceFolderCreationData, IWorkspaceIdentifier, IRecentlyOpened, IRecent } from 'vs/platform/workspaces/common/workspaces'; +import { URI } from 'vs/base/common/uri'; +import { IWorkspacesMainService } from 'vs/platform/workspaces/electron-main/workspacesMainService'; +import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows'; +import { IWorkspacesHistoryMainService } from 'vs/platform/workspaces/electron-main/workspacesHistoryMainService'; + +export class WorkspacesService implements AddFirstParameterToFunctions /* only methods, not events */, number /* window ID */> { + + _serviceBrand: undefined; + + constructor( + @IWorkspacesMainService private readonly workspacesMainService: IWorkspacesMainService, + @IWindowsMainService private readonly windowsMainService: IWindowsMainService, + @IWorkspacesHistoryMainService private readonly workspacesHistoryMainService: IWorkspacesHistoryMainService + ) { + } + + //#region Workspace Management + + async enterWorkspace(windowId: number, path: URI): Promise { + const window = this.windowsMainService.getWindowById(windowId); + if (window) { + return this.windowsMainService.enterWorkspace(window, path); + } + + return undefined; + } + + createUntitledWorkspace(windowId: number, folders?: IWorkspaceFolderCreationData[], remoteAuthority?: string): Promise { + return this.workspacesMainService.createUntitledWorkspace(folders, remoteAuthority); + } + + deleteUntitledWorkspace(windowId: number, workspace: IWorkspaceIdentifier): Promise { + return this.workspacesMainService.deleteUntitledWorkspace(workspace); + } + + getWorkspaceIdentifier(windowId: number, workspacePath: URI): Promise { + return this.workspacesMainService.getWorkspaceIdentifier(workspacePath); + } + + //#endregion + + //#region Workspaces History + + readonly onRecentlyOpenedChange = this.workspacesHistoryMainService.onRecentlyOpenedChange; + + async getRecentlyOpened(windowId: number): Promise { + const window = this.windowsMainService.getWindowById(windowId); + if (window) { + return this.workspacesHistoryMainService.getRecentlyOpened(window.config.workspace, window.config.folderUri, window.config.filesToOpenOrCreate); + } + + return this.workspacesHistoryMainService.getRecentlyOpened(); + } + + async addRecentlyOpened(windowId: number, recents: IRecent[]): Promise { + return this.workspacesHistoryMainService.addRecentlyOpened(recents); + } + + async removeFromRecentlyOpened(windowId: number, paths: URI[]): Promise { + return this.workspacesHistoryMainService.removeFromRecentlyOpened(paths); + } + + async clearRecentlyOpened(windowId: number): Promise { + return this.workspacesHistoryMainService.clearRecentlyOpened(); + } + + //#endregion +} diff --git a/src/vs/platform/workspaces/test/electron-main/workspacesHistoryStorage.test.ts b/src/vs/platform/workspaces/test/electron-main/workspacesHistoryStorage.test.ts index 1366b8b477..e0dc4bd1a7 100644 --- a/src/vs/platform/workspaces/test/electron-main/workspacesHistoryStorage.test.ts +++ b/src/vs/platform/workspaces/test/electron-main/workspacesHistoryStorage.test.ts @@ -2,14 +2,12 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import * as assert from 'assert'; import * as os from 'os'; import * as path from 'vs/base/common/path'; - -import { IWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspaceIdentifier, IRecentlyOpened, isRecentFolder, IRecentFolder, IRecentWorkspace, toStoreData, restoreRecentlyOpened } from 'vs/platform/workspaces/common/workspaces'; import { URI } from 'vs/base/common/uri'; -import { IRecentlyOpened, isRecentFolder, IRecentFolder, IRecentWorkspace } from 'vs/platform/workspaces/common/workspacesHistory'; -import { toStoreData, restoreRecentlyOpened } from 'vs/platform/workspaces/common/workspacesHistoryStorage'; import { NullLogService } from 'vs/platform/log/common/log'; function toWorkspace(uri: URI): IWorkspaceIdentifier { diff --git a/src/vs/workbench/api/browser/mainThreadKeytar.ts b/src/vs/workbench/api/browser/mainThreadKeytar.ts index 14e244f87c..b2d5477529 100644 --- a/src/vs/workbench/api/browser/mainThreadKeytar.ts +++ b/src/vs/workbench/api/browser/mainThreadKeytar.ts @@ -5,7 +5,7 @@ import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { MainContext, MainThreadKeytarShape, IExtHostContext } from 'vs/workbench/api/common/extHost.protocol'; -import { ICredentialsService } from 'vs/workbench/services/credentials/common/credentials'; +import { ICredentialsService } from 'vs/platform/credentials/common/credentials'; @extHostNamedCustomer(MainContext.MainThreadKeytar) export class MainThreadKeytar implements MainThreadKeytarShape { diff --git a/src/vs/workbench/api/browser/mainThreadWorkspace.ts b/src/vs/workbench/api/browser/mainThreadWorkspace.ts index 26ce01c497..2d4e5d31aa 100644 --- a/src/vs/workbench/api/browser/mainThreadWorkspace.ts +++ b/src/vs/workbench/api/browser/mainThreadWorkspace.ts @@ -16,7 +16,7 @@ import { IWorkspaceContextService, WorkbenchState, IWorkspace } from 'vs/platfor import { extHostNamedCustomer } from 'vs/workbench/api/common/extHostCustomers'; import { ITextQueryBuilderOptions, QueryBuilder } from 'vs/workbench/contrib/search/common/queryBuilder'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; -import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; +import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing'; import { ExtHostContext, ExtHostWorkspaceShape, IExtHostContext, MainContext, MainThreadWorkspaceShape, IWorkspaceData, ITextSearchComplete } from '../common/extHost.protocol'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; import { isEqualOrParent } from 'vs/base/common/resources'; diff --git a/src/vs/workbench/api/common/apiCommands.ts b/src/vs/workbench/api/common/apiCommands.ts index bc9e4fdfe9..492d536c28 100644 --- a/src/vs/workbench/api/common/apiCommands.ts +++ b/src/vs/workbench/api/common/apiCommands.ts @@ -12,9 +12,7 @@ import { EditorViewColumn } from 'vs/workbench/api/common/shared/editor'; import { EditorGroupLayout } from 'vs/workbench/services/editor/common/editorGroupsService'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IOpenInWindowOptions, IWindowOpenable } from 'vs/platform/windows/common/windows'; -import { IWorkspacesHistoryService } from 'vs/workbench/services/workspace/common/workspacesHistoryService'; -import { IWorkspacesService, hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces'; -import { IRecent } from 'vs/platform/workspaces/common/workspacesHistory'; +import { IWorkspacesService, hasWorkspaceFileExtension, IRecent } from 'vs/platform/workspaces/common/workspaces'; import { Schemas } from 'vs/base/common/network'; // ----------------------------------------------------------------- @@ -133,8 +131,8 @@ export class OpenAPICommand { CommandsRegistry.registerCommand(OpenAPICommand.ID, adjustHandler(OpenAPICommand.execute)); CommandsRegistry.registerCommand('_workbench.removeFromRecentlyOpened', function (accessor: ServicesAccessor, uri: URI) { - const workspacesHistoryService = accessor.get(IWorkspacesHistoryService); - return workspacesHistoryService.removeFromRecentlyOpened([uri]); + const workspacesService = accessor.get(IWorkspacesService); + return workspacesService.removeFromRecentlyOpened([uri]); }); export class RemoveFromRecentlyOpenedAPICommand { @@ -164,7 +162,6 @@ interface RecentEntry { } CommandsRegistry.registerCommand('_workbench.addToRecentlyOpened', async function (accessor: ServicesAccessor, recentEntry: RecentEntry) { - const workspacesHistoryService = accessor.get(IWorkspacesHistoryService); const workspacesService = accessor.get(IWorkspacesService); let recent: IRecent | undefined = undefined; const uri = recentEntry.uri; @@ -177,12 +174,12 @@ CommandsRegistry.registerCommand('_workbench.addToRecentlyOpened', async functio } else { recent = { fileUri: uri, label }; } - return workspacesHistoryService.addRecentlyOpened([recent]); + return workspacesService.addRecentlyOpened([recent]); }); CommandsRegistry.registerCommand('_workbench.getRecentlyOpened', async function (accessor: ServicesAccessor) { - const workspacesHistoryService = accessor.get(IWorkspacesHistoryService); - return workspacesHistoryService.getRecentlyOpened(); + const workspacesService = accessor.get(IWorkspacesService); + return workspacesService.getRecentlyOpened(); }); export class SetEditorLayoutAPICommand { diff --git a/src/vs/workbench/api/node/extHostCLIServer.ts b/src/vs/workbench/api/node/extHostCLIServer.ts index d6d6c86972..d476e88f5e 100644 --- a/src/vs/workbench/api/node/extHostCLIServer.ts +++ b/src/vs/workbench/api/node/extHostCLIServer.ts @@ -7,9 +7,10 @@ import { generateRandomPipeName } from 'vs/base/parts/ipc/node/ipc.net'; import * as http from 'http'; import * as fs from 'fs'; import { IExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; -import { IWindowOpenable, IOpenInWindowOptions } from 'vs/platform/windows/common/windows'; +import { IWindowOpenable } from 'vs/platform/windows/common/windows'; import { URI } from 'vs/base/common/uri'; import { hasWorkspaceFileExtension } from 'vs/platform/workspaces/common/workspaces'; +import { INativeOpenInWindowOptions } from 'vs/platform/windows/node/window'; export interface OpenCommandPipeArgs { type: 'open'; @@ -126,7 +127,7 @@ export class CLIServer { } if (urisToOpen.length) { const waitMarkerFileURI = waitMarkerFilePath ? URI.file(waitMarkerFilePath) : undefined; - const windowOpenArgs: IOpenInWindowOptions = { forceNewWindow, diffMode, addMode, gotoLineMode, forceReuseWindow, waitMarkerFileURI }; + const windowOpenArgs: INativeOpenInWindowOptions = { forceNewWindow, diffMode, addMode, gotoLineMode, forceReuseWindow, waitMarkerFileURI }; this._commands.executeCommand('_files.windowOpen', urisToOpen, windowOpenArgs); } res.writeHead(200); diff --git a/src/vs/workbench/browser/actions/windowActions.ts b/src/vs/workbench/browser/actions/windowActions.ts index a8c6dcd1c6..6ec04a6cd7 100644 --- a/src/vs/workbench/browser/actions/windowActions.ts +++ b/src/vs/workbench/browser/actions/windowActions.ts @@ -8,7 +8,6 @@ import 'vs/css!./media/actions'; import * as nls from 'vs/nls'; import { Action } from 'vs/base/common/actions'; import { IWindowOpenable } from 'vs/platform/windows/common/windows'; -import { IWorkspacesHistoryService } from 'vs/workbench/services/workspace/common/workspacesHistoryService'; 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'; @@ -22,7 +21,7 @@ import { ILabelService } from 'vs/platform/label/common/label'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IModelService } from 'vs/editor/common/services/modelService'; import { IModeService } from 'vs/editor/common/services/modeService'; -import { IRecentWorkspace, IRecentFolder, IRecentFile, IRecent, isRecentFolder, isRecentWorkspace } from 'vs/platform/workspaces/common/workspacesHistory'; +import { IRecentWorkspace, IRecentFolder, IRecentFile, IRecent, isRecentFolder, isRecentWorkspace, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; import { URI } from 'vs/base/common/uri'; import { getIconClasses } from 'vs/editor/common/services/getIconClasses'; import { FileKind } from 'vs/platform/files/common/files'; @@ -45,7 +44,7 @@ abstract class BaseOpenRecentAction extends Action { constructor( id: string, label: string, - private workspacesHistoryService: IWorkspacesHistoryService, + private workspacesService: IWorkspacesService, private quickInputService: IQuickInputService, private contextService: IWorkspaceContextService, private labelService: ILabelService, @@ -60,7 +59,7 @@ abstract class BaseOpenRecentAction extends Action { protected abstract isQuickNavigate(): boolean; async run(): Promise { - const { workspaces, files } = await this.workspacesHistoryService.getRecentlyOpened(); + const { workspaces, files } = await this.workspacesService.getRecentlyOpened(); this.openRecent(workspaces, files); } @@ -130,7 +129,7 @@ abstract class BaseOpenRecentAction extends Action { onKeyMods: mods => keyMods = mods, quickNavigate: this.isQuickNavigate() ? { keybindings: this.keybindingService.lookupKeybindings(this.id) } : undefined, onDidTriggerItemButton: async context => { - await this.workspacesHistoryService.removeFromRecentlyOpened([context.item.resource]); + await this.workspacesService.removeFromRecentlyOpened([context.item.resource]); context.removeItem(); } }); @@ -149,7 +148,7 @@ export class OpenRecentAction extends BaseOpenRecentAction { constructor( id: string, label: string, - @IWorkspacesHistoryService workspacesHistoryService: IWorkspacesHistoryService, + @IWorkspacesService workspacesService: IWorkspacesService, @IQuickInputService quickInputService: IQuickInputService, @IWorkspaceContextService contextService: IWorkspaceContextService, @IKeybindingService keybindingService: IKeybindingService, @@ -158,7 +157,7 @@ export class OpenRecentAction extends BaseOpenRecentAction { @ILabelService labelService: ILabelService, @IHostService hostService: IHostService ) { - super(id, label, workspacesHistoryService, quickInputService, contextService, labelService, keybindingService, modelService, modeService, hostService); + super(id, label, workspacesService, quickInputService, contextService, labelService, keybindingService, modelService, modeService, hostService); } protected isQuickNavigate(): boolean { @@ -174,7 +173,7 @@ class QuickOpenRecentAction extends BaseOpenRecentAction { constructor( id: string, label: string, - @IWorkspacesHistoryService workspacesHistoryService: IWorkspacesHistoryService, + @IWorkspacesService workspacesService: IWorkspacesService, @IQuickInputService quickInputService: IQuickInputService, @IWorkspaceContextService contextService: IWorkspaceContextService, @IKeybindingService keybindingService: IKeybindingService, @@ -183,7 +182,7 @@ class QuickOpenRecentAction extends BaseOpenRecentAction { @ILabelService labelService: ILabelService, @IHostService hostService: IHostService ) { - super(id, label, workspacesHistoryService, quickInputService, contextService, labelService, keybindingService, modelService, modeService, hostService); + super(id, label, workspacesService, quickInputService, contextService, labelService, keybindingService, modelService, modeService, hostService); } protected isQuickNavigate(): boolean { diff --git a/src/vs/workbench/browser/actions/workspaceActions.ts b/src/vs/workbench/browser/actions/workspaceActions.ts index f1b9e6fbc9..a050926693 100644 --- a/src/vs/workbench/browser/actions/workspaceActions.ts +++ b/src/vs/workbench/browser/actions/workspaceActions.ts @@ -7,7 +7,7 @@ import { Action } from 'vs/base/common/actions'; import * as nls from 'vs/nls'; 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 { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; 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'; @@ -200,7 +200,7 @@ 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); -registry.registerWorkbenchAction(new SyncActionDescriptor(CloseWorkspaceAction, CloseWorkspaceAction.ID, CloseWorkspaceAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_F) }), 'File: Close Workspace', workspacesCategory, SupportsWorkspacesContext); +registry.registerWorkbenchAction(new SyncActionDescriptor(CloseWorkspaceAction, CloseWorkspaceAction.ID, CloseWorkspaceAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_F) }), 'Workspaces: Close Workspace', workspacesCategory, SupportsWorkspacesContext); // --- Menu Registration diff --git a/src/vs/workbench/browser/actions/workspaceCommands.ts b/src/vs/workbench/browser/actions/workspaceCommands.ts index 9d4ef206f4..e46a59ad27 100644 --- a/src/vs/workbench/browser/actions/workspaceCommands.ts +++ b/src/vs/workbench/browser/actions/workspaceCommands.ts @@ -5,7 +5,7 @@ import * as nls from 'vs/nls'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; +import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing'; import * as resources from 'vs/base/common/resources'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { CancellationToken } from 'vs/base/common/cancellation'; diff --git a/src/vs/workbench/browser/dnd.ts b/src/vs/workbench/browser/dnd.ts index e2247b3447..479dbceabf 100644 --- a/src/vs/workbench/browser/dnd.ts +++ b/src/vs/workbench/browser/dnd.ts @@ -3,12 +3,11 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { hasWorkspaceFileExtension, IWorkspaceFolderCreationData } from 'vs/platform/workspaces/common/workspaces'; +import { hasWorkspaceFileExtension, IWorkspaceFolderCreationData, IRecentFile, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; import { normalize } from 'vs/base/common/path'; import { basename } from 'vs/base/common/resources'; import { IFileService } from 'vs/platform/files/common/files'; import { IWindowOpenable } from 'vs/platform/windows/common/windows'; -import { IWorkspacesHistoryService } from 'vs/workbench/services/workspace/common/workspacesHistoryService'; import { URI } from 'vs/base/common/uri'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; @@ -29,8 +28,7 @@ import { IEditorService, IResourceEditor } from 'vs/workbench/services/editor/co import { Disposable } from 'vs/base/common/lifecycle'; import { addDisposableListener, EventType } from 'vs/base/browser/dom'; import { IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { IRecentFile } from 'vs/platform/workspaces/common/workspacesHistory'; -import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; +import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing'; import { withNullAsUndefined } from 'vs/base/common/types'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; import { IHostService } from 'vs/workbench/services/host/browser/host'; @@ -162,7 +160,7 @@ export class ResourcesDropHandler { constructor( private options: IResourcesDropHandlerOptions, @IFileService private readonly fileService: IFileService, - @IWorkspacesHistoryService private readonly workspacesHistoryService: IWorkspacesHistoryService, + @IWorkspacesService private readonly workspacesService: IWorkspacesService, @ITextFileService private readonly textFileService: ITextFileService, @IBackupFileService private readonly backupFileService: IBackupFileService, @IUntitledEditorService private readonly untitledEditorService: IUntitledEditorService, @@ -192,7 +190,7 @@ export class ResourcesDropHandler { // Add external ones to recently open list unless dropped resource is a workspace const recentFiles: IRecentFile[] = untitledOrFileResources.filter(d => d.isExternal && d.resource.scheme === Schemas.file).map(d => ({ fileUri: d.resource })); if (recentFiles.length) { - this.workspacesHistoryService.addRecentlyOpened(recentFiles); + this.workspacesService.addRecentlyOpened(recentFiles); } const editors: IResourceEditor[] = untitledOrFileResources.map(untitledOrFileResource => ({ diff --git a/src/vs/workbench/browser/parts/editor/editorActions.ts b/src/vs/workbench/browser/parts/editor/editorActions.ts index c8e6a7cf9b..8d85a1a61a 100644 --- a/src/vs/workbench/browser/parts/editor/editorActions.ts +++ b/src/vs/workbench/browser/parts/editor/editorActions.ts @@ -16,12 +16,12 @@ import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { ICommandService } from 'vs/platform/commands/common/commands'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; -import { IWorkspacesHistoryService } from 'vs/workbench/services/workspace/common/workspacesHistoryService'; import { CLOSE_EDITOR_COMMAND_ID, NAVIGATE_ALL_EDITORS_GROUP_PREFIX, MOVE_ACTIVE_EDITOR_COMMAND_ID, NAVIGATE_IN_ACTIVE_GROUP_PREFIX, ActiveEditorMoveArguments, SPLIT_EDITOR_LEFT, SPLIT_EDITOR_RIGHT, SPLIT_EDITOR_UP, SPLIT_EDITOR_DOWN, splitEditor, LAYOUT_EDITOR_GROUPS_COMMAND_ID, mergeAllGroups } from 'vs/workbench/browser/parts/editor/editorCommands'; import { IEditorGroupsService, IEditorGroup, GroupsArrangement, EditorsOrder, GroupLocation, GroupDirection, preferredSideBySideGroupDirection, IFindGroupScope, GroupOrientation, EditorGroupLayout, GroupsOrder } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IEditorService, SIDE_GROUP } from 'vs/workbench/services/editor/common/editorService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { DisposableStore } from 'vs/base/common/lifecycle'; +import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; export class ExecuteCommandAction extends Action { @@ -1218,7 +1218,7 @@ export class ClearRecentFilesAction extends Action { constructor( id: string, label: string, - @IWorkspacesHistoryService private readonly workspacesHistoryService: IWorkspacesHistoryService, + @IWorkspacesService private readonly workspacesService: IWorkspacesService, @IHistoryService private readonly historyService: IHistoryService ) { super(id, label); @@ -1227,7 +1227,7 @@ export class ClearRecentFilesAction extends Action { run(): Promise { // Clear global recently opened - this.workspacesHistoryService.clearRecentlyOpened(); + this.workspacesService.clearRecentlyOpened(); // Clear workspace specific recently opened this.historyService.clearRecentlyOpened(); diff --git a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts index f42963e701..fb724aa2e7 100644 --- a/src/vs/workbench/browser/parts/titlebar/menubarControl.ts +++ b/src/vs/workbench/browser/parts/titlebar/menubarControl.ts @@ -7,7 +7,6 @@ import * as nls from 'vs/nls'; import { IMenuService, MenuId, IMenu, SubmenuItemAction } from 'vs/platform/actions/common/actions'; import { registerThemingParticipant, ITheme, ICssStyleCollector, IThemeService } from 'vs/platform/theme/common/themeService'; import { MenuBarVisibility, getTitleBarStyle, IWindowOpenable } from 'vs/platform/windows/common/windows'; -import { IWorkspacesHistoryService } from 'vs/workbench/services/workspace/common/workspacesHistoryService'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IAction, Action } from 'vs/base/common/actions'; import { Separator } from 'vs/base/browser/ui/actionbar/actionbar'; @@ -17,7 +16,7 @@ import { isMacintosh, isWeb } from 'vs/base/common/platform'; import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; import { Event, Emitter } from 'vs/base/common/event'; import { Disposable } from 'vs/base/common/lifecycle'; -import { IRecentlyOpened, isRecentFolder, IRecent, isRecentWorkspace } from 'vs/platform/workspaces/common/workspacesHistory'; +import { IRecentlyOpened, isRecentFolder, IRecent, isRecentWorkspace, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; import { RunOnceScheduler } from 'vs/base/common/async'; import { MENUBAR_SELECTION_FOREGROUND, MENUBAR_SELECTION_BACKGROUND, MENUBAR_SELECTION_BORDER, TITLE_BAR_ACTIVE_FOREGROUND, TITLE_BAR_INACTIVE_FOREGROUND, ACTIVITY_BAR_FOREGROUND, ACTIVITY_BAR_INACTIVE_FOREGROUND } from 'vs/workbench/common/theme'; import { URI } from 'vs/base/common/uri'; @@ -86,7 +85,7 @@ export abstract class MenubarControl extends Disposable { constructor( protected readonly menuService: IMenuService, - protected readonly workspacesHistoryService: IWorkspacesHistoryService, + protected readonly workspacesService: IWorkspacesService, protected readonly contextKeyService: IContextKeyService, protected readonly keybindingService: IKeybindingService, protected readonly configurationService: IConfigurationService, @@ -129,7 +128,7 @@ export abstract class MenubarControl extends Disposable { this.updateService.onStateChange(() => this.updateMenubar()); // Listen for changes in recently opened menu - this._register(this.workspacesHistoryService.onRecentlyOpenedChange(() => { this.onRecentlyOpenedChange(); })); + this._register(this.workspacesService.onRecentlyOpenedChange(() => { this.onRecentlyOpenedChange(); })); // Listen to keybindings change this._register(this.keybindingService.onDidUpdateKeybindings(() => this.updateMenubar())); @@ -191,7 +190,7 @@ export abstract class MenubarControl extends Disposable { } private onRecentlyOpenedChange(): void { - this.workspacesHistoryService.getRecentlyOpened().then(recentlyOpened => { + this.workspacesService.getRecentlyOpened().then(recentlyOpened => { this.recentlyOpened = recentlyOpened; this.updateMenubar(); }); @@ -271,7 +270,7 @@ export class CustomMenubarControl extends MenubarControl { constructor( @IMenuService menuService: IMenuService, - @IWorkspacesHistoryService workspacesHistoryService: IWorkspacesHistoryService, + @IWorkspacesService workspacesService: IWorkspacesService, @IContextKeyService contextKeyService: IContextKeyService, @IKeybindingService keybindingService: IKeybindingService, @IConfigurationService configurationService: IConfigurationService, @@ -291,7 +290,7 @@ export class CustomMenubarControl extends MenubarControl { super( menuService, - workspacesHistoryService, + workspacesService, contextKeyService, keybindingService, configurationService, @@ -308,7 +307,7 @@ export class CustomMenubarControl extends MenubarControl { this._onVisibilityChange = this._register(new Emitter()); this._onFocusStateChange = this._register(new Emitter()); - this.workspacesHistoryService.getRecentlyOpened().then((recentlyOpened) => { + this.workspacesService.getRecentlyOpened().then((recentlyOpened) => { this.recentlyOpened = recentlyOpened; }); diff --git a/src/vs/workbench/browser/style.ts b/src/vs/workbench/browser/style.ts index 20ca2d89cf..b19321d391 100644 --- a/src/vs/workbench/browser/style.ts +++ b/src/vs/workbench/browser/style.ts @@ -7,7 +7,9 @@ import 'vs/css!./media/style'; import { registerThemingParticipant, ITheme, ICssStyleCollector, HIGH_CONTRAST } from 'vs/platform/theme/common/themeService'; 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'; +import { WORKBENCH_BACKGROUND, TITLE_BAR_ACTIVE_BACKGROUND } from 'vs/workbench/common/theme'; +import { isWeb } from 'vs/base/common/platform'; +import { createMetaElement } from 'vs/base/browser/dom'; registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { @@ -143,4 +145,19 @@ registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => { `); } + // Update based on selected theme + if (isWeb) { + const titleBackground = theme.getColor(TITLE_BAR_ACTIVE_BACKGROUND); + if (titleBackground) { + const metaElementId = 'monaco-workbench-meta-theme-color'; + let metaElement = document.getElementById(metaElementId) as HTMLMetaElement | null; + if (!metaElement) { + metaElement = createMetaElement(); + metaElement.name = 'theme-color'; + metaElement.id = metaElementId; + } + + metaElement.content = titleBackground.toString(); + } + } }); diff --git a/src/vs/workbench/browser/web.main.ts b/src/vs/workbench/browser/web.main.ts index 3e63daf9fc..428c08b4ee 100644 --- a/src/vs/workbench/browser/web.main.ts +++ b/src/vs/workbench/browser/web.main.ts @@ -33,7 +33,7 @@ import { ConfigurationCache } from 'vs/workbench/services/configuration/browser/ import { ISignService } from 'vs/platform/sign/common/sign'; import { SignService } from 'vs/platform/sign/browser/signService'; import { hash } from 'vs/base/common/hash'; -import { IWorkbenchConstructionOptions } from 'vs/workbench/workbench.web.api'; +import { IWorkbenchConstructionOptions, IWorkspace } from 'vs/workbench/workbench.web.api'; import { FileUserDataProvider } from 'vs/workbench/services/userData/common/fileUserDataProvider'; import { BACKUPS } from 'vs/platform/environment/common/environment'; import { joinPath } from 'vs/base/common/resources'; @@ -47,6 +47,7 @@ import { FileLogService } from 'vs/platform/log/common/fileLogService'; import { toLocalISOString } from 'vs/base/common/date'; import { IndexedDBLogProvider } from 'vs/workbench/services/log/browser/indexedDBLogProvider'; import { InMemoryLogProvider } from 'vs/workbench/services/log/common/inMemoryLogProvider'; +import { isWorkspaceToOpen, isFolderToOpen } from 'vs/platform/windows/common/windows'; class BrowserMain extends Disposable { @@ -284,15 +285,27 @@ class BrowserMain extends Disposable { } private resolveWorkspaceInitializationPayload(): IWorkspaceInitializationPayload { + let workspace: IWorkspace | undefined = undefined; + if (this.configuration.workspaceProvider) { + workspace = this.configuration.workspaceProvider.workspace; + } else { + // TODO@ben remove me once IWorkspaceProvider API is adopted + const legacyConfiguration = this.configuration as { workspaceUri?: URI, folderUri?: URI }; + if (legacyConfiguration.workspaceUri) { + workspace = { workspaceUri: legacyConfiguration.workspaceUri }; + } else if (legacyConfiguration.folderUri) { + workspace = { folderUri: legacyConfiguration.folderUri }; + } + } // Multi-root workspace - if (this.configuration.workspaceUri) { - return { id: hash(this.configuration.workspaceUri.toString()).toString(16), configPath: this.configuration.workspaceUri }; + if (workspace && isWorkspaceToOpen(workspace)) { + return { id: hash(workspace.workspaceUri.toString()).toString(16), configPath: workspace.workspaceUri }; } // Single-folder workspace - if (this.configuration.folderUri) { - return { id: hash(this.configuration.folderUri.toString()).toString(16), folder: this.configuration.folderUri }; + if (workspace && isFolderToOpen(workspace)) { + return { id: hash(workspace.folderUri.toString()).toString(16), folder: workspace.folderUri }; } return { id: 'empty-window' }; diff --git a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts index 096f0031d7..f03be25070 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.contribution.ts @@ -5,7 +5,7 @@ import * as nls from 'vs/nls'; import { Registry } from 'vs/platform/registry/common/platform'; -import { ToggleAutoSaveAction, GlobalNewUntitledFileAction, ShowOpenedFileInNewWindow, FocusFilesExplorer, GlobalCompareResourcesAction, SaveAllAction, ShowActiveFileInExplorer, CollapseExplorerView, RefreshExplorerView, CompareWithClipboardAction, NEW_FILE_COMMAND_ID, NEW_FILE_LABEL, NEW_FOLDER_COMMAND_ID, NEW_FOLDER_LABEL, TRIGGER_RENAME_LABEL, MOVE_FILE_TO_TRASH_LABEL, COPY_FILE_LABEL, PASTE_FILE_LABEL, FileCopiedContext, renameHandler, moveFileToTrashHandler, copyFileHandler, pasteFileHandler, deleteFileHandler, cutFileHandler, DOWNLOAD_COMMAND_ID, GlobalNewUntitledPlainFileAction, openFilePreserveFocusHandler } from 'vs/workbench/contrib/files/browser/fileActions'; +import { ToggleAutoSaveAction, GlobalNewUntitledFileAction, FocusFilesExplorer, GlobalCompareResourcesAction, SaveAllAction, ShowActiveFileInExplorer, CollapseExplorerView, RefreshExplorerView, CompareWithClipboardAction, NEW_FILE_COMMAND_ID, NEW_FILE_LABEL, NEW_FOLDER_COMMAND_ID, NEW_FOLDER_LABEL, TRIGGER_RENAME_LABEL, MOVE_FILE_TO_TRASH_LABEL, COPY_FILE_LABEL, PASTE_FILE_LABEL, FileCopiedContext, renameHandler, moveFileToTrashHandler, copyFileHandler, pasteFileHandler, deleteFileHandler, cutFileHandler, DOWNLOAD_COMMAND_ID, openFilePreserveFocusHandler, GlobalNewUntitledPlainFileAction } from 'vs/workbench/contrib/files/browser/fileActions'; // {{SQL CARBON EDIT}} -- Add 'New File' command for plain untitled files import { revertLocalChangesCommand, acceptLocalChangesCommand, CONFLICT_RESOLUTION_CONTEXT } from 'vs/workbench/contrib/files/browser/saveErrorHandler'; import { SyncActionDescriptor, MenuId, MenuRegistry, ILocalizedString } from 'vs/platform/actions/common/actions'; import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; @@ -41,12 +41,9 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(ShowActiveFileInExplor registry.registerWorkbenchAction(new SyncActionDescriptor(CollapseExplorerView, CollapseExplorerView.ID, CollapseExplorerView.LABEL), 'File: Collapse Folders in Explorer', category.value); registry.registerWorkbenchAction(new SyncActionDescriptor(RefreshExplorerView, RefreshExplorerView.ID, RefreshExplorerView.LABEL), 'File: Refresh Explorer', category.value); registry.registerWorkbenchAction(new SyncActionDescriptor(GlobalNewUntitledFileAction, GlobalNewUntitledFileAction.ID, GlobalNewUntitledFileAction.LABEL, { primary: KeyMod.CtrlCmd | KeyCode.KEY_N }), 'File: New Untitled File', category.value); -// {{SQL CARBON EDIT}} -- Add 'New File' command for plain untitled files -registry.registerWorkbenchAction(new SyncActionDescriptor(GlobalNewUntitledPlainFileAction, GlobalNewUntitledPlainFileAction.ID, GlobalNewUntitledPlainFileAction.LABEL), 'File: New Plain Text File', category.value); -registry.registerWorkbenchAction(new SyncActionDescriptor(ShowOpenedFileInNewWindow, ShowOpenedFileInNewWindow.ID, ShowOpenedFileInNewWindow.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_O) }), 'File: Open Active File in New Window', category.value); registry.registerWorkbenchAction(new SyncActionDescriptor(CompareWithClipboardAction, CompareWithClipboardAction.ID, CompareWithClipboardAction.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_C) }), 'File: Compare Active File with Clipboard', category.value); registry.registerWorkbenchAction(new SyncActionDescriptor(ToggleAutoSaveAction, ToggleAutoSaveAction.ID, ToggleAutoSaveAction.LABEL), 'File: Toggle Auto Save', category.value); - +registry.registerWorkbenchAction(new SyncActionDescriptor(GlobalNewUntitledPlainFileAction, GlobalNewUntitledPlainFileAction.ID, GlobalNewUntitledPlainFileAction.LABEL), 'File: New Plain Text File', category.value); // {{SQL CARBON EDIT}} -- Add 'New File' command for plain untitled files const workspacesCategory = nls.localize('workspaces', "Workspaces"); registry.registerWorkbenchAction(new SyncActionDescriptor(OpenWorkspaceAction, OpenWorkspaceAction.ID, OpenWorkspaceAction.LABEL), 'Workspaces: Open Workspace...', workspacesCategory); diff --git a/src/vs/workbench/contrib/files/browser/fileCommands.ts b/src/vs/workbench/contrib/files/browser/fileCommands.ts index f652dfe4df..4e48bf9e3a 100644 --- a/src/vs/workbench/contrib/files/browser/fileCommands.ts +++ b/src/vs/workbench/contrib/files/browser/fileCommands.ts @@ -29,7 +29,7 @@ import { KeyMod, KeyCode, KeyChord } from 'vs/base/common/keyCodes'; import { isWindows } from 'vs/base/common/platform'; import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { getResourceForCommand, getMultiSelectedResources } from 'vs/workbench/contrib/files/browser/files'; -import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; +import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing'; import { getMultiSelectedEditorContexts } from 'vs/workbench/browser/parts/editor/editorCommands'; import { Schemas } from 'vs/base/common/network'; import { INotificationService } from 'vs/platform/notification/common/notification'; diff --git a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts index b35be0b9b0..b6527bb554 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerViewer.ts @@ -39,7 +39,7 @@ import { isMacintosh } from 'vs/base/common/platform'; import { IDialogService, IConfirmationResult, IConfirmation, getConfirmMessage } from 'vs/platform/dialogs/common/dialogs'; import { ITextFileService, ITextFileOperationResult } from 'vs/workbench/services/textfile/common/textfiles'; import { IHostService } from 'vs/workbench/services/host/browser/host'; -import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; +import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing'; import { URI } from 'vs/base/common/uri'; import { ITask, sequence } from 'vs/base/common/async'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; diff --git a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts index a1d3b67226..2ee4fd13a7 100644 --- a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts +++ b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts @@ -244,8 +244,9 @@ export class OpenEditorsView extends ViewletPanel { this.dirtyEditorFocusedContext.reset(); const element = e.elements.length ? e.elements[0] : undefined; if (element instanceof OpenEditor) { - this.dirtyEditorFocusedContext.set(this.textFileService.isDirty(withNullAsUndefined(element.getResource()))); - this.resourceContext.set(withUndefinedAsNull(element.getResource())); + const resource = element.getResource(); + this.dirtyEditorFocusedContext.set(this.textFileService.isDirty(resource)); + this.resourceContext.set(withUndefinedAsNull(resource)); } else if (!!element) { this.groupFocusedContext.set(true); } diff --git a/src/vs/workbench/contrib/files/electron-browser/fileActions.contribution.ts b/src/vs/workbench/contrib/files/electron-browser/fileActions.contribution.ts index b2a0cbfd4f..2008e6ad2e 100644 --- a/src/vs/workbench/contrib/files/electron-browser/fileActions.contribution.ts +++ b/src/vs/workbench/contrib/files/electron-browser/fileActions.contribution.ts @@ -18,9 +18,12 @@ import { getMultiSelectedResources } from 'vs/workbench/contrib/files/browser/fi import { IListService } from 'vs/platform/list/browser/listService'; import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { revealResourcesInOS } from 'vs/workbench/contrib/files/electron-browser/fileCommands'; -import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions'; +import { MenuRegistry, MenuId, SyncActionDescriptor } from 'vs/platform/actions/common/actions'; import { ResourceContextKey } from 'vs/workbench/common/resources'; import { appendToCommandPalette, appendEditorTitleContextMenuItem } from 'vs/workbench/contrib/files/browser/fileActions.contribution'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions'; +import { ShowOpenedFileInNewWindow } from 'vs/workbench/contrib/files/browser/fileActions'; const REVEAL_IN_OS_COMMAND_ID = 'revealFileInOS'; const REVEAL_IN_OS_LABEL = isWindows ? nls.localize('revealInWindows', "Reveal in Explorer") : isMacintosh ? nls.localize('revealInMac', "Reveal in Finder") : nls.localize('openContainer', "Open Containing Folder"); @@ -81,3 +84,6 @@ MenuRegistry.appendMenuItem(MenuId.ExplorerContext, { const category = { value: nls.localize('filesCategory', "File"), original: 'File' }; appendToCommandPalette(REVEAL_IN_OS_COMMAND_ID, { value: REVEAL_IN_OS_LABEL, original: isWindows ? 'Reveal in Explorer' : isMacintosh ? 'Reveal in Finder' : 'Open Containing Folder' }, category); + +const registry = Registry.as(ActionExtensions.WorkbenchActions); +registry.registerWorkbenchAction(new SyncActionDescriptor(ShowOpenedFileInNewWindow, ShowOpenedFileInNewWindow.ID, ShowOpenedFileInNewWindow.LABEL, { primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_O) }), 'File: Open Active File in New Window', category.value); diff --git a/src/vs/workbench/contrib/logs/common/logs.contribution.ts b/src/vs/workbench/contrib/logs/common/logs.contribution.ts index 671b2ee84e..f0e8009fd9 100644 --- a/src/vs/workbench/contrib/logs/common/logs.contribution.ts +++ b/src/vs/workbench/contrib/logs/common/logs.contribution.ts @@ -58,7 +58,7 @@ class LogOutputChannels extends Disposable implements IWorkbenchContribution { const workbenchActionsRegistry = Registry.as(WorkbenchActionExtensions.WorkbenchActions); const devCategory = nls.localize('developer', "Developer"); - workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenWindowSessionLogFileAction, OpenWindowSessionLogFileAction.ID, OpenWindowSessionLogFileAction.LABEL), 'Developer: Open Browser Log File (Session)...', devCategory); + workbenchActionsRegistry.registerWorkbenchAction(new SyncActionDescriptor(OpenWindowSessionLogFileAction, OpenWindowSessionLogFileAction.ID, OpenWindowSessionLogFileAction.LABEL), 'Developer: Open Window Log File (Session)...', devCategory); } private registerNativeContributions(): void { diff --git a/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts b/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts index c410a88272..35df5e1a7a 100644 --- a/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts +++ b/src/vs/workbench/contrib/preferences/browser/keybindingsEditor.ts @@ -317,7 +317,7 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor const fullTextSearchPlaceholder = localize('SearchKeybindings.FullTextSearchPlaceholder', "Type to search in keybindings"); const keybindingsSearchPlaceholder = localize('SearchKeybindings.KeybindingsSearchPlaceholder', "Recording Keys. Press Escape to exit"); - const clearInputAction = new Action(KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, localize('clearInput', "Clear Keybindings Search Input"), 'clear-input', false, () => { this.search(''); return Promise.resolve(null); }); + const clearInputAction = new Action(KEYBINDINGS_EDITOR_COMMAND_CLEAR_SEARCH_RESULTS, localize('clearInput', "Clear Keybindings Search Input"), 'codicon-clear-all', false, () => { this.search(''); return Promise.resolve(null); }); const searchContainer = DOM.append(this.headerContainer, $('.search-container')); this.searchWidget = this._register(this.instantiationService.createInstance(KeybindingsSearchWidget, searchContainer, { @@ -340,7 +340,7 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor const sortByPrecedenceActionKeybinding = this.keybindingsService.lookupKeybinding(KEYBINDINGS_EDITOR_COMMAND_SORTBY_PRECEDENCE); const sortByPrecedenceActionLabel = localize('sortByPrecedeneLabel', "Sort by Precedence"); - this.sortByPrecedenceAction = new Action('keybindings.editor.sortByPrecedence', sortByPrecedenceActionKeybinding ? localize('sortByPrecedeneLabelWithKeybinding', "{0} ({1})", sortByPrecedenceActionLabel, sortByPrecedenceActionKeybinding.getLabel()) : sortByPrecedenceActionLabel, 'sort-by-precedence'); + this.sortByPrecedenceAction = new Action('keybindings.editor.sortByPrecedence', sortByPrecedenceActionKeybinding ? localize('sortByPrecedeneLabelWithKeybinding', "{0} ({1})", sortByPrecedenceActionLabel, sortByPrecedenceActionKeybinding.getLabel()) : sortByPrecedenceActionLabel, 'codicon-sort-precedence'); this.sortByPrecedenceAction.checked = false; this._register(this.sortByPrecedenceAction.onDidChange(e => { if (e.checked !== undefined) { @@ -351,7 +351,7 @@ export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor const recordKeysActionKeybinding = this.keybindingsService.lookupKeybinding(KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS); const recordKeysActionLabel = localize('recordKeysLabel', "Record Keys"); - this.recordKeysAction = new Action(KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS, recordKeysActionKeybinding ? localize('recordKeysLabelWithKeybinding', "{0} ({1})", recordKeysActionLabel, recordKeysActionKeybinding.getLabel()) : recordKeysActionLabel, 'record-keys'); + this.recordKeysAction = new Action(KEYBINDINGS_EDITOR_COMMAND_RECORD_SEARCH_KEYS, recordKeysActionKeybinding ? localize('recordKeysLabelWithKeybinding', "{0} ({1})", recordKeysActionLabel, recordKeysActionKeybinding.getLabel()) : recordKeysActionLabel, 'codicon-record-keys'); this.recordKeysAction.checked = false; this._register(this.recordKeysAction.onDidChange(e => { if (e.checked !== undefined) { @@ -899,7 +899,7 @@ class ActionsColumn extends Column { private createAddAction(keybindingItemEntry: IKeybindingItemEntry): IAction { const keybinding = this.keybindingsService.lookupKeybinding(KEYBINDINGS_EDITOR_COMMAND_DEFINE); return { - class: 'add', + class: 'codicon-add', enabled: true, id: 'addKeybinding', tooltip: keybinding ? localize('addKeybindingLabelWithKey', "Add Keybinding {0}", `(${keybinding.getLabel()})`) : localize('addKeybindingLabel', "Add Keybinding"), diff --git a/src/vs/workbench/contrib/preferences/browser/media/add-dark.svg b/src/vs/workbench/contrib/preferences/browser/media/add-dark.svg deleted file mode 100644 index 4d9389336b..0000000000 --- a/src/vs/workbench/contrib/preferences/browser/media/add-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/preferences/browser/media/add-light.svg b/src/vs/workbench/contrib/preferences/browser/media/add-light.svg deleted file mode 100644 index 01a9de7d5a..0000000000 --- a/src/vs/workbench/contrib/preferences/browser/media/add-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/preferences/browser/media/check-dark.svg b/src/vs/workbench/contrib/preferences/browser/media/check-dark.svg deleted file mode 100644 index 51674695e1..0000000000 --- a/src/vs/workbench/contrib/preferences/browser/media/check-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/preferences/browser/media/check-light.svg b/src/vs/workbench/contrib/preferences/browser/media/check-light.svg deleted file mode 100644 index 7b1da6d720..0000000000 --- a/src/vs/workbench/contrib/preferences/browser/media/check-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/preferences/browser/media/clear-dark.svg b/src/vs/workbench/contrib/preferences/browser/media/clear-dark.svg deleted file mode 100644 index 04d64ab41c..0000000000 --- a/src/vs/workbench/contrib/preferences/browser/media/clear-dark.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/src/vs/workbench/contrib/preferences/browser/media/clear-light.svg b/src/vs/workbench/contrib/preferences/browser/media/clear-light.svg deleted file mode 100644 index f6a51c856f..0000000000 --- a/src/vs/workbench/contrib/preferences/browser/media/clear-light.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - diff --git a/src/vs/workbench/contrib/preferences/browser/media/configure-dark.svg b/src/vs/workbench/contrib/preferences/browser/media/configure-dark.svg deleted file mode 100644 index ace01a5ddf..0000000000 --- a/src/vs/workbench/contrib/preferences/browser/media/configure-dark.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/src/vs/workbench/contrib/preferences/browser/media/configure-light.svg b/src/vs/workbench/contrib/preferences/browser/media/configure-light.svg deleted file mode 100644 index 4194780bba..0000000000 --- a/src/vs/workbench/contrib/preferences/browser/media/configure-light.svg +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/src/vs/workbench/contrib/preferences/browser/media/edit-dark.svg b/src/vs/workbench/contrib/preferences/browser/media/edit-dark.svg deleted file mode 100644 index a72757482b..0000000000 --- a/src/vs/workbench/contrib/preferences/browser/media/edit-dark.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/vs/workbench/contrib/preferences/browser/media/edit-light.svg b/src/vs/workbench/contrib/preferences/browser/media/edit-light.svg deleted file mode 100644 index ae71150c0c..0000000000 --- a/src/vs/workbench/contrib/preferences/browser/media/edit-light.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/src/vs/workbench/contrib/preferences/browser/media/keybindingsEditor.css b/src/vs/workbench/contrib/preferences/browser/media/keybindingsEditor.css index 54416c6b54..0eb6f25665 100644 --- a/src/vs/workbench/contrib/preferences/browser/media/keybindingsEditor.css +++ b/src/vs/workbench/contrib/preferences/browser/media/keybindingsEditor.css @@ -45,31 +45,11 @@ margin-right: 4px; } -.keybindings-editor .monaco-action-bar .action-item > .sort-by-precedence { - background: url('sort-precedence-light.svg') center center no-repeat; -} - -.hc-black .keybindings-editor .monaco-action-bar .action-item > .sort-by-precedence, -.vs-dark .keybindings-editor .monaco-action-bar .action-item > .sort-by-precedence { - background: url('sort-precedence-dark.svg') center center no-repeat; -} - -.keybindings-editor .monaco-action-bar .action-item > .record-keys { - background: url('record-keys-light.svg') center center no-repeat; -} - -.hc-black .keybindings-editor .monaco-action-bar .action-item > .record-keys, -.vs-dark .keybindings-editor .monaco-action-bar .action-item > .record-keys { - background: url('record-keys-dark.svg') center center no-repeat; -} - -.keybindings-editor .monaco-action-bar .action-item > .clear-input { - background: url('clear-light.svg') center center no-repeat; -} - -.hc-black .keybindings-editor .monaco-action-bar .action-item > .clear-input, -.vs-dark .keybindings-editor .monaco-action-bar .action-item > .clear-input { - background: url('clear-dark.svg') center center no-repeat; +.keybindings-editor .monaco-action-bar .action-item > .codicon { + display: flex; + align-items: center; + justify-content: center; + color: inherit; } .keybindings-editor > .keybindings-header .open-keybindings-container { @@ -206,22 +186,3 @@ cursor: pointer; margin-top: 3px; } - -.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row > .column .monaco-action-bar .action-item > .icon.edit { - background: url('edit-light.svg') center center no-repeat; - transform: rotate(-90deg); -} - -.hc-black .keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row > .column .monaco-action-bar .action-item > .icon.edit, -.vs-dark .keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row > .column .monaco-action-bar .action-item > .icon.edit { - background: url('edit-dark.svg') center center no-repeat; -} - -.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row > .column .monaco-action-bar .action-item > .icon.add { - background: url('add-light.svg') center center no-repeat; -} - -.hc-black .keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row > .column .monaco-action-bar .action-item > .icon.add, -.vs-dark .keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row > .column .monaco-action-bar .action-item > .icon.add { - background: url('add-dark.svg') center center no-repeat; -} diff --git a/src/vs/workbench/contrib/preferences/browser/media/preferences.css b/src/vs/workbench/contrib/preferences/browser/media/preferences.css index 2ce1ca37c9..4dcf552240 100644 --- a/src/vs/workbench/contrib/preferences/browser/media/preferences.css +++ b/src/vs/workbench/contrib/preferences/browser/media/preferences.css @@ -216,24 +216,18 @@ transform: rotate(-90deg); } -.monaco-editor .edit-preferences-widget { - background: url('edit-light.svg') center center no-repeat; +.monaco-editor .codicon-edit { transform: rotate(-90deg); width:16px; height: 16px; cursor: pointer; } -.monaco-editor .edit-preferences-widget.hidden { +.monaco-editor .codicon-edit.hidden { display: none; visibility: hidden; } -.monaco-editor.hc-black .edit-preferences-widget, -.monaco-editor.vs-dark .edit-preferences-widget { - background: url('edit-dark.svg') center center no-repeat; -} - .monaco-editor .dim-configuration { color: #b1b1b1; } diff --git a/src/vs/workbench/contrib/preferences/browser/media/record-keys-dark.svg b/src/vs/workbench/contrib/preferences/browser/media/record-keys-dark.svg deleted file mode 100644 index 4341206b83..0000000000 --- a/src/vs/workbench/contrib/preferences/browser/media/record-keys-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/preferences/browser/media/record-keys-light.svg b/src/vs/workbench/contrib/preferences/browser/media/record-keys-light.svg deleted file mode 100644 index af98035943..0000000000 --- a/src/vs/workbench/contrib/preferences/browser/media/record-keys-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/preferences/browser/media/remove-dark.svg b/src/vs/workbench/contrib/preferences/browser/media/remove-dark.svg deleted file mode 100644 index 75644595d1..0000000000 --- a/src/vs/workbench/contrib/preferences/browser/media/remove-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/preferences/browser/media/remove-light.svg b/src/vs/workbench/contrib/preferences/browser/media/remove-light.svg deleted file mode 100644 index cf5f28ca35..0000000000 --- a/src/vs/workbench/contrib/preferences/browser/media/remove-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css b/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css index 2be433504b..c8dad70d68 100644 --- a/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css +++ b/src/vs/workbench/contrib/preferences/browser/media/settingsEditor2.css @@ -12,6 +12,10 @@ overflow: hidden; } +.settings-editor .codicon { + color: inherit !important; +} + /* header styling */ .settings-editor > .settings-header { box-sizing: border-box; @@ -88,7 +92,6 @@ .settings-editor > .settings-header > .settings-header-controls .settings-tabs-widget > .monaco-action-bar .action-item .action-label { text-transform: none; font-size: 13px; - padding-bottom: 7px; padding-top: 7px; padding-left: 8px; @@ -166,21 +169,9 @@ transition: opacity .3s; width: 22px; height: 22px; - background-position: center; - background-repeat: no-repeat; - background-size: 16px; -} - -.settings-editor > .settings-body .settings-tree-container .setting-toolbar-container > .monaco-toolbar .codicon-more::before { - content: ' '; -} - -.vs .settings-editor > .settings-body .settings-tree-container .monaco-toolbar .codicon-more { - background-image: url('configure-light.svg'); -} - -.vs-dark .settings-editor > .settings-body .settings-tree-container .monaco-toolbar .codicon-more { - background-image: url('configure-dark.svg'); + display: flex; + align-items: center; + justify-content: center; } .settings-editor > .settings-body .settings-toc-container { @@ -436,16 +427,10 @@ margin-right: 9px; margin-left: 0px; padding: 0px; - background-size: 16px !important; } -.vs .settings-editor > .settings-body > .settings-tree-container .setting-item-bool .setting-value-checkbox.checked { - background: url('check-light.svg') center center no-repeat; -} - -.vs-dark .settings-editor > .settings-body > .settings-tree-container .setting-item-bool .setting-value-checkbox.checked, -.hc-black .settings-editor > .settings-body > .settings-tree-container .setting-item-bool .setting-value-checkbox.checked { - background: url('check-dark.svg') center center no-repeat; +.settings-editor > .settings-body > .settings-tree-container .setting-item-bool .setting-value-checkbox.codicon:not(.checked)::before { + opacity: 0; } .settings-editor > .settings-body > .settings-tree-container .setting-item-contents .setting-item-value { diff --git a/src/vs/workbench/contrib/preferences/browser/media/settingsWidgets.css b/src/vs/workbench/contrib/preferences/browser/media/settingsWidgets.css index c3d773a0a7..189307f96e 100644 --- a/src/vs/workbench/contrib/preferences/browser/media/settingsWidgets.css +++ b/src/vs/workbench/contrib/preferences/browser/media/settingsWidgets.css @@ -70,28 +70,15 @@ height: 20px; padding: 2px; margin-right: 2px; + display: flex; + align-items: center; + justify-content: center; } .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-row .monaco-action-bar .setting-listAction-edit { margin-right: 4px; } -.vs .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-row .monaco-action-bar .setting-listAction-edit { - background: url("edit-light.svg") center center no-repeat; -} - -.vs-dark .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-row .monaco-action-bar .setting-listAction-edit { - background: url("edit-dark.svg") center center no-repeat; -} - -.vs .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-row .monaco-action-bar .setting-listAction-remove { - background: url("remove-light.svg") center center no-repeat; -} - -.vs-dark .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .setting-list-row .monaco-action-bar .setting-listAction-remove { - background: url("remove-dark.svg") center center no-repeat; -} - .settings-editor > .settings-body > .settings-tree-container .setting-item.setting-item-list .monaco-text-button { width: initial; padding: 2px 14px; diff --git a/src/vs/workbench/contrib/preferences/browser/media/sort-precedence-dark.svg b/src/vs/workbench/contrib/preferences/browser/media/sort-precedence-dark.svg deleted file mode 100644 index 6b533e0824..0000000000 --- a/src/vs/workbench/contrib/preferences/browser/media/sort-precedence-dark.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/preferences/browser/media/sort-precedence-light.svg b/src/vs/workbench/contrib/preferences/browser/media/sort-precedence-light.svg deleted file mode 100644 index 494e5e458c..0000000000 --- a/src/vs/workbench/contrib/preferences/browser/media/sort-precedence-light.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts index b8585f5aa0..b1bd5aa4aa 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsTree.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsTree.ts @@ -37,7 +37,7 @@ import { IContextMenuService, IContextViewService } from 'vs/platform/contextvie import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IOpenerService } from 'vs/platform/opener/common/opener'; -import { editorBackground, errorForeground, focusBorder, foreground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground } from 'vs/platform/theme/common/colorRegistry'; +import { errorForeground, focusBorder, foreground, inputValidationErrorBackground, inputValidationErrorBorder, inputValidationErrorForeground } from 'vs/platform/theme/common/colorRegistry'; import { attachButtonStyler, attachInputBoxStyler, attachSelectBoxStyler, attachStyler } from 'vs/platform/theme/common/styler'; import { ICssStyleCollector, ITheme, IThemeService, registerThemingParticipant } from 'vs/platform/theme/common/themeService'; import { ITOCEntry } from 'vs/workbench/contrib/preferences/browser/settingsLayout'; @@ -393,6 +393,13 @@ export abstract class AbstractSettingRenderer extends Disposable implements ITre toggleMenuTitle }); toolbar.setActions([], this.settingActions)(); + + // change icon from ellipsis to gear + let icon = container.querySelector('.codicon-more'); + if (icon) { + (icon).classList.add('codicon-gear'); + } + const button = container.querySelector('.toolbar-toggle-more'); if (button) { (button).tabIndex = -1; @@ -1071,7 +1078,7 @@ export class SettingBoolRenderer extends AbstractSettingRenderer implements ITre const deprecationWarningElement = DOM.append(container, $('.setting-item-deprecation-message')); const toDispose = new DisposableStore(); - const checkbox = new Checkbox({ actionClassName: 'setting-value-checkbox', isChecked: true, title: '', inputActiveOptionBorder: undefined }); + const checkbox = new Checkbox({ actionClassName: 'codicon-check setting-value-checkbox', isChecked: true, title: '', inputActiveOptionBorder: undefined }); controlElement.appendChild(checkbox.domNode); toDispose.add(checkbox); toDispose.add(checkbox.onChange(() => { @@ -1537,20 +1544,20 @@ export class SettingsTree extends ObjectTree { this.getHTMLElement().classList.add(treeClass); this.disposables.push(attachStyler(themeService, { - listActiveSelectionBackground: editorBackground, + listActiveSelectionBackground: 'transparent', listActiveSelectionForeground: foreground, - listFocusAndSelectionBackground: editorBackground, + listFocusAndSelectionBackground: 'transparent', listFocusAndSelectionForeground: foreground, - listFocusBackground: editorBackground, + listFocusBackground: 'transparent', listFocusForeground: foreground, listHoverForeground: foreground, - listHoverBackground: editorBackground, - listHoverOutline: editorBackground, - listFocusOutline: editorBackground, - listInactiveSelectionBackground: editorBackground, + listHoverBackground: 'transparent', + listHoverOutline: 'transparent', + listFocusOutline: 'transparent', + listInactiveSelectionBackground: 'transparent', listInactiveSelectionForeground: foreground, - listInactiveFocusBackground: editorBackground, - listInactiveFocusOutline: editorBackground + listInactiveFocusBackground: 'transparent', + listInactiveFocusOutline: 'transparent' }, colors => { this.style(colors); })); diff --git a/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts b/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts index f38a20fb26..ef02d586a1 100644 --- a/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts +++ b/src/vs/workbench/contrib/preferences/browser/settingsWidgets.ts @@ -352,7 +352,7 @@ export class ListSettingWidget extends Disposable { private createDeleteAction(key: string, idx: number): IAction { return { - class: 'setting-listAction-remove', + class: 'codicon-close', enabled: true, id: 'workbench.action.removeListItem', tooltip: this.getLocalizedStrings().deleteActionTooltip, @@ -362,7 +362,7 @@ export class ListSettingWidget extends Disposable { private createEditAction(idx: number): IAction { return { - class: 'setting-listAction-edit', + class: 'codicon-edit', enabled: true, id: 'workbench.action.editListItem', tooltip: this.getLocalizedStrings().editActionTooltip, diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index 0cadcfa74d..9eaddd1ee0 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -6,6 +6,7 @@ import * as nls from 'vs/nls'; import Severity from 'vs/base/common/severity'; import * as Objects from 'vs/base/common/objects'; +import * as resources from 'vs/base/common/resources'; import { URI } from 'vs/base/common/uri'; import { IStringDictionary } from 'vs/base/common/collections'; import { Action } from 'vs/base/common/actions'; @@ -77,6 +78,7 @@ import { ITextModelService } from 'vs/editor/common/services/resolverService'; import { applyEdits } from 'vs/base/common/jsonEdit'; import { ITextEditor } from 'vs/workbench/common/editor'; import { ITextEditorSelection } from 'vs/platform/editor/common/editor'; +import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; export namespace ConfigureTaskAction { export const ID = 'workbench.action.tasks.configureTaskRunner'; @@ -224,7 +226,8 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService, @ITerminalInstanceService private readonly terminalInstanceService: ITerminalInstanceService, @IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService, - @ITextModelService private readonly textModelResolverService: ITextModelService + @ITextModelService private readonly textModelResolverService: ITextModelService, + @IPreferencesService private readonly preferencesService: IPreferencesService ) { super(); @@ -389,6 +392,13 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer this.panelService.openPanel(Constants.MARKERS_PANEL_ID, true); } }); + + CommandsRegistry.registerCommand('workbench.action.tasks.configureUserTask', async () => { + const resource = this.getResourceForKind(TaskSourceKind.User); + if (resource) { + this.openTaskFile(resource); + } + }); } private get workspaceFolders(): IWorkspaceFolder[] { @@ -963,10 +973,34 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } } + private getResourceForKind(kind: string): URI | undefined { + switch (kind) { + case TaskSourceKind.User: { + return resources.joinPath(resources.dirname(this.preferencesService.userSettingsResource), 'tasks.json'); + } + case TaskSourceKind.WorkspaceFile: { + if (this._workspace && this._workspace.configuration) { + return this._workspace.configuration; + } + } + default: { + return undefined; + } + } + } + + private getResourceForTask(task: CustomTask): URI { + let uri = this.getResourceForKind(task._source.kind); + if (!uri) { + uri = task.getWorkspaceFolder().toResource(task._source.config.file); + } + return uri; + } + public openConfig(task: CustomTask | undefined): Promise { let resource: URI | undefined; if (task) { - resource = task.getWorkspaceFolder().toResource(task._source.config.file); + resource = this.getResourceForTask(task); } else { resource = (this._workspaceFolders && (this._workspaceFolders.length > 0)) ? this._workspaceFolders[0].toResource('.vscode/tasks.json') : undefined; } @@ -2206,6 +2240,52 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return result; } + private openTaskFile(resource: URI) { + let configFileCreated = false; + this.fileService.resolve(resource).then((stat) => stat, () => undefined).then((stat) => { + if (stat) { + return stat.resource; + } + return this.quickInputService.pick(getTaskTemplates(), { placeHolder: nls.localize('TaskService.template', 'Select a Task Template') }).then((selection) => { + if (!selection) { + return Promise.resolve(undefined); + } + let content = selection.content; + let editorConfig = this.configurationService.getValue(); + if (editorConfig.editor.insertSpaces) { + content = content.replace(/(\n)(\t+)/g, (_, s1, s2) => s1 + strings.repeat(' ', s2.length * editorConfig.editor.tabSize)); + } + configFileCreated = true; + type TaskServiceTemplateClassification = { + templateId?: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; + autoDetect: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; + }; + type TaskServiceEvent = { + templateId?: string; + autoDetect: boolean; + }; + return this.textFileService.create(resource, content).then((result): URI => { + this.telemetryService.publicLog2('taskService.template', { + templateId: selection.id, + autoDetect: selection.autoDetect + }); + return result.resource; + }); + }); + }).then((resource) => { + if (!resource) { + return; + } + this.editorService.openEditor({ + resource, + options: { + pinned: configFileCreated // pin only if config file is created #8727 + } + }); + }); + } + + private runConfigureTasks(): void { if (!this.canRunCommand()) { return undefined; @@ -2217,52 +2297,6 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer taskPromise = Promise.resolve(new TaskMap()); } - let openTaskFile = (workspaceFolder: IWorkspaceFolder): void => { - let resource = workspaceFolder.toResource('.vscode/tasks.json'); - let configFileCreated = false; - this.fileService.resolve(resource).then((stat) => stat, () => undefined).then((stat) => { - if (stat) { - return stat.resource; - } - return this.quickInputService.pick(getTaskTemplates(), { placeHolder: nls.localize('TaskService.template', 'Select a Task Template') }).then((selection) => { - if (!selection) { - return Promise.resolve(undefined); - } - let content = selection.content; - let editorConfig = this.configurationService.getValue(); - if (editorConfig.editor.insertSpaces) { - content = content.replace(/(\n)(\t+)/g, (_, s1, s2) => s1 + strings.repeat(' ', s2.length * editorConfig.editor.tabSize)); - } - configFileCreated = true; - type TaskServiceTemplateClassification = { - templateId?: { classification: 'SystemMetaData', purpose: 'FeatureInsight' }; - autoDetect: { classification: 'SystemMetaData', purpose: 'FeatureInsight', isMeasurement: true }; - }; - type TaskServiceEvent = { - templateId?: string; - autoDetect: boolean; - }; - return this.textFileService.create(resource, content).then((result): URI => { - this.telemetryService.publicLog2('taskService.template', { - templateId: selection.id, - autoDetect: selection.autoDetect - }); - return result.resource; - }); - }); - }).then((resource) => { - if (!resource) { - return; - } - this.editorService.openEditor({ - resource, - options: { - pinned: configFileCreated // pin only if config file is created #8727 - } - }); - }); - }; - let configureTask = (task: Task): void => { if (ContributedTask.is(task)) { this.customize(task, undefined, true); @@ -2343,7 +2377,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer if (isTaskEntry(selection)) { configureTask(selection.task); } else { - openTaskFile(selection.folder); + this.openTaskFile(selection.folder.toResource('.vscode/tasks.json')); } }); } diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.contribution.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.contribution.ts index 7168c908ce..a5e1edba21 100644 --- a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.contribution.ts +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.contribution.ts @@ -4,32 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; -import { IUserDataSyncService, SyncStatus, SyncSource, CONTEXT_SYNC_STATE, registerConfiguration } 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 { registerConfiguration } from 'vs/platform/userDataSync/common/userDataSync'; +import { Disposable } from 'vs/base/common/lifecycle'; import { Registry } from 'vs/platform/registry/common/platform'; import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle'; -import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { MenuRegistry, MenuId, IMenuItem } from 'vs/platform/actions/common/actions'; -import { 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 { 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 { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { isEqual } from 'vs/base/common/resources'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { isWeb } from 'vs/base/common/platform'; import { UserDataAutoSync } from 'vs/platform/userDataSync/common/userDataSyncService'; import { IProductService } from 'vs/platform/product/common/productService'; -import { IEditorInput } from 'vs/workbench/common/editor'; +import { UserDataSyncWorkbenchContribution } from 'vs/workbench/contrib/userDataSync/browser/userDataSync'; class UserDataSyncConfigurationContribution implements IWorkbenchContribution { @@ -54,185 +37,8 @@ class UserDataAutoSyncContribution extends Disposable implements IWorkbenchContr } } -const SYNC_PUSH_LIGHT_ICON_URI = URI.parse(registerAndGetAmdImageURL(`vs/workbench/contrib/userDataSync/browser/media/check-light.svg`)); -const SYNC_PUSH_DARK_ICON_URI = URI.parse(registerAndGetAmdImageURL(`vs/workbench/contrib/userDataSync/browser/media/check-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, - @IWorkbenchEnvironmentService private readonly workbenchEnvironmentService: IWorkbenchEnvironmentService, - ) { - 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 { - const previewEditorInput = this.getPreviewEditorInput(); - if (previewEditorInput) { - previewEditorInput.dispose(); - } - this.conflictsWarningDisposable.clear(); - } - } - - private async continueSync(): Promise { - // Get the preview editor - const previewEditorInput = this.getPreviewEditorInput(); - // Save the preview - if (previewEditorInput && previewEditorInput.isDirty()) { - await this.textFileService.save(previewEditorInput.getResource()!); - } - try { - // Continue Sync - await this.userDataSyncService.sync(true); - } catch (error) { - this.notificationService.error(error); - return; - } - // Close the preview editor - if (previewEditorInput) { - previewEditorInput.dispose(); - } - } - - private getPreviewEditorInput(): IEditorInput | undefined { - return this.editorService.editors.filter(input => isEqual(input.getResource(), this.workbenchEnvironmentService.settingsSyncPreviewResource))[0]; - } - - private async handleConflicts(): Promise { - if (this.userDataSyncService.conflictsSource === SyncSource.Settings) { - const resourceInput = { - resource: this.workbenchEnvironmentService.settingsSyncPreviewResource, - options: { - preserveFocus: false, - pinned: false, - revealIfVisible: true, - }, - mode: 'jsonc' - }; - this.editorService.openEditor(resourceInput) - .then(editor => { - this.historyService.remove(resourceInput); - if (editor && editor.input) { - // Trigger sync after closing the conflicts editor. - const disposable = editor.input.onDispose(() => { - disposable.dispose(); - this.userDataSyncService.sync(true); - }); - } - }); - } - } - - private registerActions(): void { - - const startSyncMenuItem: IMenuItem = { - group: '5_sync', - command: { - id: 'workbench.userData.actions.syncStart', - title: localize('start sync', "Configuration Sync: Turn On") - }, - when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), ContextKeyExpr.not('config.configurationSync.enable')), - }; - CommandsRegistry.registerCommand(startSyncMenuItem.command.id, () => this.configurationService.updateValue('configurationSync.enable', 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', "Configuration Sync: Turn Off") - }, - when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), ContextKeyExpr.has('config.configurationSync.enable')), - }; - CommandsRegistry.registerCommand(stopSyncMenuItem.command.id, () => this.configurationService.updateValue('configurationSync.enable', false)); - MenuRegistry.appendMenuItem(MenuId.GlobalActivity, stopSyncMenuItem); - MenuRegistry.appendMenuItem(MenuId.CommandPalette, stopSyncMenuItem); - - const resolveConflictsMenuItem: IMenuItem = { - group: '5_sync', - command: { - id: 'sync.resolveConflicts', - title: localize('resolveConflicts', "Configuration 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', "Configuration Sync: Continue") - }, - when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.isEqualTo(SyncStatus.HasConflicts)), - }); - MenuRegistry.appendMenuItem(MenuId.EditorTitle, { - command: { - id: continueSyncCommandId, - title: localize('continue sync', "Configuration 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.Resource.isEqualTo(this.workbenchEnvironmentService.settingsSyncPreviewResource.toString())), - }); - } -} const workbenchRegistry = Registry.as(WorkbenchExtensions.Workbench); workbenchRegistry.registerWorkbenchContribution(UserDataSyncConfigurationContribution, LifecyclePhase.Starting); -workbenchRegistry.registerWorkbenchContribution(SyncActionsContribution, LifecyclePhase.Restored); +workbenchRegistry.registerWorkbenchContribution(UserDataSyncWorkbenchContribution, LifecyclePhase.Restored); workbenchRegistry.registerWorkbenchContribution(UserDataAutoSyncContribution, LifecyclePhase.Restored); diff --git a/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts new file mode 100644 index 0000000000..d4f130ea57 --- /dev/null +++ b/src/vs/workbench/contrib/userDataSync/browser/userDataSync.ts @@ -0,0 +1,283 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { IUserDataSyncService, SyncStatus, SyncSource, CONTEXT_SYNC_STATE } 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 { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { MenuRegistry, MenuId, IMenuItem } from 'vs/platform/actions/common/actions'; +import { IContextKeyService, IContextKey, ContextKeyExpr, RawContextKey } 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 { 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 { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { isEqual } from 'vs/base/common/resources'; +import { IEditorInput } from 'vs/workbench/common/editor'; +import { IAuthTokenService, AuthTokenStatus } from 'vs/platform/auth/common/auth'; +import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput'; +import { timeout } from 'vs/base/common/async'; + +const CONTEXT_AUTH_TOKEN_STATE = new RawContextKey('authTokenStatus', AuthTokenStatus.Inactive); +const SYNC_PUSH_LIGHT_ICON_URI = URI.parse(registerAndGetAmdImageURL(`vs/workbench/contrib/userDataSync/browser/media/check-light.svg`)); +const SYNC_PUSH_DARK_ICON_URI = URI.parse(registerAndGetAmdImageURL(`vs/workbench/contrib/userDataSync/browser/media/check-dark.svg`)); + +export class UserDataSyncWorkbenchContribution extends Disposable implements IWorkbenchContribution { + + private readonly syncStatusContext: IContextKey; + private readonly authTokenContext: IContextKey; + private readonly badgeDisposable = this._register(new MutableDisposable()); + private readonly conflictsWarningDisposable = this._register(new MutableDisposable()); + private readonly signInNotificationDisposable = this._register(new MutableDisposable()); + + constructor( + @IUserDataSyncService private readonly userDataSyncService: IUserDataSyncService, + @IAuthTokenService private readonly authTokenService: IAuthTokenService, + @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, + @IWorkbenchEnvironmentService private readonly workbenchEnvironmentService: IWorkbenchEnvironmentService, + @IQuickInputService private readonly quickInputService: IQuickInputService, + ) { + super(); + this.syncStatusContext = CONTEXT_SYNC_STATE.bindTo(contextKeyService); + this.authTokenContext = CONTEXT_AUTH_TOKEN_STATE.bindTo(contextKeyService); + + this.onDidChangeAuthTokenStatus(this.authTokenService.status); + this.onDidChangeSyncStatus(this.userDataSyncService.status); + this._register(Event.debounce(authTokenService.onDidChangeStatus, () => undefined, 500)(() => this.onDidChangeAuthTokenStatus(this.authTokenService.status))); + this._register(Event.debounce(userDataSyncService.onDidChangeStatus, () => undefined, 500)(() => this.onDidChangeSyncStatus(this.userDataSyncService.status))); + this._register(Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('configurationSync.enable'))(() => this.updateBadge())); + this.registerActions(); + + timeout(2000).then(() => { + if (this.authTokenService.status === AuthTokenStatus.Inactive && configurationService.getValue('configurationSync.enable')) { + this.showSignInNotification(); + } + }); + } + + private onDidChangeAuthTokenStatus(status: AuthTokenStatus) { + this.authTokenContext.set(status); + if (status === AuthTokenStatus.Active) { + this.signInNotificationDisposable.clear(); + } + this.updateBadge(); + } + + private onDidChangeSyncStatus(status: SyncStatus) { + this.syncStatusContext.set(status); + + this.updateBadge(); + + if (this.userDataSyncService.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 { + const previewEditorInput = this.getPreviewEditorInput(); + if (previewEditorInput) { + previewEditorInput.dispose(); + } + this.conflictsWarningDisposable.clear(); + } + } + + private updateBadge(): void { + this.badgeDisposable.clear(); + + let badge: IBadge | undefined = undefined; + let clazz: string | undefined; + + if (this.authTokenService.status === AuthTokenStatus.Inactive && this.configurationService.getValue('configurationSync.enable')) { + badge = new NumberBadge(1, () => localize('sign in', "Sign in...")); + } else if (this.userDataSyncService.status === SyncStatus.HasConflicts) { + badge = new NumberBadge(1, () => localize('resolve conflicts', "Resolve Conflicts")); + } else if (this.userDataSyncService.status === SyncStatus.Syncing) { + badge = new ProgressBadge(() => localize('syncing', "Synchronising User Configuration...")); + clazz = 'progress-badge'; + } + + if (badge) { + this.badgeDisposable.value = this.activityService.showActivity(GLOBAL_ACTIVITY_ID, badge, clazz); + } + } + + private showSignInNotification(): void { + const handle = this.notificationService.prompt(Severity.Info, localize('show sign in', "Please sign in to Settings Sync service to start syncing configuration."), + [ + { + label: localize('sign in', "Sign in..."), + run: () => this.signIn() + } + ]); + this.signInNotificationDisposable.value = toDisposable(() => handle.close()); + handle.onDidClose(() => this.signInNotificationDisposable.clear()); + } + + private async signIn(): Promise { + const token = await this.quickInputService.input({ placeHolder: localize('enter token', "Please provide the auth bearer token"), ignoreFocusLost: true, }); + if (token) { + await this.authTokenService.updateToken(token); + } + } + + private async signOut(): Promise { + await this.authTokenService.deleteToken(); + } + + private async continueSync(): Promise { + // Get the preview editor + const previewEditorInput = this.getPreviewEditorInput(); + // Save the preview + if (previewEditorInput && previewEditorInput.isDirty()) { + await this.textFileService.save(previewEditorInput.getResource()!); + } + try { + // Continue Sync + await this.userDataSyncService.sync(true); + } catch (error) { + this.notificationService.error(error); + return; + } + // Close the preview editor + if (previewEditorInput) { + previewEditorInput.dispose(); + } + } + + private getPreviewEditorInput(): IEditorInput | undefined { + return this.editorService.editors.filter(input => isEqual(input.getResource(), this.workbenchEnvironmentService.settingsSyncPreviewResource))[0]; + } + + private async handleConflicts(): Promise { + if (this.userDataSyncService.conflictsSource === SyncSource.Settings) { + const resourceInput = { + resource: this.workbenchEnvironmentService.settingsSyncPreviewResource, + options: { + preserveFocus: false, + pinned: false, + revealIfVisible: true, + }, + mode: 'jsonc' + }; + this.editorService.openEditor(resourceInput) + .then(editor => { + this.historyService.remove(resourceInput); + if (editor && editor.input) { + // Trigger sync after closing the conflicts editor. + const disposable = editor.input.onDispose(() => { + disposable.dispose(); + this.userDataSyncService.sync(true); + }); + } + }); + } + } + + private registerActions(): void { + + const signInMenuItem: IMenuItem = { + group: '5_sync', + command: { + id: 'workbench.userData.actions.login', + title: localize('sign in', "Sign in...") + }, + when: ContextKeyExpr.and(CONTEXT_AUTH_TOKEN_STATE.isEqualTo(AuthTokenStatus.Inactive), ContextKeyExpr.has('config.configurationSync.enable')), + }; + CommandsRegistry.registerCommand(signInMenuItem.command.id, () => this.signIn()); + MenuRegistry.appendMenuItem(MenuId.GlobalActivity, signInMenuItem); + MenuRegistry.appendMenuItem(MenuId.CommandPalette, signInMenuItem); + + const signOutMenuItem: IMenuItem = { + command: { + id: 'workbench.userData.actions.logout', + title: localize('sign out', "Sign Out") + }, + when: ContextKeyExpr.and(CONTEXT_AUTH_TOKEN_STATE.isEqualTo(AuthTokenStatus.Active)), + }; + CommandsRegistry.registerCommand(signOutMenuItem.command.id, () => this.signOut()); + MenuRegistry.appendMenuItem(MenuId.CommandPalette, signOutMenuItem); + + const startSyncMenuItem: IMenuItem = { + group: '5_sync', + command: { + id: 'workbench.userData.actions.syncStart', + title: localize('start sync', "Configuration Sync: Turn On") + }, + when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), ContextKeyExpr.not('config.configurationSync.enable')), + }; + CommandsRegistry.registerCommand(startSyncMenuItem.command.id, () => this.configurationService.updateValue('configurationSync.enable', 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', "Configuration Sync: Turn Off") + }, + when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.notEqualsTo(SyncStatus.Uninitialized), ContextKeyExpr.has('config.configurationSync.enable')), + }; + CommandsRegistry.registerCommand(stopSyncMenuItem.command.id, () => this.configurationService.updateValue('configurationSync.enable', false)); + MenuRegistry.appendMenuItem(MenuId.GlobalActivity, stopSyncMenuItem); + MenuRegistry.appendMenuItem(MenuId.CommandPalette, stopSyncMenuItem); + + const resolveConflictsMenuItem: IMenuItem = { + group: '5_sync', + command: { + id: 'sync.resolveConflicts', + title: localize('resolveConflicts', "Configuration 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', "Configuration Sync: Continue") + }, + when: ContextKeyExpr.and(CONTEXT_SYNC_STATE.isEqualTo(SyncStatus.HasConflicts)), + }); + MenuRegistry.appendMenuItem(MenuId.EditorTitle, { + command: { + id: continueSyncCommandId, + title: localize('continue sync', "Configuration 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.Resource.isEqualTo(this.workbenchEnvironmentService.settingsSyncPreviewResource.toString())), + }); + } +} diff --git a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts index f7d0161825..7cdfdc963a 100644 --- a/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts +++ b/src/vs/workbench/contrib/welcome/page/browser/welcomePage.ts @@ -15,7 +15,6 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { onUnexpectedError, isPromiseCanceledError } from 'vs/base/common/errors'; import { IWindowOpenable } from 'vs/platform/windows/common/windows'; -import { IWorkspacesHistoryService } from 'vs/workbench/services/workspace/common/workspacesHistoryService'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IConfigurationService, ConfigurationTarget } from 'vs/platform/configuration/common/configuration'; import { localize } from 'vs/nls'; @@ -41,7 +40,7 @@ import { ILabelService } from 'vs/platform/label/common/label'; import { IFileService } from 'vs/platform/files/common/files'; import { ExtensionType } from 'vs/platform/extensions/common/extensions'; import { joinPath } from 'vs/base/common/resources'; -import { IRecentlyOpened, isRecentWorkspace, IRecentWorkspace, IRecentFolder, isRecentFolder } from 'vs/platform/workspaces/common/workspacesHistory'; +import { IRecentlyOpened, isRecentWorkspace, IRecentWorkspace, IRecentFolder, isRecentFolder, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; import { CancellationToken } from 'vs/base/common/cancellation'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; // {{SQL CARBON EDIT}} import { setProductQuality } from 'sql/workbench/contrib/welcome/page/browser/az_data_welcome_page'; // {{SQL CARBON EDIT}} @@ -255,7 +254,7 @@ class WelcomePage extends Disposable { constructor( @IEditorService private readonly editorService: IEditorService, @IInstantiationService private readonly instantiationService: IInstantiationService, - @IWorkspacesHistoryService private readonly workspacesHistoryService: IWorkspacesHistoryService, + @IWorkspacesService private readonly workspacesService: IWorkspacesService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @IConfigurationService private readonly configurationService: IConfigurationService, @ILabelService private readonly labelService: ILabelService, @@ -273,7 +272,7 @@ class WelcomePage extends Disposable { super(); this._register(lifecycleService.onShutdown(() => this.dispose())); - const recentlyOpened = this.workspacesHistoryService.getRecentlyOpened(); + const recentlyOpened = this.workspacesService.getRecentlyOpened(); const installedExtensions = this.instantiationService.invokeFunction(getInstalledExtensions); // {{SQL CARBON EDIT}} - Redirect to ADS welcome page setProductQuality(this.environmentService.appQuality); diff --git a/src/vs/workbench/electron-browser/actions/workspaceActions.ts b/src/vs/workbench/electron-browser/actions/workspaceActions.ts index 035beb1280..11426143b0 100644 --- a/src/vs/workbench/electron-browser/actions/workspaceActions.ts +++ b/src/vs/workbench/electron-browser/actions/workspaceActions.ts @@ -7,7 +7,7 @@ import { Action } from 'vs/base/common/actions'; import * as nls from 'vs/nls'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; -import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; +import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing'; import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; diff --git a/src/vs/workbench/electron-browser/window.ts b/src/vs/workbench/electron-browser/window.ts index a2e04b4fb5..690a1c022c 100644 --- a/src/vs/workbench/electron-browser/window.ts +++ b/src/vs/workbench/electron-browser/window.ts @@ -23,14 +23,14 @@ import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/c import { IResourceInput } from 'vs/platform/editor/common/editor'; import { KeyboardMapperFactory } from 'vs/workbench/services/keybinding/electron-browser/nativeKeymapService'; import { ipcRenderer as ipc, webFrame, crashReporter, Event } from 'electron'; -import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; +import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing'; import { IMenuService, MenuId, IMenu, MenuItemAction, ICommandAction, SubmenuItemAction, MenuRegistry } from 'vs/platform/actions/common/actions'; import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { createAndFillInActionBarActions } from 'vs/platform/actions/browser/menuEntryActionViewItem'; import { RunOnceScheduler } from 'vs/base/common/async'; import { IDisposable, Disposable, DisposableStore } from 'vs/base/common/lifecycle'; import { LifecyclePhase, ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; -import { IWorkspaceFolderCreationData } from 'vs/platform/workspaces/common/workspaces'; +import { IWorkspaceFolderCreationData, IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; import { IIntegrityService } from 'vs/workbench/services/integrity/common/integrity'; import { isRootUser, isWindows, isMacintosh, isLinux } from 'vs/base/common/platform'; import product from 'vs/platform/product/common/product'; @@ -61,7 +61,6 @@ import { ITunnelService, extractLocalHostUriMetaDataForPortMapping } from 'vs/pl import { IWorkbenchLayoutService, Parts } from 'vs/workbench/services/layout/browser/layoutService'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { IElectronEnvironmentService } from 'vs/workbench/services/electron/electron-browser/electronEnvironmentService'; -import { IWorkspacesHistoryService } from 'vs/workbench/services/workspace/common/workspacesHistoryService'; const TextInputActions: IAction[] = [ new Action('undo', nls.localize('undo', "Undo"), undefined, true, () => Promise.resolve(document.execCommand('undo'))), @@ -681,7 +680,7 @@ export class ElectronWindow extends Disposable { class NativeMenubarControl extends MenubarControl { constructor( @IMenuService menuService: IMenuService, - @IWorkspacesHistoryService workspacesHistoryService: IWorkspacesHistoryService, + @IWorkspacesService workspacesService: IWorkspacesService, @IContextKeyService contextKeyService: IContextKeyService, @IKeybindingService keybindingService: IKeybindingService, @IConfigurationService configurationService: IConfigurationService, @@ -698,7 +697,7 @@ class NativeMenubarControl extends MenubarControl { ) { super( menuService, - workspacesHistoryService, + workspacesService, contextKeyService, keybindingService, configurationService, @@ -725,7 +724,7 @@ class NativeMenubarControl extends MenubarControl { } (async () => { - this.recentlyOpened = await this.workspacesHistoryService.getRecentlyOpened(); + this.recentlyOpened = await this.workspacesService.getRecentlyOpened(); this.doUpdateMenubar(true); })(); diff --git a/src/vs/workbench/services/authToken/electron-browser/authTokenService.ts b/src/vs/workbench/services/authToken/electron-browser/authTokenService.ts new file mode 100644 index 0000000000..c9537db934 --- /dev/null +++ b/src/vs/workbench/services/authToken/electron-browser/authTokenService.ts @@ -0,0 +1,58 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; +import { Disposable } from 'vs/base/common/lifecycle'; +import { Emitter, Event } from 'vs/base/common/event'; +import { IChannel } from 'vs/base/parts/ipc/common/ipc'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IAuthTokenService, AuthTokenStatus } from 'vs/platform/auth/common/auth'; + +export class AuthTokenService extends Disposable implements IAuthTokenService { + + _serviceBrand: undefined; + + private readonly channel: IChannel; + + private _status: AuthTokenStatus = AuthTokenStatus.Disabled; + get status(): AuthTokenStatus { return this._status; } + private _onDidChangeStatus: Emitter = this._register(new Emitter()); + readonly onDidChangeStatus: Event = this._onDidChangeStatus.event; + + constructor( + @ISharedProcessService sharedProcessService: ISharedProcessService + ) { + super(); + this.channel = sharedProcessService.getChannel('authToken'); + this.channel.call('_getInitialStatus').then(status => { + this.updateStatus(status); + this._register(this.channel.listen('onDidChangeStatus')(status => this.updateStatus(status))); + }); + } + + getToken(): Promise { + return this.channel.call('getToken'); + } + + updateToken(token: string): Promise { + return this.channel.call('updateToken', [token]); + } + + refreshToken(): Promise { + return this.channel.call('getToken'); + } + + deleteToken(): Promise { + return this.channel.call('deleteToken'); + } + + private async updateStatus(status: AuthTokenStatus): Promise { + this._status = status; + this._onDidChangeStatus.fire(status); + } + +} + +registerSingleton(IAuthTokenService, AuthTokenService); diff --git a/src/vs/platform/clipboard/browser/clipboardService.ts b/src/vs/workbench/services/clipboard/browser/clipboardService.ts similarity index 100% rename from src/vs/platform/clipboard/browser/clipboardService.ts rename to src/vs/workbench/services/clipboard/browser/clipboardService.ts diff --git a/src/vs/platform/clipboard/electron-browser/clipboardService.ts b/src/vs/workbench/services/clipboard/electron-browser/clipboardService.ts similarity index 78% rename from src/vs/platform/clipboard/electron-browser/clipboardService.ts rename to src/vs/workbench/services/clipboard/electron-browser/clipboardService.ts index db17482b56..1421f539a1 100644 --- a/src/vs/platform/clipboard/electron-browser/clipboardService.ts +++ b/src/vs/workbench/services/clipboard/electron-browser/clipboardService.ts @@ -7,8 +7,9 @@ import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService import { clipboard } from 'electron'; import { URI } from 'vs/base/common/uri'; import { isMacintosh } from 'vs/base/common/platform'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -export class ClipboardService implements IClipboardService { +export class NativeClipboardService implements IClipboardService { private static FILE_FORMAT = 'code/file-list'; // Clipboard format for files @@ -42,16 +43,16 @@ export class ClipboardService implements IClipboardService { writeResources(resources: URI[]): void { if (resources.length) { - clipboard.writeBuffer(ClipboardService.FILE_FORMAT, this.resourcesToBuffer(resources)); + clipboard.writeBuffer(NativeClipboardService.FILE_FORMAT, this.resourcesToBuffer(resources)); } } readResources(): URI[] { - return this.bufferToResources(clipboard.readBuffer(ClipboardService.FILE_FORMAT)); + return this.bufferToResources(clipboard.readBuffer(NativeClipboardService.FILE_FORMAT)); } hasResources(): boolean { - return clipboard.has(ClipboardService.FILE_FORMAT); + return clipboard.has(NativeClipboardService.FILE_FORMAT); } private resourcesToBuffer(resources: URI[]): Buffer { @@ -75,3 +76,5 @@ export class ClipboardService implements IClipboardService { } } } + +registerSingleton(IClipboardService, NativeClipboardService, true); diff --git a/src/vs/workbench/services/credentials/browser/credentialsService.ts b/src/vs/workbench/services/credentials/browser/credentialsService.ts index 795e61ab28..8303a976fa 100644 --- a/src/vs/workbench/services/credentials/browser/credentialsService.ts +++ b/src/vs/workbench/services/credentials/browser/credentialsService.ts @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ICredentialsService } from 'vs/workbench/services/credentials/common/credentials'; +import { ICredentialsService } from 'vs/platform/credentials/common/credentials'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; diff --git a/src/vs/workbench/services/credentials/node/credentialsService.ts b/src/vs/workbench/services/credentials/node/credentialsService.ts index d6c20bdf9a..d20bad0457 100644 --- a/src/vs/workbench/services/credentials/node/credentialsService.ts +++ b/src/vs/workbench/services/credentials/node/credentialsService.ts @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ICredentialsService } from 'vs/workbench/services/credentials/common/credentials'; +import { ICredentialsService } from 'vs/platform/credentials/common/credentials'; import { IdleValue } from 'vs/base/common/async'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; diff --git a/src/vs/workbench/services/electron/electron-browser/electronService.ts b/src/vs/workbench/services/electron/electron-browser/electronService.ts index ceb8be782c..46387199e4 100644 --- a/src/vs/workbench/services/electron/electron-browser/electronService.ts +++ b/src/vs/workbench/services/electron/electron-browser/electronService.ts @@ -5,9 +5,9 @@ import { IElectronService } from 'vs/platform/electron/node/electron'; import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; -import { createChannelSender } from 'vs/base/parts/ipc/node/ipcChannelCreator'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IElectronEnvironmentService } from 'vs/workbench/services/electron/electron-browser/electronEnvironmentService'; +import { createChannelSender } from 'vs/base/parts/ipc/node/ipc'; export class ElectronService { diff --git a/src/vs/workbench/services/history/browser/history.ts b/src/vs/workbench/services/history/browser/history.ts index 8230a23250..471ab505b9 100644 --- a/src/vs/workbench/services/history/browser/history.ts +++ b/src/vs/workbench/services/history/browser/history.ts @@ -19,7 +19,6 @@ import { Registry } from 'vs/platform/registry/common/platform'; import { Event } from 'vs/base/common/event'; import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; import { IEditorGroupsService, IEditorGroup } from 'vs/workbench/services/editor/common/editorGroupsService'; -import { IWorkspacesHistoryService } from 'vs/workbench/services/workspace/common/workspacesHistoryService'; import { getCodeEditor, ICodeEditor } from 'vs/editor/browser/editorBrowser'; import { getExcludes, ISearchConfiguration } from 'vs/workbench/services/search/common/search'; import { IExpression } from 'vs/base/common/glob'; @@ -33,6 +32,7 @@ import { coalesce } from 'vs/base/common/arrays'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { withNullAsUndefined } from 'vs/base/common/types'; import { addDisposableListener, EventType, EventHelper } from 'vs/base/browser/dom'; +import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; /** * Stores the selection & view state of an editor and allows to compare it to other selection states. @@ -138,7 +138,7 @@ export class HistoryService extends Disposable implements IHistoryService { @IStorageService private readonly storageService: IStorageService, @IConfigurationService private readonly configurationService: IConfigurationService, @IFileService private readonly fileService: IFileService, - @IWorkspacesHistoryService private readonly workspacesHistoryService: IWorkspacesHistoryService, + @IWorkspacesService private readonly workspacesService: IWorkspacesService, @IInstantiationService private readonly instantiationService: IInstantiationService, @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService, @IContextKeyService private readonly contextKeyService: IContextKeyService, @@ -781,7 +781,7 @@ export class HistoryService extends Disposable implements IHistoryService { const input = arg1 as IResourceInput; - this.workspacesHistoryService.removeFromRecentlyOpened([input.resource]); + this.workspacesService.removeFromRecentlyOpened([input.resource]); } private isFileOpened(resource: URI, group: IEditorGroup): boolean { diff --git a/src/vs/workbench/services/host/browser/browserHostService.ts b/src/vs/workbench/services/host/browser/browserHostService.ts index 2aa96fd162..bdd550ddd2 100644 --- a/src/vs/workbench/services/host/browser/browserHostService.ts +++ b/src/vs/workbench/services/host/browser/browserHostService.ts @@ -15,6 +15,32 @@ import { IFileService } from 'vs/platform/files/common/files'; import { ILabelService } from 'vs/platform/label/common/label'; import { trackFocus } from 'vs/base/browser/dom'; import { Disposable } from 'vs/base/common/lifecycle'; +import { URI } from 'vs/base/common/uri'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; + +/** + * A workspace to open in the workbench can either be: + * - a workspace file with 0-N folders (via `workspaceUri`) + * - a single folder (via `folderUri`) + * - empty (via `undefined`) + */ +export type IWorkspace = { workspaceUri: URI } | { folderUri: URI } | undefined; + +export interface IWorkspaceProvider { + + /** + * The initial workspace to open. + */ + readonly workspace: IWorkspace; + + /** + * Asks to open a workspace in the current or a new window. + * + * @param workspace the workspace to open. + * @param options wether to open inside the current window or a new window. + */ + open(workspace: IWorkspace, options?: { reuse?: boolean }): Promise; +} export class BrowserHostService extends Disposable implements IHostService { @@ -27,15 +53,27 @@ export class BrowserHostService extends Disposable implements IHostService { //#endregion + private workspaceProvider: IWorkspaceProvider; + constructor( @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService, @IEditorService private readonly editorService: IEditorService, @IConfigurationService private readonly configurationService: IConfigurationService, @IFileService private readonly fileService: IFileService, - @ILabelService private readonly labelService: ILabelService + @ILabelService private readonly labelService: ILabelService, + @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService ) { super(); + if (environmentService.options && environmentService.options.workspaceProvider) { + this.workspaceProvider = environmentService.options.workspaceProvider; + } else { + this.workspaceProvider = new class implements IWorkspaceProvider { + readonly workspace = undefined; + async open() { } + }; + } + this.registerListeners(); } @@ -54,33 +92,21 @@ export class BrowserHostService extends Disposable implements IHostService { readonly windowCount = Promise.resolve(1); async openInWindow(toOpen: IWindowOpenable[], options?: IOpenInWindowOptions): Promise { - // TODO@Ben delegate to embedder - const { openFolderInNewWindow } = this.shouldOpenNewWindow(options); for (let i = 0; i < toOpen.length; i++) { const openable = toOpen[i]; openable.label = openable.label || this.getRecentLabel(openable); // Folder if (isFolderToOpen(openable)) { - const newAddress = `${document.location.origin}${document.location.pathname}?folder=${openable.folderUri.path}`; - if (openFolderInNewWindow) { - window.open(newAddress); - } else { - window.location.href = newAddress; - } + this.workspaceProvider.open({ folderUri: openable.folderUri }, { reuse: this.shouldReuse(options) }); } // Workspace else if (isWorkspaceToOpen(openable)) { - const newAddress = `${document.location.origin}${document.location.pathname}?workspace=${openable.workspaceUri.path}`; - if (openFolderInNewWindow) { - window.open(newAddress); - } else { - window.location.href = newAddress; - } + this.workspaceProvider.open({ workspaceUri: openable.workspaceUri }, { reuse: this.shouldReuse(options) }); } - // File + // File: open via editor service in current window else if (isFileToOpen(openable)) { const inputs: IResourceEditor[] = await pathsToEditors([openable], this.fileService); this.editorService.openEditors(inputs); @@ -100,7 +126,7 @@ export class BrowserHostService extends Disposable implements IHostService { return this.labelService.getUriLabel(openable.fileUri); } - private shouldOpenNewWindow(options: IOpenInWindowOptions = {}): { openFolderInNewWindow: boolean } { + private shouldReuse(options: IOpenInWindowOptions = {}): boolean { const windowConfig = this.configurationService.getValue('window'); const openFolderInNewWindowConfig = (windowConfig && windowConfig.openFoldersInNewWindow) || 'default' /* default */; @@ -109,17 +135,11 @@ export class BrowserHostService extends Disposable implements IHostService { openFolderInNewWindow = (openFolderInNewWindowConfig === 'on'); } - return { openFolderInNewWindow }; + return !openFolderInNewWindow; } async openEmptyWindow(options?: IOpenEmptyWindowOptions): Promise { - // TODO@Ben delegate to embedder - const targetHref = `${document.location.origin}${document.location.pathname}?ew=true`; - if (options && options.reuse) { - window.location.href = targetHref; - } else { - window.open(targetHref); - } + this.workspaceProvider.open(undefined, { reuse: options && options.reuse }); } async toggleFullScreen(): Promise { diff --git a/src/vs/platform/issue/electron-browser/issueService.ts b/src/vs/workbench/services/issue/electron-browser/issueService.ts similarity index 78% rename from src/vs/platform/issue/electron-browser/issueService.ts rename to src/vs/workbench/services/issue/electron-browser/issueService.ts index e357c1835b..60532cce26 100644 --- a/src/vs/platform/issue/electron-browser/issueService.ts +++ b/src/vs/workbench/services/issue/electron-browser/issueService.ts @@ -5,7 +5,8 @@ import { IIssueService } from 'vs/platform/issue/node/issue'; import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; -import { createChannelSender } from 'vs/base/parts/ipc/node/ipcChannelCreator'; +import { createChannelSender } from 'vs/base/parts/ipc/node/ipc'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; export class IssueService { @@ -15,3 +16,5 @@ export class IssueService { return createChannelSender(mainProcessService.getChannel('issue')); } } + +registerSingleton(IIssueService, IssueService, true); diff --git a/src/vs/workbench/services/localizations/electron-browser/localizationsService.ts b/src/vs/workbench/services/localizations/electron-browser/localizationsService.ts new file mode 100644 index 0000000000..836a49a9a7 --- /dev/null +++ b/src/vs/workbench/services/localizations/electron-browser/localizationsService.ts @@ -0,0 +1,22 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the Source EULA. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { createChannelSender } from 'vs/base/parts/ipc/node/ipc'; +import { ILocalizationsService } from 'vs/platform/localizations/common/localizations'; +import { ISharedProcessService } from 'vs/platform/ipc/electron-browser/sharedProcessService'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; + +export class LocalizationsService { + + _serviceBrand: undefined; + + constructor( + @ISharedProcessService sharedProcessService: ISharedProcessService, + ) { + return createChannelSender(sharedProcessService.getChannel('localizations')); + } +} + +registerSingleton(ILocalizationsService, LocalizationsService, true); diff --git a/src/vs/platform/menubar/electron-browser/menubarService.ts b/src/vs/workbench/services/menubar/electron-browser/menubarService.ts similarity index 78% rename from src/vs/platform/menubar/electron-browser/menubarService.ts rename to src/vs/workbench/services/menubar/electron-browser/menubarService.ts index 68185934c5..5a931b63e9 100644 --- a/src/vs/platform/menubar/electron-browser/menubarService.ts +++ b/src/vs/workbench/services/menubar/electron-browser/menubarService.ts @@ -5,7 +5,8 @@ import { IMenubarService } from 'vs/platform/menubar/node/menubar'; import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; -import { createChannelSender } from 'vs/base/parts/ipc/node/ipcChannelCreator'; +import { createChannelSender } from 'vs/base/parts/ipc/node/ipc'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; export class MenubarService { @@ -15,3 +16,5 @@ export class MenubarService { return createChannelSender(mainProcessService.getChannel('menubar')); } } + +registerSingleton(IMenubarService, MenubarService, true); diff --git a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts index 844da9a88e..3d7ae14169 100644 --- a/src/vs/workbench/services/textfile/common/textFileEditorModel.ts +++ b/src/vs/workbench/services/textfile/common/textFileEditorModel.ts @@ -80,7 +80,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil private contentEncoding: string; // encoding as reported from disk private preferredEncoding: string; // encoding as chosen by the user - private preferredMode: string; // mode as chosen by the user + private preferredMode: string | undefined; // mode as chosen by the user private versionId: number; private bufferSavedVersionId: number; @@ -108,7 +108,7 @@ export class TextFileEditorModel extends BaseTextEditorModel implements ITextFil constructor( resource: URI, preferredEncoding: string, - preferredMode: string, + preferredMode: string | undefined, @INotificationService private readonly notificationService: INotificationService, @IModeService modeService: IModeService, @IModelService modelService: IModelService, diff --git a/src/vs/workbench/services/update/browser/updateService.ts b/src/vs/workbench/services/update/browser/updateService.ts index 79b5cba00b..152bcfc8ec 100644 --- a/src/vs/workbench/services/update/browser/updateService.ts +++ b/src/vs/workbench/services/update/browser/updateService.ts @@ -24,7 +24,7 @@ export interface IUpdateProvider { checkForUpdate(): Promise; } -export class UpdateService extends Disposable implements IUpdateService { +export class BrowserUpdateService extends Disposable implements IUpdateService { _serviceBrand: undefined; @@ -92,4 +92,4 @@ export class UpdateService extends Disposable implements IUpdateService { } } -registerSingleton(IUpdateService, UpdateService); +registerSingleton(IUpdateService, BrowserUpdateService); diff --git a/src/vs/platform/update/electron-browser/updateService.ts b/src/vs/workbench/services/update/electron-browser/updateService.ts similarity index 90% rename from src/vs/platform/update/electron-browser/updateService.ts rename to src/vs/workbench/services/update/electron-browser/updateService.ts index f107d75cdd..ce01d35e7f 100644 --- a/src/vs/platform/update/electron-browser/updateService.ts +++ b/src/vs/workbench/services/update/electron-browser/updateService.ts @@ -7,8 +7,9 @@ import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { Event, Emitter } from 'vs/base/common/event'; import { IUpdateService, State } from 'vs/platform/update/common/update'; import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -export class UpdateService implements IUpdateService { +export class NativeUpdateService implements IUpdateService { _serviceBrand: undefined; @@ -56,3 +57,5 @@ export class UpdateService implements IUpdateService { return this.channel.call('isLatestVersion'); } } + +registerSingleton(IUpdateService, NativeUpdateService); diff --git a/src/vs/workbench/services/url/electron-browser/urlService.ts b/src/vs/workbench/services/url/electron-browser/urlService.ts index 723d7be4da..2027a487d2 100644 --- a/src/vs/workbench/services/url/electron-browser/urlService.ts +++ b/src/vs/workbench/services/url/electron-browser/urlService.ts @@ -6,12 +6,13 @@ import { IURLService, IURLHandler } from 'vs/platform/url/common/url'; import { URI, UriComponents } from 'vs/base/common/uri'; import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; -import { URLServiceChannelClient, URLHandlerChannel } from 'vs/platform/url/common/urlIpc'; +import { 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/common/product'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IElectronEnvironmentService } from 'vs/workbench/services/electron/electron-browser/electronEnvironmentService'; +import { createChannelSender } from 'vs/base/parts/ipc/node/ipc'; export class RelayURLService extends URLService implements IURLHandler { @@ -24,7 +25,7 @@ export class RelayURLService extends URLService implements IURLHandler { ) { super(); - this.urlService = new URLServiceChannelClient(mainProcessService.getChannel('url')); + this.urlService = createChannelSender(mainProcessService.getChannel('url')); mainProcessService.registerChannel('urlHandler', new URLHandlerChannel(this)); openerService.registerOpener(this); diff --git a/src/vs/workbench/services/workspace/browser/workspacesService.ts b/src/vs/workbench/services/workspace/browser/workspacesService.ts deleted file mode 100644 index 5100923e9b..0000000000 --- a/src/vs/workbench/services/workspace/browser/workspacesService.ts +++ /dev/null @@ -1,31 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { IWorkspacesService, IWorkspaceFolderCreationData, IWorkspaceIdentifier, IEnterWorkspaceResult } from 'vs/platform/workspaces/common/workspaces'; -import { URI } from 'vs/base/common/uri'; - -export class WorkspacesService implements IWorkspacesService { - - _serviceBrand: undefined; - - enterWorkspace(path: URI): Promise { - throw new Error('Untitled workspaces are currently unsupported in Web'); - } - - createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[], remoteAuthority?: string): Promise { - throw new Error('Untitled workspaces are currently unsupported in Web'); - } - - deleteUntitledWorkspace(workspace: IWorkspaceIdentifier): Promise { - throw new Error('Untitled workspaces are currently unsupported in Web'); - } - - getWorkspaceIdentifier(workspacePath: URI): Promise { - throw new Error('Untitled workspaces are currently unsupported in Web'); - } -} - -registerSingleton(IWorkspacesService, WorkspacesService, true); diff --git a/src/vs/workbench/services/workspace/common/workspacesHistoryService.ts b/src/vs/workbench/services/workspace/common/workspacesHistoryService.ts deleted file mode 100644 index 714fb485ca..0000000000 --- a/src/vs/workbench/services/workspace/common/workspacesHistoryService.ts +++ /dev/null @@ -1,25 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the Source EULA. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { Event } from 'vs/base/common/event'; -import { URI } from 'vs/base/common/uri'; -import { IRecent, IRecentlyOpened } from 'vs/platform/workspaces/common/workspacesHistory'; - -export const IWorkspacesHistoryService = createDecorator('workspacesHistoryService'); - -export interface IWorkspacesHistoryService { - - _serviceBrand: undefined; - - readonly onRecentlyOpenedChange: Event; - - addRecentlyOpened(recents: IRecent[]): Promise; - - removeFromRecentlyOpened(workspaces: URI[]): Promise; - clearRecentlyOpened(): Promise; - - getRecentlyOpened(): Promise; -} diff --git a/src/vs/workbench/services/workspace/electron-browser/workspacesHistoryService.ts b/src/vs/workbench/services/workspace/electron-browser/workspacesHistoryService.ts deleted file mode 100644 index e84dad690c..0000000000 --- a/src/vs/workbench/services/workspace/electron-browser/workspacesHistoryService.ts +++ /dev/null @@ -1,39 +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 { URI } from 'vs/base/common/uri'; -import { IRecent, IRecentlyOpened } from 'vs/platform/workspaces/common/workspacesHistory'; -import { IWorkspacesHistoryService } from 'vs/workbench/services/workspace/common/workspacesHistoryService'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { IElectronService } from 'vs/platform/electron/node/electron'; - -export class NativeWorkspacesHistoryService implements IWorkspacesHistoryService { - - _serviceBrand: undefined; - - readonly onRecentlyOpenedChange = this.electronService.onRecentlyOpenedChange; - - constructor( - @IElectronService private readonly electronService: IElectronService - ) { } - - async getRecentlyOpened(): Promise { - return this.electronService.getRecentlyOpened(); - } - - async addRecentlyOpened(recents: IRecent[]): Promise { - return this.electronService.addRecentlyOpened(recents); - } - - async removeFromRecentlyOpened(paths: URI[]): Promise { - return this.electronService.removeFromRecentlyOpened(paths); - } - - async clearRecentlyOpened(): Promise { - return this.electronService.clearRecentlyOpened(); - } -} - -registerSingleton(IWorkspacesHistoryService, NativeWorkspacesHistoryService, true); diff --git a/src/vs/workbench/services/workspace/electron-browser/workspacesService.ts b/src/vs/workbench/services/workspace/electron-browser/workspacesService.ts deleted file mode 100644 index c91a7c1dd7..0000000000 --- a/src/vs/workbench/services/workspace/electron-browser/workspacesService.ts +++ /dev/null @@ -1,48 +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 { IChannel } from 'vs/base/parts/ipc/common/ipc'; -import { IWorkspacesService, IWorkspaceIdentifier, IWorkspaceFolderCreationData, reviveWorkspaceIdentifier, IEnterWorkspaceResult } 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'; -import { IElectronEnvironmentService } from 'vs/workbench/services/electron/electron-browser/electronEnvironmentService'; - -export class WorkspacesService implements IWorkspacesService { - - _serviceBrand: undefined; - - private channel: IChannel; - - constructor( - @IMainProcessService mainProcessService: IMainProcessService, - @IElectronEnvironmentService private readonly electronEnvironmentService: IElectronEnvironmentService - ) { - this.channel = mainProcessService.getChannel('workspaces'); - } - - async enterWorkspace(path: URI): Promise { - const result: IEnterWorkspaceResult = await this.channel.call('enterWorkspace', [this.electronEnvironmentService.windowId, path]); - if (result) { - result.workspace = reviveWorkspaceIdentifier(result.workspace); - } - - return result; - } - - createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[], remoteAuthority?: string): Promise { - return this.channel.call('createUntitledWorkspace', [folders, remoteAuthority]).then(reviveWorkspaceIdentifier); - } - - deleteUntitledWorkspace(workspaceIdentifier: IWorkspaceIdentifier): Promise { - return this.channel.call('deleteUntitledWorkspace', workspaceIdentifier); - } - - getWorkspaceIdentifier(configPath: URI): Promise { - return this.channel.call('getWorkspaceIdentifier', configPath).then(reviveWorkspaceIdentifier); - } -} - -registerSingleton(IWorkspacesService, WorkspacesService, true); diff --git a/src/vs/workbench/services/workspace/browser/abstractWorkspaceEditingService.ts b/src/vs/workbench/services/workspaces/browser/abstractWorkspaceEditingService.ts similarity index 99% rename from src/vs/workbench/services/workspace/browser/abstractWorkspaceEditingService.ts rename to src/vs/workbench/services/workspaces/browser/abstractWorkspaceEditingService.ts index 083f968fd8..82436c5c0f 100644 --- a/src/vs/workbench/services/workspace/browser/abstractWorkspaceEditingService.ts +++ b/src/vs/workbench/services/workspaces/browser/abstractWorkspaceEditingService.ts @@ -3,7 +3,7 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; +import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing'; import { URI } from 'vs/base/common/uri'; import * as nls from 'vs/nls'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; diff --git a/src/vs/workbench/services/workspace/browser/workspaceEditingService.ts b/src/vs/workbench/services/workspaces/browser/workspaceEditingService.ts similarity index 97% rename from src/vs/workbench/services/workspace/browser/workspaceEditingService.ts rename to src/vs/workbench/services/workspaces/browser/workspaceEditingService.ts index bda610980d..339c8fef6e 100644 --- a/src/vs/workbench/services/workspace/browser/workspaceEditingService.ts +++ b/src/vs/workbench/services/workspaces/browser/workspaceEditingService.ts @@ -18,8 +18,8 @@ import { IFileDialogService, IDialogService } from 'vs/platform/dialogs/common/d import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IHostService } from 'vs/workbench/services/host/browser/host'; -import { AbstractWorkspaceEditingService } from 'vs/workbench/services/workspace/browser/abstractWorkspaceEditingService'; -import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; +import { AbstractWorkspaceEditingService } from 'vs/workbench/services/workspaces/browser/abstractWorkspaceEditingService'; +import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; export class BrowserWorkspaceEditingService extends AbstractWorkspaceEditingService { diff --git a/src/vs/workbench/services/workspace/browser/workspacesHistoryService.ts b/src/vs/workbench/services/workspaces/browser/workspacesService.ts similarity index 68% rename from src/vs/workbench/services/workspace/browser/workspacesHistoryService.ts rename to src/vs/workbench/services/workspaces/browser/workspacesService.ts index dc5fe9d1ed..e010e44cde 100644 --- a/src/vs/workbench/services/workspace/browser/workspacesHistoryService.ts +++ b/src/vs/workbench/services/workspaces/browser/workspacesService.ts @@ -3,18 +3,16 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event, Emitter } from 'vs/base/common/event'; -import { URI } from 'vs/base/common/uri'; -import { IRecent, IRecentlyOpened, isRecentFolder, isRecentFile } from 'vs/platform/workspaces/common/workspacesHistory'; -import { IWorkspacesHistoryService } from 'vs/workbench/services/workspace/common/workspacesHistoryService'; -import { restoreRecentlyOpened, toStoreData } from 'vs/platform/workspaces/common/workspacesHistoryStorage'; -import { StorageScope, IStorageService } from 'vs/platform/storage/common/storage'; -import { WorkbenchState, IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { ILogService } from 'vs/platform/log/common/log'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IWorkspacesService, IWorkspaceFolderCreationData, IWorkspaceIdentifier, IEnterWorkspaceResult, IRecentlyOpened, restoreRecentlyOpened, IRecent, isRecentFile, isRecentFolder, toStoreData } from 'vs/platform/workspaces/common/workspaces'; +import { URI } from 'vs/base/common/uri'; +import { Event, Emitter } from 'vs/base/common/event'; +import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; +import { ILogService } from 'vs/platform/log/common/log'; import { Disposable } from 'vs/base/common/lifecycle'; -export class BrowserWorkspacesHistoryService extends Disposable implements IWorkspacesHistoryService { +export class BrowserWorkspacesService extends Disposable implements IWorkspacesService { static readonly RECENTLY_OPENED_KEY = 'recently.opened'; @@ -37,7 +35,7 @@ export class BrowserWorkspacesHistoryService extends Disposable implements IWork private registerListeners(): void { this._register(this.storageService.onDidChangeStorage(event => { - if (event.key === BrowserWorkspacesHistoryService.RECENTLY_OPENED_KEY && event.scope === StorageScope.GLOBAL) { + if (event.key === BrowserWorkspacesService.RECENTLY_OPENED_KEY && event.scope === StorageScope.GLOBAL) { this._onRecentlyOpenedChange.fire(); } })); @@ -55,8 +53,10 @@ export class BrowserWorkspacesHistoryService extends Disposable implements IWork } } + //#region Workspaces History + async getRecentlyOpened(): Promise { - const recentlyOpenedRaw = this.storageService.get(BrowserWorkspacesHistoryService.RECENTLY_OPENED_KEY, StorageScope.GLOBAL); + const recentlyOpenedRaw = this.storageService.get(BrowserWorkspacesService.RECENTLY_OPENED_KEY, StorageScope.GLOBAL); if (recentlyOpenedRaw) { return restoreRecentlyOpened(JSON.parse(recentlyOpenedRaw), this.logService); } @@ -102,12 +102,34 @@ export class BrowserWorkspacesHistoryService extends Disposable implements IWork } private async saveRecentlyOpened(data: IRecentlyOpened): Promise { - return this.storageService.store(BrowserWorkspacesHistoryService.RECENTLY_OPENED_KEY, JSON.stringify(toStoreData(data)), StorageScope.GLOBAL); + return this.storageService.store(BrowserWorkspacesService.RECENTLY_OPENED_KEY, JSON.stringify(toStoreData(data)), StorageScope.GLOBAL); } async clearRecentlyOpened(): Promise { - this.storageService.remove(BrowserWorkspacesHistoryService.RECENTLY_OPENED_KEY, StorageScope.GLOBAL); + this.storageService.remove(BrowserWorkspacesService.RECENTLY_OPENED_KEY, StorageScope.GLOBAL); } + + //#endregion + + //#region Workspace Management + + enterWorkspace(path: URI): Promise { + throw new Error('Untitled workspaces are currently unsupported in Web'); + } + + createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[], remoteAuthority?: string): Promise { + throw new Error('Untitled workspaces are currently unsupported in Web'); + } + + deleteUntitledWorkspace(workspace: IWorkspaceIdentifier): Promise { + throw new Error('Untitled workspaces are currently unsupported in Web'); + } + + getWorkspaceIdentifier(workspacePath: URI): Promise { + throw new Error('Untitled workspaces are currently unsupported in Web'); + } + + //#endregion } -registerSingleton(IWorkspacesHistoryService, BrowserWorkspacesHistoryService, true); +registerSingleton(IWorkspacesService, BrowserWorkspacesService, true); diff --git a/src/vs/workbench/services/workspace/common/workspaceEditing.ts b/src/vs/workbench/services/workspaces/common/workspaceEditing.ts similarity index 100% rename from src/vs/workbench/services/workspace/common/workspaceEditing.ts rename to src/vs/workbench/services/workspaces/common/workspaceEditing.ts diff --git a/src/vs/workbench/services/workspace/electron-browser/workspaceEditingService.ts b/src/vs/workbench/services/workspaces/electron-browser/workspaceEditingService.ts similarity index 94% rename from src/vs/workbench/services/workspace/electron-browser/workspaceEditingService.ts rename to src/vs/workbench/services/workspaces/electron-browser/workspaceEditingService.ts index dc91cdaa4a..75d350224a 100644 --- a/src/vs/workbench/services/workspace/electron-browser/workspaceEditingService.ts +++ b/src/vs/workbench/services/workspaces/electron-browser/workspaceEditingService.ts @@ -3,11 +3,10 @@ * Licensed under the Source EULA. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; +import { IWorkspaceEditingService } from 'vs/workbench/services/workspaces/common/workspaceEditing'; import { URI } from 'vs/base/common/uri'; import * as nls from 'vs/nls'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import { IWorkspacesHistoryService } from 'vs/workbench/services/workspace/common/workspacesHistoryService'; import { IJSONEditingService } from 'vs/workbench/services/configuration/common/jsonEditing'; import { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; import { WorkspaceService } from 'vs/workbench/services/configuration/browser/configurationService'; @@ -26,7 +25,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ILabelService } from 'vs/platform/label/common/label'; import { ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { IHostService } from 'vs/workbench/services/host/browser/host'; -import { AbstractWorkspaceEditingService } from 'vs/workbench/services/workspace/browser/abstractWorkspaceEditingService'; +import { AbstractWorkspaceEditingService } from 'vs/workbench/services/workspaces/browser/abstractWorkspaceEditingService'; import { IElectronService } from 'vs/platform/electron/node/electron'; import { isMacintosh, isWindows, isLinux } from 'vs/base/common/platform'; import { mnemonicButtonLabel } from 'vs/base/common/labels'; @@ -47,7 +46,6 @@ export class NativeWorkspaceEditingService extends AbstractWorkspaceEditingServi @ICommandService commandService: ICommandService, @IFileService fileService: IFileService, @ITextFileService textFileService: ITextFileService, - @IWorkspacesHistoryService private readonly workspacesHistoryService: IWorkspacesHistoryService, @IWorkspacesService workspacesService: IWorkspacesService, @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, @IFileDialogService fileDialogService: IFileDialogService, @@ -135,7 +133,7 @@ export class NativeWorkspaceEditingService extends AbstractWorkspaceEditingServi const newWorkspaceIdentifier = await this.workspacesService.getWorkspaceIdentifier(newWorkspacePath); const label = this.labelService.getWorkspaceLabel(newWorkspaceIdentifier, { verbose: true }); - this.workspacesHistoryService.addRecentlyOpened([{ label, workspace: newWorkspaceIdentifier }]); + this.workspacesService.addRecentlyOpened([{ label, workspace: newWorkspaceIdentifier }]); this.workspacesService.deleteUntitledWorkspace(workspaceIdentifier); } catch (error) { diff --git a/src/vs/workbench/services/workspaces/electron-browser/workspacesService.ts b/src/vs/workbench/services/workspaces/electron-browser/workspacesService.ts new file mode 100644 index 0000000000..9aec284b32 --- /dev/null +++ b/src/vs/workbench/services/workspaces/electron-browser/workspacesService.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 { IWorkspacesService } from 'vs/platform/workspaces/common/workspaces'; +import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { IElectronEnvironmentService } from 'vs/workbench/services/electron/electron-browser/electronEnvironmentService'; +import { createChannelSender } from 'vs/base/parts/ipc/node/ipc'; + +export class NativeWorkspacesService { + + _serviceBrand: undefined; + + constructor( + @IMainProcessService mainProcessService: IMainProcessService, + @IElectronEnvironmentService electronEnvironmentService: IElectronEnvironmentService + ) { + return createChannelSender(mainProcessService.getChannel('workspaces'), { context: electronEnvironmentService.windowId }); + } +} + +registerSingleton(IWorkspacesService, NativeWorkspacesService, true); diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index b41645da8f..7b0a6bf852 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -47,35 +47,26 @@ import 'vs/workbench/services/extensionManagement/node/extensionManagementServic import 'vs/workbench/services/accessibility/node/accessibilityService'; 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 'vs/workbench/services/workspaces/electron-browser/workspacesService'; +import 'vs/workbench/services/workspaces/electron-browser/workspaceEditingService'; import 'vs/workbench/services/userDataSync/electron-browser/userDataSyncService'; +import 'vs/workbench/services/authToken/electron-browser/authTokenService'; import 'vs/workbench/services/host/electron-browser/desktopHostService'; import 'vs/workbench/services/request/electron-browser/requestService'; import 'vs/workbench/services/lifecycle/electron-browser/lifecycleService'; import 'vs/workbench/services/sharedProcess/electron-browser/sharedProcessService'; import 'vs/workbench/services/electron/electron-browser/electronService'; -import 'vs/workbench/services/workspace/electron-browser/workspacesHistoryService'; -import 'vs/workbench/services/workspace/electron-browser/workspaceEditingService'; +import 'vs/workbench/services/localizations/electron-browser/localizationsService'; +import 'vs/workbench/services/clipboard/electron-browser/clipboardService'; +import 'vs/workbench/services/update/electron-browser/updateService'; +import 'vs/workbench/services/issue/electron-browser/issueService'; +import 'vs/workbench/services/menubar/electron-browser/menubarService'; -import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; -import { ClipboardService } from 'vs/platform/clipboard/electron-browser/clipboardService'; -import { ILocalizationsService } from 'vs/platform/localizations/common/localizations'; -import { LocalizationsService } from 'vs/platform/localizations/electron-browser/localizationsService'; -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 { IMenubarService } from 'vs/platform/menubar/node/menubar'; -import { MenubarService } from 'vs/platform/menubar/electron-browser/menubarService'; +import { ICredentialsService } from 'vs/platform/credentials/common/credentials'; +import { KeytarCredentialsService } from 'vs/platform/credentials/node/credentialsService'; -registerSingleton(IClipboardService, ClipboardService, true); -registerSingleton(ILocalizationsService, LocalizationsService); -registerSingleton(IUpdateService, UpdateService); -registerSingleton(IIssueService, IssueService); -registerSingleton(IMenubarService, MenubarService); +registerSingleton(ICredentialsService, KeytarCredentialsService, true); //#endregion @@ -159,6 +150,7 @@ import 'vs/workbench/contrib/welcome/gettingStarted/electron-browser/openWebsite // Configuration Exporter import 'vs/workbench/contrib/configExporter/node/configurationExportHelper.contribution'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; //#endregion diff --git a/src/vs/workbench/workbench.web.api.ts b/src/vs/workbench/workbench.web.api.ts index 835eef1591..cb240dfbb4 100644 --- a/src/vs/workbench/workbench.web.api.ts +++ b/src/vs/workbench/workbench.web.api.ts @@ -15,6 +15,7 @@ import { LogLevel } from 'vs/platform/log/common/log'; 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'; +import { IWorkspaceProvider, IWorkspace } from 'vs/workbench/services/host/browser/browserHostService'; interface IWorkbenchConstructionOptions { @@ -36,14 +37,9 @@ interface IWorkbenchConstructionOptions { webviewEndpoint?: string; /** - * Experimental: An optional folder that is set as workspace context for the workbench. + * Experimental: a handler for opening workspaces and providing the initial workspace. */ - folderUri?: URI; - - /** - * Experimental: An optional workspace that is set as workspace context for the workbench. - */ - workspaceUri?: URI; + workspaceProvider?: IWorkspaceProvider; /** * Experimental: The userDataProvider is used to handle user specific application @@ -91,7 +87,6 @@ interface IWorkbenchConstructionOptions { */ updateProvider?: IUpdateProvider; - /** * Experimental: Support adding additional properties to telemetry. */ @@ -127,6 +122,10 @@ export { IDisposable, Disposable, + // Workspace + IWorkspace, + IWorkspaceProvider, + // FileSystem IFileSystemProvider, FileSystemProviderCapabilities, diff --git a/src/vs/workbench/workbench.web.main.ts b/src/vs/workbench/workbench.web.main.ts index 7e0ecea010..52c4f64769 100644 --- a/src/vs/workbench/workbench.web.main.ts +++ b/src/vs/workbench/workbench.web.main.ts @@ -40,18 +40,16 @@ 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/workspaces/browser/workspacesService'; +import 'vs/workbench/services/workspaces/browser/workspaceEditingService'; import 'vs/workbench/services/dialogs/browser/dialogService'; import 'vs/workbench/services/dialogs/browser/fileDialogService'; import 'vs/workbench/services/host/browser/browserHostService'; import 'vs/workbench/services/request/browser/requestService'; -import 'vs/workbench/services/workspace/browser/workspacesHistoryService'; -import 'vs/workbench/services/workspace/browser/workspaceEditingService'; import 'vs/workbench/services/lifecycle/browser/lifecycleService'; +import 'vs/workbench/services/clipboard/browser/clipboardService'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; -import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; -import { BrowserClipboardService } from 'vs/platform/clipboard/browser/clipboardService'; import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; import { BrowserAccessibilityService } from 'vs/platform/accessibility/common/accessibilityService'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; @@ -64,6 +62,8 @@ import { ITunnelService } from 'vs/platform/remote/common/tunnel'; import { NoOpTunnelService } from 'vs/platform/remote/common/tunnelService'; import { ILoggerService } from 'vs/platform/log/common/log'; import { FileLoggerService } from 'vs/platform/log/common/fileLogService'; +import { IAuthTokenService } from 'vs/platform/auth/common/auth'; +import { AuthTokenService } from 'vs/platform/auth/common/authTokenService'; import { IUserDataSyncStoreService, IUserDataSyncService, IUserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSync'; import { UserDataSyncLogService } from 'vs/platform/userDataSync/common/userDataSyncLog'; import { UserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; @@ -71,11 +71,11 @@ import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyn registerSingleton(IExtensionManagementService, ExtensionManagementService); registerSingleton(IBackupFileService, BackupFileService); -registerSingleton(IClipboardService, BrowserClipboardService, true); registerSingleton(IAccessibilityService, BrowserAccessibilityService, true); registerSingleton(IContextMenuService, ContextMenuService); registerSingleton(ITunnelService, NoOpTunnelService, true); registerSingleton(ILoggerService, FileLoggerService); +registerSingleton(IAuthTokenService, AuthTokenService); registerSingleton(IUserDataSyncLogService, UserDataSyncLogService); registerSingleton(IUserDataSyncStoreService, UserDataSyncStoreService); registerSingleton(IUserDataSyncService, UserDataSyncService);