Archived
1
0

Set platform based on server (#32)

* Set platform based on server

Had to refactor a bit to ensure our values get set before VS Code tries
to use them.

* Pave the way for mnemonics on all platforms

* Fix context menus on Mac

* Fix a bunch of things on Mac including menu bar

* Set keybindings based on client's OS
This commit is contained in:
Asher
2019-02-26 12:01:14 -06:00
committed by GitHub
parent 0c2c957312
commit 14da71499f
18 changed files with 976 additions and 444 deletions

View File

@ -5,6 +5,7 @@ import { upload } from "./upload";
import { client } from "./fill/client";
import { clipboard } from "./fill/clipboard";
import { INotificationService, IProgressService } from "./fill/notification";
import "./fill/os"; // Ensure it fills before anything else waiting on initData.
/**
* A general abstraction of an IDE client.

View File

@ -78,7 +78,7 @@ export class Dialog {
this.buttons = this.options.buttons.map((buttonText, buttonIndex) => {
const button = document.createElement("button");
// TODO: support mnemonics.
button.innerText = buttonText.replace("_", "");
button.innerText = buttonText.replace("&&", "");
button.addEventListener("click", () => {
this.actionEmitter.emit({
buttonIndex,

View File

@ -145,6 +145,14 @@ class Clipboard {
return false;
}
public readFindText(): string {
return "";
}
public writeFindText(_text: string): void {
// Nothing.
}
public writeText(value: string): Promise<void> {
return clipboard.writeText(value);
}

View File

@ -1,14 +1,13 @@
import { InitData } from "@coder/protocol";
import { OperatingSystem, InitData } from "@coder/protocol";
import { client } from "./client";
class OS {
private _homedir: string | undefined;
private _tmpdir: string | undefined;
private _platform: NodeJS.Platform | undefined;
public constructor() {
client.initData.then((data) => {
this.initialize(data);
});
client.initData.then((d) => this.initialize(d));
}
public homedir(): string {
@ -30,6 +29,11 @@ class OS {
public initialize(data: InitData): void {
this._homedir = data.homeDirectory;
this._tmpdir = data.tmpDirectory;
switch (data.os) {
case OperatingSystem.Windows: this._platform = "win32"; break;
case OperatingSystem.Mac: this._platform = "darwin"; break;
default: this._platform = "linux"; break;
}
}
public release(): string {
@ -37,14 +41,11 @@ class OS {
}
public platform(): NodeJS.Platform {
if (navigator.appVersion.indexOf("Win") != -1) {
return "win32";
}
if (navigator.appVersion.indexOf("Mac") != -1) {
return "darwin";
if (typeof this._platform === "undefined") {
throw new Error("trying to access platform before it has been set");
}
return "linux";
return this._platform;
}
}

View File

@ -3336,6 +3336,13 @@ source-map@~0.1.38:
dependencies:
amdefine ">=0.0.4"
sourcemap-blender@1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/sourcemap-blender/-/sourcemap-blender-1.0.5.tgz#d361f3d12381c4e477178113878fdf984a91bdbc"
integrity sha512-GPhjCmDtJ8YY6zt1L6kP6WtBg6WrdWt5hw2Wmgt9rwC3yiwLo9vEuabh/YYSZ5KmFV20hVkGdkTwpXtT2E65TA==
dependencies:
source-map "^0.7.3"
split-string@^3.0.1, split-string@^3.0.2:
version "3.1.0"
resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2"

View File

@ -1,216 +1,21 @@
import { IdeClient } from "@coder/ide";
import * as paths from "./fill/paths";
import "./fill/platform";
import "./fill/storageDatabase";
import "./fill/windowsService";
import "./fill/workspacesService";
import "./fill/environmentService";
import "./fill/vscodeTextmate";
import "./fill/codeEditor";
import "./fill/mouseEvent";
import "./fill/menuRegistry";
import "./fill/workbenchRegistry";
import { PasteAction } from "./fill/paste";
import "./fill/dom";
import "./vscode.scss";
import { IdeClient, IProgress, INotificationHandle } from "@coder/ide";
import { registerContextMenuListener } from "vs/base/parts/contextmenu/electron-main/contextmenu";
import { LogLevel } from "vs/platform/log/common/log";
import { URI } from "vs/base/common/uri";
import { INotificationService } from "vs/platform/notification/common/notification";
import { IProgressService2, ProgressLocation } from "vs/platform/progress/common/progress";
import { ExplorerItem, ExplorerModel } from "vs/workbench/parts/files/common/explorerModel";
import { DragMouseEvent } from "vs/base/browser/mouseEvent";
import { IEditorService, IResourceEditor } from "vs/workbench/services/editor/common/editorService";
import { IEditorGroup } from "vs/workbench/services/group/common/editorGroupsService";
import { IWindowsService, IWindowConfiguration } from "vs/platform/windows/common/windows";
import { ServiceCollection } from "vs/platform/instantiation/common/serviceCollection";
import { RawContextKey, IContextKeyService } from "vs/platform/contextkey/common/contextkey";
import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from "vs/platform/workspaces/common/workspaces";
export class Client extends IdeClient {
private readonly windowId = parseInt(new Date().toISOString().replace(/[-:.TZ]/g, ""), 10);
private _serviceCollection: ServiceCollection | undefined;
private _clipboardContextKey: RawContextKey<boolean> | undefined;
private _builtInExtensionsDirectory: string | undefined;
public get builtInExtensionsDirectory(): string {
if (!this._builtInExtensionsDirectory) {
throw new Error("trying to access builtin extensions directory before it has been set");
}
return this._builtInExtensionsDirectory;
}
public async handleExternalDrop(target: ExplorerItem | ExplorerModel, originalEvent: DragEvent): Promise<void> {
await this.upload.uploadDropped(
originalEvent,
(target instanceof ExplorerItem ? target : target.roots[0]).resource,
);
}
public handleDrop(event: DragEvent, resolveTargetGroup: () => IEditorGroup, afterDrop: (targetGroup: IEditorGroup) => void, targetIndex?: number): void {
this.initData.then((d) => {
this.upload.uploadDropped(event, URI.file(d.workingDirectory)).then((paths) => {
const uris = paths.map((p) => URI.file(p));
if (uris.length) {
(this.serviceCollection.get(IWindowsService) as IWindowsService).addRecentlyOpened(uris);
}
const editors: IResourceEditor[] = uris.map(uri => ({
resource: uri,
options: {
pinned: true,
index: targetIndex,
},
}));
const targetGroup = resolveTargetGroup();
(this.serviceCollection.get(IEditorService) as IEditorService).openEditors(editors, targetGroup).then(() => {
afterDrop(targetGroup);
});
});
});
}
/**
* Use to toggle the paste option inside editors based on the native clipboard.
*/
public get clipboardContextKey(): RawContextKey<boolean> {
if (!this._clipboardContextKey) {
throw new Error("Trying to access clipboard context key before it has been set");
}
return this._clipboardContextKey;
}
public get clipboardText(): Promise<string> {
return this.clipboard.readText();
}
/**
* Create a paste action for use in text inputs.
*/
public get pasteAction(): PasteAction {
return new PasteAction();
}
public set workspace(ws: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | undefined) {
if (typeof ws === "undefined") {
window.localStorage.removeItem("workspace");
} else {
window.localStorage.setItem("workspace", JSON.stringify(ws));
}
location.reload();
}
public get workspace(): undefined | IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier {
const ws = window.localStorage.getItem("workspace");
try {
return JSON.parse(ws!);
} catch (ex) {
return undefined;
}
}
public get serviceCollection(): ServiceCollection {
if (!this._serviceCollection) {
throw new Error("Trying to access service collection before it has been set");
}
return this._serviceCollection;
}
public set serviceCollection(collection: ServiceCollection) {
this._serviceCollection = collection;
this.progressService = {
start: <T>(title: string, task: (progress: IProgress) => Promise<T>, onCancel: () => void): Promise<T> => {
let lastProgress = 0;
return (this.serviceCollection.get(IProgressService2) as IProgressService2).withProgress({
location: ProgressLocation.Notification,
title,
cancellable: true,
}, (progress) => {
return task({
report: (p): void => {
progress.report({ increment: p - lastProgress });
lastProgress = p;
},
});
}, () => {
onCancel();
});
},
};
this.notificationService = {
error: (error: Error): void => (this.serviceCollection.get(INotificationService) as INotificationService).error(error),
prompt: (severity, message, buttons, onCancel): INotificationHandle => {
const handle = (this.serviceCollection.get(INotificationService) as INotificationService).prompt(
severity, message, buttons, { onCancel },
);
return {
close: (): void => handle.close(),
updateMessage: (message): void => handle.updateMessage(message),
updateButtons: (buttons): void => handle.updateActions({
primary: buttons.map((button) => ({
id: "",
label: button.label,
tooltip: "",
class: undefined,
enabled: true,
checked: false,
radio: false,
dispose: (): void => undefined,
run: (): Promise<void> => Promise.resolve(button.run()),
})),
}),
};
},
};
}
// NOTE: shouldn't import anything from VS Code here or anything that will
// depend on a synchronous fill like `os`.
class VSClient extends IdeClient {
protected initialize(): Promise<void> {
registerContextMenuListener();
this._clipboardContextKey = new RawContextKey("nativeClipboard", this.clipboard.isEnabled);
return this.task("Start workbench", 1000, async (data, sharedData) => {
paths._paths.initialize(data, sharedData);
this._builtInExtensionsDirectory = data.builtInExtensionsDirectory;
process.env.SHELL = data.shell;
const workspace = this.workspace || URI.file(data.workingDirectory);
const { startup } = require("./startup") as typeof import("vs/workbench/electron-browser/main");
const config: IWindowConfiguration = {
machineId: "1",
windowId: this.windowId,
logLevel: LogLevel.Info,
mainPid: 1,
appRoot: data.dataDirectory,
execPath: data.tmpDirectory,
userEnv: {},
nodeCachedDataDir: data.tmpDirectory,
perfEntries: [],
_: [],
};
if ((workspace as IWorkspaceIdentifier).configPath) {
config.workspace = workspace as IWorkspaceIdentifier;
} else {
config.folderUri = workspace as URI;
}
await startup(config);
const contextKeys = this.serviceCollection.get(IContextKeyService) as IContextKeyService;
const bounded = this.clipboardContextKey.bindTo(contextKeys);
this.clipboard.onPermissionChange((enabled) => {
bounded.set(enabled);
});
this.clipboard.initialize();
// At this point everything should be filled, including `os`. `os` also
// relies on `initData` but it listens first so it initialize before this
// callback, meaning we are safe to include everything from VS Code now.
const { workbench } = require("./workbench") as typeof import("./workbench");
await workbench.initialize();
}, this.initData, this.sharedProcessData);
}
}
export const client = new Client();
export const client = new VSClient();

View File

@ -3,7 +3,7 @@ import * as environment from "vs/platform/environment/node/environmentService";
export class EnvironmentService extends environment.EnvironmentService {
public get sharedIPCHandle(): string {
return paths._paths.socketPath || super.sharedIPCHandle;
return paths.getSocketPath() || super.sharedIPCHandle;
}
}

View File

@ -0,0 +1,10 @@
import * as labels from "vs/base/common/labels";
// Here we simply disable translation of mnemonics and leave everything as &&.
// Since we're in the browser, we can handle all platforms in the same way.
const target = labels as typeof labels;
target.mnemonicMenuLabel = (label: string, forceDisable?: boolean): string => {
return forceDisable ? label.replace(/\(&&\w\)|&&/g, "") : label;
};
target.mnemonicButtonLabel = (label: string): string => { return label; };
target.unmnemonicLabel = (label: string): string => { return label; };

View File

@ -4,7 +4,7 @@ import { TERMINAL_COMMAND_ID } from "vs/workbench/parts/terminal/common/terminal
import { ITerminalService } from "vs/workbench/parts/terminal/common/terminal";
import * as actions from "vs/workbench/parts/terminal/electron-browser/terminalActions";
import * as instance from "vs/workbench/parts/terminal/electron-browser/terminalInstance";
import { clipboard } from "@coder/ide";
import { client } from "../client";
const getLabel = (key: string, enabled: boolean): string => {
return enabled
@ -18,13 +18,13 @@ export class PasteAction extends Action {
public constructor() {
super(
"editor.action.clipboardPasteAction",
getLabel(PasteAction.KEY, clipboard.isEnabled),
getLabel(PasteAction.KEY, client.clipboard.isEnabled),
undefined,
clipboard.isEnabled,
async (): Promise<boolean> => clipboard.paste(),
client.clipboard.isEnabled,
async (): Promise<boolean> => client.clipboard.paste(),
);
clipboard.onPermissionChange((enabled) => {
client.clipboard.onPermissionChange((enabled) => {
this.label = getLabel(PasteAction.KEY, enabled);
this.enabled = enabled;
});
@ -36,17 +36,17 @@ class TerminalPasteAction extends Action {
public static readonly ID = TERMINAL_COMMAND_ID.PASTE;
public static readonly LABEL = nls.localize("workbench.action.terminal.paste", "Paste into Active Terminal");
public static readonly SHORT_LABEL = getLabel(TerminalPasteAction.KEY, clipboard.isEnabled);
public static readonly SHORT_LABEL = getLabel(TerminalPasteAction.KEY, client.clipboard.isEnabled);
public constructor(
id: string, label: string,
@ITerminalService private terminalService: ITerminalService,
) {
super(id, label);
clipboard.onPermissionChange((enabled) => {
client.clipboard.onPermissionChange((enabled) => {
this._setLabel(getLabel(TerminalPasteAction.KEY, enabled));
});
this._setLabel(getLabel(TerminalPasteAction.KEY, clipboard.isEnabled));
this._setLabel(getLabel(TerminalPasteAction.KEY, client.clipboard.isEnabled));
}
public run(): Promise<void> {
@ -63,8 +63,8 @@ class TerminalPasteAction extends Action {
class TerminalInstance extends instance.TerminalInstance {
public async paste(): Promise<void> {
this.focus();
if (clipboard.isEnabled) {
const text = await clipboard.readText();
if (client.clipboard.isEnabled) {
const text = await client.clipboard.readText();
this.sendText(text, false);
} else {
document.execCommand("paste");

View File

@ -4,6 +4,8 @@ class Paths {
private _appData: string | undefined;
private _defaultUserData: string | undefined;
private _socketPath: string | undefined;
private _builtInExtensionsDirectory: string | undefined;
private _workingDirectory: string | undefined;
public get appData(): string {
if (typeof this._appData === "undefined") {
@ -29,14 +31,35 @@ class Paths {
return this._socketPath;
}
public get builtInExtensionsDirectory(): string {
if (!this._builtInExtensionsDirectory) {
throw new Error("trying to access builtin extensions directory before it has been set");
}
return this._builtInExtensionsDirectory;
}
public get workingDirectory(): string {
if (!this._workingDirectory) {
throw new Error("trying to access working directory before it has been set");
}
return this._workingDirectory;
}
public initialize(data: InitData, sharedData: SharedProcessData): void {
process.env.VSCODE_LOGS = sharedData.logPath;
this._appData = data.dataDirectory;
this._defaultUserData = data.dataDirectory;
this._socketPath = sharedData.socketPath;
this._builtInExtensionsDirectory = data.builtInExtensionsDirectory;
this._workingDirectory = data.workingDirectory;
}
}
export const _paths = new Paths();
export const getAppDataPath = (): string => _paths.appData;
export const getDefaultUserDataPath = (): string => _paths.defaultUserData;
export const getWorkingDirectory = (): string => _paths.workingDirectory;
export const getBuiltInExtensionsDirectory = (): string => _paths.builtInExtensionsDirectory;
export const getSocketPath = (): string => _paths.socketPath;

View File

@ -1,12 +1,25 @@
import * as os from "os";
import * as platform from "vs/base/common/platform";
// tslint:disable no-any to override const
// Use en instead of en-US since that's vscode default and it uses
// that to determine whether to output aliases which will be redundant.
if (platform.locale === "en-US") {
// tslint:disable-next-line no-any to override const
(platform as any).locale = "en";
}
if (platform.language === "en-US") {
// tslint:disable-next-line no-any to override const
(platform as any).language = "en";
}
// Use the server's platform instead of the client's. For example, this affects
// how VS Code handles paths (and more) because different platforms give
// different results. We'll have to counter for things that shouldn't change,
// like keybindings.
(platform as any).isLinux = os.platform() === "linux";
(platform as any).isWindows = os.platform() === "win32";
(platform as any).isMacintosh = os.platform() === "darwin";
// This is used for keybindings, and in one place to choose between \r\n and \n
// (which we change to use platform.isWindows instead).
(platform as any).OS = (platform.isMacintosh ? platform.OperatingSystem.Macintosh : (platform.isWindows ? platform.OperatingSystem.Windows : platform.OperatingSystem.Linux));

View File

@ -2,13 +2,13 @@ import { readFile, writeFile, mkdir } from "fs";
import * as path from "path";
import { promisify } from "util";
import { IDisposable } from "@coder/disposable";
import { logger, field } from "@coder/logger";
import { Event } from "vs/base/common/event";
import * as workspaceStorage from "vs/base/node/storage";
import * as globalStorage from "vs/platform/storage/node/storageIpc";
import * as paths from "./paths";
import { logger, field } from "@coder/logger";
import { client } from "@coder/vscode/src/client";
import { IStorageService, WillSaveStateReason } from "vs/platform/storage/common/storage";
import * as paths from "./paths";
import { workbench } from "../workbench";
class StorageDatabase implements workspaceStorage.IStorageDatabase {
public readonly onDidChangeItemsExternal = Event.None;
@ -80,7 +80,7 @@ class StorageDatabase implements workspaceStorage.IStorageDatabase {
private triggerFlush(reason: WillSaveStateReason = WillSaveStateReason.NONE): boolean {
// tslint:disable-next-line:no-any
const storageService = client.serviceCollection.get<IStorageService>(IStorageService) as any;
const storageService = workbench.serviceCollection.get<IStorageService>(IStorageService) as any;
if (reason === WillSaveStateReason.SHUTDOWN && storageService.close) {
storageService.close();

View File

@ -9,6 +9,7 @@ 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
@ -79,7 +80,7 @@ class WindowsService implements IWindowsService {
openDirectory: true,
},
}).then((path) => {
client.workspace = URI.file(path);
workbench.workspace = URI.file(path);
}).catch((ex) => {
//
});
@ -150,12 +151,12 @@ class WindowsService implements IWindowsService {
public enterWorkspace(_windowId: number, _path: URI): Promise<IEnterWorkspaceResult> {
if (_path.path.endsWith(".json")) {
client.workspace = {
workbench.workspace = {
id: "Untitled",
configPath: _path.path,
};
} else {
client.workspace = _path;
workbench.workspace = _path;
}
return undefined!;

View File

@ -8,7 +8,7 @@ import { ToggleDevToolsAction } from "vs/workbench/electron-browser/actions/deve
import { TerminalPasteAction } from "vs/workbench/parts/terminal/electron-browser/terminalActions";
import { KEYBINDING_CONTEXT_TERMINAL_FOCUS } from "vs/workbench/parts/terminal/common/terminal";
import { KeyCode, KeyMod } from "vs/base/common/keyCodes";
import { client } from "../client";
import { workbench } from "../workbench";
// Intercept adding workbench actions so we can skip actions that won't work or
// modify actions that need different conditions, keybindings, etc.
@ -32,7 +32,7 @@ registry.registerWorkbenchAction = (descriptor: SyncActionDescriptor, alias: str
mac: { primary: 0 },
};
// tslint:disable-next-line no-any override private
(descriptor as any)._keybindingContext = ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_FOCUS, client.clipboardContextKey);
(descriptor as any)._keybindingContext = ContextKeyExpr.and(KEYBINDING_CONTEXT_TERMINAL_FOCUS, workbench.clipboardContextKey);
}
return originalRegister(descriptor, alias, category, when);

View File

@ -4,7 +4,7 @@ import { ILogService } from "vs/platform/log/common/log";
import { IWorkspaceFolderCreationData, IWorkspaceIdentifier, IWorkspacesService } from "vs/platform/workspaces/common/workspaces";
import { WorkspacesMainService } from "vs/platform/workspaces/electron-main/workspacesMainService";
import * as workspacesIpc from "vs/platform/workspaces/node/workspacesIpc";
import { client } from "../client";
import { workbench } from "../workbench";
/**
* Instead of going to the shared process, we'll directly run these methods on
@ -16,8 +16,8 @@ class WorkspacesService implements IWorkspacesService {
public createUntitledWorkspace(folders?: IWorkspaceFolderCreationData[] | undefined): Promise<IWorkspaceIdentifier> {
const mainService = new WorkspacesMainService(
client.serviceCollection.get<IEnvironmentService>(IEnvironmentService) as IEnvironmentService,
client.serviceCollection.get<ILogService>(ILogService) as ILogService,
workbench.serviceCollection.get<IEnvironmentService>(IEnvironmentService) as IEnvironmentService,
workbench.serviceCollection.get<ILogService>(ILogService) as ILogService,
);
// lib/vscode/src/vs/platform/workspaces/node/workspacesIpc.ts

View File

@ -1,183 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import "vs/loader";
//#region --- workbench/editor core
import "vs/editor/editor.all";
import "vs/workbench/api/electron-browser/extensionHost.contribution";
import "vs/workbench/electron-browser/shell.contribution";
import "vs/workbench/browser/workbench.contribution";
import { startup } from "vs/workbench/electron-browser/main";
//#endregion
//#region --- workbench actions
import "vs/workbench/browser/actions/layoutActions";
import "vs/workbench/browser/actions/listCommands";
import "vs/workbench/browser/actions/navigationActions";
import "vs/workbench/browser/parts/quickopen/quickOpenActions";
import "vs/workbench/browser/parts/quickinput/quickInputActions";
//#endregion
//#region --- API Extension Points
import "vs/workbench/api/common/menusExtensionPoint";
import "vs/workbench/api/common/configurationExtensionPoint";
import "vs/workbench/api/browser/viewsExtensionPoint";
//#endregion
//#region --- workbench services
import "vs/workbench/services/bulkEdit/electron-browser/bulkEditService";
//#endregion
//#region --- workbench parts
// Localizations
import "vs/workbench/parts/localizations/electron-browser/localizations.contribution";
// Preferences
import "vs/workbench/parts/preferences/electron-browser/preferences.contribution";
import "vs/workbench/parts/preferences/browser/keybindingsEditorContribution";
// Logs
import "vs/workbench/parts/logs/electron-browser/logs.contribution";
// Quick Open Handlers
import "vs/workbench/parts/quickopen/browser/quickopen.contribution";
// Explorer
import "vs/workbench/parts/files/electron-browser/explorerViewlet";
import "vs/workbench/parts/files/electron-browser/fileActions.contribution";
import "vs/workbench/parts/files/electron-browser/files.contribution";
// Backup
import "vs/workbench/parts/backup/common/backup.contribution";
// Stats
import "vs/workbench/parts/stats/node/stats.contribution";
// Rapid Render Splash
import "vs/workbench/parts/splash/electron-browser/partsSplash.contribution";
// Search
import "vs/workbench/parts/search/electron-browser/search.contribution";
import "vs/workbench/parts/search/browser/searchView";
import "vs/workbench/parts/search/browser/openAnythingHandler";
// SCM
import "vs/workbench/parts/scm/electron-browser/scm.contribution";
import "vs/workbench/parts/scm/electron-browser/scmViewlet";
// Debug
import "vs/workbench/parts/debug/electron-browser/debug.contribution";
import "vs/workbench/parts/debug/browser/debugQuickOpen";
import "vs/workbench/parts/debug/electron-browser/repl";
import "vs/workbench/parts/debug/browser/debugViewlet";
// Markers
import "vs/workbench/parts/markers/electron-browser/markers.contribution";
// Comments
import "vs/workbench/parts/comments/electron-browser/comments.contribution";
// HTML Preview
import "vs/workbench/parts/html/electron-browser/html.contribution";
// URL Support
import "vs/workbench/parts/url/electron-browser/url.contribution";
// Webview
import "vs/workbench/parts/webview/electron-browser/webview.contribution";
// Extensions Management
import "vs/workbench/parts/extensions/electron-browser/extensions.contribution";
import "vs/workbench/parts/extensions/browser/extensionsQuickOpen";
import "vs/workbench/parts/extensions/electron-browser/extensionsViewlet";
// Output Panel
import "vs/workbench/parts/output/electron-browser/output.contribution";
import "vs/workbench/parts/output/browser/outputPanel";
// Terminal
import "vs/workbench/parts/terminal/electron-browser/terminal.contribution";
import "vs/workbench/parts/terminal/browser/terminalQuickOpen";
import "vs/workbench/parts/terminal/electron-browser/terminalPanel";
// Relauncher
import "vs/workbench/parts/relauncher/electron-browser/relauncher.contribution";
// Tasks
import "vs/workbench/parts/tasks/electron-browser/task.contribution";
// Emmet
import "vs/workbench/parts/emmet/browser/emmet.browser.contribution";
import "vs/workbench/parts/emmet/electron-browser/emmet.contribution";
// CodeEditor Contributions
import "vs/workbench/parts/codeEditor/electron-browser/codeEditor.contribution";
// Execution
import "vs/workbench/parts/execution/electron-browser/execution.contribution";
// Snippets
import "vs/workbench/parts/snippets/electron-browser/snippets.contribution";
import "vs/workbench/parts/snippets/electron-browser/snippetsService";
import "vs/workbench/parts/snippets/electron-browser/insertSnippet";
import "vs/workbench/parts/snippets/electron-browser/configureSnippets";
import "vs/workbench/parts/snippets/electron-browser/tabCompletion";
// Send a Smile
import "vs/workbench/parts/feedback/electron-browser/feedback.contribution";
// Update
import "vs/workbench/parts/update/electron-browser/update.contribution";
// Surveys
import "vs/workbench/parts/surveys/electron-browser/nps.contribution";
import "vs/workbench/parts/surveys/electron-browser/languageSurveys.contribution";
// Performance
import "vs/workbench/parts/performance/electron-browser/performance.contribution";
// CLI
import "vs/workbench/parts/cli/electron-browser/cli.contribution";
// Themes Support
import "vs/workbench/parts/themes/electron-browser/themes.contribution";
import "vs/workbench/parts/themes/test/electron-browser/themes.test.contribution";
// Watermark
import "vs/workbench/parts/watermark/electron-browser/watermark";
// Welcome
import "vs/workbench/parts/welcome/walkThrough/electron-browser/walkThrough.contribution";
import "vs/workbench/parts/welcome/gettingStarted/electron-browser/gettingStarted.contribution";
import "vs/workbench/parts/welcome/overlay/browser/welcomeOverlay";
import "vs/workbench/parts/welcome/page/electron-browser/welcomePage.contribution";
// Outline
import "vs/workbench/parts/outline/electron-browser/outline.contribution";
// Experiments
import "vs/workbench/parts/experiments/electron-browser/experiments.contribution";
//#endregion
import { URI } from "vs/base/common/uri";
export {
URI,
startup,
};

View File

@ -0,0 +1,204 @@
import * as os from "os";
import { IProgress, INotificationHandle } from "@coder/ide";
import { client } from "./client";
import "./fill/platform";
import "./fill/dom";
import "./fill/codeEditor";
import "./fill/environmentService";
import "./fill/labels";
import "./fill/menuRegistry";
import "./fill/mouseEvent";
import "./fill/storageDatabase";
import "./fill/vscodeTextmate";
import "./fill/windowsService";
import "./fill/workbenchRegistry";
import "./fill/workspacesService";
import * as paths from "./fill/paths";
import { PasteAction } from "./fill/paste";
import { ExplorerItem, ExplorerModel } from "vs/workbench/parts/files/common/explorerModel";
import { IEditorGroup } from "vs/workbench/services/group/common/editorGroupsService";
import { IEditorService, IResourceEditor } from "vs/workbench/services/editor/common/editorService";
import { INotificationService } from "vs/platform/notification/common/notification";
import { IProgressService2, ProgressLocation } from "vs/platform/progress/common/progress";
import { ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from "vs/platform/workspaces/common/workspaces";
import { IWindowsService, IWindowConfiguration } from "vs/platform/windows/common/windows";
import { LogLevel } from "vs/platform/log/common/log";
import { RawContextKey, IContextKeyService } from "vs/platform/contextkey/common/contextkey";
import { ServiceCollection } from "vs/platform/instantiation/common/serviceCollection";
import { URI } from "vs/base/common/uri";
import "vs/loader";
export class Workbench {
private readonly windowId = parseInt(new Date().toISOString().replace(/[-:.TZ]/g, ""), 10);
private _serviceCollection: ServiceCollection | undefined;
private _clipboardContextKey: RawContextKey<boolean> | undefined;
public async handleExternalDrop(target: ExplorerItem | ExplorerModel, originalEvent: DragEvent): Promise<void> {
await client.upload.uploadDropped(
originalEvent,
(target instanceof ExplorerItem ? target : target.roots[0]).resource,
);
}
public handleDrop(event: DragEvent, resolveTargetGroup: () => IEditorGroup, afterDrop: (targetGroup: IEditorGroup) => void, targetIndex?: number): void {
client.upload.uploadDropped(event, URI.file(paths.getWorkingDirectory())).then((paths) => {
const uris = paths.map((p) => URI.file(p));
if (uris.length) {
(this.serviceCollection.get(IWindowsService) as IWindowsService).addRecentlyOpened(uris);
}
const editors: IResourceEditor[] = uris.map(uri => ({
resource: uri,
options: {
pinned: true,
index: targetIndex,
},
}));
const targetGroup = resolveTargetGroup();
(this.serviceCollection.get(IEditorService) as IEditorService).openEditors(editors, targetGroup).then(() => {
afterDrop(targetGroup);
});
});
}
/**
* Use to toggle the paste option inside editors based on the native clipboard.
*/
public get clipboardContextKey(): RawContextKey<boolean> {
if (!this._clipboardContextKey) {
throw new Error("Trying to access clipboard context key before it has been set");
}
return this._clipboardContextKey;
}
public get clipboardText(): Promise<string> {
return client.clipboard.readText();
}
/**
* Create a paste action for use in text inputs.
*/
public get pasteAction(): PasteAction {
return new PasteAction();
}
public set workspace(ws: IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier | undefined) {
if (typeof ws === "undefined") {
window.localStorage.removeItem("workspace");
} else {
window.localStorage.setItem("workspace", JSON.stringify(ws));
}
location.reload();
}
public get workspace(): undefined | IWorkspaceIdentifier | ISingleFolderWorkspaceIdentifier {
const ws = window.localStorage.getItem("workspace");
try {
return JSON.parse(ws!);
} catch (ex) {
return undefined;
}
}
public get serviceCollection(): ServiceCollection {
if (!this._serviceCollection) {
throw new Error("Trying to access service collection before it has been set");
}
return this._serviceCollection;
}
public set serviceCollection(collection: ServiceCollection) {
this._serviceCollection = collection;
client.progressService = {
start: <T>(title: string, task: (progress: IProgress) => Promise<T>, onCancel: () => void): Promise<T> => {
let lastProgress = 0;
return (this.serviceCollection.get(IProgressService2) as IProgressService2).withProgress({
location: ProgressLocation.Notification,
title,
cancellable: true,
}, (progress) => {
return task({
report: (p): void => {
progress.report({ increment: p - lastProgress });
lastProgress = p;
},
});
}, () => {
onCancel();
});
},
};
client.notificationService = {
error: (error: Error): void => (this.serviceCollection.get(INotificationService) as INotificationService).error(error),
prompt: (severity, message, buttons, onCancel): INotificationHandle => {
const handle = (this.serviceCollection.get(INotificationService) as INotificationService).prompt(
severity, message, buttons, { onCancel },
);
return {
close: (): void => handle.close(),
updateMessage: (message): void => handle.updateMessage(message),
updateButtons: (buttons): void => handle.updateActions({
primary: buttons.map((button) => ({
id: "",
label: button.label,
tooltip: "",
class: undefined,
enabled: true,
checked: false,
radio: false,
dispose: (): void => undefined,
run: (): Promise<void> => Promise.resolve(button.run()),
})),
}),
};
},
};
}
public async initialize(): Promise<void> {
this._clipboardContextKey = new RawContextKey("nativeClipboard", client.clipboard.isEnabled);
const workspace = this.workspace || URI.file(paths.getWorkingDirectory());
// If we try to import this above, workbench will be undefined due to
// circular imports.
require("vs/workbench/workbench.main");
const { startup } = require("vs/workbench/electron-browser/main");
const config: IWindowConfiguration = {
machineId: "1",
windowId: this.windowId,
logLevel: LogLevel.Info,
mainPid: 1,
appRoot: paths.getDefaultUserDataPath(),
execPath: os.tmpdir(),
userEnv: {},
nodeCachedDataDir: os.tmpdir(),
perfEntries: [],
_: [],
};
if ((workspace as IWorkspaceIdentifier).configPath) {
config.workspace = workspace as IWorkspaceIdentifier;
} else {
config.folderUri = workspace as URI;
}
await startup(config);
const contextKeys = this.serviceCollection.get(IContextKeyService) as IContextKeyService;
const bounded = this.clipboardContextKey.bindTo(contextKeys);
client.clipboard.onPermissionChange((enabled) => {
bounded.set(enabled);
});
client.clipboard.initialize();
}
}
export const workbench = new Workbench();