From ebe5e1b1a9a12ecf0049d3c088b96be45c9babf8 Mon Sep 17 00:00:00 2001 From: Asher Date: Wed, 30 Jan 2019 15:40:01 -0600 Subject: [PATCH] Uploader online (#26) --- packages/ide/src/client.ts | 36 ++--- packages/ide/src/fill/child_process.ts | 8 +- packages/ide/src/fill/client.ts | 2 +- packages/ide/src/fill/notification.ts | 114 +++++++++++++++ packages/ide/src/fill/uri.ts | 18 +++ packages/ide/src/fill/util.ts | 2 +- packages/ide/src/index.ts | 3 +- packages/ide/src/retry.ts | 72 ++-------- packages/ide/src/upload.ts | 88 +++++------- packages/protocol/src/browser/command.ts | 2 +- packages/protocol/src/node/server.ts | 6 +- packages/server/src/cli.ts | 2 +- packages/vscode/src/client.ts | 151 ++++++++++++++------ packages/vscode/src/fill/iconv-lite.ts | 6 +- packages/vscode/src/fill/require.ts | 8 +- packages/vscode/src/fill/storageDatabase.ts | 15 +- packages/vscode/src/fill/vscodeTextmate.ts | 23 +-- packages/vscode/src/fill/windowsService.ts | 5 +- packages/vscode/src/upload.ts | 59 -------- scripts/vscode.patch | 74 ++++++++++ 20 files changed, 430 insertions(+), 264 deletions(-) create mode 100644 packages/ide/src/fill/notification.ts create mode 100644 packages/ide/src/fill/uri.ts delete mode 100644 packages/vscode/src/upload.ts diff --git a/packages/ide/src/client.ts b/packages/ide/src/client.ts index 29020787..9738a430 100644 --- a/packages/ide/src/client.ts +++ b/packages/ide/src/client.ts @@ -1,28 +1,12 @@ import { Event } from "@coder/events"; import { field, logger, time, Time } from "@coder/logger"; import { InitData, ISharedProcessData } from "@coder/protocol"; -import { retry, Retry } from "./retry"; +import { retry } from "./retry"; +import { Upload } from "./upload"; import { client } from "./fill/client"; import { Clipboard, clipboard } from "./fill/clipboard"; - -export interface IURI { - - readonly path: string; - readonly fsPath: string; - readonly scheme: string; - -} - -export interface IURIFactory { - - /** - * Convert the object to an instance of a real URI. - */ - create(uri: IURI): T; - file(path: string): IURI; - parse(raw: string): IURI; - -} +import { INotificationService, NotificationService, IProgressService, ProgressService } from "./fill/notification"; +import { IURIFactory } from "./fill/uri"; /** * A general abstraction of an IDE client. @@ -34,9 +18,10 @@ export interface IURIFactory { */ export abstract class Client { - public readonly retry: Retry = retry; + public readonly retry = retry; public readonly clipboard: Clipboard = clipboard; public readonly uriFactory: IURIFactory; + public readonly upload = new Upload(new NotificationService(), new ProgressService()); private start: Time | undefined; private readonly progressElement: HTMLElement | undefined; private tasks: string[] = []; @@ -187,6 +172,15 @@ export abstract class Client { return this.sharedProcessDataPromise; } + public set notificationService(service: INotificationService) { + this.retry.notificationService = service; + this.upload.notificationService = service; + } + + public set progressService(service: IProgressService) { + this.upload.progressService = service; + } + /** * Initialize the IDE. */ diff --git a/packages/ide/src/fill/child_process.ts b/packages/ide/src/fill/child_process.ts index dbe950a7..a7415d04 100644 --- a/packages/ide/src/fill/child_process.ts +++ b/packages/ide/src/fill/child_process.ts @@ -1,4 +1,10 @@ import { CP } from "@coder/protocol"; import { client } from "./client"; +import { promisify } from "./util"; -export = new CP(client); +const cp = new CP(client); + +// tslint:disable-next-line no-any makes util.promisify return an object +(cp as any).exec[promisify.customPromisifyArgs] = ["stdout", "stderr"]; + +export = cp; diff --git a/packages/ide/src/fill/client.ts b/packages/ide/src/fill/client.ts index ceb16a32..c389348f 100644 --- a/packages/ide/src/fill/client.ts +++ b/packages/ide/src/fill/client.ts @@ -16,7 +16,7 @@ class Connection implements ReadWriteConnection { private readonly downEmitter: Emitter = new Emitter(); private readonly messageBuffer: Uint8Array[] = []; private socketTimeoutDelay = 60 * 1000; - private retryName = "Web socket"; + private retryName = "Socket"; private isUp: boolean = false; private closed: boolean = false; diff --git a/packages/ide/src/fill/notification.ts b/packages/ide/src/fill/notification.ts new file mode 100644 index 00000000..71e2e672 --- /dev/null +++ b/packages/ide/src/fill/notification.ts @@ -0,0 +1,114 @@ +import { logger, field } from "@coder/logger"; + +/** + * Handle for a notification that allows it to be closed and updated. + */ +export interface INotificationHandle { + + /** + * Closes the notification. + */ + close(): void; + + /** + * Update the message. + */ + updateMessage(message: string): void; + + /** + * Update the buttons. + */ + updateButtons(buttons: INotificationButton[]): void; + +} + +/** + * Notification severity. + */ +export enum Severity { + Ignore = 0, + Info = 1, + Warning = 2, + Error = 3, +} + +/** + * Notification button. + */ +export interface INotificationButton { + label: string; + run(): void; +} + +/** + * Optional notification service. + */ +export interface INotificationService { + + /** + * Display an error message. + */ + error(error: Error): void; + + /** + * Show a notification. + */ + prompt(severity: Severity, message: string, buttons: INotificationButton[], onCancel: () => void): INotificationHandle; + +} + +/** + * Updatable progress. + */ +export interface IProgress { + + /** + * Report progress. Progress is the completed percentage from 0 to 100. + */ + report(progress: number): void; + +} + +/** + * Option progress reporting service. + */ +export interface IProgressService { + + /** + * Start a new progress bar that resolves & disappears when the task finishes. + */ + start(title: string, task: (progress: IProgress) => Promise, onCancel: () => void): Promise; + +} + +/** + * Temporary notification service. + */ +export class NotificationService implements INotificationService { + + public error(error: Error): void { + logger.error(error.message, field("error", error)); + } + + public prompt(_severity: Severity, message: string, _buttons: INotificationButton[], _onCancel: () => void): INotificationHandle { + throw new Error(`cannot prompt using the console: ${message}`); + } + +} + +/** + * Temporary progress service. + */ +export class ProgressService implements IProgressService { + + public start(title: string, task: (progress: IProgress) => Promise): Promise { + logger.info(title); + + return task({ + report: (progress): void => { + logger.info(`${title} progress: ${progress}`); + }, + }); + } + +} diff --git a/packages/ide/src/fill/uri.ts b/packages/ide/src/fill/uri.ts new file mode 100644 index 00000000..6e306e16 --- /dev/null +++ b/packages/ide/src/fill/uri.ts @@ -0,0 +1,18 @@ +export interface IURI { + + readonly path: string; + readonly fsPath: string; + readonly scheme: string; + +} + +export interface IURIFactory { + + /** + * Convert the object to an instance of a real URI. + */ + create(uri: IURI): T; + file(path: string): IURI; + parse(raw: string): IURI; + +} diff --git a/packages/ide/src/fill/util.ts b/packages/ide/src/fill/util.ts index ef135cbd..f0342290 100644 --- a/packages/ide/src/fill/util.ts +++ b/packages/ide/src/fill/util.ts @@ -1,4 +1,4 @@ export * from "../../../../node_modules/util"; -import { implementation } from "util.promisify"; +import { implementation } from "../../../../node_modules/util.promisify"; export const promisify = implementation; diff --git a/packages/ide/src/index.ts b/packages/ide/src/index.ts index 8e82397f..1c81e19a 100644 --- a/packages/ide/src/index.ts +++ b/packages/ide/src/index.ts @@ -1,2 +1,3 @@ export * from "./client"; -export * from "./upload"; +export * from "./fill/uri"; +export * from "./fill/notification"; diff --git a/packages/ide/src/retry.ts b/packages/ide/src/retry.ts index 36f625e3..fb84adde 100644 --- a/packages/ide/src/retry.ts +++ b/packages/ide/src/retry.ts @@ -1,56 +1,5 @@ import { logger } from "@coder/logger"; - -/** - * Handle for a notification that allows it to be closed and updated. - */ -export interface INotificationHandle { - - /** - * Closes the notification. - */ - close(): void; - - /** - * Update the message. - */ - updateMessage(message: string): void; - - /** - * Update the buttons. - */ - updateButtons(buttons: INotificationButton[]): void; - -} - -/** - * Notification severity. - */ -enum Severity { - Ignore = 0, - Info = 1, - Warning = 2, - Error = 3, -} - -/** - * Notification button. - */ -export interface INotificationButton { - label: string; - run(): void; -} - -/** - * Optional notification service. - */ -export interface INotificationService { - - /** - * Show a notification. - */ - prompt(severity: Severity, message: string, buttons: INotificationButton[], onCancel: () => void): INotificationHandle; - -} +import { NotificationService, INotificationHandle, INotificationService, Severity } from "./fill/notification"; interface IRetryItem { count?: number; @@ -91,15 +40,16 @@ export class Retry { // for reasoning.) private waitDelay = 50; - public constructor(private notificationService?: INotificationService) { + public constructor(private _notificationService: INotificationService) { this.items = new Map(); } - /** - * Set notification service. - */ - public setNotificationService(notificationService?: INotificationService): void { - this.notificationService = notificationService; + public set notificationService(service: INotificationService) { + this._notificationService = service; + } + + public get notificationService(): INotificationService { + return this._notificationService; } /** @@ -262,10 +212,6 @@ export class Retry { * Update, close, or show the notification. */ private updateNotification(): void { - if (!this.notificationService) { - return; - } - // tslint:disable-next-line no-any because NodeJS.Timer is valid. clearTimeout(this.updateTimeout as any); @@ -343,4 +289,4 @@ export class Retry { // Global instance so we can block other retries when retrying the main // connection. -export const retry = new Retry(); +export const retry = new Retry(new NotificationService()); diff --git a/packages/ide/src/upload.ts b/packages/ide/src/upload.ts index 9bb62343..aac6c0f7 100644 --- a/packages/ide/src/upload.ts +++ b/packages/ide/src/upload.ts @@ -3,7 +3,8 @@ import { appendFile } from "fs"; import { promisify } from "util"; import { logger, Logger } from "@coder/logger"; import { escapePath } from "@coder/protocol"; -import { IURI } from "./uri"; +import { IURI } from "./fill/uri"; +import { INotificationService, IProgressService, IProgress, Severity } from "./fill/notification"; /** * Represents an uploadable directory, so we can query for existing files once. @@ -27,47 +28,6 @@ interface IEntry { }); } -/** - * Updatable progress. - */ -interface IProgress { - - /** - * Report progress. Progress is the completed percentage from 0 to 100. - */ - report(progress: number): void; - -} - -/** - * Service for reporting progress. - */ -interface IProgressService { - - /** - * Start a new progress bar that resolves & disappears when the task finishes. - */ - start(title:string, task: (progress: IProgress) => Promise): Promise; - -} - -/** - * Service for notifications. - */ -interface INotificationService { - - /** - * Display an error message. - */ - error(error: Error): void; - - /** - * Ask for a decision. - */ - prompt(message: string, choices: string[]): Promise; - -} - /** * Handles file uploads. */ @@ -76,8 +36,6 @@ export class Upload { private readonly maxParallelUploads = 100; private readonly readSize = 32000; // ~32kb max while reading in the file. private readonly packetSize = 32000; // ~32kb max when writing. - private readonly notificationService: INotificationService; - private readonly progressService: IProgressService; private readonly logger: Logger; private readonly currentlyUploadingFiles: Map; private readonly queueByDirectory: Map; @@ -88,9 +46,10 @@ export class Upload { private uploadedFilePaths: string[]; private total: number; - public constructor(notificationService: INotificationService, progressService: IProgressService) { - this.notificationService = notificationService; - this.progressService = progressService; + public constructor( + private _notificationService: INotificationService, + private _progressService: IProgressService, + ) { this.logger = logger.named("Upload"); this.currentlyUploadingFiles = new Map(); this.queueByDirectory = new Map(); @@ -99,6 +58,22 @@ export class Upload { this.total = 0; } + public set notificationService(service: INotificationService) { + this._notificationService = service; + } + + public get notificationService(): INotificationService { + return this._notificationService; + } + + public set progressService(service: IProgressService) { + this._progressService = service; + } + + public get progressService(): IProgressService { + return this._progressService; + } + /** * Upload dropped files. This will try to upload everything it can. Errors * will show via notifications. If an upload operation is ongoing, the files @@ -125,6 +100,8 @@ export class Upload { resolve(uploaded); }; }); + }, () => { + this.cancel(); }); } this.uploadFiles(); @@ -214,8 +191,21 @@ export class Upload { */ private async uploadFile(path: string, file: File, existingFiles: string[]): Promise { if (existingFiles.includes(path)) { - const choice = await this.notificationService.prompt(`${path} already exists. Overwrite?`, ["Yes", "No"]); - if (choice !== "Yes") { + const shouldOverwrite = await new Promise((resolve): void => { + this.notificationService.prompt( + Severity.Error, + `${path} already exists. Overwrite?`, + [{ + label: "Yes", + run: (): void => resolve(true), + }, { + label: "No", + run: (): void => resolve(false), + }], + () => resolve(false), + ); + }); + if (!shouldOverwrite) { return; } } diff --git a/packages/protocol/src/browser/command.ts b/packages/protocol/src/browser/command.ts index ce19e289..6e1be26f 100644 --- a/packages/protocol/src/browser/command.ts +++ b/packages/protocol/src/browser/command.ts @@ -367,4 +367,4 @@ export interface ActiveEval { on(event: "close", cb: () => void): void; on(event: "error", cb: (err: Error) => void): void; on(event: string, cb: (...args: any[]) => void): void; -} \ No newline at end of file +} diff --git a/packages/protocol/src/node/server.ts b/packages/protocol/src/node/server.ts index 3120eaba..99785823 100644 --- a/packages/protocol/src/node/server.ts +++ b/packages/protocol/src/node/server.ts @@ -122,11 +122,13 @@ export class Server { this.evals.set(evalMessage.getId(), resp); } } else if (message.hasEvalEvent()) { - const e = this.evals.get(message.getEvalEvent()!.getId()); + const evalEventMessage = message.getEvalEvent()!; + logger.debug("EvalEventMessage", field("id", evalEventMessage.getId())); + const e = this.evals.get(evalEventMessage.getId()); if (!e) { return; } - e.onEvent(message.getEvalEvent()!); + e.onEvent(evalEventMessage); } else if (message.hasNewSession()) { const sessionMessage = message.getNewSession()!; logger.debug("NewSession", field("id", sessionMessage.getId())); diff --git a/packages/server/src/cli.ts b/packages/server/src/cli.ts index b2a7f82e..19e7f9d3 100644 --- a/packages/server/src/cli.ts +++ b/packages/server/src/cli.ts @@ -1,4 +1,4 @@ -import { field, logger, Level } from "@coder/logger"; +import { field, logger } from "@coder/logger"; import { ServerMessage, SharedProcessActiveMessage } from "@coder/protocol/src/proto"; import { Command, flags } from "@oclif/command"; import * as fs from "fs"; diff --git a/packages/vscode/src/client.ts b/packages/vscode/src/client.ts index 92ad7098..59598b36 100644 --- a/packages/vscode/src/client.ts +++ b/packages/vscode/src/client.ts @@ -6,17 +6,125 @@ import "./fill/environmentService"; import "./fill/vscodeTextmate"; import "./fill/dom"; import "./vscode.scss"; - -import { Client as IDEClient, IURI, IURIFactory } from "@coder/ide"; - +import { Client as IDEClient, IURI, IURIFactory, IProgress, INotificationHandle } from "@coder/ide"; import { registerContextMenuListener } from "vs/base/parts/contextmenu/electron-main/contextmenu"; import { LogLevel } from "vs/platform/log/common/log"; // import { RawContextKey, IContextKeyService } from "vs/platform/contextkey/common/contextkey"; 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, Model } 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 } from "vs/platform/windows/common/windows"; +import { ServiceCollection } from "vs/platform/instantiation/common/serviceCollection"; export class Client extends IDEClient { private readonly windowId = parseInt(new Date().toISOString().replace(/[-:.TZ]/g, ""), 10); + private _serviceCollection: ServiceCollection | undefined; + + public async handleExternalDrop(target: ExplorerItem | Model, originalEvent: DragMouseEvent): Promise { + await this.upload.uploadDropped( + originalEvent.browserEvent as DragEvent, + (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); + }); + }); + }); + } + + 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: (title: string, task: (progress: IProgress) => Promise, onCancel: () => void): Promise => { + 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 => Promise.resolve(button.run()), + })), + }), + }; + }, + }; + } + + protected createUriFactory(): IURIFactory { + return { + // TODO: not sure why this is an error. + // tslint:disable-next-line no-any + create: (uri: IURI): URI => URI.from(uri) as any, + file: (path: string): IURI => URI.file(path), + parse: (raw: string): IURI => URI.parse(raw), + }; + } protected initialize(): Promise { registerContextMenuListener(); @@ -46,33 +154,6 @@ export class Client extends IDEClient { folderUri: URI.file(data.workingDirectory), }); - // TODO: Set notification service for retrying. - // this.retry.setNotificationService({ - // prompt: (severity, message, buttons, onCancel) => { - // const handle = getNotificationService().prompt(severity, message, buttons, onCancel); - // return { - // close: () => handle.close(), - // updateMessage: (message) => handle.updateMessage(message), - // updateButtons: (buttons) => handle.updateActions({ - // primary: buttons.map((button) => ({ - // id: undefined, - // label: button.label, - // tooltip: undefined, - // class: undefined, - // enabled: true, - // checked: false, - // radio: false, - // dispose: () => undefined, - // run: () => { - // button.run(); - // return Promise.resolve(); - // }, - // })), - // }), - // }; - // } - // }); - // TODO: Set up clipboard context. // const workbench = workbenchShell.workbench; // const contextKeys = workbench.workbenchParams.serviceCollection.get(IContextKeyService) as IContextKeyService; @@ -85,16 +166,6 @@ export class Client extends IDEClient { }, this.initData, pathSets); } - protected createUriFactory(): IURIFactory { - return { - // TODO: not sure why this is an error. - // tslint:disable-next-line no-any - create: (uri: IURI): URI => URI.from(uri) as any, - file: (path: string): IURI => URI.file(path), - parse: (raw: string): IURI => URI.parse(raw), - }; - } - } export const client = new Client(); diff --git a/packages/vscode/src/fill/iconv-lite.ts b/packages/vscode/src/fill/iconv-lite.ts index 9683b972..1ea47d6a 100644 --- a/packages/vscode/src/fill/iconv-lite.ts +++ b/packages/vscode/src/fill/iconv-lite.ts @@ -59,7 +59,7 @@ const decodeStream = (encoding: string): NodeJS.ReadWriteStream => { return new IconvLiteDecoderStream({ encoding }); }; -// @ts-ignore -iconv.decodeStream = decodeStream; +const target = iconv as typeof iconv; +target.decodeStream = decodeStream; -export = iconv; +export = target; diff --git a/packages/vscode/src/fill/require.ts b/packages/vscode/src/fill/require.ts index 9e294f4f..92853b94 100644 --- a/packages/vscode/src/fill/require.ts +++ b/packages/vscode/src/fill/require.ts @@ -1,3 +1,7 @@ -// TODO: ? +import { join } from "path"; + // tslint:disable-next-line no-any -(global as any).requireToUrl = (path: string): string => `${location.protocol}//{location.host}/${path}`; +(global as any).requireToUrl = (path: string): string => { + // TODO: can start with vs/... + return join(`${location.protocol}//${location.host}/resource`, path); +}; diff --git a/packages/vscode/src/fill/storageDatabase.ts b/packages/vscode/src/fill/storageDatabase.ts index 1e947122..161949c4 100644 --- a/packages/vscode/src/fill/storageDatabase.ts +++ b/packages/vscode/src/fill/storageDatabase.ts @@ -8,7 +8,7 @@ import * as globalStorage from "vs/platform/storage/node/storageIpc"; import * as paths from "./paths"; import { logger, field } from "@coder/logger"; -export class StorageDatabase implements workspaceStorage.IStorageDatabase { +class StorageDatabase implements workspaceStorage.IStorageDatabase { public readonly onDidChangeItemsExternal = Event.None; private items = new Map(); @@ -81,7 +81,7 @@ export class StorageDatabase implements workspaceStorage.IStorageDatabase { } -export class GlobalStorageDatabase extends StorageDatabase implements IDisposable { +class GlobalStorageDatabase extends StorageDatabase implements IDisposable { public constructor() { super(path.join(paths.getAppDataPath(), "globalStorage", "state.vscdb")); @@ -93,7 +93,10 @@ export class GlobalStorageDatabase extends StorageDatabase implements IDisposabl } -// @ts-ignore -workspaceStorage.SQLiteStorageDatabase = StorageDatabase; -// @ts-ignore -globalStorage.GlobalStorageDatabaseChannelClient = GlobalStorageDatabase; +const workspaceTarget = workspaceStorage as typeof workspaceStorage; +// @ts-ignore TODO: don't ignore it. +workspaceTarget.SQLiteStorageDatabase = StorageDatabase; + +const globalTarget = globalStorage as typeof globalStorage; +// @ts-ignore TODO: don't ignore it. +globalTarget.GlobalStorageDatabaseChannelClient = GlobalStorageDatabase; diff --git a/packages/vscode/src/fill/vscodeTextmate.ts b/packages/vscode/src/fill/vscodeTextmate.ts index 81e10c1c..06a097fa 100644 --- a/packages/vscode/src/fill/vscodeTextmate.ts +++ b/packages/vscode/src/fill/vscodeTextmate.ts @@ -8,28 +8,29 @@ target.Registry = class Registry extends vscodeTextmate.Registry { ...opts, getOnigLib: (): Promise => { return new Promise((res, rej) => { - const onigasm = require('onigasm'); - const wasmUrl = require('!!file-loader!onigasm/lib/onigasm.wasm'); + const onigasm = require("onigasm"); + const wasmUrl = require("!!file-loader!onigasm/lib/onigasm.wasm"); + return fetch(wasmUrl).then(resp => resp.arrayBuffer()).then(buffer => { return onigasm.loadWASM(buffer); }).then(() => { res({ createOnigScanner: function (patterns) { return new onigasm.OnigScanner(patterns); }, - createOnigString: function (s) { return new onigasm.OnigString(s); } - }) + createOnigString: function (s) { return new onigasm.OnigString(s); }, + }); }).catch(reason => rej(reason)); }); }, }); } -} +}; enum StandardTokenType { Other = 0, - Comment = 1, - String = 2, - RegEx = 4, -}; + Comment = 1, + String = 2, + RegEx = 4, +} -// Any needed here to override const -(target).StandardTokenType = StandardTokenType; +// tslint:disable-next-line no-any to override const +(target as any).StandardTokenType = StandardTokenType; diff --git a/packages/vscode/src/fill/windowsService.ts b/packages/vscode/src/fill/windowsService.ts index 2453b13f..6945e8d4 100644 --- a/packages/vscode/src/fill/windowsService.ts +++ b/packages/vscode/src/fill/windowsService.ts @@ -281,5 +281,6 @@ class WindowsService implements IWindowsService { } -// @ts-ignore -windowsIpc.WindowsChannelClient = WindowsService; +const target = windowsIpc as typeof windowsIpc; +// @ts-ignore TODO: don't ignore it. +target.WindowsChannelClient = WindowsService; diff --git a/packages/vscode/src/upload.ts b/packages/vscode/src/upload.ts deleted file mode 100644 index 6e5996f7..00000000 --- a/packages/vscode/src/upload.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { Upload as BaseUpload, IURI } from "@coder/ide"; -import { client } from "./entry"; -import { INotificationService, Severity } from "vs/platform/notification/common/notification"; -import { IProgressService2, ProgressLocation } from "vs/workbench/services/progress/common/progress"; - -export class Upload extends BaseUpload { - - public constructor( - @INotificationService notificationService: INotificationService, - @IProgressService2 progressService: IProgressService2, - ) { - super({ - error: (error) => { - notificationService.error(error); - }, - prompt: (message, choices) => { - return new Promise((resolve) => { - notificationService.prompt( - Severity.Error, - message, - choices.map((label) => ({ - label, - run: () => { - resolve(label); - }, - })), - () => { - resolve(undefined); - }, - ); - }); - }, - }, { - start: (title, task) => { - let lastProgress = 0; - - return progressService.withProgress({ - location: ProgressLocation.Notification, - title, - cancellable: true, - }, (progress) => { - return task({ - report: (p) => { - progress.report({ increment: p - lastProgress }); - lastProgress = p; - }, - }); - }, () => { - this.cancel(); - }); - }, - }); - } - - public async uploadDropped(event: DragEvent, uri?: IURI): Promise { - return super.uploadDropped(event, uri || (await client.workspace).mountUri); - } - -} diff --git a/scripts/vscode.patch b/scripts/vscode.patch index ba2af3aa..ea0eb7c2 100644 --- a/scripts/vscode.patch +++ b/scripts/vscode.patch @@ -47,6 +47,27 @@ index 2bf7fe37d7..81cc668f12 100644 } catch (err) { errorback(err); +diff --git a/src/vs/workbench/browser/dnd.ts b/src/vs/workbench/browser/dnd.ts +index 38bf337a61..aae3a68ff5 100644 +--- a/src/vs/workbench/browser/dnd.ts ++++ b/src/vs/workbench/browser/dnd.ts +@@ -31,6 +31,7 @@ import { IEditorService, IResourceEditor } from 'vs/workbench/services/editor/co + import { Disposable } from 'vs/base/common/lifecycle'; + import { addDisposableListener, EventType } from 'vs/base/browser/dom'; + import { IEditorGroup } from 'vs/workbench/services/group/common/editorGroupsService'; ++import { client } from "../../../../../../packages/vscode"; + + export interface IDraggedResource { + resource: URI; +@@ -168,7 +169,7 @@ export class ResourcesDropHandler { + handleDrop(event: DragEvent, resolveTargetGroup: () => IEditorGroup, afterDrop: (targetGroup: IEditorGroup) => void, targetIndex?: number): void { + const untitledOrFileResources = extractResources(event).filter(r => this.fileService.canHandleResource(r.resource) || r.resource.scheme === Schemas.untitled); + if (!untitledOrFileResources.length) { +- return; ++ return client.handleDrop(event, resolveTargetGroup, afterDrop, targetIndex); + } + + // Make the window active to handle the drop properly within diff --git a/src/vs/workbench/electron-browser/main.ts b/src/vs/workbench/electron-browser/main.ts index a43d63aa51..4c6df2fcd9 100644 --- a/src/vs/workbench/electron-browser/main.ts @@ -73,6 +94,26 @@ index a43d63aa51..4c6df2fcd9 100644 }); }); }); +diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts +index 35bc4a82b3..9cc84bdf28 100644 +--- a/src/vs/workbench/electron-browser/workbench.ts ++++ b/src/vs/workbench/electron-browser/workbench.ts +@@ -114,6 +114,7 @@ import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/work + import { IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; + import { FileDialogService } from 'vs/workbench/services/dialogs/electron-browser/dialogService'; + import { LogStorageAction } from 'vs/platform/storage/node/storageService'; ++import { client } from "../../../../../../packages/vscode"; + + interface WorkbenchParams { + configuration: IWindowConfiguration; +@@ -248,6 +249,7 @@ export class Workbench extends Disposable implements IPartService { + super(); + + this.workbenchParams = { configuration, serviceCollection }; ++ client.serviceCollection = serviceCollection; + + this.hasInitialFilesToOpen = + (configuration.filesToCreate && configuration.filesToCreate.length > 0) || diff --git a/src/vs/workbench/node/extensionHostProcess.ts b/src/vs/workbench/node/extensionHostProcess.ts index 8d182d18d9..69d51e1aea 100644 --- a/src/vs/workbench/node/extensionHostProcess.ts @@ -86,6 +127,26 @@ index 8d182d18d9..69d51e1aea 100644 } catch (e) { onTerminate(); } +diff --git a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts +index e600fb2f78..5d65a3124e 100644 +--- a/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts ++++ b/src/vs/workbench/parts/files/electron-browser/views/explorerViewer.ts +@@ -55,6 +55,7 @@ import { IDialogService, IConfirmationResult, IConfirmation, getConfirmMessage } + import { INotificationService } from 'vs/platform/notification/common/notification'; + import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from 'vs/workbench/services/editor/common/editorService'; + import { fillInContextMenuActions } from 'vs/platform/actions/browser/menuItemActionItem'; ++import { client } from "../../../../../../../../../packages/vscode"; + + export class FileDataSource implements IDataSource { + constructor( +@@ -932,6 +933,7 @@ export class FileDragAndDrop extends SimpleFileResourceDragAndDrop { + } + + private handleExternalDrop(tree: ITree, data: DesktopDragAndDropData, target: ExplorerItem | Model, originalEvent: DragMouseEvent): TPromise { ++ return client.handleExternalDrop(target, originalEvent); + const droppedResources = extractResources(originalEvent.browserEvent as DragEvent, true); + + // Check for dropped external files to be folders diff --git a/src/vs/workbench/parts/welcome/walkThrough/node/walkThroughContentProvider.ts b/src/vs/workbench/parts/welcome/walkThrough/node/walkThroughContentProvider.ts index 7b4e8721ac..8f26dc2f28 100644 --- a/src/vs/workbench/parts/welcome/walkThrough/node/walkThroughContentProvider.ts @@ -101,3 +162,16 @@ index 7b4e8721ac..8f26dc2f28 100644 try { resolve(content.default()); } catch (err) { +diff --git a/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.ts b/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.ts +index 5b4136989f..25ccc0fe9e 100644 +--- a/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.ts ++++ b/src/vs/workbench/services/themes/electron-browser/fileIconThemeData.ts +@@ -178,7 +178,7 @@ function _processIconThemeDocument(id: string, iconThemeDocumentLocation: URI, i + + const iconThemeDocumentLocationDirname = resources.dirname(iconThemeDocumentLocation); + function resolvePath(path: string) { +- return resources.joinPath(iconThemeDocumentLocationDirname, path); ++ return "/resource" + resources.joinPath(iconThemeDocumentLocationDirname, path).path; + } + + function collectSelectors(associations: IconsAssociation, baseThemeClassName?: string) {