Uploader online (#26)
This commit is contained in:
parent
62b1e0ef00
commit
ebe5e1b1a9
|
@ -1,28 +1,12 @@
|
||||||
import { Event } from "@coder/events";
|
import { Event } from "@coder/events";
|
||||||
import { field, logger, time, Time } from "@coder/logger";
|
import { field, logger, time, Time } from "@coder/logger";
|
||||||
import { InitData, ISharedProcessData } from "@coder/protocol";
|
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 { client } from "./fill/client";
|
||||||
import { Clipboard, clipboard } from "./fill/clipboard";
|
import { Clipboard, clipboard } from "./fill/clipboard";
|
||||||
|
import { INotificationService, NotificationService, IProgressService, ProgressService } from "./fill/notification";
|
||||||
export interface IURI {
|
import { IURIFactory } from "./fill/uri";
|
||||||
|
|
||||||
readonly path: string;
|
|
||||||
readonly fsPath: string;
|
|
||||||
readonly scheme: string;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface IURIFactory {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Convert the object to an instance of a real URI.
|
|
||||||
*/
|
|
||||||
create<T extends IURI>(uri: IURI): T;
|
|
||||||
file(path: string): IURI;
|
|
||||||
parse(raw: string): IURI;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A general abstraction of an IDE client.
|
* A general abstraction of an IDE client.
|
||||||
|
@ -34,9 +18,10 @@ export interface IURIFactory {
|
||||||
*/
|
*/
|
||||||
export abstract class Client {
|
export abstract class Client {
|
||||||
|
|
||||||
public readonly retry: Retry = retry;
|
public readonly retry = retry;
|
||||||
public readonly clipboard: Clipboard = clipboard;
|
public readonly clipboard: Clipboard = clipboard;
|
||||||
public readonly uriFactory: IURIFactory;
|
public readonly uriFactory: IURIFactory;
|
||||||
|
public readonly upload = new Upload(new NotificationService(), new ProgressService());
|
||||||
private start: Time | undefined;
|
private start: Time | undefined;
|
||||||
private readonly progressElement: HTMLElement | undefined;
|
private readonly progressElement: HTMLElement | undefined;
|
||||||
private tasks: string[] = [];
|
private tasks: string[] = [];
|
||||||
|
@ -187,6 +172,15 @@ export abstract class Client {
|
||||||
return this.sharedProcessDataPromise;
|
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.
|
* Initialize the IDE.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -1,4 +1,10 @@
|
||||||
import { CP } from "@coder/protocol";
|
import { CP } from "@coder/protocol";
|
||||||
import { client } from "./client";
|
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;
|
||||||
|
|
|
@ -16,7 +16,7 @@ class Connection implements ReadWriteConnection {
|
||||||
private readonly downEmitter: Emitter<void> = new Emitter();
|
private readonly downEmitter: Emitter<void> = new Emitter();
|
||||||
private readonly messageBuffer: Uint8Array[] = [];
|
private readonly messageBuffer: Uint8Array[] = [];
|
||||||
private socketTimeoutDelay = 60 * 1000;
|
private socketTimeoutDelay = 60 * 1000;
|
||||||
private retryName = "Web socket";
|
private retryName = "Socket";
|
||||||
private isUp: boolean = false;
|
private isUp: boolean = false;
|
||||||
private closed: boolean = false;
|
private closed: boolean = false;
|
||||||
|
|
||||||
|
|
|
@ -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<T>(title: string, task: (progress: IProgress) => Promise<T>, onCancel: () => void): Promise<T>;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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<T>(title: string, task: (progress: IProgress) => Promise<T>): Promise<T> {
|
||||||
|
logger.info(title);
|
||||||
|
|
||||||
|
return task({
|
||||||
|
report: (progress): void => {
|
||||||
|
logger.info(`${title} progress: ${progress}`);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -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<T extends IURI>(uri: IURI): T;
|
||||||
|
file(path: string): IURI;
|
||||||
|
parse(raw: string): IURI;
|
||||||
|
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
export * from "../../../../node_modules/util";
|
export * from "../../../../node_modules/util";
|
||||||
import { implementation } from "util.promisify";
|
import { implementation } from "../../../../node_modules/util.promisify";
|
||||||
|
|
||||||
export const promisify = implementation;
|
export const promisify = implementation;
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
export * from "./client";
|
export * from "./client";
|
||||||
export * from "./upload";
|
export * from "./fill/uri";
|
||||||
|
export * from "./fill/notification";
|
||||||
|
|
|
@ -1,56 +1,5 @@
|
||||||
import { logger } from "@coder/logger";
|
import { logger } from "@coder/logger";
|
||||||
|
import { NotificationService, INotificationHandle, INotificationService, Severity } from "./fill/notification";
|
||||||
/**
|
|
||||||
* 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;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
interface IRetryItem {
|
interface IRetryItem {
|
||||||
count?: number;
|
count?: number;
|
||||||
|
@ -91,15 +40,16 @@ export class Retry {
|
||||||
// for reasoning.)
|
// for reasoning.)
|
||||||
private waitDelay = 50;
|
private waitDelay = 50;
|
||||||
|
|
||||||
public constructor(private notificationService?: INotificationService) {
|
public constructor(private _notificationService: INotificationService) {
|
||||||
this.items = new Map();
|
this.items = new Map();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public set notificationService(service: INotificationService) {
|
||||||
* Set notification service.
|
this._notificationService = service;
|
||||||
*/
|
}
|
||||||
public setNotificationService(notificationService?: INotificationService): void {
|
|
||||||
this.notificationService = notificationService;
|
public get notificationService(): INotificationService {
|
||||||
|
return this._notificationService;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -262,10 +212,6 @@ export class Retry {
|
||||||
* Update, close, or show the notification.
|
* Update, close, or show the notification.
|
||||||
*/
|
*/
|
||||||
private updateNotification(): void {
|
private updateNotification(): void {
|
||||||
if (!this.notificationService) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// tslint:disable-next-line no-any because NodeJS.Timer is valid.
|
// tslint:disable-next-line no-any because NodeJS.Timer is valid.
|
||||||
clearTimeout(this.updateTimeout as any);
|
clearTimeout(this.updateTimeout as any);
|
||||||
|
|
||||||
|
@ -343,4 +289,4 @@ export class Retry {
|
||||||
|
|
||||||
// Global instance so we can block other retries when retrying the main
|
// Global instance so we can block other retries when retrying the main
|
||||||
// connection.
|
// connection.
|
||||||
export const retry = new Retry();
|
export const retry = new Retry(new NotificationService());
|
||||||
|
|
|
@ -3,7 +3,8 @@ import { appendFile } from "fs";
|
||||||
import { promisify } from "util";
|
import { promisify } from "util";
|
||||||
import { logger, Logger } from "@coder/logger";
|
import { logger, Logger } from "@coder/logger";
|
||||||
import { escapePath } from "@coder/protocol";
|
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.
|
* 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<T>(title:string, task: (progress: IProgress) => Promise<T>): Promise<T>;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Service for notifications.
|
|
||||||
*/
|
|
||||||
interface INotificationService {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Display an error message.
|
|
||||||
*/
|
|
||||||
error(error: Error): void;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ask for a decision.
|
|
||||||
*/
|
|
||||||
prompt(message: string, choices: string[]): Promise<string>;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles file uploads.
|
* Handles file uploads.
|
||||||
*/
|
*/
|
||||||
|
@ -76,8 +36,6 @@ export class Upload {
|
||||||
private readonly maxParallelUploads = 100;
|
private readonly maxParallelUploads = 100;
|
||||||
private readonly readSize = 32000; // ~32kb max while reading in the file.
|
private readonly readSize = 32000; // ~32kb max while reading in the file.
|
||||||
private readonly packetSize = 32000; // ~32kb max when writing.
|
private readonly packetSize = 32000; // ~32kb max when writing.
|
||||||
private readonly notificationService: INotificationService;
|
|
||||||
private readonly progressService: IProgressService;
|
|
||||||
private readonly logger: Logger;
|
private readonly logger: Logger;
|
||||||
private readonly currentlyUploadingFiles: Map<string, File>;
|
private readonly currentlyUploadingFiles: Map<string, File>;
|
||||||
private readonly queueByDirectory: Map<string, IUploadableDirectory>;
|
private readonly queueByDirectory: Map<string, IUploadableDirectory>;
|
||||||
|
@ -88,9 +46,10 @@ export class Upload {
|
||||||
private uploadedFilePaths: string[];
|
private uploadedFilePaths: string[];
|
||||||
private total: number;
|
private total: number;
|
||||||
|
|
||||||
public constructor(notificationService: INotificationService, progressService: IProgressService) {
|
public constructor(
|
||||||
this.notificationService = notificationService;
|
private _notificationService: INotificationService,
|
||||||
this.progressService = progressService;
|
private _progressService: IProgressService,
|
||||||
|
) {
|
||||||
this.logger = logger.named("Upload");
|
this.logger = logger.named("Upload");
|
||||||
this.currentlyUploadingFiles = new Map();
|
this.currentlyUploadingFiles = new Map();
|
||||||
this.queueByDirectory = new Map();
|
this.queueByDirectory = new Map();
|
||||||
|
@ -99,6 +58,22 @@ export class Upload {
|
||||||
this.total = 0;
|
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
|
* Upload dropped files. This will try to upload everything it can. Errors
|
||||||
* will show via notifications. If an upload operation is ongoing, the files
|
* will show via notifications. If an upload operation is ongoing, the files
|
||||||
|
@ -125,6 +100,8 @@ export class Upload {
|
||||||
resolve(uploaded);
|
resolve(uploaded);
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
}, () => {
|
||||||
|
this.cancel();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
this.uploadFiles();
|
this.uploadFiles();
|
||||||
|
@ -214,8 +191,21 @@ export class Upload {
|
||||||
*/
|
*/
|
||||||
private async uploadFile(path: string, file: File, existingFiles: string[]): Promise<void> {
|
private async uploadFile(path: string, file: File, existingFiles: string[]): Promise<void> {
|
||||||
if (existingFiles.includes(path)) {
|
if (existingFiles.includes(path)) {
|
||||||
const choice = await this.notificationService.prompt(`${path} already exists. Overwrite?`, ["Yes", "No"]);
|
const shouldOverwrite = await new Promise((resolve): void => {
|
||||||
if (choice !== "Yes") {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -122,11 +122,13 @@ export class Server {
|
||||||
this.evals.set(evalMessage.getId(), resp);
|
this.evals.set(evalMessage.getId(), resp);
|
||||||
}
|
}
|
||||||
} else if (message.hasEvalEvent()) {
|
} 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) {
|
if (!e) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
e.onEvent(message.getEvalEvent()!);
|
e.onEvent(evalEventMessage);
|
||||||
} else if (message.hasNewSession()) {
|
} else if (message.hasNewSession()) {
|
||||||
const sessionMessage = message.getNewSession()!;
|
const sessionMessage = message.getNewSession()!;
|
||||||
logger.debug("NewSession", field("id", sessionMessage.getId()));
|
logger.debug("NewSession", field("id", sessionMessage.getId()));
|
||||||
|
|
|
@ -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 { ServerMessage, SharedProcessActiveMessage } from "@coder/protocol/src/proto";
|
||||||
import { Command, flags } from "@oclif/command";
|
import { Command, flags } from "@oclif/command";
|
||||||
import * as fs from "fs";
|
import * as fs from "fs";
|
||||||
|
|
|
@ -6,17 +6,125 @@ import "./fill/environmentService";
|
||||||
import "./fill/vscodeTextmate";
|
import "./fill/vscodeTextmate";
|
||||||
import "./fill/dom";
|
import "./fill/dom";
|
||||||
import "./vscode.scss";
|
import "./vscode.scss";
|
||||||
|
import { Client as IDEClient, IURI, IURIFactory, IProgress, INotificationHandle } from "@coder/ide";
|
||||||
import { Client as IDEClient, IURI, IURIFactory } from "@coder/ide";
|
|
||||||
|
|
||||||
import { registerContextMenuListener } from "vs/base/parts/contextmenu/electron-main/contextmenu";
|
import { registerContextMenuListener } from "vs/base/parts/contextmenu/electron-main/contextmenu";
|
||||||
import { LogLevel } from "vs/platform/log/common/log";
|
import { LogLevel } from "vs/platform/log/common/log";
|
||||||
// import { RawContextKey, IContextKeyService } from "vs/platform/contextkey/common/contextkey";
|
// import { RawContextKey, IContextKeyService } from "vs/platform/contextkey/common/contextkey";
|
||||||
import { URI } from "vs/base/common/uri";
|
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 {
|
export class Client extends IDEClient {
|
||||||
|
|
||||||
private readonly windowId = parseInt(new Date().toISOString().replace(/[-:.TZ]/g, ""), 10);
|
private readonly windowId = parseInt(new Date().toISOString().replace(/[-:.TZ]/g, ""), 10);
|
||||||
|
private _serviceCollection: ServiceCollection | undefined;
|
||||||
|
|
||||||
|
public async handleExternalDrop(target: ExplorerItem | Model, originalEvent: DragMouseEvent): Promise<void> {
|
||||||
|
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: <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()),
|
||||||
|
})),
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected createUriFactory(): IURIFactory {
|
||||||
|
return {
|
||||||
|
// TODO: not sure why this is an error.
|
||||||
|
// tslint:disable-next-line no-any
|
||||||
|
create: <URI>(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<void> {
|
protected initialize(): Promise<void> {
|
||||||
registerContextMenuListener();
|
registerContextMenuListener();
|
||||||
|
@ -46,33 +154,6 @@ export class Client extends IDEClient {
|
||||||
folderUri: URI.file(data.workingDirectory),
|
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.
|
// TODO: Set up clipboard context.
|
||||||
// const workbench = workbenchShell.workbench;
|
// const workbench = workbenchShell.workbench;
|
||||||
// const contextKeys = workbench.workbenchParams.serviceCollection.get(IContextKeyService) as IContextKeyService;
|
// const contextKeys = workbench.workbenchParams.serviceCollection.get(IContextKeyService) as IContextKeyService;
|
||||||
|
@ -85,16 +166,6 @@ export class Client extends IDEClient {
|
||||||
}, this.initData, pathSets);
|
}, this.initData, pathSets);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected createUriFactory(): IURIFactory {
|
|
||||||
return {
|
|
||||||
// TODO: not sure why this is an error.
|
|
||||||
// tslint:disable-next-line no-any
|
|
||||||
create: <URI>(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();
|
export const client = new Client();
|
||||||
|
|
|
@ -59,7 +59,7 @@ const decodeStream = (encoding: string): NodeJS.ReadWriteStream => {
|
||||||
return new IconvLiteDecoderStream({ encoding });
|
return new IconvLiteDecoderStream({ encoding });
|
||||||
};
|
};
|
||||||
|
|
||||||
// @ts-ignore
|
const target = iconv as typeof iconv;
|
||||||
iconv.decodeStream = decodeStream;
|
target.decodeStream = decodeStream;
|
||||||
|
|
||||||
export = iconv;
|
export = target;
|
||||||
|
|
|
@ -1,3 +1,7 @@
|
||||||
// TODO: ?
|
import { join } from "path";
|
||||||
|
|
||||||
// tslint:disable-next-line no-any
|
// 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);
|
||||||
|
};
|
||||||
|
|
|
@ -8,7 +8,7 @@ import * as globalStorage from "vs/platform/storage/node/storageIpc";
|
||||||
import * as paths from "./paths";
|
import * as paths from "./paths";
|
||||||
import { logger, field } from "@coder/logger";
|
import { logger, field } from "@coder/logger";
|
||||||
|
|
||||||
export class StorageDatabase implements workspaceStorage.IStorageDatabase {
|
class StorageDatabase implements workspaceStorage.IStorageDatabase {
|
||||||
|
|
||||||
public readonly onDidChangeItemsExternal = Event.None;
|
public readonly onDidChangeItemsExternal = Event.None;
|
||||||
private items = new Map<string, string>();
|
private items = new Map<string, string>();
|
||||||
|
@ -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() {
|
public constructor() {
|
||||||
super(path.join(paths.getAppDataPath(), "globalStorage", "state.vscdb"));
|
super(path.join(paths.getAppDataPath(), "globalStorage", "state.vscdb"));
|
||||||
|
@ -93,7 +93,10 @@ export class GlobalStorageDatabase extends StorageDatabase implements IDisposabl
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ts-ignore
|
const workspaceTarget = workspaceStorage as typeof workspaceStorage;
|
||||||
workspaceStorage.SQLiteStorageDatabase = StorageDatabase;
|
// @ts-ignore TODO: don't ignore it.
|
||||||
// @ts-ignore
|
workspaceTarget.SQLiteStorageDatabase = StorageDatabase;
|
||||||
globalStorage.GlobalStorageDatabaseChannelClient = GlobalStorageDatabase;
|
|
||||||
|
const globalTarget = globalStorage as typeof globalStorage;
|
||||||
|
// @ts-ignore TODO: don't ignore it.
|
||||||
|
globalTarget.GlobalStorageDatabaseChannelClient = GlobalStorageDatabase;
|
||||||
|
|
|
@ -8,28 +8,29 @@ target.Registry = class Registry extends vscodeTextmate.Registry {
|
||||||
...opts,
|
...opts,
|
||||||
getOnigLib: (): Promise<vscodeTextmate.IOnigLib> => {
|
getOnigLib: (): Promise<vscodeTextmate.IOnigLib> => {
|
||||||
return new Promise<vscodeTextmate.IOnigLib>((res, rej) => {
|
return new Promise<vscodeTextmate.IOnigLib>((res, rej) => {
|
||||||
const onigasm = require('onigasm');
|
const onigasm = require("onigasm");
|
||||||
const wasmUrl = require('!!file-loader!onigasm/lib/onigasm.wasm');
|
const wasmUrl = require("!!file-loader!onigasm/lib/onigasm.wasm");
|
||||||
|
|
||||||
return fetch(wasmUrl).then(resp => resp.arrayBuffer()).then(buffer => {
|
return fetch(wasmUrl).then(resp => resp.arrayBuffer()).then(buffer => {
|
||||||
return onigasm.loadWASM(buffer);
|
return onigasm.loadWASM(buffer);
|
||||||
}).then(() => {
|
}).then(() => {
|
||||||
res({
|
res({
|
||||||
createOnigScanner: function (patterns) { return new onigasm.OnigScanner(patterns); },
|
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));
|
}).catch(reason => rej(reason));
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
enum StandardTokenType {
|
enum StandardTokenType {
|
||||||
Other = 0,
|
Other = 0,
|
||||||
Comment = 1,
|
Comment = 1,
|
||||||
String = 2,
|
String = 2,
|
||||||
RegEx = 4,
|
RegEx = 4,
|
||||||
};
|
}
|
||||||
|
|
||||||
// Any needed here to override const
|
// tslint:disable-next-line no-any to override const
|
||||||
(<any>target).StandardTokenType = StandardTokenType;
|
(target as any).StandardTokenType = StandardTokenType;
|
||||||
|
|
|
@ -281,5 +281,6 @@ class WindowsService implements IWindowsService {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// @ts-ignore
|
const target = windowsIpc as typeof windowsIpc;
|
||||||
windowsIpc.WindowsChannelClient = WindowsService;
|
// @ts-ignore TODO: don't ignore it.
|
||||||
|
target.WindowsChannelClient = WindowsService;
|
||||||
|
|
|
@ -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<string[]> {
|
|
||||||
return super.uploadDropped(event, uri || (await client.workspace).mountUri);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -47,6 +47,27 @@ index 2bf7fe37d7..81cc668f12 100644
|
||||||
}
|
}
|
||||||
catch (err) {
|
catch (err) {
|
||||||
errorback(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
|
diff --git a/src/vs/workbench/electron-browser/main.ts b/src/vs/workbench/electron-browser/main.ts
|
||||||
index a43d63aa51..4c6df2fcd9 100644
|
index a43d63aa51..4c6df2fcd9 100644
|
||||||
--- a/src/vs/workbench/electron-browser/main.ts
|
--- 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
|
diff --git a/src/vs/workbench/node/extensionHostProcess.ts b/src/vs/workbench/node/extensionHostProcess.ts
|
||||||
index 8d182d18d9..69d51e1aea 100644
|
index 8d182d18d9..69d51e1aea 100644
|
||||||
--- a/src/vs/workbench/node/extensionHostProcess.ts
|
--- a/src/vs/workbench/node/extensionHostProcess.ts
|
||||||
|
@ -86,6 +127,26 @@ index 8d182d18d9..69d51e1aea 100644
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
onTerminate();
|
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<void> {
|
||||||
|
+ 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
|
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
|
index 7b4e8721ac..8f26dc2f28 100644
|
||||||
--- a/src/vs/workbench/parts/welcome/walkThrough/node/walkThroughContentProvider.ts
|
--- a/src/vs/workbench/parts/welcome/walkThrough/node/walkThroughContentProvider.ts
|
||||||
|
@ -101,3 +162,16 @@ index 7b4e8721ac..8f26dc2f28 100644
|
||||||
try {
|
try {
|
||||||
resolve(content.default());
|
resolve(content.default());
|
||||||
} catch (err) {
|
} 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) {
|
||||||
|
|
Loading…
Reference in New Issue