test(lsp): migrate client tests to effect fixtures (#29045)
This commit is contained in:
parent
9d2e2af865
commit
f00a681fc5
1 changed files with 246 additions and 330 deletions
|
|
@ -1,11 +1,16 @@
|
||||||
import { beforeEach, describe, expect, test } from "bun:test"
|
import { beforeEach, describe, expect } from "bun:test"
|
||||||
|
import { AppFileSystem } from "@opencode-ai/core/filesystem"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import { pathToFileURL } from "url"
|
import { pathToFileURL } from "url"
|
||||||
import { tmpdir, withTestInstance } from "../fixture/fixture"
|
import { Effect } from "effect"
|
||||||
|
import { pollWithTimeout, testEffect } from "../lib/effect"
|
||||||
|
import { requireInstance, TestInstance } from "../fixture/fixture"
|
||||||
import { LSPClient } from "@/lsp/client"
|
import { LSPClient } from "@/lsp/client"
|
||||||
import * as LSPServer from "@/lsp/server"
|
import * as LSPServer from "@/lsp/server"
|
||||||
import * as Log from "@opencode-ai/core/util/log"
|
import * as Log from "@opencode-ai/core/util/log"
|
||||||
|
|
||||||
|
const it = testEffect(AppFileSystem.defaultLayer)
|
||||||
|
|
||||||
function spawnFakeServer() {
|
function spawnFakeServer() {
|
||||||
const { spawn } = require("child_process")
|
const { spawn } = require("child_process")
|
||||||
const serverPath = path.join(__dirname, "../fixture/lsp/fake-lsp-server.js")
|
const serverPath = path.join(__dirname, "../fixture/lsp/fake-lsp-server.js")
|
||||||
|
|
@ -16,202 +21,164 @@ function spawnFakeServer() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const createClient = (handle: LSPServer.Handle, initialization?: LSPServer.Handle["initialization"]) =>
|
||||||
|
Effect.gen(function* () {
|
||||||
|
const test = yield* TestInstance
|
||||||
|
const instance = yield* requireInstance
|
||||||
|
return yield* Effect.promise(() =>
|
||||||
|
LSPClient.create({
|
||||||
|
serverID: "fake",
|
||||||
|
server: initialization ? { ...handle, initialization } : handle,
|
||||||
|
root: test.directory,
|
||||||
|
directory: test.directory,
|
||||||
|
instance,
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const createScopedClient = (handle: LSPServer.Handle, initialization?: LSPServer.Handle["initialization"]) =>
|
||||||
|
Effect.gen(function* () {
|
||||||
|
const client = yield* createClient(handle, initialization)
|
||||||
|
yield* Effect.addFinalizer(() => Effect.promise(() => client.shutdown()).pipe(Effect.ignore))
|
||||||
|
return client
|
||||||
|
})
|
||||||
|
|
||||||
|
const writeFile = (file: string, content: string) => AppFileSystem.use.writeWithDirs(file, content)
|
||||||
|
|
||||||
describe("LSPClient interop", () => {
|
describe("LSPClient interop", () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
await Log.init({ print: true })
|
await Log.init({ print: true })
|
||||||
})
|
})
|
||||||
|
|
||||||
test("handles workspace/workspaceFolders request", async () => {
|
it.instance("handles workspace/workspaceFolders request", () =>
|
||||||
const handle = spawnFakeServer() as any
|
Effect.gen(function* () {
|
||||||
|
const client = yield* createScopedClient(spawnFakeServer())
|
||||||
|
|
||||||
const client = await withTestInstance({
|
yield* Effect.promise(() =>
|
||||||
directory: process.cwd(),
|
client.connection.sendNotification("test/trigger", {
|
||||||
fn: (ctx) =>
|
method: "workspace/workspaceFolders",
|
||||||
LSPClient.create({
|
|
||||||
serverID: "fake",
|
|
||||||
server: handle as unknown as LSPServer.Handle,
|
|
||||||
root: process.cwd(),
|
|
||||||
directory: process.cwd(),
|
|
||||||
instance: ctx,
|
|
||||||
}),
|
}),
|
||||||
})
|
)
|
||||||
|
|
||||||
await client.connection.sendNotification("test/trigger", {
|
yield* Effect.promise(() => client.connection.sendRequest("test/get-diagnostic-request-count", {}))
|
||||||
method: "workspace/workspaceFolders",
|
expect(client.connection).toBeDefined()
|
||||||
})
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 100))
|
it.instance("handles client/registerCapability request", () =>
|
||||||
expect(client.connection).toBeDefined()
|
Effect.gen(function* () {
|
||||||
await client.shutdown()
|
const client = yield* createScopedClient(spawnFakeServer())
|
||||||
})
|
|
||||||
|
|
||||||
test("handles client/registerCapability request", async () => {
|
yield* Effect.promise(() =>
|
||||||
const handle = spawnFakeServer() as any
|
client.connection.sendNotification("test/trigger", {
|
||||||
|
method: "client/registerCapability",
|
||||||
const client = await withTestInstance({
|
|
||||||
directory: process.cwd(),
|
|
||||||
fn: (ctx) =>
|
|
||||||
LSPClient.create({
|
|
||||||
serverID: "fake",
|
|
||||||
server: handle as unknown as LSPServer.Handle,
|
|
||||||
root: process.cwd(),
|
|
||||||
directory: process.cwd(),
|
|
||||||
instance: ctx,
|
|
||||||
}),
|
}),
|
||||||
})
|
)
|
||||||
|
|
||||||
await client.connection.sendNotification("test/trigger", {
|
yield* Effect.promise(() => client.connection.sendRequest("test/get-diagnostic-request-count", {}))
|
||||||
method: "client/registerCapability",
|
expect(client.connection).toBeDefined()
|
||||||
})
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 100))
|
it.instance("handles client/unregisterCapability request", () =>
|
||||||
expect(client.connection).toBeDefined()
|
Effect.gen(function* () {
|
||||||
await client.shutdown()
|
const client = yield* createScopedClient(spawnFakeServer())
|
||||||
})
|
|
||||||
|
|
||||||
test("handles client/unregisterCapability request", async () => {
|
yield* Effect.promise(() =>
|
||||||
const handle = spawnFakeServer() as any
|
client.connection.sendNotification("test/trigger", {
|
||||||
|
method: "client/unregisterCapability",
|
||||||
const client = await withTestInstance({
|
|
||||||
directory: process.cwd(),
|
|
||||||
fn: (ctx) =>
|
|
||||||
LSPClient.create({
|
|
||||||
serverID: "fake",
|
|
||||||
server: handle as unknown as LSPServer.Handle,
|
|
||||||
root: process.cwd(),
|
|
||||||
directory: process.cwd(),
|
|
||||||
instance: ctx,
|
|
||||||
}),
|
}),
|
||||||
})
|
)
|
||||||
|
|
||||||
await client.connection.sendNotification("test/trigger", {
|
yield* Effect.promise(() => client.connection.sendRequest("test/get-diagnostic-request-count", {}))
|
||||||
method: "client/unregisterCapability",
|
expect(client.connection).toBeDefined()
|
||||||
})
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
await new Promise((resolve) => setTimeout(resolve, 100))
|
it.instance("initialize does not overclaim unsupported diagnostics capabilities", () =>
|
||||||
expect(client.connection).toBeDefined()
|
Effect.gen(function* () {
|
||||||
await client.shutdown()
|
const client = yield* createScopedClient(spawnFakeServer())
|
||||||
})
|
|
||||||
|
|
||||||
test("initialize does not overclaim unsupported diagnostics capabilities", async () => {
|
const params = yield* Effect.promise(() =>
|
||||||
const handle = spawnFakeServer() as any
|
client.connection.sendRequest<{
|
||||||
|
capabilities: {
|
||||||
|
workspace: { diagnostics: { refreshSupport: boolean } }
|
||||||
|
textDocument: { publishDiagnostics: { versionSupport: boolean } }
|
||||||
|
}
|
||||||
|
}>("test/get-initialize-params", {}),
|
||||||
|
)
|
||||||
|
expect(params.capabilities.workspace.diagnostics.refreshSupport).toBe(false)
|
||||||
|
expect(params.capabilities.textDocument.publishDiagnostics.versionSupport).toBe(false)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
const client = await withTestInstance({
|
it.instance("workspace/configuration returns one result per requested item", () =>
|
||||||
directory: process.cwd(),
|
Effect.gen(function* () {
|
||||||
fn: (ctx) =>
|
const initialization = {
|
||||||
LSPClient.create({
|
alpha: {
|
||||||
serverID: "fake",
|
beta: 1,
|
||||||
server: handle as unknown as LSPServer.Handle,
|
},
|
||||||
root: process.cwd(),
|
gamma: true,
|
||||||
directory: process.cwd(),
|
}
|
||||||
instance: ctx,
|
|
||||||
|
const client = yield* createScopedClient(spawnFakeServer(), initialization)
|
||||||
|
|
||||||
|
const response = yield* Effect.promise(() =>
|
||||||
|
client.connection.sendRequest<unknown[]>("test/request-configuration", {
|
||||||
|
items: [{ section: "alpha" }, { section: "alpha.beta" }, { section: "missing" }, {}],
|
||||||
}),
|
}),
|
||||||
})
|
)
|
||||||
|
|
||||||
const params = await client.connection.sendRequest<any>("test/get-initialize-params", {})
|
expect(response).toEqual([{ beta: 1 }, 1, null, initialization])
|
||||||
expect(params.capabilities.workspace.diagnostics.refreshSupport).toBe(false)
|
}),
|
||||||
expect(params.capabilities.textDocument.publishDiagnostics.versionSupport).toBe(false)
|
)
|
||||||
|
|
||||||
await client.shutdown()
|
it.instance("sends ranged didChange for incremental sync servers", () =>
|
||||||
})
|
Effect.gen(function* () {
|
||||||
|
const test = yield* TestInstance
|
||||||
|
const file = path.join(test.directory, "client.ts")
|
||||||
|
yield* writeFile(file, "first\n")
|
||||||
|
|
||||||
test("workspace/configuration returns one result per requested item", async () => {
|
const client = yield* createScopedClient(spawnFakeServer())
|
||||||
const handle = spawnFakeServer() as any
|
|
||||||
const initialization = {
|
|
||||||
alpha: {
|
|
||||||
beta: 1,
|
|
||||||
},
|
|
||||||
gamma: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
const client = await withTestInstance({
|
yield* Effect.promise(() => client.notify.open({ path: file }))
|
||||||
directory: process.cwd(),
|
yield* writeFile(file, "second\nthird\n")
|
||||||
fn: (ctx) =>
|
yield* Effect.promise(() => client.notify.open({ path: file }))
|
||||||
LSPClient.create({
|
|
||||||
serverID: "fake",
|
|
||||||
server: {
|
|
||||||
...(handle as unknown as LSPServer.Handle),
|
|
||||||
initialization,
|
|
||||||
},
|
|
||||||
root: process.cwd(),
|
|
||||||
directory: process.cwd(),
|
|
||||||
instance: ctx,
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
|
|
||||||
const response = await client.connection.sendRequest<any[]>("test/request-configuration", {
|
const change = yield* Effect.promise(() =>
|
||||||
items: [{ section: "alpha" }, { section: "alpha.beta" }, { section: "missing" }, {}],
|
client.connection.sendRequest<{
|
||||||
})
|
|
||||||
|
|
||||||
expect(response).toEqual([{ beta: 1 }, 1, null, initialization])
|
|
||||||
|
|
||||||
await client.shutdown()
|
|
||||||
})
|
|
||||||
|
|
||||||
test("sends ranged didChange for incremental sync servers", async () => {
|
|
||||||
const handle = spawnFakeServer() as any
|
|
||||||
await using tmp = await tmpdir()
|
|
||||||
const file = path.join(tmp.path, "client.ts")
|
|
||||||
await Bun.write(file, "first\n")
|
|
||||||
|
|
||||||
await withTestInstance({
|
|
||||||
directory: tmp.path,
|
|
||||||
fn: async (ctx) => {
|
|
||||||
const client = await LSPClient.create({
|
|
||||||
serverID: "fake",
|
|
||||||
server: handle as unknown as LSPServer.Handle,
|
|
||||||
root: tmp.path,
|
|
||||||
directory: tmp.path,
|
|
||||||
instance: ctx,
|
|
||||||
})
|
|
||||||
|
|
||||||
await client.notify.open({ path: file })
|
|
||||||
await Bun.write(file, "second\nthird\n")
|
|
||||||
await client.notify.open({ path: file })
|
|
||||||
|
|
||||||
const change = await client.connection.sendRequest<{
|
|
||||||
textDocument: { version: number }
|
textDocument: { version: number }
|
||||||
contentChanges: {
|
contentChanges: {
|
||||||
range?: { start: { line: number; character: number }; end: { line: number; character: number } }
|
range?: { start: { line: number; character: number }; end: { line: number; character: number } }
|
||||||
text: string
|
text: string
|
||||||
}[]
|
}[]
|
||||||
}>("test/get-last-change", {})
|
}>("test/get-last-change", {}),
|
||||||
expect(change.textDocument.version).toBe(1)
|
)
|
||||||
expect(change.contentChanges).toEqual([
|
expect(change.textDocument.version).toBe(1)
|
||||||
{
|
expect(change.contentChanges).toEqual([
|
||||||
range: {
|
{
|
||||||
start: { line: 0, character: 0 },
|
range: {
|
||||||
end: { line: 1, character: 0 },
|
start: { line: 0, character: 0 },
|
||||||
},
|
end: { line: 1, character: 0 },
|
||||||
text: "second\nthird\n",
|
|
||||||
},
|
},
|
||||||
])
|
text: "second\nthird\n",
|
||||||
|
},
|
||||||
|
])
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
await client.shutdown()
|
it.instance("document mode falls back to push diagnostics", () =>
|
||||||
},
|
Effect.gen(function* () {
|
||||||
})
|
const test = yield* TestInstance
|
||||||
})
|
const file = path.join(test.directory, "client.ts")
|
||||||
|
yield* writeFile(file, "const x = 1\n")
|
||||||
|
|
||||||
test("document mode falls back to push diagnostics", async () => {
|
const client = yield* createScopedClient(spawnFakeServer())
|
||||||
const handle = spawnFakeServer() as any
|
|
||||||
await using tmp = await tmpdir()
|
|
||||||
const file = path.join(tmp.path, "client.ts")
|
|
||||||
await Bun.write(file, "const x = 1\n")
|
|
||||||
|
|
||||||
await withTestInstance({
|
const version = yield* Effect.promise(() => client.notify.open({ path: file }))
|
||||||
directory: tmp.path,
|
const wait = client.waitForDiagnostics({ path: file, version, mode: "document" })
|
||||||
fn: async (ctx) => {
|
yield* Effect.promise(() =>
|
||||||
const client = await LSPClient.create({
|
client.connection.sendNotification("test/publish-diagnostics", {
|
||||||
serverID: "fake",
|
|
||||||
server: handle as unknown as LSPServer.Handle,
|
|
||||||
root: tmp.path,
|
|
||||||
directory: tmp.path,
|
|
||||||
instance: ctx,
|
|
||||||
})
|
|
||||||
|
|
||||||
const version = await client.notify.open({ path: file })
|
|
||||||
const wait = client.waitForDiagnostics({ path: file, version, mode: "document" })
|
|
||||||
await client.connection.sendNotification("test/publish-diagnostics", {
|
|
||||||
uri: pathToFileURL(file).href,
|
uri: pathToFileURL(file).href,
|
||||||
version,
|
version,
|
||||||
diagnostics: [
|
diagnostics: [
|
||||||
|
|
@ -224,40 +191,30 @@ describe("LSPClient interop", () => {
|
||||||
severity: 1,
|
severity: 1,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
}),
|
||||||
await wait
|
)
|
||||||
|
yield* Effect.promise(() => wait)
|
||||||
|
|
||||||
const diagnostics = client.diagnostics.get(file) ?? []
|
const diagnostics = client.diagnostics.get(file) ?? []
|
||||||
expect(diagnostics).toHaveLength(1)
|
expect(diagnostics).toHaveLength(1)
|
||||||
expect(diagnostics[0]?.message).toBe("push diagnostic")
|
expect(diagnostics[0]?.message).toBe("push diagnostic")
|
||||||
|
|
||||||
const count = await client.connection.sendRequest("test/get-diagnostic-request-count", {})
|
const count = yield* Effect.promise(() => client.connection.sendRequest("test/get-diagnostic-request-count", {}))
|
||||||
expect(count).toBe(0)
|
expect(count).toBe(0)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
await client.shutdown()
|
it.instance("document mode accepts matching push diagnostics published before waiting", () =>
|
||||||
},
|
Effect.gen(function* () {
|
||||||
})
|
const test = yield* TestInstance
|
||||||
})
|
const file = path.join(test.directory, "client.ts")
|
||||||
|
yield* writeFile(file, "const x = 1\n")
|
||||||
|
|
||||||
test("document mode accepts matching push diagnostics published before waiting", async () => {
|
const client = yield* createScopedClient(spawnFakeServer())
|
||||||
const handle = spawnFakeServer() as any
|
|
||||||
await using tmp = await tmpdir()
|
|
||||||
const file = path.join(tmp.path, "client.ts")
|
|
||||||
await Bun.write(file, "const x = 1\n")
|
|
||||||
|
|
||||||
await withTestInstance({
|
const version = yield* Effect.promise(() => client.notify.open({ path: file }))
|
||||||
directory: tmp.path,
|
yield* Effect.promise(() =>
|
||||||
fn: async (ctx) => {
|
client.connection.sendNotification("test/publish-diagnostics", {
|
||||||
const client = await LSPClient.create({
|
|
||||||
serverID: "fake",
|
|
||||||
server: handle as unknown as LSPServer.Handle,
|
|
||||||
root: tmp.path,
|
|
||||||
directory: tmp.path,
|
|
||||||
instance: ctx,
|
|
||||||
})
|
|
||||||
|
|
||||||
const version = await client.notify.open({ path: file })
|
|
||||||
await client.connection.sendNotification("test/publish-diagnostics", {
|
|
||||||
uri: pathToFileURL(file).href,
|
uri: pathToFileURL(file).href,
|
||||||
version,
|
version,
|
||||||
diagnostics: [
|
diagnostics: [
|
||||||
|
|
@ -270,41 +227,31 @@ describe("LSPClient interop", () => {
|
||||||
severity: 1,
|
severity: 1,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
})
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
for (let i = 0; i < 20 && (client.diagnostics.get(file)?.length ?? 0) === 0; i++) {
|
const diagnostic = yield* pollWithTimeout(
|
||||||
await new Promise((resolve) => setTimeout(resolve, 25))
|
Effect.sync(() => client.diagnostics.get(file)?.[0]),
|
||||||
}
|
"push diagnostic was not published",
|
||||||
|
)
|
||||||
|
expect(diagnostic.message).toBe("push diagnostic")
|
||||||
|
|
||||||
expect(client.diagnostics.get(file)?.[0]?.message).toBe("push diagnostic")
|
const started = Date.now()
|
||||||
|
yield* Effect.promise(() => client.waitForDiagnostics({ path: file, version, mode: "document" }))
|
||||||
|
expect(Date.now() - started).toBeLessThan(1_000)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
const started = Date.now()
|
it.instance("document mode waits for pull diagnostics", () =>
|
||||||
await client.waitForDiagnostics({ path: file, version, mode: "document" })
|
Effect.gen(function* () {
|
||||||
expect(Date.now() - started).toBeLessThan(1_000)
|
const test = yield* TestInstance
|
||||||
|
const file = path.join(test.directory, "client.cs")
|
||||||
|
yield* writeFile(file, "class C {}\n")
|
||||||
|
|
||||||
await client.shutdown()
|
const client = yield* createScopedClient(spawnFakeServer())
|
||||||
},
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
test("document mode waits for pull diagnostics", async () => {
|
yield* Effect.promise(() =>
|
||||||
const handle = spawnFakeServer() as any
|
client.connection.sendRequest("test/configure-pull-diagnostics", {
|
||||||
await using tmp = await tmpdir()
|
|
||||||
const file = path.join(tmp.path, "client.cs")
|
|
||||||
await Bun.write(file, "class C {}\n")
|
|
||||||
|
|
||||||
await withTestInstance({
|
|
||||||
directory: tmp.path,
|
|
||||||
fn: async (ctx) => {
|
|
||||||
const client = await LSPClient.create({
|
|
||||||
serverID: "fake",
|
|
||||||
server: handle as unknown as LSPServer.Handle,
|
|
||||||
root: tmp.path,
|
|
||||||
directory: tmp.path,
|
|
||||||
instance: ctx,
|
|
||||||
})
|
|
||||||
|
|
||||||
await client.connection.sendRequest("test/configure-pull-diagnostics", {
|
|
||||||
registerOn: "didOpen",
|
registerOn: "didOpen",
|
||||||
registrations: [{ identifier: "DocumentCompilerSemantic" }],
|
registrations: [{ identifier: "DocumentCompilerSemantic" }],
|
||||||
documentDiagnosticsByIdentifier: {
|
documentDiagnosticsByIdentifier: {
|
||||||
|
|
@ -319,41 +266,31 @@ describe("LSPClient interop", () => {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
})
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
const version = await client.notify.open({ path: file })
|
const version = yield* Effect.promise(() => client.notify.open({ path: file }))
|
||||||
await client.waitForDiagnostics({ path: file, version, mode: "document" })
|
yield* Effect.promise(() => client.waitForDiagnostics({ path: file, version, mode: "document" }))
|
||||||
|
|
||||||
const diagnostics = client.diagnostics.get(file) ?? []
|
const diagnostics = client.diagnostics.get(file) ?? []
|
||||||
expect(diagnostics).toHaveLength(1)
|
expect(diagnostics).toHaveLength(1)
|
||||||
expect(diagnostics[0]?.message).toBe("pull diagnostic")
|
expect(diagnostics[0]?.message).toBe("pull diagnostic")
|
||||||
|
|
||||||
const count = await client.connection.sendRequest("test/get-diagnostic-request-count", {})
|
const count = yield* Effect.promise(() => client.connection.sendRequest("test/get-diagnostic-request-count", {}))
|
||||||
expect(count).toBeGreaterThan(0)
|
expect(count).toBeGreaterThan(0)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
await client.shutdown()
|
it.instance("document mode does not wait for the slowest pull identifier after current-file diagnostics arrive", () =>
|
||||||
},
|
Effect.gen(function* () {
|
||||||
})
|
const test = yield* TestInstance
|
||||||
})
|
const file = path.join(test.directory, "client.cs")
|
||||||
|
yield* writeFile(file, "class C {}\n")
|
||||||
|
|
||||||
test("document mode does not wait for the slowest pull identifier after current-file diagnostics arrive", async () => {
|
const client = yield* createScopedClient(spawnFakeServer())
|
||||||
const handle = spawnFakeServer() as any
|
|
||||||
await using tmp = await tmpdir()
|
|
||||||
const file = path.join(tmp.path, "client.cs")
|
|
||||||
await Bun.write(file, "class C {}\n")
|
|
||||||
|
|
||||||
await withTestInstance({
|
yield* Effect.promise(() =>
|
||||||
directory: tmp.path,
|
client.connection.sendRequest("test/configure-pull-diagnostics", {
|
||||||
fn: async (ctx) => {
|
|
||||||
const client = await LSPClient.create({
|
|
||||||
serverID: "fake",
|
|
||||||
server: handle as unknown as LSPServer.Handle,
|
|
||||||
root: tmp.path,
|
|
||||||
directory: tmp.path,
|
|
||||||
instance: ctx,
|
|
||||||
})
|
|
||||||
|
|
||||||
await client.connection.sendRequest("test/configure-pull-diagnostics", {
|
|
||||||
registrations: [{ identifier: "fast" }, { identifier: "slow" }],
|
registrations: [{ identifier: "fast" }, { identifier: "slow" }],
|
||||||
documentDiagnosticsByIdentifier: {
|
documentDiagnosticsByIdentifier: {
|
||||||
fast: [
|
fast: [
|
||||||
|
|
@ -371,43 +308,34 @@ describe("LSPClient interop", () => {
|
||||||
documentDelayMsByIdentifier: {
|
documentDelayMsByIdentifier: {
|
||||||
slow: 2_500,
|
slow: 2_500,
|
||||||
},
|
},
|
||||||
})
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
const version = await client.notify.open({ path: file })
|
const version = yield* Effect.promise(() => client.notify.open({ path: file }))
|
||||||
await client.connection.sendRequest("test/register-configured-pull-diagnostics", {})
|
yield* Effect.promise(() => client.connection.sendRequest("test/register-configured-pull-diagnostics", {}))
|
||||||
await new Promise((resolve) => setTimeout(resolve, 100))
|
const started = Date.now()
|
||||||
const started = Date.now()
|
yield* Effect.promise(() => client.waitForDiagnostics({ path: file, version, mode: "document" }))
|
||||||
await client.waitForDiagnostics({ path: file, version, mode: "document" })
|
|
||||||
|
|
||||||
expect(Date.now() - started).toBeLessThan(1_000)
|
expect(Date.now() - started).toBeLessThan(1_000)
|
||||||
expect(client.diagnostics.get(file)?.[0]?.message).toBe("fast diagnostic")
|
expect(client.diagnostics.get(file)?.[0]?.message).toBe("fast diagnostic")
|
||||||
expect(await client.connection.sendRequest("test/get-diagnostic-request-count", {})).toBeGreaterThan(1)
|
expect(
|
||||||
|
yield* Effect.promise(() => client.connection.sendRequest("test/get-diagnostic-request-count", {})),
|
||||||
|
).toBeGreaterThan(1)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
await client.shutdown()
|
it.instance("full mode includes workspace pull diagnostics", () =>
|
||||||
},
|
Effect.gen(function* () {
|
||||||
})
|
const test = yield* TestInstance
|
||||||
})
|
const file = path.join(test.directory, "client.cs")
|
||||||
|
const related = path.join(test.directory, "other.cs")
|
||||||
|
yield* writeFile(file, "class C {}\n")
|
||||||
|
yield* writeFile(related, "class D {}\n")
|
||||||
|
|
||||||
test("full mode includes workspace pull diagnostics", async () => {
|
const client = yield* createScopedClient(spawnFakeServer())
|
||||||
const handle = spawnFakeServer() as any
|
|
||||||
await using tmp = await tmpdir()
|
|
||||||
const file = path.join(tmp.path, "client.cs")
|
|
||||||
const related = path.join(tmp.path, "other.cs")
|
|
||||||
await Bun.write(file, "class C {}\n")
|
|
||||||
await Bun.write(related, "class D {}\n")
|
|
||||||
|
|
||||||
await withTestInstance({
|
yield* Effect.promise(() =>
|
||||||
directory: tmp.path,
|
client.connection.sendRequest("test/configure-pull-diagnostics", {
|
||||||
fn: async (ctx) => {
|
|
||||||
const client = await LSPClient.create({
|
|
||||||
serverID: "fake",
|
|
||||||
server: handle as unknown as LSPServer.Handle,
|
|
||||||
root: tmp.path,
|
|
||||||
directory: tmp.path,
|
|
||||||
instance: ctx,
|
|
||||||
})
|
|
||||||
|
|
||||||
await client.connection.sendRequest("test/configure-pull-diagnostics", {
|
|
||||||
registerOn: "didOpen",
|
registerOn: "didOpen",
|
||||||
registrations: [
|
registrations: [
|
||||||
{ identifier: "DocumentCompilerSemantic" },
|
{ identifier: "DocumentCompilerSemantic" },
|
||||||
|
|
@ -442,52 +370,40 @@ describe("LSPClient interop", () => {
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
})
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
const version = await client.notify.open({ path: file })
|
const version = yield* Effect.promise(() => client.notify.open({ path: file }))
|
||||||
await client.waitForDiagnostics({ path: file, version, mode: "full" })
|
yield* Effect.promise(() => client.waitForDiagnostics({ path: file, version, mode: "full" }))
|
||||||
|
|
||||||
expect(client.diagnostics.get(file)?.[0]?.message).toBe("current file")
|
expect(client.diagnostics.get(file)?.[0]?.message).toBe("current file")
|
||||||
expect(client.diagnostics.get(related)?.[0]?.message).toBe("workspace file")
|
expect(client.diagnostics.get(related)?.[0]?.message).toBe("workspace file")
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
await client.shutdown()
|
it.instance("full mode treats an empty workspace pull response as handled", () =>
|
||||||
},
|
Effect.gen(function* () {
|
||||||
})
|
const test = yield* TestInstance
|
||||||
})
|
const file = path.join(test.directory, "client.cs")
|
||||||
|
yield* writeFile(file, "class C {}\n")
|
||||||
|
|
||||||
test("full mode treats an empty workspace pull response as handled", async () => {
|
const client = yield* createScopedClient(spawnFakeServer())
|
||||||
const handle = spawnFakeServer() as any
|
|
||||||
await using tmp = await tmpdir()
|
|
||||||
const file = path.join(tmp.path, "client.cs")
|
|
||||||
await Bun.write(file, "class C {}\n")
|
|
||||||
|
|
||||||
await withTestInstance({
|
yield* Effect.promise(() =>
|
||||||
directory: tmp.path,
|
client.connection.sendRequest("test/configure-pull-diagnostics", {
|
||||||
fn: async (ctx) => {
|
|
||||||
const client = await LSPClient.create({
|
|
||||||
serverID: "fake",
|
|
||||||
server: handle as unknown as LSPServer.Handle,
|
|
||||||
root: tmp.path,
|
|
||||||
directory: tmp.path,
|
|
||||||
instance: ctx,
|
|
||||||
})
|
|
||||||
|
|
||||||
await client.connection.sendRequest("test/configure-pull-diagnostics", {
|
|
||||||
registerOn: "didOpen",
|
registerOn: "didOpen",
|
||||||
registrations: [{ identifier: "WorkspaceDocumentsAndProject", workspaceDiagnostics: true }],
|
registrations: [{ identifier: "WorkspaceDocumentsAndProject", workspaceDiagnostics: true }],
|
||||||
workspaceDiagnosticsByIdentifier: {
|
workspaceDiagnosticsByIdentifier: {
|
||||||
WorkspaceDocumentsAndProject: [],
|
WorkspaceDocumentsAndProject: [],
|
||||||
},
|
},
|
||||||
})
|
}),
|
||||||
|
)
|
||||||
|
|
||||||
const version = await client.notify.open({ path: file })
|
const version = yield* Effect.promise(() => client.notify.open({ path: file }))
|
||||||
const started = Date.now()
|
const started = Date.now()
|
||||||
await client.waitForDiagnostics({ path: file, version, mode: "full" })
|
yield* Effect.promise(() => client.waitForDiagnostics({ path: file, version, mode: "full" }))
|
||||||
|
|
||||||
expect(Date.now() - started).toBeLessThan(1_000)
|
expect(Date.now() - started).toBeLessThan(1_000)
|
||||||
|
}),
|
||||||
await client.shutdown()
|
)
|
||||||
},
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue