2019-01-18 21:46:40 +00:00
|
|
|
import * as electron from "electron";
|
|
|
|
import { Emitter } from "@coder/events";
|
2019-04-08 23:04:41 +00:00
|
|
|
import { logger } from "@coder/logger";
|
|
|
|
import { IWindowsService, INativeOpenDialogOptions, MessageBoxOptions, SaveDialogOptions, OpenDialogOptions, IMessageBoxResult, IDevToolsOptions, IEnterWorkspaceResult, CrashReporterStartOptions, INewWindowOptions, IOpenFileRequest, IAddFoldersRequest, IURIToOpen, IOpenSettings } from "vs/platform/windows/common/windows";
|
2019-01-18 21:46:40 +00:00
|
|
|
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";
|
2019-04-08 23:04:41 +00:00
|
|
|
import { IRecentlyOpened, IRecent } from "vs/platform/history/common/history";
|
2019-01-18 21:46:40 +00:00
|
|
|
import { ISerializableCommandAction } from "vs/platform/actions/common/actions";
|
2019-01-22 18:27:59 +00:00
|
|
|
import { client } from "../client";
|
2019-02-21 17:55:42 +00:00
|
|
|
import { showOpenDialog } from "../dialog";
|
2019-02-26 18:01:14 +00:00
|
|
|
import { workbench } from "../workbench";
|
2019-01-18 21:46:40 +00:00
|
|
|
|
2019-04-08 23:04:41 +00:00
|
|
|
// tslint:disable completed-docs
|
|
|
|
|
2019-04-08 22:36:49 +00:00
|
|
|
// VS Code overrides window.open to call openExternal, but we then call
|
|
|
|
// window.open which results in an infinite loop. Store the function but also
|
|
|
|
// make it unable to be set (doesn't work otherwise).
|
|
|
|
const windowOpen = window.open;
|
|
|
|
Object.defineProperty(window, "open", {
|
|
|
|
set: (): void => { /* Not allowed. */ },
|
|
|
|
get: (): Function => windowOpen,
|
|
|
|
});
|
|
|
|
|
2019-01-18 21:46:40 +00:00
|
|
|
/**
|
|
|
|
* 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.
|
|
|
|
*/
|
2019-04-05 23:49:29 +00:00
|
|
|
export class WindowsService implements IWindowsService {
|
2019-01-18 21:46:40 +00:00
|
|
|
// tslint:disable-next-line no-any
|
|
|
|
public _serviceBrand: any;
|
|
|
|
|
2019-02-06 17:53:23 +00:00
|
|
|
private readonly openEmitter = new Emitter<number>();
|
|
|
|
private readonly focusEmitter = new Emitter<number>();
|
|
|
|
private readonly blurEmitter = new Emitter<number>();
|
|
|
|
private readonly maximizeEmitter = new Emitter<number>();
|
|
|
|
private readonly unmaximizeEmitter = new Emitter<number>();
|
|
|
|
private readonly recentlyOpenedChangeEmitter = new Emitter<void>();
|
2019-01-18 21:46:40 +00:00
|
|
|
|
2019-02-06 17:53:23 +00:00
|
|
|
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;
|
2019-01-18 21:46:40 +00:00
|
|
|
|
2019-02-06 17:53:23 +00:00
|
|
|
private readonly window = new electron.BrowserWindow();
|
2019-01-18 21:46:40 +00:00
|
|
|
|
|
|
|
// Dialogs
|
2019-02-21 17:55:42 +00:00
|
|
|
public async pickFileFolderAndOpen(_options: INativeOpenDialogOptions): Promise<void> {
|
|
|
|
showOpenDialog({
|
|
|
|
...(_options.dialogOptions || {}),
|
|
|
|
properties: {
|
|
|
|
openFile: true,
|
|
|
|
openDirectory: true,
|
|
|
|
},
|
|
|
|
}).then((path) => {
|
|
|
|
// tslint:disable-next-line:no-any
|
|
|
|
(<any>electron.ipcMain).send("vscode:openFiles", {
|
|
|
|
filesToOpen: [{
|
|
|
|
fileUri: URI.file(path),
|
|
|
|
}],
|
|
|
|
} as IOpenFileRequest);
|
|
|
|
}).catch((ex) => {
|
2019-04-08 23:04:41 +00:00
|
|
|
logger.error(ex.message);
|
2019-02-21 17:55:42 +00:00
|
|
|
});
|
2019-01-18 21:46:40 +00:00
|
|
|
}
|
|
|
|
|
2019-02-21 17:55:42 +00:00
|
|
|
public async pickFileAndOpen(_options: INativeOpenDialogOptions): Promise<void> {
|
|
|
|
showOpenDialog({
|
|
|
|
...(_options.dialogOptions || {}),
|
|
|
|
properties: {
|
|
|
|
openFile: true,
|
|
|
|
},
|
|
|
|
}).then((path) => {
|
|
|
|
// tslint:disable-next-line:no-any
|
|
|
|
(<any>electron.ipcMain).send("vscode:openFiles", {
|
|
|
|
filesToOpen: [{
|
|
|
|
fileUri: URI.file(path),
|
|
|
|
}],
|
|
|
|
} as IOpenFileRequest);
|
|
|
|
}).catch((ex) => {
|
2019-04-08 23:04:41 +00:00
|
|
|
logger.error(ex.message);
|
2019-02-21 17:55:42 +00:00
|
|
|
});
|
2019-01-18 21:46:40 +00:00
|
|
|
}
|
|
|
|
|
2019-02-21 17:55:42 +00:00
|
|
|
public async pickFolderAndOpen(_options: INativeOpenDialogOptions): Promise<void> {
|
|
|
|
showOpenDialog({
|
|
|
|
...(_options.dialogOptions || {}),
|
|
|
|
properties: {
|
|
|
|
openDirectory: true,
|
|
|
|
},
|
|
|
|
}).then((path) => {
|
2019-02-26 18:01:14 +00:00
|
|
|
workbench.workspace = URI.file(path);
|
2019-02-21 17:55:42 +00:00
|
|
|
}).catch((ex) => {
|
2019-04-08 23:04:41 +00:00
|
|
|
logger.error(ex.message);
|
2019-02-21 17:55:42 +00:00
|
|
|
});
|
2019-01-18 21:46:40 +00:00
|
|
|
}
|
|
|
|
|
2019-02-21 17:55:42 +00:00
|
|
|
public async pickWorkspaceAndOpen(_options: INativeOpenDialogOptions): Promise<void> {
|
|
|
|
showOpenDialog({
|
|
|
|
...(_options.dialogOptions || {}),
|
|
|
|
properties: {
|
|
|
|
openDirectory: true,
|
|
|
|
},
|
|
|
|
}).then((path) => {
|
|
|
|
// tslint:disable-next-line:no-any
|
|
|
|
(<any>electron.ipcMain).send("vscode:addFolders", {
|
|
|
|
foldersToAdd: [URI.file(path)],
|
|
|
|
} as IAddFoldersRequest);
|
|
|
|
}).catch((ex) => {
|
2019-04-08 23:04:41 +00:00
|
|
|
logger.error(ex.message);
|
2019-02-21 17:55:42 +00:00
|
|
|
});
|
2019-01-18 21:46:40 +00:00
|
|
|
}
|
|
|
|
|
2019-01-26 00:18:21 +00:00
|
|
|
public showMessageBox(windowId: number, options: MessageBoxOptions): Promise<IMessageBoxResult> {
|
|
|
|
return new Promise((resolve): void => {
|
|
|
|
electron.dialog.showMessageBox(this.getWindowById(windowId), options, (response, checkboxChecked) => {
|
|
|
|
resolve({
|
|
|
|
button: response,
|
|
|
|
checkboxChecked,
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
2019-01-18 21:46:40 +00:00
|
|
|
}
|
|
|
|
|
2019-01-26 00:18:21 +00:00
|
|
|
public showSaveDialog(windowId: number, options: SaveDialogOptions): Promise<string> {
|
|
|
|
return new Promise((resolve): void => {
|
|
|
|
electron.dialog.showSaveDialog(this.getWindowById(windowId), options, (filename, _bookmark) => {
|
|
|
|
resolve(filename);
|
|
|
|
});
|
|
|
|
});
|
2019-01-18 21:46:40 +00:00
|
|
|
}
|
|
|
|
|
2019-04-08 23:04:41 +00:00
|
|
|
public async showOpenDialog(_windowId: number, options: OpenDialogOptions): Promise<string[]> {
|
|
|
|
return [await showOpenDialog({
|
2019-02-21 17:55:42 +00:00
|
|
|
...(options || {}),
|
|
|
|
properties: {
|
|
|
|
openDirectory: true,
|
|
|
|
openFile: true,
|
|
|
|
},
|
2019-04-08 23:04:41 +00:00
|
|
|
})];
|
2019-01-18 21:46:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public reloadWindow(windowId: number, _args?: ParsedArgs): Promise<void> {
|
|
|
|
return Promise.resolve(this.getWindowById(windowId).reload());
|
|
|
|
}
|
|
|
|
|
|
|
|
public openDevTools(_windowId: number, _options?: IDevToolsOptions): Promise<void> {
|
|
|
|
throw new Error("not implemented");
|
|
|
|
}
|
|
|
|
|
|
|
|
public toggleDevTools(_windowId: number): Promise<void> {
|
2019-02-04 17:27:36 +00:00
|
|
|
throw new Error("Toggling developer tools from JavaScript is not supported.");
|
2019-01-18 21:46:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public closeWorkspace(_windowId: number): Promise<void> {
|
|
|
|
throw new Error("not implemented");
|
|
|
|
}
|
|
|
|
|
2019-04-08 23:04:41 +00:00
|
|
|
public enterWorkspace(_windowId: number, uri: URI): Promise<IEnterWorkspaceResult> {
|
|
|
|
if (uri.path.endsWith(".json")) {
|
2019-02-26 18:01:14 +00:00
|
|
|
workbench.workspace = {
|
2019-02-21 17:55:42 +00:00
|
|
|
id: "Untitled",
|
2019-04-08 23:04:41 +00:00
|
|
|
configPath: uri,
|
2019-02-21 17:55:42 +00:00
|
|
|
};
|
|
|
|
} else {
|
2019-04-08 23:04:41 +00:00
|
|
|
workbench.workspace = uri;
|
2019-02-21 17:55:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return undefined!;
|
2019-01-18 21:46:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public createAndEnterWorkspace(_windowId: number, _folders?: IWorkspaceFolderCreationData[], _path?: string): Promise<IEnterWorkspaceResult> {
|
|
|
|
throw new Error("not implemented");
|
|
|
|
}
|
|
|
|
|
|
|
|
public saveAndEnterWorkspace(_windowId: number, _path: string): Promise<IEnterWorkspaceResult> {
|
|
|
|
throw new Error("not implemented");
|
|
|
|
}
|
|
|
|
|
|
|
|
public toggleFullScreen(windowId: number): Promise<void> {
|
|
|
|
const win = this.getWindowById(windowId);
|
|
|
|
|
|
|
|
return Promise.resolve(win.setFullScreen(!win.isFullScreen()));
|
|
|
|
}
|
|
|
|
|
|
|
|
public setRepresentedFilename(windowId: number, fileName: string): Promise<void> {
|
|
|
|
return Promise.resolve(this.getWindowById(windowId).setRepresentedFilename(fileName));
|
|
|
|
}
|
|
|
|
|
2019-04-08 23:04:41 +00:00
|
|
|
public addRecentlyOpened(_files: IRecent[]): Promise<void> {
|
2019-01-18 21:46:40 +00:00
|
|
|
throw new Error("not implemented");
|
|
|
|
}
|
|
|
|
|
|
|
|
public removeFromRecentlyOpened(_paths: (IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | URI | string)[]): Promise<void> {
|
|
|
|
throw new Error("not implemented");
|
|
|
|
}
|
|
|
|
|
|
|
|
public clearRecentlyOpened(): Promise<void> {
|
|
|
|
throw new Error("not implemented");
|
|
|
|
}
|
|
|
|
|
|
|
|
public getRecentlyOpened(_windowId: number): Promise<IRecentlyOpened> {
|
|
|
|
// TODO: properly implement.
|
|
|
|
return Promise.resolve({
|
|
|
|
workspaces: [],
|
|
|
|
files: [],
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
public focusWindow(windowId: number): Promise<void> {
|
|
|
|
return Promise.resolve(this.getWindowById(windowId).focus());
|
|
|
|
}
|
|
|
|
|
|
|
|
public closeWindow(_windowId: number): Promise<void> {
|
|
|
|
throw new Error("not implemented");
|
|
|
|
}
|
|
|
|
|
|
|
|
public isFocused(windowId: number): Promise<boolean> {
|
|
|
|
return Promise.resolve(this.getWindowById(windowId).isFocused());
|
|
|
|
}
|
|
|
|
|
|
|
|
public isMaximized(_windowId: number): Promise<boolean> {
|
|
|
|
throw new Error("not implemented");
|
|
|
|
}
|
|
|
|
|
|
|
|
public maximizeWindow(_windowId: number): Promise<void> {
|
|
|
|
throw new Error("not implemented");
|
|
|
|
}
|
|
|
|
|
|
|
|
public unmaximizeWindow(_windowId: number): Promise<void> {
|
|
|
|
throw new Error("not implemented");
|
|
|
|
}
|
|
|
|
|
|
|
|
public minimizeWindow(_windowId: number): Promise<void> {
|
|
|
|
throw new Error("not implemented");
|
|
|
|
}
|
|
|
|
|
|
|
|
public onWindowTitleDoubleClick(_windowId: number): Promise<void> {
|
|
|
|
throw new Error("not implemented");
|
|
|
|
}
|
|
|
|
|
|
|
|
public setDocumentEdited(_windowId: number, _flag: boolean): Promise<void> {
|
|
|
|
throw new Error("not implemented");
|
|
|
|
}
|
|
|
|
|
|
|
|
public quit(): Promise<void> {
|
|
|
|
throw new Error("not implemented");
|
|
|
|
}
|
|
|
|
|
|
|
|
public relaunch(_options: { addArgs?: string[], removeArgs?: string[] }): Promise<void> {
|
|
|
|
throw new Error("not implemented");
|
|
|
|
}
|
|
|
|
|
|
|
|
// macOS Native Tabs
|
|
|
|
public newWindowTab(): Promise<void> {
|
|
|
|
throw new Error("not implemented");
|
|
|
|
}
|
|
|
|
|
|
|
|
public showPreviousWindowTab(): Promise<void> {
|
|
|
|
throw new Error("not implemented");
|
|
|
|
}
|
|
|
|
|
|
|
|
public showNextWindowTab(): Promise<void> {
|
|
|
|
throw new Error("not implemented");
|
|
|
|
}
|
|
|
|
|
|
|
|
public moveWindowTabToNewWindow(): Promise<void> {
|
|
|
|
throw new Error("not implemented");
|
|
|
|
}
|
|
|
|
|
|
|
|
public mergeAllWindowTabs(): Promise<void> {
|
|
|
|
throw new Error("not implemented");
|
|
|
|
}
|
|
|
|
|
|
|
|
public toggleWindowTabsBar(): Promise<void> {
|
|
|
|
throw new Error("not implemented");
|
|
|
|
}
|
|
|
|
|
|
|
|
// macOS TouchBar
|
|
|
|
public updateTouchBar(_windowId: number, _items: ISerializableCommandAction[][]): Promise<void> {
|
|
|
|
throw new Error("not implemented");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Shared process
|
2019-01-22 18:27:59 +00:00
|
|
|
public async whenSharedProcessReady(): Promise<void> {
|
|
|
|
await client.sharedProcessData;
|
2019-01-18 21:46:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public toggleSharedProcess(): Promise<void> {
|
|
|
|
throw new Error("not implemented");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Global methods
|
2019-04-08 23:04:41 +00:00
|
|
|
public openWindow(_windowId: number, _uris: IURIToOpen[], _options?: IOpenSettings): Promise<void> {
|
2019-01-18 21:46:40 +00:00
|
|
|
throw new Error("not implemented");
|
|
|
|
}
|
|
|
|
|
|
|
|
public openNewWindow(_options?: INewWindowOptions): Promise<void> {
|
|
|
|
throw new Error("not implemented");
|
|
|
|
}
|
|
|
|
|
|
|
|
public showWindow(windowId: number): Promise<void> {
|
|
|
|
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<number> {
|
|
|
|
return Promise.resolve(1);
|
|
|
|
}
|
|
|
|
|
|
|
|
public log(_severity: string, ..._messages: string[]): Promise<void> {
|
|
|
|
throw new Error("not implemented");
|
|
|
|
}
|
|
|
|
|
2019-04-08 23:04:41 +00:00
|
|
|
public async showItemInFolder(uri: URI): Promise<void> {
|
|
|
|
workbench.workspace = uri;
|
2019-01-18 21:46:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public getActiveWindowId(): Promise<number | undefined> {
|
|
|
|
return Promise.resolve(1);
|
|
|
|
}
|
|
|
|
|
2019-02-21 17:55:42 +00:00
|
|
|
public async openExternal(_url: string): Promise<boolean> {
|
|
|
|
return typeof window.open(_url, "_blank") !== "undefined";
|
2019-01-18 21:46:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
public startCrashReporter(_config: CrashReporterStartOptions): Promise<void> {
|
|
|
|
throw new Error("not implemented");
|
|
|
|
}
|
|
|
|
|
|
|
|
public openAboutDialog(): Promise<void> {
|
|
|
|
throw new Error("not implemented");
|
|
|
|
}
|
|
|
|
|
|
|
|
public resolveProxy(windowId: number, url: string): Promise<string | undefined> {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|