From 21cfeb9da00769c9a36eccf9b5e87f2d0fe09303 Mon Sep 17 00:00:00 2001 From: Asher Date: Thu, 27 Feb 2020 12:04:23 -0600 Subject: [PATCH] Add the ability to kill running VS Code instance --- src/browser/pages/error.css | 4 ++++ src/browser/pages/home.css | 19 +++------------ src/browser/pages/home.html | 45 +++++++++++++++++------------------ src/node/app/api.ts | 47 ++++++++++++++++++++++++++----------- src/node/app/app.ts | 6 +++-- src/node/app/bin.ts | 2 ++ src/node/app/vscode.ts | 16 ++++++++++++- src/node/entry.ts | 4 ++-- src/node/http.ts | 11 +++++++++ 9 files changed, 96 insertions(+), 58 deletions(-) diff --git a/src/browser/pages/error.css b/src/browser/pages/error.css index 42ccf8c7b..15b7d4aca 100644 --- a/src/browser/pages/error.css +++ b/src/browser/pages/error.css @@ -26,3 +26,7 @@ .error-display > .links > .link:hover { text-decoration: underline; } + +.error-display .success { + color: green; +} diff --git a/src/browser/pages/home.css b/src/browser/pages/home.css index 795eeb7d5..698bbac33 100644 --- a/src/browser/pages/home.css +++ b/src/browser/pages/home.css @@ -32,28 +32,15 @@ } .block-row > .item > .icon.-missing { - background-color: rgb(87, 114, 245); - color: #fff; + background-color: rgba(87, 114, 245, 0.2); text-align: center; } -.block-row > .item > .icon.-missing::after { - content: "?"; - font-size: 0.7rem; - vertical-align: middle; -} - .kill-form { display: inline-block; } .kill-form > .kill { - background-color: rgb(87, 114, 245); - border: none; - color: #fff; - cursor: pointer; - font-size: 1rem; - line-height: 1rem; - margin: 0; - padding: 0; + border-radius: 3px; + padding: 2px 5px; } diff --git a/src/browser/pages/home.html b/src/browser/pages/home.html index ce2343644..1e38bc490 100644 --- a/src/browser/pages/home.html +++ b/src/browser/pages/home.html @@ -20,27 +20,36 @@
- - - - - +
+
+

Running

+
Currently running applications.
+
+
+ {{APP_LIST:RUNNING}} +
+
-

Launch

-
Choose an application to launch below.
+

Editors

+
Choose an editor to launch below.
- + {{APP_LIST:EDITORS}}
+ + + + + + + + + +

Version

@@ -50,16 +59,6 @@ {{UPDATE:NAME}}
- - - - - - - - - -
diff --git a/src/node/app/api.ts b/src/node/app/api.ts index 12c32f4af..b5f2e9381 100644 --- a/src/node/app/api.ts +++ b/src/node/app/api.ts @@ -19,7 +19,8 @@ import { import { ApiEndpoint, HttpCode } from "../../common/http" import { normalize } from "../../common/util" import { HttpProvider, HttpProviderOptions, HttpResponse, HttpServer, Route } from "../http" -import { findApplications, findWhitelistedApplications } from "./bin" +import { findApplications, findWhitelistedApplications, Vscode } from "./bin" +import { VscodeHttpProvider } from "./vscode" interface ServerSession { process?: cp.ChildProcess @@ -42,6 +43,7 @@ export class ApiHttpProvider extends HttpProvider { public constructor( options: HttpProviderOptions, private readonly server: HttpServer, + private readonly vscode: VscodeHttpProvider, private readonly dataDir?: string, ) { super(options) @@ -256,17 +258,24 @@ export class ApiHttpProvider extends HttpProvider { /** * Kill a session identified by `app.sessionId`. */ - public deleteSession(sessionId: string): HttpResponse { + public async deleteSession(sessionId: string): Promise { logger.debug("deleting session", field("sessionId", sessionId)) - const session = this.sessions.get(sessionId) - if (!session) { - throw new Error("session does not exist") + switch (sessionId) { + case "vscode": + await this.vscode.dispose() + return { code: HttpCode.Ok } + default: { + const session = this.sessions.get(sessionId) + if (!session) { + throw new Error("session does not exist") + } + if (session.process) { + session.process.kill() + } + this.sessions.delete(sessionId) + return { code: HttpCode.Ok } + } } - if (session.process) { - session.process.kill() - } - this.sessions.delete(sessionId) - return { code: HttpCode.Ok } } /** @@ -350,10 +359,20 @@ export class ApiHttpProvider extends HttpProvider { */ public async running(): Promise { return { - applications: Array.from(this.sessions).map(([sessionId, session]) => ({ - ...session.app, - sessionId, - })), + applications: (this.vscode.running + ? [ + { + ...Vscode, + sessionId: "vscode", + }, + ] + : [] + ).concat( + Array.from(this.sessions).map(([sessionId, session]) => ({ + ...session.app, + sessionId, + })), + ), } } diff --git a/src/node/app/app.ts b/src/node/app/app.ts index e132d1a05..ca26c7fd7 100644 --- a/src/node/app/app.ts +++ b/src/node/app/app.ts @@ -124,7 +124,9 @@ export class MainHttpProvider extends HttpProvider { } private getAppRows(apps: ReadonlyArray): string { - return apps.length > 0 ? apps.map((app) => this.getAppRow(app)).join("\n") : `
None
` + return apps.length > 0 + ? apps.map((app) => this.getAppRow(app)).join("\n") + : `
No applications are currently running.
` } private getAppRow(app: Application): string { @@ -141,7 +143,7 @@ export class MainHttpProvider extends HttpProvider { app.sessionId ? `
- +
` : "" } diff --git a/src/node/app/bin.ts b/src/node/app/bin.ts index 4cf57c735..b278e3fe4 100644 --- a/src/node/app/bin.ts +++ b/src/node/app/bin.ts @@ -1,3 +1,4 @@ +import * as fs from "fs" import * as path from "path" import { Application } from "../../common/api" @@ -11,6 +12,7 @@ const getVscodeVersion = (): string => { export const Vscode: Application = { categories: ["Editor"], + icon: fs.readFileSync(path.resolve(__dirname, "../../../lib/vscode/resources/linux/code.png")).toString("base64"), installed: true, name: "VS Code", path: "/vscode", diff --git a/src/node/app/vscode.ts b/src/node/app/vscode.ts index 56578b33c..a95ca06ea 100644 --- a/src/node/app/vscode.ts +++ b/src/node/app/vscode.ts @@ -31,6 +31,19 @@ export class VscodeHttpProvider extends HttpProvider { this.serverRootPath = path.join(this.vsRootPath, "out/vs/server") } + public get running(): boolean { + return !!this._vscode + } + + public async dispose(): Promise { + if (this._vscode) { + const vscode = await this._vscode + vscode.removeAllListeners() + this._vscode = undefined + vscode.kill() + } + } + private async initialize(options: VscodeOptions): Promise { const id = generateUuid() const vscode = await this.fork() @@ -126,7 +139,8 @@ export class VscodeHttpProvider extends HttpProvider { } catch (error) { const message = `
VS Code failed to load.
${ this.isDev - ? "
It might not have finished compiling.
Check for 'Finished compilation' in the output." + ? `
It might not have finished compiling.
` + + `Check for Finished compilation in the output.` : "" }

${error}` return this.getErrorRoot(route, "VS Code failed to load", "500", message) diff --git a/src/node/entry.ts b/src/node/entry.ts index e9c9562eb..1feef71d9 100644 --- a/src/node/entry.ts +++ b/src/node/entry.ts @@ -44,9 +44,9 @@ const main = async (args: Args): Promise => { } const httpServer = new HttpServer(options) - const api = httpServer.registerHttpProvider("/api", ApiHttpProvider, httpServer, args["user-data-dir"]) + const vscode = httpServer.registerHttpProvider("/vscode", VscodeHttpProvider, args) + const api = httpServer.registerHttpProvider("/api", ApiHttpProvider, httpServer, vscode, args["user-data-dir"]) const update = httpServer.registerHttpProvider("/update", UpdateHttpProvider, !args["disable-updates"]) - httpServer.registerHttpProvider("/vscode", VscodeHttpProvider, args) httpServer.registerHttpProvider("/login", LoginHttpProvider) httpServer.registerHttpProvider("/", MainHttpProvider, api, update) diff --git a/src/node/http.ts b/src/node/http.ts index 26336a891..99fa47e7c 100644 --- a/src/node/http.ts +++ b/src/node/http.ts @@ -360,6 +360,10 @@ export interface HttpProvider2 { new (options: HttpProviderOptions, a1: A1, a2: A2): T } +export interface HttpProvider3 { + new (options: HttpProviderOptions, a1: A1, a2: A2, a3: A3): T +} + /** * An HTTP server. Its main role is to route incoming HTTP requests to the * appropriate provider for that endpoint then write out the response. It also @@ -417,6 +421,13 @@ export class HttpServer { a1: A1, a2: A2, ): T + public registerHttpProvider( + endpoint: string, + provider: HttpProvider3, + a1: A1, + a2: A2, + a3: A3, + ): T // eslint-disable-next-line @typescript-eslint/no-explicit-any public registerHttpProvider(endpoint: string, provider: any, ...args: any[]): any { endpoint = endpoint.replace(/^\/+|\/+$/g, "")