diff --git a/js/src/app/index.ts b/js/src/app/index.ts index 363c398d..f0d371a3 100644 --- a/js/src/app/index.ts +++ b/js/src/app/index.ts @@ -22,7 +22,13 @@ export namespace App { log.info("created", { path: dataDir }); - const services = new Map(); + const services = new Map< + any, + { + state: any; + shutdown?: (input: any) => Promise; + } + >(); const result = { get services() { @@ -39,15 +45,22 @@ export namespace App { return result; } - export function state any>(key: any, init: T) { + export function state( + key: any, + init: (app: Info) => State, + shutdown?: (state: Awaited) => Promise, + ) { return () => { const app = ctx.use(); const services = app.services; if (!services.has(key)) { log.info("registering service", { name: key }); - services.set(key, init(app)); + services.set(key, { + state: init(app), + shutdown: shutdown, + }); } - return services.get(key) as ReturnType; + return services.get(key)?.state as State; }; } @@ -62,7 +75,12 @@ export namespace App { const app = await create(input); return ctx.provide(app, async () => { - return cb(app); + const result = await cb(app); + for (const [key, entry] of app.services.entries()) { + log.info("shutdown", { name: key }); + await entry.shutdown?.(await entry.state); + } + return result; }); } } diff --git a/js/src/id/id.ts b/js/src/id/id.ts index 0cb1afe9..62c6a12b 100644 --- a/js/src/id/id.ts +++ b/js/src/id/id.ts @@ -30,11 +30,20 @@ export namespace Identifier { descending: boolean, given?: string, ): string { - if (given) { - if (given.startsWith(prefixes[prefix])) return given; + if (!given) { + return generateNewID(prefix, descending); + } + + if (!given.startsWith(prefixes[prefix])) { throw new Error(`ID ${given} does not start with ${prefixes[prefix]}`); } + return given; + } + function generateNewID( + prefix: keyof typeof prefixes, + descending: boolean, + ): string { const currentTimestamp = Date.now(); if (currentTimestamp !== lastTimestamp) { @@ -45,9 +54,7 @@ export namespace Identifier { let now = BigInt(currentTimestamp) * BigInt(0x1000) + BigInt(counter); - if (descending) { - now = ~now; - } + now = descending ? ~now : now; const timeBytes = Buffer.alloc(6); for (let i = 0; i < 6; i++) { diff --git a/js/src/lsp/client.ts b/js/src/lsp/client.ts index f07b21c7..0c9f9d06 100644 --- a/js/src/lsp/client.ts +++ b/js/src/lsp/client.ts @@ -178,6 +178,11 @@ export namespace LSPClient { }), ]); }, + async shutdown() { + log.info("shutting down"); + connection.end(); + connection.dispose(); + }, }; return result; diff --git a/js/src/lsp/index.ts b/js/src/lsp/index.ts index f20a15e7..80503fb1 100644 --- a/js/src/lsp/index.ts +++ b/js/src/lsp/index.ts @@ -5,22 +5,30 @@ import { LSPClient } from "./client"; export namespace LSP { const log = Log.create({ service: "lsp" }); - const state = App.state("lsp", async () => { - const clients = new Map(); + const state = App.state( + "lsp", + async () => { + const clients = new Map(); - // QUESTION: how lazy should lsp auto discovery be? should it not initialize until a file is opened? - clients.set( - "typescript", - await LSPClient.create({ - cmd: ["bun", "x", "typescript-language-server", "--stdio"], - }), - ); + // QUESTION: how lazy should lsp auto discovery be? should it not initialize until a file is opened? + clients.set( + "typescript", + await LSPClient.create({ + cmd: ["bun", "x", "typescript-language-server", "--stdio"], + }), + ); - return { - clients, - diagnostics: new Map(), - }; - }); + return { + clients, + diagnostics: new Map(), + }; + }, + async (state) => { + for (const client of state.clients.values()) { + await client.shutdown(); + } + }, + ); export async function run( input: (client: LSPClient.Info) => Promise, diff --git a/js/src/lsp/language.ts b/js/src/lsp/language.ts index 964ebc80..14b32edf 100644 --- a/js/src/lsp/language.ts +++ b/js/src/lsp/language.ts @@ -1,4 +1,4 @@ -export const LANGUAGE_EXTENSIONS = { +export const LANGUAGE_EXTENSIONS: Record = { ".abap": "abap", ".bat": "bat", ".bib": "bibtex",