Dax Raad 9 months ago
parent
commit
b87ba57819
5 changed files with 63 additions and 25 deletions
  1. 23 5
      js/src/app/index.ts
  2. 12 5
      js/src/id/id.ts
  3. 5 0
      js/src/lsp/client.ts
  4. 22 14
      js/src/lsp/index.ts
  5. 1 1
      js/src/lsp/language.ts

+ 23 - 5
js/src/app/index.ts

@@ -22,7 +22,13 @@ export namespace App {
 
     log.info("created", { path: dataDir });
 
-    const services = new Map<any, any>();
+    const services = new Map<
+      any,
+      {
+        state: any;
+        shutdown?: (input: any) => Promise<void>;
+      }
+    >();
 
     const result = {
       get services() {
@@ -39,15 +45,22 @@ export namespace App {
     return result;
   }
 
-  export function state<T extends (app: Info) => any>(key: any, init: T) {
+  export function state<State>(
+    key: any,
+    init: (app: Info) => State,
+    shutdown?: (state: Awaited<State>) => Promise<void>,
+  ) {
     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<T>;
+      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;
     });
   }
 }

+ 12 - 5
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++) {

+ 5 - 0
js/src/lsp/client.ts

@@ -178,6 +178,11 @@ export namespace LSPClient {
           }),
         ]);
       },
+      async shutdown() {
+        log.info("shutting down");
+        connection.end();
+        connection.dispose();
+      },
     };
 
     return result;

+ 22 - 14
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<string, LSPClient.Info>();
+  const state = App.state(
+    "lsp",
+    async () => {
+      const clients = new Map<string, LSPClient.Info>();
 
-    // 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<string, any>(),
-    };
-  });
+      return {
+        clients,
+        diagnostics: new Map<string, any>(),
+      };
+    },
+    async (state) => {
+      for (const client of state.clients.values()) {
+        await client.shutdown();
+      }
+    },
+  );
 
   export async function run<T>(
     input: (client: LSPClient.Info) => Promise<T>,

+ 1 - 1
js/src/lsp/language.ts

@@ -1,4 +1,4 @@
-export const LANGUAGE_EXTENSIONS = {
+export const LANGUAGE_EXTENSIONS: Record<string, string> = {
   ".abap": "abap",
   ".bat": "bat",
   ".bib": "bibtex",