Some cleanup

- Use whateverEmitter.event for the onWhatever methods.
- Add readonly to a bunch of things.
- Remove some redundancy in types.
- Move initializations out of the constructor and into the declarations
  where it was reasonable to do so.
- Disable a few no-any violations.
This commit is contained in:
Asher 2019-02-06 11:53:23 -06:00
parent ddf96077a3
commit 588da0443c
No known key found for this signature in database
GPG Key ID: 7BB4BA9C783D2BBC
16 changed files with 98 additions and 164 deletions

View File

@ -1,6 +1,5 @@
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 { ISharedProcessData } from "@coder/protocol";
import { retry } from "./retry"; import { retry } from "./retry";
import { upload } from "./upload"; import { upload } from "./upload";
import { client } from "./fill/client"; import { client } from "./fill/client";
@ -24,14 +23,16 @@ export abstract class IdeClient {
private readonly tasks = <string[]>[]; private readonly tasks = <string[]>[];
private finishedTaskCount = 0; private finishedTaskCount = 0;
private readonly loadTime: Time; private readonly loadTime: Time;
private readonly sharedProcessDataPromise: Promise<ISharedProcessData>;
public readonly initData = client.initData;
public readonly sharedProcessData: Promise<ISharedProcessData>;
public readonly onSharedProcessActive = client.onSharedProcessActive;
public constructor() { public constructor() {
logger.info("Loading IDE"); logger.info("Loading IDE");
this.loadTime = time(2500); this.loadTime = time(2500);
this.sharedProcessDataPromise = new Promise((resolve): void => { this.sharedProcessData = new Promise((resolve): void => {
client.onSharedProcessActive(resolve); client.onSharedProcessActive(resolve);
}); });
@ -96,27 +97,6 @@ export abstract class IdeClient {
} }
} }
/**
* A promise that resolves with initialization data.
*/
public get initData(): Promise<InitData> {
return client.initData;
}
/**
* An event that fires every time the shared process (re-)starts.
*/
public get onSharedProcessActive(): Event<ISharedProcessData> {
return client.onSharedProcessActive;
}
/**
* A promise that resolves with *initial* shared process data.
*/
public get sharedProcessData(): Promise<ISharedProcessData> {
return this.sharedProcessDataPromise;
}
public set notificationService(service: INotificationService) { public set notificationService(service: INotificationService) {
this.retry.notificationService = service; this.retry.notificationService = service;
this.upload.notificationService = service; this.upload.notificationService = service;

View File

@ -9,36 +9,28 @@ import { retry } from "../retry";
*/ */
class Connection implements ReadWriteConnection { class Connection implements ReadWriteConnection {
private activeSocket: WebSocket | undefined; private activeSocket: WebSocket | undefined;
private readonly messageEmitter: Emitter<Uint8Array> = new Emitter(); private readonly messageBuffer = <Uint8Array[]>[];
private readonly closeEmitter: Emitter<void> = new Emitter(); private readonly socketTimeoutDelay = 60 * 1000;
private readonly upEmitter: Emitter<void> = new Emitter(); private readonly retryName = "Socket";
private readonly downEmitter: Emitter<void> = new Emitter();
private readonly messageBuffer: Uint8Array[] = [];
private socketTimeoutDelay = 60 * 1000;
private retryName = "Socket";
private isUp: boolean = false; private isUp: boolean = false;
private closed: boolean = false; private closed: boolean = false;
private readonly messageEmitter = new Emitter<Uint8Array>();
private readonly closeEmitter = new Emitter<void>();
private readonly upEmitter = new Emitter<void>();
private readonly downEmitter = new Emitter<void>();
public readonly onUp = this.upEmitter.event;
public readonly onClose = this.closeEmitter.event;
public readonly onDown = this.downEmitter.event;
public readonly onMessage = this.messageEmitter.event;
public constructor() { public constructor() {
retry.register(this.retryName, () => this.connect()); retry.register(this.retryName, () => this.connect());
retry.block(this.retryName); retry.block(this.retryName);
retry.run(this.retryName); retry.run(this.retryName);
} }
/**
* Register a function to be called when the connection goes up.
*/
public onUp(cb: () => void): void {
this.upEmitter.event(cb);
}
/**
* Register a function to be called when the connection goes down.
*/
public onDown(cb: () => void): void {
this.downEmitter.event(cb);
}
public send(data: Buffer | Uint8Array): void { public send(data: Buffer | Uint8Array): void {
if (this.closed) { if (this.closed) {
throw new Error("web socket is closed"); throw new Error("web socket is closed");
@ -50,14 +42,6 @@ class Connection implements ReadWriteConnection {
} }
} }
public onMessage(cb: (data: Uint8Array | Buffer) => void): void {
this.messageEmitter.event(cb);
}
public onClose(cb: () => void): void {
this.closeEmitter.event(cb);
}
public close(): void { public close(): void {
this.closed = true; this.closed = true;
this.dispose(); this.dispose();

View File

@ -1,11 +1,11 @@
import { IDisposable } from "@coder/disposable";
import { Emitter } from "@coder/events"; import { Emitter } from "@coder/events";
/** /**
* Wrapper around the native clipboard with some fallbacks. * Wrapper around the native clipboard with some fallbacks.
*/ */
export class Clipboard { export class Clipboard {
private readonly enableEmitter: Emitter<boolean> = new Emitter(); private readonly enableEmitter = new Emitter<boolean>();
public readonly onPermissionChange = this.enableEmitter.event;
private _isEnabled: boolean = false; private _isEnabled: boolean = false;
/** /**
@ -70,14 +70,6 @@ export class Clipboard {
// tslint:enable no-any // tslint:enable no-any
} }
/**
* Register a function to be called when the native clipboard is
* enabled/disabled.
*/
public onPermissionChange(cb: (enabled: boolean) => void): IDisposable {
return this.enableEmitter.event(cb);
}
/** /**
* Read text from the clipboard. * Read text from the clipboard.
*/ */

View File

@ -1,4 +1,3 @@
import { IDisposable } from "@coder/disposable";
import { Emitter } from "@coder/events"; import { Emitter } from "@coder/events";
import "./dialog.scss"; import "./dialog.scss";
@ -27,19 +26,16 @@ export enum IKey {
} }
export class Dialog { export class Dialog {
private options: IDialogOptions; private readonly overlay: HTMLElement;
private overlay: HTMLElement;
private cachedActiveElement: HTMLElement | undefined; private cachedActiveElement: HTMLElement | undefined;
private input: HTMLInputElement | undefined; private input: HTMLInputElement | undefined;
private actionEmitter: Emitter<IDialogAction>;
private errors: HTMLElement; private errors: HTMLElement;
private buttons: HTMLElement[] | undefined; private buttons: HTMLElement[] | undefined;
public constructor(options: IDialogOptions) { private actionEmitter = new Emitter<IDialogAction>();
this.options = options; public onAction = this.actionEmitter.event;
this.actionEmitter = new Emitter();
public constructor(private readonly options: IDialogOptions) {
const msgBox = document.createElement("div"); const msgBox = document.createElement("div");
msgBox.classList.add("msgbox"); msgBox.classList.add("msgbox");
@ -108,13 +104,6 @@ export class Dialog {
}); });
} }
/**
* Register a function to be called when the user performs an action.
*/
public onAction(callback: (action: IDialogAction) => void): IDisposable {
return this.actionEmitter.event(callback);
}
/** /**
* Input value if this dialog has an input. * Input value if this dialog has an input.
*/ */

View File

@ -13,7 +13,7 @@ class OS {
public homedir(): string { public homedir(): string {
if (typeof this._homedir === "undefined") { if (typeof this._homedir === "undefined") {
throw new Error("not initialized"); throw new Error("trying to access homedir before it has been set");
} }
return this._homedir; return this._homedir;
@ -21,7 +21,7 @@ class OS {
public tmpdir(): string { public tmpdir(): string {
if (typeof this._tmpdir === "undefined") { if (typeof this._tmpdir === "undefined") {
throw new Error("not initialized"); throw new Error("trying to access tmpdir before it has been set");
} }
return this._tmpdir; return this._tmpdir;

View File

@ -21,7 +21,7 @@ interface IRetryItem {
* to the user explaining what is happening with an option to immediately retry. * to the user explaining what is happening with an option to immediately retry.
*/ */
export class Retry { export class Retry {
private items: Map<string, IRetryItem>; private items = new Map<string, IRetryItem>();
// Times are in seconds. // Times are in seconds.
private readonly retryMinDelay = 1; private readonly retryMinDelay = 1;
@ -31,17 +31,15 @@ export class Retry {
private blocked: string | boolean | undefined; private blocked: string | boolean | undefined;
private notificationHandle: INotificationHandle | undefined; private notificationHandle: INotificationHandle | undefined;
private updateDelay = 1; private readonly updateDelay = 1;
private updateTimeout: number | NodeJS.Timer | undefined; private updateTimeout: number | NodeJS.Timer | undefined;
private notificationThreshold = 3; private readonly notificationThreshold = 3;
// Time in milliseconds to wait before restarting a service. (See usage below // Time in milliseconds to wait before restarting a service. (See usage below
// for reasoning.) // for reasoning.)
private waitDelay = 50; private readonly waitDelay = 50;
public constructor(private _notificationService: INotificationService) { public constructor(private _notificationService: INotificationService) {}
this.items = new Map();
}
public set notificationService(service: INotificationService) { public set notificationService(service: INotificationService) {
this._notificationService = service; this._notificationService = service;

View File

@ -1,7 +1,7 @@
import { exec } from "child_process"; import { exec } from "child_process";
import { appendFile } from "fs"; import { appendFile } from "fs";
import { promisify } from "util"; import { promisify } from "util";
import { logger, Logger } from "@coder/logger"; import { logger } from "@coder/logger";
import { escapePath } from "@coder/protocol"; import { escapePath } from "@coder/protocol";
import { NotificationService, INotificationService, ProgressService, IProgressService, IProgress, Severity } from "./fill/notification"; import { NotificationService, INotificationService, ProgressService, IProgressService, IProgress, Severity } from "./fill/notification";
@ -40,27 +40,20 @@ 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 logger: Logger; private readonly logger = logger.named("Upload");
private readonly currentlyUploadingFiles: Map<string, File>; private readonly currentlyUploadingFiles = new Map<string, File>();
private readonly queueByDirectory: Map<string, IUploadableDirectory>; private readonly queueByDirectory = new Map<string, IUploadableDirectory>();
private progress: IProgress | undefined; private progress: IProgress | undefined;
private uploadPromise: Promise<string[]> | undefined; private uploadPromise: Promise<string[]> | undefined;
private resolveUploadPromise: (() => void) | undefined; private resolveUploadPromise: (() => void) | undefined;
private finished: number; private finished = 0;
private uploadedFilePaths: string[]; private uploadedFilePaths = <string[]>[];
private total: number; private total = 0;
public constructor( public constructor(
private _notificationService: INotificationService, private _notificationService: INotificationService,
private _progressService: IProgressService, private _progressService: IProgressService,
) { ) {}
this.logger = logger.named("Upload");
this.currentlyUploadingFiles = new Map();
this.queueByDirectory = new Map();
this.uploadedFilePaths = [];
this.finished = 0;
this.total = 0;
}
public set notificationService(service: INotificationService) { public set notificationService(service: INotificationService) {
this._notificationService = service; this._notificationService = service;

View File

@ -113,8 +113,8 @@ const hashStringToColor = (str: string): string => {
* currently built items and appends to that. * currently built items and appends to that.
*/ */
export abstract class Formatter { export abstract class Formatter {
protected format: string = ""; protected format = "";
protected args: string[] = []; protected args = <string[]>[];
/** /**
* Add a tag. * Add a tag.
@ -250,14 +250,13 @@ export class Logger {
public level = Level.Info; public level = Level.Info;
private readonly nameColor?: string; private readonly nameColor?: string;
private muted: boolean; private muted: boolean = false;
public constructor( public constructor(
private _formatter: Formatter, private _formatter: Formatter,
private readonly name?: string, private readonly name?: string,
private readonly defaultFields?: FieldArray, private readonly defaultFields?: FieldArray,
) { ) {
this.muted = false;
if (name) { if (name) {
this.nameColor = hashStringToColor(name); this.nameColor = hashStringToColor(name);
} }
@ -388,10 +387,6 @@ export class Logger {
times = fields.filter((f) => f.value instanceof Time); times = fields.filter((f) => f.value instanceof Time);
} }
// Format is:
// [TAG] [NAME?] MESSAGE TIME?
// field1 (type)?: value
// field2 (type)?: value
this._formatter.tag(options.type.toUpperCase(), options.tagColor); this._formatter.tag(options.type.toUpperCase(), options.tagColor);
if (this.name && this.nameColor) { if (this.name && this.nameColor) {
this._formatter.tag(this.name.toUpperCase(), this.nameColor); this._formatter.tag(this.name.toUpperCase(), this.nameColor);

View File

@ -9,27 +9,28 @@ import { EventEmitter } from "events";
* Client accepts an arbitrary connection intended to communicate with the Server. * Client accepts an arbitrary connection intended to communicate with the Server.
*/ */
export class Client { export class Client {
public Socket: typeof ServerSocket; public readonly Socket: typeof ServerSocket;
private evalId: number = 0; private evalId = 0;
private evalDoneEmitter: Emitter<EvalDoneMessage> = new Emitter(); private readonly evalDoneEmitter = new Emitter<EvalDoneMessage>();
private evalFailedEmitter: Emitter<EvalFailedMessage> = new Emitter(); private readonly evalFailedEmitter = new Emitter<EvalFailedMessage>();
private evalEventEmitter: Emitter<EvalEventMessage> = new Emitter(); private readonly evalEventEmitter = new Emitter<EvalEventMessage>();
private sessionId: number = 0; private sessionId = 0;
private readonly sessions: Map<number, ServerProcess> = new Map(); private readonly sessions = new Map<number, ServerProcess>();
private connectionId: number = 0; private connectionId = 0;
private readonly connections: Map<number, ServerSocket> = new Map(); private readonly connections = new Map<number, ServerSocket>();
private serverId: number = 0; private serverId = 0;
private readonly servers: Map<number, ServerListener> = new Map(); private readonly servers = new Map<number, ServerListener>();
private _initData: InitData | undefined; private _initData: InitData | undefined;
private initDataEmitter = new Emitter<InitData>(); private readonly initDataEmitter = new Emitter<InitData>();
private initDataPromise: Promise<InitData>; private readonly initDataPromise: Promise<InitData>;
private sharedProcessActiveEmitter = new Emitter<ISharedProcessData>(); private readonly sharedProcessActiveEmitter = new Emitter<ISharedProcessData>();
public readonly onSharedProcessActive = this.sharedProcessActiveEmitter.event;
/** /**
* @param connection Established connection to the server * @param connection Established connection to the server
@ -63,10 +64,6 @@ export class Client {
return this.initDataPromise; return this.initDataPromise;
} }
public get onSharedProcessActive(): Event<ISharedProcessData> {
return this.sharedProcessActiveEmitter.event;
}
public run(func: (ae: ActiveEval) => void | Promise<void>): ActiveEval; public run(func: (ae: ActiveEval) => void | Promise<void>): ActiveEval;
public run<T1>(func: (ae: ActiveEval, a1: T1) => void | Promise<void>, a1: T1): ActiveEval; public run<T1>(func: (ae: ActiveEval, a1: T1) => void | Promise<void>, a1: T1): ActiveEval;
public run<T1, T2>(func: (ae: ActiveEval, a1: T1, a2: T2) => void | Promise<void>, a1: T1, a2: T2): ActiveEval; public run<T1, T2>(func: (ae: ActiveEval, a1: T1, a2: T2) => void | Promise<void>, a1: T1, a2: T2): ActiveEval;
@ -95,8 +92,8 @@ export class Client {
}); });
return { return {
on: (event: string, cb: (...args: any[]) => void) => eventEmitter.on(event, cb), on: (event: string, cb: (...args: any[]) => void): EventEmitter => eventEmitter.on(event, cb),
emit: (event: string, ...args: any[]) => { emit: (event: string, ...args: any[]): void => {
const eventsMsg = new EvalEventMessage(); const eventsMsg = new EvalEventMessage();
eventsMsg.setId(doEval.id); eventsMsg.setId(doEval.id);
eventsMsg.setEvent(event); eventsMsg.setEvent(event);

View File

@ -20,12 +20,12 @@ export interface ServerOptions {
} }
export class Server { export class Server {
private readonly sessions: Map<number, Process> = new Map(); private readonly sessions = new Map<number, Process>();
private readonly connections: Map<number, net.Socket> = new Map(); private readonly connections = new Map<number, net.Socket>();
private readonly servers: Map<number, net.Server> = new Map(); private readonly servers = new Map<number, net.Server>();
private readonly evals: Map<number, ActiveEvaluation> = new Map(); private readonly evals = new Map<number, ActiveEvaluation>();
private connectionId: number = Number.MAX_SAFE_INTEGER; private connectionId = Number.MAX_SAFE_INTEGER;
public constructor( public constructor(
private readonly connection: ReadWriteConnection, private readonly connection: ReadWriteConnection,

View File

@ -3,7 +3,7 @@ import { ChildProcess } from "child_process";
export interface IpcMessage { export interface IpcMessage {
readonly event: string; readonly event: string;
readonly args: any[]; readonly args: any[]; // tslint:disable-line no-any
} }
export class StdioIpcHandler extends EventEmitter { export class StdioIpcHandler extends EventEmitter {
@ -15,21 +15,28 @@ export class StdioIpcHandler extends EventEmitter {
super(); super();
} }
// tslint:disable-next-line no-any
public on(event: string, cb: (...args: any[]) => void): this { public on(event: string, cb: (...args: any[]) => void): this {
this.listen(); this.listen();
return super.on(event, cb); return super.on(event, cb);
} }
// tslint:disable-next-line no-any
public once(event: string, cb: (...args: any[]) => void): this { public once(event: string, cb: (...args: any[]) => void): this {
this.listen(); this.listen();
return super.once(event, cb); return super.once(event, cb);
} }
// tslint:disable-next-line no-any
public addListener(event: string, cb: (...args: any[]) => void): this { public addListener(event: string, cb: (...args: any[]) => void): this {
this.listen(); this.listen();
return super.addListener(event, cb); return super.addListener(event, cb);
} }
// tslint:disable-next-line no-any
public send(event: string, ...args: any[]): void { public send(event: string, ...args: any[]): void {
const msg: IpcMessage = { const msg: IpcMessage = {
event, event,
@ -47,7 +54,8 @@ export class StdioIpcHandler extends EventEmitter {
if (this.isListening) { if (this.isListening) {
return; return;
} }
const onData = (data: any) => { // tslint:disable-next-line no-any
const onData = (data: any): void => {
try { try {
const d = JSON.parse(data.toString()) as IpcMessage; const d = JSON.parse(data.toString()) as IpcMessage;
this.emit(d.event, ...d.args); this.emit(d.event, ...d.args);

View File

@ -26,20 +26,16 @@ export class SharedProcess {
private _state: SharedProcessState = SharedProcessState.Stopped; private _state: SharedProcessState = SharedProcessState.Stopped;
private activeProcess: ChildProcess | undefined; private activeProcess: ChildProcess | undefined;
private ipcHandler: StdioIpcHandler | undefined; private ipcHandler: StdioIpcHandler | undefined;
private readonly onStateEmitter: Emitter<SharedProcessEvent>; private readonly onStateEmitter = new Emitter<SharedProcessEvent>();
public readonly onState = this.onStateEmitter.event;
public constructor( public constructor(
private readonly userDataDir: string, private readonly userDataDir: string,
private readonly builtInExtensionsDir: string, private readonly builtInExtensionsDir: string,
) { ) {
this.onStateEmitter = new Emitter();
this.restart(); this.restart();
} }
public get onState(): Event<SharedProcessEvent> {
return this.onStateEmitter.event;
}
public get state(): SharedProcessState { public get state(): SharedProcessState {
return this._state; return this._state;
} }

View File

@ -9,11 +9,10 @@ type nodePtyType = typeof nodePty;
* Implementation of nodePty for the browser. * Implementation of nodePty for the browser.
*/ */
class Pty implements nodePty.IPty { class Pty implements nodePty.IPty {
private readonly emitter: EventEmitter; private readonly emitter = new EventEmitter();
private readonly cp: ChildProcess; private readonly cp: ChildProcess;
public constructor(file: string, args: string[] | string, options: nodePty.IPtyForkOptions) { public constructor(file: string, args: string[] | string, options: nodePty.IPtyForkOptions) {
this.emitter = new EventEmitter();
this.cp = client.spawn(file, Array.isArray(args) ? args : [args], { this.cp = client.spawn(file, Array.isArray(args) ? args : [args], {
...options, ...options,
tty: { tty: {
@ -38,7 +37,8 @@ class Pty implements nodePty.IPty {
return this.cp.title!; return this.cp.title!;
} }
public on(event: string, listener: (...args) => void): void { // tslint:disable-next-line no-any
public on(event: string, listener: (...args: any[]) => void): void {
this.emitter.on(event, listener); this.emitter.on(event, listener);
} }

View File

@ -4,11 +4,13 @@ import { IpcRenderer } from "electron";
export * from "@coder/ide/src/fill/electron"; export * from "@coder/ide/src/fill/electron";
class StdioIpcRenderer extends StdioIpcHandler implements IpcRenderer { class StdioIpcRenderer extends StdioIpcHandler implements IpcRenderer {
public sendTo(windowId: number, channel: string, ...args: any[]): void { // tslint:disable-next-line no-any
public sendTo(_windowId: number, _channel: string, ..._args: any[]): void {
throw new Error("Method not implemented."); throw new Error("Method not implemented.");
} }
public sendToHost(channel: string, ...args: any[]): void { // tslint:disable-next-line no-any
public sendToHost(_channel: string, ..._args: any[]): void {
throw new Error("Method not implemented."); throw new Error("Method not implemented.");
} }

View File

@ -10,7 +10,7 @@ import { logger, field } from "@coder/logger";
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 readonly items = new Map<string, string>();
private fetched: boolean = false; private fetched: boolean = false;
public constructor(private readonly path: string) { public constructor(private readonly path: string) {

View File

@ -17,21 +17,21 @@ class WindowsService implements IWindowsService {
// tslint:disable-next-line no-any // tslint:disable-next-line no-any
public _serviceBrand: any; public _serviceBrand: any;
private openEmitter = new Emitter<number>(); private readonly openEmitter = new Emitter<number>();
private focusEmitter = new Emitter<number>(); private readonly focusEmitter = new Emitter<number>();
private blurEmitter = new Emitter<number>(); private readonly blurEmitter = new Emitter<number>();
private maximizeEmitter = new Emitter<number>(); private readonly maximizeEmitter = new Emitter<number>();
private unmaximizeEmitter = new Emitter<number>(); private readonly unmaximizeEmitter = new Emitter<number>();
private recentlyOpenedChangeEmitter = new Emitter<void>(); private readonly recentlyOpenedChangeEmitter = new Emitter<void>();
public onWindowOpen = this.openEmitter.event; public readonly onWindowOpen = this.openEmitter.event;
public onWindowFocus = this.focusEmitter.event; public readonly onWindowFocus = this.focusEmitter.event;
public onWindowBlur = this.blurEmitter.event; public readonly onWindowBlur = this.blurEmitter.event;
public onWindowMaximize = this.maximizeEmitter.event; public readonly onWindowMaximize = this.maximizeEmitter.event;
public onWindowUnmaximize = this.unmaximizeEmitter.event; public readonly onWindowUnmaximize = this.unmaximizeEmitter.event;
public onRecentlyOpenedChange = this.recentlyOpenedChangeEmitter.event; public readonly onRecentlyOpenedChange = this.recentlyOpenedChangeEmitter.event;
private window = new electron.BrowserWindow(); private readonly window = new electron.BrowserWindow();
// Dialogs // Dialogs
public pickFileFolderAndOpen(_options: INativeOpenDialogOptions): Promise<void> { public pickFileFolderAndOpen(_options: INativeOpenDialogOptions): Promise<void> {