From d0b67392e7f5d32ae7f268daad44e64e0bc3f06e Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 25 Sep 2019 09:35:28 +0200 Subject: [PATCH] debt - getWindows is electron only --- src/vs/base/common/marshalling.ts | 5 +- src/vs/code/electron-main/windows.ts | 4 +- .../electron-main/electronMainService.ts | 40 ++++----- src/vs/platform/electron/node/electron.ts | 9 ++- src/vs/platform/ipc/node/simpleIpcProxy.ts | 15 +++- src/vs/platform/windows/common/windows.ts | 15 +++- src/vs/platform/windows/common/windowsIpc.ts | 1 - .../electron-browser/windowsService.ts | 24 +----- .../electron-main/legacyWindowsMainService.ts | 15 ---- .../platform/windows/electron-main/windows.ts | 4 +- .../api/browser/mainThreadCommands.ts | 4 +- .../workbench/api/common/extHostCommands.ts | 4 +- .../workbench/browser/web.simpleservices.ts | 6 -- .../contrib/files/browser/fileCommands.ts | 4 +- .../electron-browser/actions/windowActions.ts | 23 ++---- .../host/browser/browserHostService.ts | 4 +- .../workbench/services/host/browser/host.ts | 4 +- .../electron-browser/desktopHostService.ts | 6 +- .../browser/workspaceEditingService.ts | 30 ++----- .../workspaceEditingService.ts | 81 +++++++++++++++++++ .../workbench/test/workbenchTestServices.ts | 8 +- src/vs/workbench/workbench.desktop.main.ts | 3 + src/vs/workbench/workbench.web.main.ts | 3 + 23 files changed, 173 insertions(+), 139 deletions(-) create mode 100644 src/vs/workbench/services/workspace/electron-browser/workspaceEditingService.ts diff --git a/src/vs/base/common/marshalling.ts b/src/vs/base/common/marshalling.ts index e479a2db973..335ee5691e0 100644 --- a/src/vs/base/common/marshalling.ts +++ b/src/vs/base/common/marshalling.ts @@ -12,7 +12,7 @@ export function stringify(obj: any): string { export function parse(text: string): any { let data = JSON.parse(text); - data = revive(data, 0); + data = revive(data); return data; } @@ -32,8 +32,7 @@ function replacer(key: string, value: any): any { return value; } -export function revive(obj: any, depth: number): any { - +export function revive(obj: any, depth = 0): any { if (!obj || depth > 200) { return obj; } diff --git a/src/vs/code/electron-main/windows.ts b/src/vs/code/electron-main/windows.ts index 9b265df2dfa..ded45e47860 100644 --- a/src/vs/code/electron-main/windows.ts +++ b/src/vs/code/electron-main/windows.ts @@ -18,7 +18,7 @@ import { parseLineAndColumnAware } from 'vs/code/node/paths'; import { ILifecycleMainService, UnloadReason, LifecycleMainService, LifecycleMainPhase } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ILogService } from 'vs/platform/log/common/log'; -import { IWindowSettings, OpenContext, IPath, IWindowConfiguration, INativeOpenDialogOptions, IPathsToWaitFor, isFileToOpen, isWorkspaceToOpen, isFolderToOpen, IWindowOpenable } from 'vs/platform/windows/common/windows'; +import { IWindowSettings, OpenContext, IPath, IWindowConfiguration, INativeOpenDialogOptions, IPathsToWaitFor, isFileToOpen, isWorkspaceToOpen, isFolderToOpen, IWindowOpenable, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows'; import { getLastActiveWindow, findBestWindowOrFolderForFile, findWindowOnWorkspace, findWindowOnExtensionDevelopmentPath, findWindowOnWorkspaceOrFolderUri } from 'vs/code/node/windowsFinder'; import { Event as CommonEvent, Emitter } from 'vs/base/common/event'; import product from 'vs/platform/product/common/product'; @@ -1617,7 +1617,7 @@ export class WindowsManager extends Disposable implements IWindowsMainService { return getLastActiveWindow(WindowsManager.WINDOWS.filter(window => window.remoteAuthority === remoteAuthority)); } - openEmptyWindow(context: OpenContext, options?: { reuse?: boolean, remoteAuthority?: string }): ICodeWindow[] { + openEmptyWindow(context: OpenContext, options?: IOpenEmptyWindowOptions): ICodeWindow[] { let cli = this.environmentService.args; const remote = options && options.remoteAuthority; if (cli && (cli.remote !== remote)) { diff --git a/src/vs/platform/electron/electron-main/electronMainService.ts b/src/vs/platform/electron/electron-main/electronMainService.ts index 579c4c66b0c..48ba9ca7f34 100644 --- a/src/vs/platform/electron/electron-main/electronMainService.ts +++ b/src/vs/platform/electron/electron-main/electronMainService.ts @@ -6,13 +6,12 @@ import { IWindowsMainService } from 'vs/platform/windows/electron-main/windows'; import { MessageBoxOptions, MessageBoxReturnValue, shell, OpenDevToolsOptions, SaveDialogOptions, SaveDialogReturnValue, OpenDialogOptions, OpenDialogReturnValue, CrashReporterStartOptions, crashReporter, Menu } from 'electron'; import { ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; -import { OpenContext, INativeOpenDialogOptions, IWindowOpenable, IOpenInWindowOptions, isWorkspaceToOpen, isFolderToOpen } from 'vs/platform/windows/common/windows'; +import { OpenContext, INativeOpenDialogOptions, IWindowOpenable, IOpenInWindowOptions, IOpenedWindow, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows'; import { isMacintosh } from 'vs/base/common/platform'; import { IElectronService } from 'vs/platform/electron/node/electron'; import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; import { AddContextToFunctions } from 'vs/platform/ipc/node/simpleIpcProxy'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { URI } from 'vs/base/common/uri'; export class ElectronMainService implements AddContextToFunctions { @@ -27,31 +26,28 @@ export class ElectronMainService implements AddContextToFunctions { + async getWindows(): Promise { + const windows = this.windowsMainService.getWindows(); + + return windows.map(window => ({ + id: window.id, + workspace: window.openedWorkspace, + folderUri: window.openedFolderUri, + title: window.win.getTitle(), + filename: window.getRepresentedFilename() + })); + } + + async getWindowCount(windowId: number): Promise { return this.windowsMainService.getWindowCount(); } - async openEmptyWindow(windowId: number, options?: { reuse?: boolean, remoteAuthority?: string }): Promise { + async openEmptyWindow(windowId: number, options?: IOpenEmptyWindowOptions): Promise { this.windowsMainService.openEmptyWindow(OpenContext.API, options); } async openInWindow(windowId: number, toOpen: IWindowOpenable[], options: IOpenInWindowOptions = Object.create(null)): Promise { if (toOpen.length > 0) { - - // Revive URIs - toOpen.forEach(openable => { - if (isWorkspaceToOpen(openable)) { - openable.workspaceUri = URI.revive(openable.workspaceUri); - } else if (isFolderToOpen(openable)) { - openable.folderUri = URI.revive(openable.folderUri); - } else { - openable.fileUri = URI.revive(openable.fileUri); - } - }); - - options.waitMarkerFileURI = options.waitMarkerFileURI && URI.revive(options.waitMarkerFileURI); - - // Open via Windows Service this.windowsMainService.open({ context: OpenContext.API, contextWindowId: windowId, @@ -112,7 +108,11 @@ export class ElectronMainService implements AddContextToFunctions { + async focusWindow(windowId: number, options?: { windowId?: number; }): Promise { + if (options && typeof options.windowId === 'number') { + windowId = options.windowId; + } + const window = this.windowsMainService.getWindowById(windowId); if (window) { if (isMacintosh) { diff --git a/src/vs/platform/electron/node/electron.ts b/src/vs/platform/electron/node/electron.ts index 8fa00e4fa9b..d7c33695d6d 100644 --- a/src/vs/platform/electron/node/electron.ts +++ b/src/vs/platform/electron/node/electron.ts @@ -5,7 +5,7 @@ import { MessageBoxOptions, MessageBoxReturnValue, OpenDevToolsOptions, SaveDialogOptions, OpenDialogOptions, OpenDialogReturnValue, SaveDialogReturnValue, CrashReporterStartOptions } from 'electron'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { INativeOpenDialogOptions, IWindowOpenable, IOpenInWindowOptions } from 'vs/platform/windows/common/windows'; +import { INativeOpenDialogOptions, IWindowOpenable, IOpenInWindowOptions, IOpenedWindow, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows'; import { ISerializableCommandAction } from 'vs/platform/actions/common/actions'; export const IElectronService = createDecorator('electronService'); @@ -15,9 +15,10 @@ export interface IElectronService { _serviceBrand: undefined; // Window - windowCount(): Promise; + getWindows(): Promise; + getWindowCount(): Promise; - openEmptyWindow(options?: { reuse?: boolean, remoteAuthority?: string }): Promise; + openEmptyWindow(options?: IOpenEmptyWindowOptions): Promise; openInWindow(toOpen: IWindowOpenable[], options?: IOpenInWindowOptions): Promise; toggleFullScreen(): Promise; @@ -29,7 +30,7 @@ export interface IElectronService { unmaximizeWindow(): Promise; minimizeWindow(): Promise; - focusWindow(): Promise; + focusWindow(options?: { windowId?: number }): Promise; // Dialogs showMessageBox(options: MessageBoxOptions): Promise; diff --git a/src/vs/platform/ipc/node/simpleIpcProxy.ts b/src/vs/platform/ipc/node/simpleIpcProxy.ts index 66cfa69c2df..84e9beee2a8 100644 --- a/src/vs/platform/ipc/node/simpleIpcProxy.ts +++ b/src/vs/platform/ipc/node/simpleIpcProxy.ts @@ -5,6 +5,7 @@ import { Event } from 'vs/base/common/event'; import { IChannel, IServerChannel } from 'vs/base/parts/ipc/common/ipc'; +import { revive } from 'vs/base/common/marshalling'; // // Use both `SimpleServiceProxyChannel` and `createSimpleChannelProxy` @@ -53,10 +54,17 @@ export class SimpleServiceProxyChannel implements IServerChannel { const target = this.service[command]; if (typeof target === 'function') { if (Array.isArray(args)) { + + // Deserialize context if any const context = deserializeContext(args[0]); if (context) { args[0] = context; } + + // Revive: handles URIs and RegExp + for (let i = 0; i < args.length; i++) { + args[i] = revive(args[i]); + } } return target.apply(this.service, args); @@ -72,7 +80,9 @@ export function createSimpleChannelProxy(channel: IChannel, context?: unknown return new Proxy({}, { get(_target, propKey, _receiver) { if (typeof propKey === 'string') { - return function (...args: any[]) { + return async function (...args: any[]) { + + // Serialize context if any let methodArgs: any[]; if (serializedContext) { methodArgs = [context, ...args]; @@ -80,7 +90,8 @@ export function createSimpleChannelProxy(channel: IChannel, context?: unknown methodArgs = args; } - return channel.call(propKey, methodArgs); + // Revive: handles URIs and RegExp + return revive(await channel.call(propKey, methodArgs)); }; } diff --git a/src/vs/platform/windows/common/windows.ts b/src/vs/platform/windows/common/windows.ts index a7f7a797cf9..088f21421fa 100644 --- a/src/vs/platform/windows/common/windows.ts +++ b/src/vs/platform/windows/common/windows.ts @@ -45,13 +45,19 @@ export interface IWindowsService { getRecentlyOpened(windowId: number): Promise; isFocused(windowId: number): Promise; - // Global methods - getWindows(): Promise<{ id: number; workspace?: IWorkspaceIdentifier; folderUri?: ISingleFolderWorkspaceIdentifier; title: string; filename?: string; }[]>; getActiveWindowId(): Promise; } export const IWindowService = createDecorator('windowService'); +export interface IOpenedWindow { + id: number; + workspace?: IWorkspaceIdentifier; + folderUri?: ISingleFolderWorkspaceIdentifier; + title: string; + filename?: string; +} + export interface IOpenInWindowOptions { forceNewWindow?: boolean; forceReuseWindow?: boolean; @@ -62,6 +68,11 @@ export interface IOpenInWindowOptions { waitMarkerFileURI?: URI; } +export interface IOpenEmptyWindowOptions { + reuse?: boolean; + remoteAuthority?: string; +} + export type IWindowOpenable = IWorkspaceToOpen | IFolderToOpen | IFileToOpen; export interface IBaseWindowOpenable { diff --git a/src/vs/platform/windows/common/windowsIpc.ts b/src/vs/platform/windows/common/windowsIpc.ts index a36c713650e..acd99acaaa4 100644 --- a/src/vs/platform/windows/common/windowsIpc.ts +++ b/src/vs/platform/windows/common/windowsIpc.ts @@ -58,7 +58,6 @@ export class WindowsChannel implements IServerChannel { case 'getRecentlyOpened': return this.service.getRecentlyOpened(arg); case 'isFocused': return this.service.isFocused(arg); case 'openExtensionDevelopmentHostWindow': return (this.service as any).openExtensionDevelopmentHostWindow(arg[0], arg[1]); // TODO@Isidor move - case 'getWindows': return this.service.getWindows(); case 'getActiveWindowId': return this.service.getActiveWindowId(); } diff --git a/src/vs/platform/windows/electron-browser/windowsService.ts b/src/vs/platform/windows/electron-browser/windowsService.ts index 4d04de1f323..48b2e2aa4dc 100644 --- a/src/vs/platform/windows/electron-browser/windowsService.ts +++ b/src/vs/platform/windows/electron-browser/windowsService.ts @@ -6,7 +6,7 @@ import { Event } from 'vs/base/common/event'; import { IChannel } from 'vs/base/parts/ipc/common/ipc'; import { IWindowsService } from 'vs/platform/windows/common/windows'; -import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, reviveWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; +import { reviveWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IRecentlyOpened, IRecent, isRecentWorkspace } from 'vs/platform/history/common/history'; import { URI } from 'vs/base/common/uri'; import { ParsedArgs } from 'vs/platform/environment/common/environment'; @@ -58,28 +58,6 @@ export class WindowsService implements IWindowsService { return this.channel.call('openExtensionDevelopmentHostWindow', [args, env]); } - async getWindows(): Promise<{ id: number; workspace?: IWorkspaceIdentifier; folderUri?: ISingleFolderWorkspaceIdentifier; title: string; filename?: string; }[]> { - const result = await this.channel.call<{ - id: number; - workspace?: IWorkspaceIdentifier; - folderUri?: ISingleFolderWorkspaceIdentifier; - title: string; - filename?: string; - }[]>('getWindows'); - - for (const win of result) { - if (win.folderUri) { - win.folderUri = URI.revive(win.folderUri); - } - - if (win.workspace) { - win.workspace = reviveWorkspaceIdentifier(win.workspace); - } - } - - return result; - } - getActiveWindowId(): Promise { return this.channel.call('getActiveWindowId'); } diff --git a/src/vs/platform/windows/electron-main/legacyWindowsMainService.ts b/src/vs/platform/windows/electron-main/legacyWindowsMainService.ts index 0d8d47f3609..01adda9089c 100644 --- a/src/vs/platform/windows/electron-main/legacyWindowsMainService.ts +++ b/src/vs/platform/windows/electron-main/legacyWindowsMainService.ts @@ -14,7 +14,6 @@ import { IURLService, IURLHandler } from 'vs/platform/url/common/url'; import { IWindowsMainService, ICodeWindow } from 'vs/platform/windows/electron-main/windows'; import { IRecentlyOpened, IRecent } from 'vs/platform/history/common/history'; import { IHistoryMainService } from 'vs/platform/history/electron-main/historyMainService'; -import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { Schemas } from 'vs/base/common/network'; import { IProcessEnvironment } from 'vs/base/common/platform'; import { ILogService } from 'vs/platform/log/common/log'; @@ -97,20 +96,6 @@ export class LegacyWindowsMainService extends Disposable implements IWindowsServ } } - async getWindows(): Promise<{ id: number; workspace?: IWorkspaceIdentifier; folderUri?: ISingleFolderWorkspaceIdentifier; title: string; filename?: string; }[]> { - this.logService.trace('windowsService#getWindows'); - - const windows = this.windowsMainService.getWindows(); - - return windows.map(window => ({ - id: window.id, - workspace: window.openedWorkspace, - folderUri: window.openedFolderUri, - title: window.win.getTitle(), - filename: window.getRepresentedFilename() - })); - } - async getActiveWindowId(): Promise { return this._activeWindowId; } diff --git a/src/vs/platform/windows/electron-main/windows.ts b/src/vs/platform/windows/electron-main/windows.ts index f5f235bdc75..4eab25d137e 100644 --- a/src/vs/platform/windows/electron-main/windows.ts +++ b/src/vs/platform/windows/electron-main/windows.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { OpenContext, IWindowConfiguration, INativeOpenDialogOptions, IWindowOpenable } from 'vs/platform/windows/common/windows'; +import { OpenContext, IWindowConfiguration, INativeOpenDialogOptions, IWindowOpenable, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows'; import { ParsedArgs } from 'vs/platform/environment/common/environment'; import { Event } from 'vs/base/common/event'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; @@ -108,7 +108,7 @@ export interface IWindowsMainService { focusLastActive(cli: ParsedArgs, context: OpenContext): ICodeWindow; getLastActiveWindow(): ICodeWindow | undefined; waitForWindowCloseOrLoad(windowId: number): Promise; - openEmptyWindow(context: OpenContext, options?: { reuse?: boolean, remoteAuthority?: string }): ICodeWindow[]; + openEmptyWindow(context: OpenContext, options?: IOpenEmptyWindowOptions): ICodeWindow[]; openNewTabbedWindow(context: OpenContext): ICodeWindow[]; openExternal(url: string): Promise; sendToFocused(channel: string, ...args: any[]): void; diff --git a/src/vs/workbench/api/browser/mainThreadCommands.ts b/src/vs/workbench/api/browser/mainThreadCommands.ts index f6a63722a34..9e0e591c640 100644 --- a/src/vs/workbench/api/browser/mainThreadCommands.ts +++ b/src/vs/workbench/api/browser/mainThreadCommands.ts @@ -58,7 +58,7 @@ export class MainThreadCommands implements MainThreadCommandsShape { id, CommandsRegistry.registerCommand(id, (accessor, ...args) => { return this._proxy.$executeContributedCommand(id, ...args).then(result => { - return revive(result, 0); + return revive(result); }); }) ); @@ -74,7 +74,7 @@ export class MainThreadCommands implements MainThreadCommandsShape { async $executeCommand(id: string, args: any[], retry: boolean): Promise { for (let i = 0; i < args.length; i++) { - args[i] = revive(args[i], 0); + args[i] = revive(args[i]); } if (retry && args.length > 0 && !CommandsRegistry.getCommand(id)) { await this._extensionService.activateByEvent(`onCommand:${id}`); diff --git a/src/vs/workbench/api/common/extHostCommands.ts b/src/vs/workbench/api/common/extHostCommands.ts index 6df4dc8a417..a64b024a65c 100644 --- a/src/vs/workbench/api/common/extHostCommands.ts +++ b/src/vs/workbench/api/common/extHostCommands.ts @@ -52,7 +52,7 @@ export class ExtHostCommands implements ExtHostCommandsShape { { processArgument(a) { // URI, Regex - return revive(a, 0); + return revive(a); } }, { @@ -141,7 +141,7 @@ export class ExtHostCommands implements ExtHostCommandsShape { try { const result = await this._proxy.$executeCommand(id, toArgs, retry); - return revive(result, 0); + return revive(result); } catch (e) { // Rerun the command when it wasn't known, had arguments, and when retry // is enabled. We do this because the command might be registered inside diff --git a/src/vs/workbench/browser/web.simpleservices.ts b/src/vs/workbench/browser/web.simpleservices.ts index 6fddcd2bd9e..58120dd86cf 100644 --- a/src/vs/workbench/browser/web.simpleservices.ts +++ b/src/vs/workbench/browser/web.simpleservices.ts @@ -11,7 +11,6 @@ import { ILogService } from 'vs/platform/log/common/log'; import { Disposable } from 'vs/base/common/lifecycle'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows'; -import { IWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier } from 'vs/platform/workspaces/common/workspaces'; import { IRecentlyOpened, IRecent, isRecentFile, isRecentFolder } from 'vs/platform/history/common/history'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { addDisposableListener, EventType } from 'vs/base/browser/dom'; @@ -167,11 +166,6 @@ export class SimpleWindowsService implements IWindowsService { }); } - // Global methods - getWindows(): Promise<{ id: number; workspace?: IWorkspaceIdentifier; folderUri?: ISingleFolderWorkspaceIdentifier; title: string; filename?: string; }[]> { - return Promise.resolve([]); - } - getActiveWindowId(): Promise { return Promise.resolve(0); } diff --git a/src/vs/workbench/contrib/files/browser/fileCommands.ts b/src/vs/workbench/contrib/files/browser/fileCommands.ts index 565d0f62adb..c17b9ce80db 100644 --- a/src/vs/workbench/contrib/files/browser/fileCommands.ts +++ b/src/vs/workbench/contrib/files/browser/fileCommands.ts @@ -6,7 +6,7 @@ import * as nls from 'vs/nls'; import { URI } from 'vs/base/common/uri'; import { toResource, IEditorCommandsContext, SideBySideEditor } from 'vs/workbench/common/editor'; -import { IWindowOpenable, IOpenInWindowOptions, isWorkspaceToOpen } from 'vs/platform/windows/common/windows'; +import { IWindowOpenable, IOpenInWindowOptions, isWorkspaceToOpen, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows'; import { IHostService } from 'vs/workbench/services/host/browser/host'; import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; @@ -98,7 +98,7 @@ export const openWindowCommand = (accessor: ServicesAccessor, toOpen: IWindowOpe } }; -export const newWindowCommand = (accessor: ServicesAccessor, options?: { reuse?: boolean, remoteAuthority?: string }) => { +export const newWindowCommand = (accessor: ServicesAccessor, options?: IOpenEmptyWindowOptions) => { const hostService = accessor.get(IHostService); hostService.openEmptyWindow(options); }; diff --git a/src/vs/workbench/electron-browser/actions/windowActions.ts b/src/vs/workbench/electron-browser/actions/windowActions.ts index 4f4d2db4b3c..440cba68b56 100644 --- a/src/vs/workbench/electron-browser/actions/windowActions.ts +++ b/src/vs/workbench/electron-browser/actions/windowActions.ts @@ -5,7 +5,7 @@ import { URI } from 'vs/base/common/uri'; import { Action } from 'vs/base/common/actions'; -import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows'; +import { IWindowService } from 'vs/platform/windows/common/windows'; import * as nls from 'vs/nls'; import * as browser from 'vs/base/browser/browser'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; @@ -19,7 +19,6 @@ import { ICommandHandler } from 'vs/platform/commands/common/commands'; import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IElectronService } from 'vs/platform/electron/node/electron'; -import { IHostService } from 'vs/workbench/services/host/browser/host'; export class CloseCurrentWindowAction extends Action { @@ -168,14 +167,12 @@ export abstract class BaseSwitchWindow extends Action { constructor( id: string, label: string, - private windowsService: IWindowsService, private windowService: IWindowService, private quickInputService: IQuickInputService, private keybindingService: IKeybindingService, private modelService: IModelService, private modeService: IModeService, - private electronService: IElectronService, - private hostService: IHostService + private electronService: IElectronService ) { super(id, label); } @@ -185,7 +182,7 @@ export abstract class BaseSwitchWindow extends Action { async run(): Promise { const currentWindowId = this.windowService.windowId; - const windows = await this.windowsService.getWindows(); + const windows = await this.electronService.getWindows(); const placeHolder = nls.localize('switchWindowPlaceHolder', "Select a window to switch to"); const picks = windows.map(win => { const resource = win.filename ? URI.file(win.filename) : win.folderUri ? win.folderUri : win.workspace ? win.workspace.configPath : undefined; @@ -212,7 +209,7 @@ export abstract class BaseSwitchWindow extends Action { }); if (pick) { - this.hostService.focus(); + this.electronService.focusWindow({ windowId: pick.payload }); } } } @@ -225,16 +222,14 @@ export class SwitchWindow extends BaseSwitchWindow { constructor( id: string, label: string, - @IWindowsService windowsService: IWindowsService, @IWindowService windowService: IWindowService, @IQuickInputService quickInputService: IQuickInputService, @IKeybindingService keybindingService: IKeybindingService, @IModelService modelService: IModelService, @IModeService modeService: IModeService, - @IElectronService electronService: IElectronService, - @IHostService hostService: IHostService + @IElectronService electronService: IElectronService ) { - super(id, label, windowsService, windowService, quickInputService, keybindingService, modelService, modeService, electronService, hostService); + super(id, label, windowService, quickInputService, keybindingService, modelService, modeService, electronService); } protected isQuickNavigate(): boolean { @@ -250,16 +245,14 @@ export class QuickSwitchWindow extends BaseSwitchWindow { constructor( id: string, label: string, - @IWindowsService windowsService: IWindowsService, @IWindowService windowService: IWindowService, @IQuickInputService quickInputService: IQuickInputService, @IKeybindingService keybindingService: IKeybindingService, @IModelService modelService: IModelService, @IModeService modeService: IModeService, - @IElectronService electronService: IElectronService, - @IHostService hostService: IHostService + @IElectronService electronService: IElectronService ) { - super(id, label, windowsService, windowService, quickInputService, keybindingService, modelService, modeService, electronService, hostService); + super(id, label, windowService, quickInputService, keybindingService, modelService, modeService, electronService); } protected isQuickNavigate(): boolean { diff --git a/src/vs/workbench/services/host/browser/browserHostService.ts b/src/vs/workbench/services/host/browser/browserHostService.ts index 8b50c39a839..1d3cdcf65f2 100644 --- a/src/vs/workbench/services/host/browser/browserHostService.ts +++ b/src/vs/workbench/services/host/browser/browserHostService.ts @@ -8,7 +8,7 @@ import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { IWorkbenchLayoutService } from 'vs/workbench/services/layout/browser/layoutService'; import { IResourceEditor, IEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IWindowSettings, IWindowOpenable, IOpenInWindowOptions, isFolderToOpen, isWorkspaceToOpen, isFileToOpen } from 'vs/platform/windows/common/windows'; +import { IWindowSettings, IWindowOpenable, IOpenInWindowOptions, isFolderToOpen, isWorkspaceToOpen, isFileToOpen, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows'; import { pathsToEditors } from 'vs/workbench/common/editor'; import { IFileService } from 'vs/platform/files/common/files'; import { ILabelService } from 'vs/platform/label/common/label'; @@ -88,7 +88,7 @@ export class BrowserHostService implements IHostService { return { openFolderInNewWindow }; } - async openEmptyWindow(options?: { reuse?: boolean, remoteAuthority?: string }): Promise { + async openEmptyWindow(options?: IOpenEmptyWindowOptions): Promise { // TODO@Ben delegate to embedder const targetHref = `${document.location.origin}${document.location.pathname}?ew=true`; if (options && options.reuse) { diff --git a/src/vs/workbench/services/host/browser/host.ts b/src/vs/workbench/services/host/browser/host.ts index ad29524ffc9..c10c9a08c13 100644 --- a/src/vs/workbench/services/host/browser/host.ts +++ b/src/vs/workbench/services/host/browser/host.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IWindowOpenable, IOpenInWindowOptions } from 'vs/platform/windows/common/windows'; +import { IWindowOpenable, IOpenInWindowOptions, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows'; export const IHostService = createDecorator('hostService'); @@ -28,7 +28,7 @@ export interface IHostService { * Opens an empty window. The optional parameter allows to define if * a new window should open or the existing one change to an empty. */ - openEmptyWindow(options?: { reuse?: boolean, remoteAuthority?: string }): Promise; + openEmptyWindow(options?: IOpenEmptyWindowOptions): Promise; /** * Switch between fullscreen and normal window. diff --git a/src/vs/workbench/services/host/electron-browser/desktopHostService.ts b/src/vs/workbench/services/host/electron-browser/desktopHostService.ts index d356221a937..3f59e6662a1 100644 --- a/src/vs/workbench/services/host/electron-browser/desktopHostService.ts +++ b/src/vs/workbench/services/host/electron-browser/desktopHostService.ts @@ -8,7 +8,7 @@ import { IElectronService } from 'vs/platform/electron/node/electron'; import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; import { ILabelService } from 'vs/platform/label/common/label'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; -import { IWindowOpenable, IOpenInWindowOptions, isFolderToOpen, isWorkspaceToOpen } from 'vs/platform/windows/common/windows'; +import { IWindowOpenable, IOpenInWindowOptions, isFolderToOpen, isWorkspaceToOpen, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows'; export class DesktopHostService implements IHostService { @@ -22,7 +22,7 @@ export class DesktopHostService implements IHostService { //#region Window - get windowCount(): Promise { return this.electronService.windowCount(); } + get windowCount(): Promise { return this.electronService.getWindowCount(); } openInWindow(toOpen: IWindowOpenable[], options?: IOpenInWindowOptions): Promise { if (!!this.environmentService.configuration.remoteAuthority) { @@ -44,7 +44,7 @@ export class DesktopHostService implements IHostService { return this.labelService.getUriLabel(openable.fileUri); } - openEmptyWindow(options?: { reuse?: boolean, remoteAuthority?: string }): Promise { + openEmptyWindow(options?: IOpenEmptyWindowOptions): Promise { return this.electronService.openEmptyWindow(options); } diff --git a/src/vs/workbench/services/workspace/browser/workspaceEditingService.ts b/src/vs/workbench/services/workspace/browser/workspaceEditingService.ts index 8d964d6c31a..6e24d0d6f5b 100644 --- a/src/vs/workbench/services/workspace/browser/workspaceEditingService.ts +++ b/src/vs/workbench/services/workspace/browser/workspaceEditingService.ts @@ -7,7 +7,7 @@ import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common import { URI } from 'vs/base/common/uri'; import * as nls from 'vs/nls'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; -import { IWindowService, IWindowsService } from 'vs/platform/windows/common/windows'; +import { IWindowService } from 'vs/platform/windows/common/windows'; import { IJSONEditingService, JSONEditingError, JSONEditingErrorCode } from 'vs/workbench/services/configuration/common/jsonEditing'; import { IWorkspaceIdentifier, IWorkspaceFolderCreationData, IWorkspacesService, rewriteWorkspaceFileForNewLocation, WORKSPACE_FILTER } from 'vs/platform/workspaces/common/workspaces'; import { WorkspaceService } from 'vs/workbench/services/configuration/browser/configurationService'; @@ -20,7 +20,7 @@ import { BackupFileService } from 'vs/workbench/services/backup/common/backupFil import { ICommandService } from 'vs/platform/commands/common/commands'; import { distinct } from 'vs/base/common/arrays'; import { isLinux, isWindows, isMacintosh, isWeb } from 'vs/base/common/platform'; -import { isEqual, basename, isEqualOrParent, getComparisonKey } from 'vs/base/common/resources'; +import { isEqual, isEqualOrParent, getComparisonKey } from 'vs/base/common/resources'; import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; import { IFileService } from 'vs/platform/files/common/files'; import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; @@ -28,7 +28,6 @@ import { ILifecycleService, ShutdownReason } from 'vs/platform/lifecycle/common/ import { IFileDialogService, IDialogService } from 'vs/platform/dialogs/common/dialogs'; import { mnemonicButtonLabel } from 'vs/base/common/labels'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -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'; @@ -49,13 +48,12 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { @ICommandService private readonly commandService: ICommandService, @IFileService private readonly fileService: IFileService, @ITextFileService private readonly textFileService: ITextFileService, - @IWindowsService private readonly windowsService: IWindowsService, @IWorkspacesService private readonly workspacesService: IWorkspacesService, @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, @IFileDialogService private readonly fileDialogService: IFileDialogService, - @IDialogService private readonly dialogService: IDialogService, - @ILifecycleService readonly lifecycleService: ILifecycleService, - @ILabelService readonly labelService: ILabelService, + @IDialogService protected readonly dialogService: IDialogService, + @ILifecycleService private readonly lifecycleService: ILifecycleService, + @ILabelService private readonly labelService: ILabelService, @IHostService private readonly hostService: IHostService ) { this.registerListeners(); @@ -319,22 +317,6 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { } async isValidTargetWorkspacePath(path: URI): Promise { - const windows = await this.windowsService.getWindows(); - - // Prevent overwriting a workspace that is currently opened in another window - if (windows.some(window => !!window.workspace && isEqual(window.workspace.configPath, path))) { - await this.dialogService.show( - Severity.Info, - nls.localize('workspaceOpenedMessage', "Unable to save workspace '{0}'", basename(path)), - [nls.localize('ok', "OK")], - { - detail: nls.localize('workspaceOpenedDetail', "The workspace is already opened in another window. Please close that window first and then try again.") - } - ); - - return false; - } - return true; // OK } @@ -461,5 +443,3 @@ export class WorkspaceEditingService implements IWorkspaceEditingService { return undefined; } } - -registerSingleton(IWorkspaceEditingService, WorkspaceEditingService, true); diff --git a/src/vs/workbench/services/workspace/electron-browser/workspaceEditingService.ts b/src/vs/workbench/services/workspace/electron-browser/workspaceEditingService.ts new file mode 100644 index 00000000000..d4cd7087f34 --- /dev/null +++ b/src/vs/workbench/services/workspace/electron-browser/workspaceEditingService.ts @@ -0,0 +1,81 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; +import { URI } from 'vs/base/common/uri'; +import * as nls from 'vs/nls'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import { IWindowService } from 'vs/platform/windows/common/windows'; +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'; +import { IStorageService } from 'vs/platform/storage/common/storage'; +import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions'; +import { IBackupFileService } from 'vs/workbench/services/backup/common/backup'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { isEqual, basename } from 'vs/base/common/resources'; +import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; +import { IFileService } from 'vs/platform/files/common/files'; +import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService'; +import { ILifecycleService } from 'vs/platform/lifecycle/common/lifecycle'; +import { IFileDialogService, IDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +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 { WorkspaceEditingService } from 'vs/workbench/services/workspace/browser/workspaceEditingService'; +import { IElectronService } from 'vs/platform/electron/node/electron'; + +export class NativeWorkspaceEditingService extends WorkspaceEditingService { + + _serviceBrand: undefined; + + constructor( + @IJSONEditingService jsonEditingService: IJSONEditingService, + @IWorkspaceContextService contextService: WorkspaceService, + @IElectronService private electronService: IElectronService, + @IConfigurationService configurationService: IConfigurationService, + @IStorageService storageService: IStorageService, + @IExtensionService extensionService: IExtensionService, + @IBackupFileService backupFileService: IBackupFileService, + @INotificationService notificationService: INotificationService, + @ICommandService commandService: ICommandService, + @IFileService fileService: IFileService, + @ITextFileService textFileService: ITextFileService, + @IWindowService windowService: IWindowService, + @IWorkspacesService workspacesService: IWorkspacesService, + @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, + @IFileDialogService fileDialogService: IFileDialogService, + @IDialogService protected dialogService: IDialogService, + @ILifecycleService lifecycleService: ILifecycleService, + @ILabelService labelService: ILabelService, + @IHostService hostService: IHostService + ) { + super(jsonEditingService, contextService, windowService, configurationService, storageService, extensionService, backupFileService, notificationService, commandService, fileService, textFileService, workspacesService, environmentService, fileDialogService, dialogService, lifecycleService, labelService, hostService); + } + + async isValidTargetWorkspacePath(path: URI): Promise { + const windows = await this.electronService.getWindows(); + + // Prevent overwriting a workspace that is currently opened in another window + if (windows.some(window => !!window.workspace && isEqual(window.workspace.configPath, path))) { + await this.dialogService.show( + Severity.Info, + nls.localize('workspaceOpenedMessage', "Unable to save workspace '{0}'", basename(path)), + [nls.localize('ok', "OK")], + { + detail: nls.localize('workspaceOpenedDetail', "The workspace is already opened in another window. Please close that window first and then try again.") + } + ); + + return false; + } + + return true; // OK + } +} + +registerSingleton(IWorkspaceEditingService, NativeWorkspaceEditingService, true); diff --git a/src/vs/workbench/test/workbenchTestServices.ts b/src/vs/workbench/test/workbenchTestServices.ts index e9d60c72ad5..d585c154e16 100644 --- a/src/vs/workbench/test/workbenchTestServices.ts +++ b/src/vs/workbench/test/workbenchTestServices.ts @@ -35,7 +35,7 @@ import { IModeService } from 'vs/editor/common/services/modeService'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; -import { IWindowsService, IWindowService, MenuBarVisibility, IWindowConfiguration, IWindowOpenable, IOpenInWindowOptions } from 'vs/platform/windows/common/windows'; +import { IWindowsService, IWindowService, MenuBarVisibility, IWindowConfiguration, IWindowOpenable, IOpenInWindowOptions, IOpenEmptyWindowOptions } from 'vs/platform/windows/common/windows'; import { TestWorkspace } from 'vs/platform/workspace/test/common/testWorkspace'; import { createTextBufferFactoryFromStream } from 'vs/editor/common/model/textModel'; import { IEnvironmentService } from 'vs/platform/environment/common/environment'; @@ -1281,10 +1281,6 @@ export class TestWindowsService implements IWindowsService { }); } - getWindows(): Promise<{ id: number; workspace?: IWorkspaceIdentifier; folderUri?: ISingleFolderWorkspaceIdentifier; title: string; filename?: string; }[]> { - throw new Error('not implemented'); - } - getActiveWindowId(): Promise { return Promise.resolve(undefined); } @@ -1386,7 +1382,7 @@ export class TestHostService implements IHostService { async focus(): Promise { } - async openEmptyWindow(options?: { reuse?: boolean, remoteAuthority?: string }): Promise { } + async openEmptyWindow(options?: IOpenEmptyWindowOptions): Promise { } async openInWindow(toOpen: IWindowOpenable[], options?: IOpenInWindowOptions): Promise { } async toggleFullScreen(): Promise { } diff --git a/src/vs/workbench/workbench.desktop.main.ts b/src/vs/workbench/workbench.desktop.main.ts index 66904df5fa3..a1e46ccee59 100644 --- a/src/vs/workbench/workbench.desktop.main.ts +++ b/src/vs/workbench/workbench.desktop.main.ts @@ -72,6 +72,8 @@ import { IMenubarService } from 'vs/platform/menubar/node/menubar'; import { MenubarService } from 'vs/platform/menubar/electron-browser/menubarService'; import { IElectronService } from 'vs/platform/electron/node/electron'; import { ElectronService } from 'vs/platform/electron/electron-browser/electronService'; +import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; +import { NativeWorkspaceEditingService } from 'vs/workbench/services/workspace/electron-browser/workspaceEditingService'; registerSingleton(IClipboardService, ClipboardService, true); registerSingleton(ILifecycleService, LifecycleService); @@ -82,6 +84,7 @@ registerSingleton(IUpdateService, UpdateService); registerSingleton(IIssueService, IssueService); registerSingleton(IMenubarService, MenubarService); registerSingleton(IElectronService, ElectronService, true); +registerSingleton(IWorkspaceEditingService, NativeWorkspaceEditingService, true); //#endregion diff --git a/src/vs/workbench/workbench.web.main.ts b/src/vs/workbench/workbench.web.main.ts index 86e9d4f1f5b..ee239acabf7 100644 --- a/src/vs/workbench/workbench.web.main.ts +++ b/src/vs/workbench/workbench.web.main.ts @@ -65,6 +65,8 @@ import { NoOpTunnelService } from 'vs/platform/remote/common/tunnelService'; import { IUserDataSyncStoreService, IUserDataSyncService } from 'vs/platform/userDataSync/common/userDataSync'; import { UserDataSyncStoreService } from 'vs/platform/userDataSync/common/userDataSyncStoreService'; import { UserDataSyncService } from 'vs/platform/userDataSync/common/userDataSyncService'; +import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; +import { WorkspaceEditingService } from 'vs/workbench/services/workspace/browser/workspaceEditingService'; registerSingleton(IExtensionManagementService, ExtensionManagementService); registerSingleton(IBackupFileService, BackupFileService); @@ -75,6 +77,7 @@ registerSingleton(IContextMenuService, ContextMenuService); registerSingleton(ITunnelService, NoOpTunnelService, true); registerSingleton(IUserDataSyncStoreService, UserDataSyncStoreService); registerSingleton(IUserDataSyncService, UserDataSyncService); +registerSingleton(IWorkspaceEditingService, WorkspaceEditingService, true); //#endregion