|
1 | 1 | import { join } from "node:path"; |
| 2 | +import { LSP_SEMANTIC_TOKEN_MODIFIERS, LSP_SEMANTIC_TOKEN_TYPES } from "@coder-studio/core"; |
2 | 3 | import { describe, expect, it, vi } from "vitest"; |
3 | 4 | import { LspSession } from "./session.js"; |
4 | 5 |
|
@@ -53,7 +54,8 @@ describe.sequential("LspSession", () => { |
53 | 54 | }, |
54 | 55 | }); |
55 | 56 |
|
56 | | - await session.start(); |
| 57 | + const summary = await session.start(); |
| 58 | + expect(summary.capabilities.semanticTokens).toBe(true); |
57 | 59 | await session.openDocument({ |
58 | 60 | path: "e2e/fixtures/lsp-workspace/broken.ts", |
59 | 61 | languageId: "typescript", |
@@ -102,6 +104,21 @@ describe.sequential("LspSession", () => { |
102 | 104 |
|
103 | 105 | expect(symbols?.[0]?.name).toBe("sharedValue"); |
104 | 106 |
|
| 107 | + const semanticTokens = await session.semanticTokens({ |
| 108 | + path: "e2e/fixtures/lsp-workspace/shared.ts", |
| 109 | + }); |
| 110 | + |
| 111 | + expect(semanticTokens).toEqual({ |
| 112 | + resultId: "semantic-1", |
| 113 | + data: [ |
| 114 | + 0, |
| 115 | + 13, |
| 116 | + 11, |
| 117 | + LSP_SEMANTIC_TOKEN_TYPES.indexOf("variable"), |
| 118 | + 1 << LSP_SEMANTIC_TOKEN_MODIFIERS.indexOf("declaration"), |
| 119 | + ], |
| 120 | + }); |
| 121 | + |
105 | 122 | expect(diagnostics).toHaveBeenCalledWith( |
106 | 123 | expect.objectContaining({ |
107 | 124 | workspaceId: "ws-1", |
@@ -493,66 +510,55 @@ describe.sequential("LspSession", () => { |
493 | 510 | it("kills the companion process when the primary exits", async () => { |
494 | 511 | // If Volar crashes we must not leave the TypeScript companion alive |
495 | 512 | // (otherwise idle-TTL cleanup leaks a process per session). |
496 | | - const previous = process.env.CODER_STUDIO_FAKE_LSP_EXIT_AFTER_INIT_MS; |
497 | | - process.env.CODER_STUDIO_FAKE_LSP_EXIT_AFTER_INIT_MS = "150"; |
498 | | - |
499 | | - try { |
500 | | - const session = new LspSession({ |
501 | | - workspaceId: "ws-1", |
502 | | - workspacePath: process.cwd(), |
503 | | - spec: { |
504 | | - serverKind: "vue", |
505 | | - // Primary exits 150ms after initialize. |
| 513 | + const session = new LspSession({ |
| 514 | + workspaceId: "ws-1", |
| 515 | + workspacePath: process.cwd(), |
| 516 | + spec: { |
| 517 | + serverKind: "vue", |
| 518 | + // Primary exits 150ms after initialize. |
| 519 | + command: "node", |
| 520 | + args: [FAKE_LSP, "--exit-after-init-ms=150"], |
| 521 | + rootPath: process.cwd(), |
| 522 | + companion: { |
| 523 | + // Companion stays alive normally. |
506 | 524 | command: "node", |
507 | 525 | args: [FAKE_LSP], |
508 | | - rootPath: process.cwd(), |
509 | | - companion: { |
510 | | - // Companion stays alive normally. |
511 | | - command: "node", |
512 | | - args: [FAKE_LSP], |
513 | | - }, |
514 | | - bridges: { tsserverRequest: true }, |
515 | | - }, |
516 | | - onDiagnostics: vi.fn(), |
517 | | - requestTimeoutMs: 2000, |
518 | | - logger: { |
519 | | - info: vi.fn(), |
520 | | - warn: vi.fn(), |
521 | | - error: vi.fn(), |
522 | 526 | }, |
523 | | - }); |
| 527 | + bridges: { tsserverRequest: true }, |
| 528 | + }, |
| 529 | + onDiagnostics: vi.fn(), |
| 530 | + requestTimeoutMs: 2000, |
| 531 | + logger: { |
| 532 | + info: vi.fn(), |
| 533 | + warn: vi.fn(), |
| 534 | + error: vi.fn(), |
| 535 | + }, |
| 536 | + }); |
524 | 537 |
|
525 | | - // Pull the companion field via a typed accessor for inspection. |
526 | | - type WithCompanion = LspSession & { |
527 | | - companion: null | { child: { killed: boolean } }; |
528 | | - }; |
| 538 | + // Pull the companion field via a typed accessor for inspection. |
| 539 | + type WithCompanion = LspSession & { |
| 540 | + companion: null | { child: { killed: boolean } }; |
| 541 | + }; |
529 | 542 |
|
530 | | - await session.start(); |
531 | | - // Companion was spawned alongside primary. |
532 | | - expect((session as WithCompanion).companion).not.toBeNull(); |
533 | | - const companionChild = (session as WithCompanion).companion?.child; |
534 | | - expect(companionChild).toBeDefined(); |
535 | | - |
536 | | - // Wait long enough for the primary to exit and the termination handler |
537 | | - // to fire. |
538 | | - await vi.waitFor( |
539 | | - () => { |
540 | | - expect((session as WithCompanion).companion).toBeNull(); |
541 | | - }, |
542 | | - { timeout: 2000 } |
543 | | - ); |
544 | | - // The companion's process should have received SIGTERM. |
545 | | - expect(companionChild?.killed).toBe(true); |
546 | | - expect(session.getSummary().status).toBe("stopped"); |
| 543 | + await session.start(); |
| 544 | + // Companion was spawned alongside primary. |
| 545 | + expect((session as WithCompanion).companion).not.toBeNull(); |
| 546 | + const companionChild = (session as WithCompanion).companion?.child; |
| 547 | + expect(companionChild).toBeDefined(); |
547 | 548 |
|
548 | | - await session.stop(); |
549 | | - } finally { |
550 | | - if (previous === undefined) { |
551 | | - delete process.env.CODER_STUDIO_FAKE_LSP_EXIT_AFTER_INIT_MS; |
552 | | - } else { |
553 | | - process.env.CODER_STUDIO_FAKE_LSP_EXIT_AFTER_INIT_MS = previous; |
554 | | - } |
555 | | - } |
| 549 | + // Wait long enough for the primary to exit and the termination handler |
| 550 | + // to fire. |
| 551 | + await vi.waitFor( |
| 552 | + () => { |
| 553 | + expect((session as WithCompanion).companion).toBeNull(); |
| 554 | + }, |
| 555 | + { timeout: 2000 } |
| 556 | + ); |
| 557 | + // The companion's process should have received SIGTERM. |
| 558 | + expect(companionChild?.killed).toBe(true); |
| 559 | + expect(session.getSummary().status).toBe("stopped"); |
| 560 | + |
| 561 | + await session.stop(); |
556 | 562 | }); |
557 | 563 |
|
558 | 564 | it("stops the companion when the session is explicitly stopped", async () => { |
|
0 commit comments