import * as electron from "electron"; import { Emitter } from "@coder/events"; import * as windowsIpc from "vs/platform/windows/node/windowsIpc"; import { IWindowsService, INativeOpenDialogOptions, MessageBoxOptions, SaveDialogOptions, OpenDialogOptions, IMessageBoxResult, IDevToolsOptions, IEnterWorkspaceResult, CrashReporterStartOptions, INewWindowOptions, IOpenFileRequest, IAddFoldersRequest } from "vs/platform/windows/common/windows"; import { ParsedArgs } from "vs/platform/environment/common/environment"; import { IWorkspaceIdentifier, IWorkspaceFolderCreationData, ISingleFolderWorkspaceIdentifier } from "vs/platform/workspaces/common/workspaces"; import { URI } from "vs/base/common/uri"; import { IRecentlyOpened } from "vs/platform/history/common/history"; import { ISerializableCommandAction } from "vs/platform/actions/common/actions"; import { client } from "../client"; import { showOpenDialog } from "../dialog"; import { workbench } from "../workbench"; /** * Instead of going to the shared process, we'll directly run these methods on * the client. This setup means we can only control the current window. */ class WindowsService implements IWindowsService { // tslint:disable-next-line no-any public _serviceBrand: any; private readonly openEmitter = new Emitter(); private readonly focusEmitter = new Emitter(); private readonly blurEmitter = new Emitter(); private readonly maximizeEmitter = new Emitter(); private readonly unmaximizeEmitter = new Emitter(); private readonly recentlyOpenedChangeEmitter = new Emitter(); public readonly onWindowOpen = this.openEmitter.event; public readonly onWindowFocus = this.focusEmitter.event; public readonly onWindowBlur = this.blurEmitter.event; public readonly onWindowMaximize = this.maximizeEmitter.event; public readonly onWindowUnmaximize = this.unmaximizeEmitter.event; public readonly onRecentlyOpenedChange = this.recentlyOpenedChangeEmitter.event; private readonly window = new electron.BrowserWindow(); // Dialogs public async pickFileFolderAndOpen(_options: INativeOpenDialogOptions): Promise { showOpenDialog({ ...(_options.dialogOptions || {}), properties: { openFile: true, openDirectory: true, }, }).then((path) => { // tslint:disable-next-line:no-any (electron.ipcMain).send("vscode:openFiles", { filesToOpen: [{ fileUri: URI.file(path), }], } as IOpenFileRequest); }).catch((ex) => { // }); } public async pickFileAndOpen(_options: INativeOpenDialogOptions): Promise { showOpenDialog({ ...(_options.dialogOptions || {}), properties: { openFile: true, }, }).then((path) => { // tslint:disable-next-line:no-any (electron.ipcMain).send("vscode:openFiles", { filesToOpen: [{ fileUri: URI.file(path), }], } as IOpenFileRequest); }).catch((ex) => { // }); } public async pickFolderAndOpen(_options: INativeOpenDialogOptions): Promise { showOpenDialog({ ...(_options.dialogOptions || {}), properties: { openDirectory: true, }, }).then((path) => { workbench.workspace = URI.file(path); }).catch((ex) => { // }); } public async pickWorkspaceAndOpen(_options: INativeOpenDialogOptions): Promise { showOpenDialog({ ...(_options.dialogOptions || {}), properties: { openDirectory: true, }, }).then((path) => { // tslint:disable-next-line:no-any (electron.ipcMain).send("vscode:addFolders", { foldersToAdd: [URI.file(path)], } as IAddFoldersRequest); }).catch((ex) => { // }); } public showMessageBox(windowId: number, options: MessageBoxOptions): Promise { return new Promise((resolve): void => { electron.dialog.showMessageBox(this.getWindowById(windowId), options, (response, checkboxChecked) => { resolve({ button: response, checkboxChecked, }); }); }); } public showSaveDialog(windowId: number, options: SaveDialogOptions): Promise { return new Promise((resolve): void => { electron.dialog.showSaveDialog(this.getWindowById(windowId), options, (filename, _bookmark) => { resolve(filename); }); }); } public showOpenDialog(windowId: number, options: OpenDialogOptions): Promise { return showOpenDialog({ ...(options || {}), properties: { openDirectory: true, openFile: true, }, }).then((path) => { return [path]; }); } public reloadWindow(windowId: number, _args?: ParsedArgs): Promise { return Promise.resolve(this.getWindowById(windowId).reload()); } public openDevTools(_windowId: number, _options?: IDevToolsOptions): Promise { throw new Error("not implemented"); } public toggleDevTools(_windowId: number): Promise { throw new Error("Toggling developer tools from JavaScript is not supported."); } public closeWorkspace(_windowId: number): Promise { throw new Error("not implemented"); } public enterWorkspace(_windowId: number, _path: URI): Promise { if (_path.path.endsWith(".json")) { workbench.workspace = { id: "Untitled", configPath: _path.path, }; } else { workbench.workspace = _path; } return undefined!; } public createAndEnterWorkspace(_windowId: number, _folders?: IWorkspaceFolderCreationData[], _path?: string): Promise { throw new Error("not implemented"); } public saveAndEnterWorkspace(_windowId: number, _path: string): Promise { throw new Error("not implemented"); } public toggleFullScreen(windowId: number): Promise { const win = this.getWindowById(windowId); return Promise.resolve(win.setFullScreen(!win.isFullScreen())); } public setRepresentedFilename(windowId: number, fileName: string): Promise { return Promise.resolve(this.getWindowById(windowId).setRepresentedFilename(fileName)); } public addRecentlyOpened(_files: URI[]): Promise { throw new Error("not implemented"); } public removeFromRecentlyOpened(_paths: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | URI | string)[]): Promise { throw new Error("not implemented"); } public clearRecentlyOpened(): Promise { throw new Error("not implemented"); } public getRecentlyOpened(_windowId: number): Promise { // TODO: properly implement. return Promise.resolve({ workspaces: [], files: [], }); } public focusWindow(windowId: number): Promise { return Promise.resolve(this.getWindowById(windowId).focus()); } public closeWindow(_windowId: number): Promise { throw new Error("not implemented"); } public isFocused(windowId: number): Promise { return Promise.resolve(this.getWindowById(windowId).isFocused()); } public isMaximized(_windowId: number): Promise { throw new Error("not implemented"); } public maximizeWindow(_windowId: number): Promise { throw new Error("not implemented"); } public unmaximizeWindow(_windowId: number): Promise { throw new Error("not implemented"); } public minimizeWindow(_windowId: number): Promise { throw new Error("not implemented"); } public onWindowTitleDoubleClick(_windowId: number): Promise { throw new Error("not implemented"); } public setDocumentEdited(_windowId: number, _flag: boolean): Promise { throw new Error("not implemented"); } public quit(): Promise { throw new Error("not implemented"); } public relaunch(_options: { addArgs?: string[], removeArgs?: string[] }): Promise { throw new Error("not implemented"); } // macOS Native Tabs public newWindowTab(): Promise { throw new Error("not implemented"); } public showPreviousWindowTab(): Promise { throw new Error("not implemented"); } public showNextWindowTab(): Promise { throw new Error("not implemented"); } public moveWindowTabToNewWindow(): Promise { throw new Error("not implemented"); } public mergeAllWindowTabs(): Promise { throw new Error("not implemented"); } public toggleWindowTabsBar(): Promise { throw new Error("not implemented"); } // macOS TouchBar public updateTouchBar(_windowId: number, _items: ISerializableCommandAction[][]): Promise { throw new Error("not implemented"); } // Shared process public async whenSharedProcessReady(): Promise { await client.sharedProcessData; } public toggleSharedProcess(): Promise { throw new Error("not implemented"); } // Global methods public openWindow(_windowId: number, _paths: URI[], _options?: { forceNewWindow?: boolean, forceReuseWindow?: boolean, forceOpenWorkspaceAsFile?: boolean, args?: ParsedArgs }): Promise { throw new Error("not implemented"); } public openNewWindow(_options?: INewWindowOptions): Promise { throw new Error("not implemented"); } public showWindow(windowId: number): Promise { return Promise.resolve(this.getWindowById(windowId).show()); } public getWindows(): Promise<{ id: number; workspace?: IWorkspaceIdentifier; folderUri?: ISingleFolderWorkspaceIdentifier; title: string; filename?: string; }[]> { throw new Error("not implemented"); } public getWindowCount(): Promise { return Promise.resolve(1); } public log(_severity: string, ..._messages: string[]): Promise { throw new Error("not implemented"); } public showItemInFolder(_path: string): Promise { throw new Error("not implemented"); } public getActiveWindowId(): Promise { return Promise.resolve(1); } public async openExternal(_url: string): Promise { return typeof window.open(_url, "_blank") !== "undefined"; } public startCrashReporter(_config: CrashReporterStartOptions): Promise { throw new Error("not implemented"); } public openAboutDialog(): Promise { throw new Error("not implemented"); } public resolveProxy(windowId: number, url: string): Promise { return new Promise((resolve): void => { this.getWindowById(windowId).webContents.session.resolveProxy(url, (proxy) => { resolve(proxy); }); }); } /** * Get window by ID. For now this is always the current window. */ private getWindowById(_windowId: number): electron.BrowserWindow { return this.window; } } const target = windowsIpc as typeof windowsIpc; // @ts-ignore TODO: don't ignore it. target.WindowsChannelClient = WindowsService;