linol/ocaml-lsp-server/test/e2e/__tests__/workspace-symbol.test.ts
Simon Cruanes 7fbc187548 Squashed 'thirdparty/lsp/' content from commit aae69863
git-subtree-dir: thirdparty/lsp
git-subtree-split: aae6986391a8519de3da6a7a341f2bd3376e0d2f
2025-04-10 15:44:25 -04:00

242 lines
8.8 KiB
TypeScript

import * as child_process from "node:child_process";
import * as path from "node:path";
import * as Protocol from "vscode-languageserver-protocol";
import * as Types from "vscode-languageserver-types";
import { URI } from "vscode-uri";
import * as LanguageServer from "./../src/LanguageServer";
const createWorkspaces = (names: string[]) => {
const createSingleWorkspace = (name: string) => {
const workspacePath = path.join(__dirname, name);
const uri = LanguageServer.toURI(workspacePath);
const folder: Protocol.WorkspaceFolder = { name, uri };
return {
name,
path: workspacePath,
uri,
folder,
};
};
const workspaces = names.map(createSingleWorkspace);
const toTestResult = (
symbol: Types.SymbolInformation | Types.WorkspaceSymbol,
) => {
const location = Types.Location.is(symbol.location)
? `${symbol.location.range.start.line}:${symbol.location.range.start.character} ${symbol.location.range.end.line}:${symbol.location.range.end.character}`
: "";
return `${symbol.name} ${symbol.kind} ${workspaces.reduce(
(acc, { path: workspacePath, name: workspaceName }) => {
const uri = symbol.location.uri;
const fsPath = URI.parse(uri).fsPath;
if (!fsPath.includes(workspaceName)) return acc;
return URI.file(
path.relative(path.resolve(workspacePath, ".."), fsPath),
).path;
},
"",
)} ${location}`;
};
return {
workspaces,
toTestResult,
};
};
const buildProject = (cwd: string): void => {
child_process.execSync("dune build --root . @check", { cwd: cwd });
};
describe("workspace/symbol", () => {
let languageServer: LanguageServer.LanguageServer;
const {
workspaces: [workspaceA, workspaceB],
toTestResult,
} = createWorkspaces(["workspace_symbol_A", "workspace_symbol_B"]);
afterEach(async () => {
await LanguageServer.exit(languageServer);
});
async function queryWorkspaceSymbol(params: Protocol.WorkspaceSymbolParams) {
return (
(await languageServer.sendRequest(
Protocol.WorkspaceSymbolRequest.type,
params,
)) ?? []
);
}
it("returns all symbols from workspace", async () => {
languageServer = await LanguageServer.startAndInitialize({
workspaceFolders: [workspaceA.folder],
});
buildProject(workspaceA.path);
const symbols = await queryWorkspaceSymbol({
query: "",
});
expect(symbols.map(toTestResult)).toMatchInlineSnapshot(`
[
"stack_of_ints 5 /workspace_symbol_A/bin/a.ml 51:0 65:5",
"size 6 /workspace_symbol_A/bin/a.ml 64:11 64:15",
"peek 6 /workspace_symbol_A/bin/a.ml 62:11 62:15",
"pop 6 /workspace_symbol_A/bin/a.ml 57:11 57:14",
"push 6 /workspace_symbol_A/bin/a.ml 55:11 55:15",
"the_list 12 /workspace_symbol_A/bin/a.ml 53:16 53:24",
"Foo 9 /workspace_symbol_A/bin/a.ml 49:0 49:23",
"Increment 2 /workspace_symbol_A/bin/a.ml 45:0 47:3",
"increment_x 12 /workspace_symbol_A/bin/a.ml 46:2 46:27",
"X_int 2 /workspace_symbol_A/bin/a.ml 41:0 43:3",
"x 12 /workspace_symbol_A/bin/a.ml 42:2 42:13",
"A_Mod 2 /workspace_symbol_A/bin/a.ml 29:0 39:3",
"compare 12 /workspace_symbol_A/bin/a.ml 38:2 38:30",
"private_mod_fn 12 /workspace_symbol_A/bin/a.ml 36:2 36:33",
"t 15 /workspace_symbol_A/bin/a.ml 34:2 34:14",
"My_string 2 /workspace_symbol_A/bin/a.ml 27:0 27:25",
"StringMap 2 /workspace_symbol_A/bin/a.ml 26:0 26:36",
"a_i 12 /workspace_symbol_A/bin/a.ml 22:0 24:7",
"a_arr 12 /workspace_symbol_A/bin/a.ml 18:0 18:14",
"a_u 12 /workspace_symbol_A/bin/a.ml 16:0 16:15",
"user 15 /workspace_symbol_A/bin/a.ml 12:0 14:12",
"NotAdmin 9 /workspace_symbol_A/bin/a.ml 14:2 14:12",
"Admin 9 /workspace_symbol_A/bin/a.ml 13:2 13:9",
"a_d 12 /workspace_symbol_A/bin/a.ml 7:0 10:14",
"A_B 2 /workspace_symbol_A/bin/a.ml 2:0 5:3",
"a_b 12 /workspace_symbol_A/bin/a.ml 4:2 4:19",
"a_b_t 15 /workspace_symbol_A/bin/a.ml 3:2 3:21",
"a_x 12 /workspace_symbol_A/bin/a.ml 0:0 0:11",
"main_y 12 /workspace_symbol_A/bin/main.ml 0:0 0:22",
"vendored_x 12 /workspace_symbol_A/lib/lib.ml 14:0 14:31",
"lib_private_fn 12 /workspace_symbol_A/lib/lib.ml 10:0 10:38",
"hd 12 /workspace_symbol_A/lib/lib.ml 8:0 8:16",
"lib_x 12 /workspace_symbol_A/lib/lib.ml 6:0 6:14",
"user 15 /workspace_symbol_A/lib/lib.ml 3:0 5:1",
"name 7 /workspace_symbol_A/lib/lib.ml 4:2 4:14",
"t 15 /workspace_symbol_A/lib/LibTypes.mli 0:0 0:15",
"x 12 /workspace_symbol_A/vendor/vendored_lib.ml 0:0 0:9",
]
`);
});
it("returns filtered symbols from workspace", async () => {
languageServer = await LanguageServer.startAndInitialize({
workspaceFolders: [workspaceA.folder],
});
buildProject(workspaceA.path);
const symbols = await queryWorkspaceSymbol({
query: "a_",
});
expect(symbols.map(toTestResult)).toMatchInlineSnapshot(`
[
"a_i 12 /workspace_symbol_A/bin/a.ml 22:0 24:7",
"a_arr 12 /workspace_symbol_A/bin/a.ml 18:0 18:14",
"a_u 12 /workspace_symbol_A/bin/a.ml 16:0 16:15",
"a_d 12 /workspace_symbol_A/bin/a.ml 7:0 10:14",
"a_b 12 /workspace_symbol_A/bin/a.ml 4:2 4:19",
"a_b_t 15 /workspace_symbol_A/bin/a.ml 3:2 3:21",
"a_x 12 /workspace_symbol_A/bin/a.ml 0:0 0:11",
]
`);
});
it("handles mutiple wokspaces", async () => {
languageServer = await LanguageServer.startAndInitialize({
workspaceFolders: [workspaceA.folder, workspaceB.folder],
});
buildProject(workspaceA.path);
buildProject(workspaceB.path);
const symbols = await queryWorkspaceSymbol({
query: "",
});
/* FIXME: symbol lib_type from lib.ml is missing */
expect(symbols.map(toTestResult)).toMatchInlineSnapshot(`
[
"stack_of_ints 5 /workspace_symbol_A/bin/a.ml 51:0 65:5",
"size 6 /workspace_symbol_A/bin/a.ml 64:11 64:15",
"peek 6 /workspace_symbol_A/bin/a.ml 62:11 62:15",
"pop 6 /workspace_symbol_A/bin/a.ml 57:11 57:14",
"push 6 /workspace_symbol_A/bin/a.ml 55:11 55:15",
"the_list 12 /workspace_symbol_A/bin/a.ml 53:16 53:24",
"Foo 9 /workspace_symbol_A/bin/a.ml 49:0 49:23",
"Increment 2 /workspace_symbol_A/bin/a.ml 45:0 47:3",
"increment_x 12 /workspace_symbol_A/bin/a.ml 46:2 46:27",
"X_int 2 /workspace_symbol_A/bin/a.ml 41:0 43:3",
"x 12 /workspace_symbol_A/bin/a.ml 42:2 42:13",
"A_Mod 2 /workspace_symbol_A/bin/a.ml 29:0 39:3",
"compare 12 /workspace_symbol_A/bin/a.ml 38:2 38:30",
"private_mod_fn 12 /workspace_symbol_A/bin/a.ml 36:2 36:33",
"t 15 /workspace_symbol_A/bin/a.ml 34:2 34:14",
"My_string 2 /workspace_symbol_A/bin/a.ml 27:0 27:25",
"StringMap 2 /workspace_symbol_A/bin/a.ml 26:0 26:36",
"a_i 12 /workspace_symbol_A/bin/a.ml 22:0 24:7",
"a_arr 12 /workspace_symbol_A/bin/a.ml 18:0 18:14",
"a_u 12 /workspace_symbol_A/bin/a.ml 16:0 16:15",
"user 15 /workspace_symbol_A/bin/a.ml 12:0 14:12",
"NotAdmin 9 /workspace_symbol_A/bin/a.ml 14:2 14:12",
"Admin 9 /workspace_symbol_A/bin/a.ml 13:2 13:9",
"a_d 12 /workspace_symbol_A/bin/a.ml 7:0 10:14",
"A_B 2 /workspace_symbol_A/bin/a.ml 2:0 5:3",
"a_b 12 /workspace_symbol_A/bin/a.ml 4:2 4:19",
"a_b_t 15 /workspace_symbol_A/bin/a.ml 3:2 3:21",
"a_x 12 /workspace_symbol_A/bin/a.ml 0:0 0:11",
"main_y 12 /workspace_symbol_A/bin/main.ml 0:0 0:22",
"vendored_x 12 /workspace_symbol_A/lib/lib.ml 14:0 14:31",
"lib_private_fn 12 /workspace_symbol_A/lib/lib.ml 10:0 10:38",
"hd 12 /workspace_symbol_A/lib/lib.ml 8:0 8:16",
"lib_x 12 /workspace_symbol_A/lib/lib.ml 6:0 6:14",
"user 15 /workspace_symbol_A/lib/lib.ml 3:0 5:1",
"name 7 /workspace_symbol_A/lib/lib.ml 4:2 4:14",
"t 15 /workspace_symbol_A/lib/LibTypes.mli 0:0 0:15",
"x 12 /workspace_symbol_A/vendor/vendored_lib.ml 0:0 0:9",
"workspace_B 12 /workspace_symbol_B/main.ml 0:0 0:31",
]
`);
});
it("shows a notification message if no build directory found", async () => {
languageServer = await LanguageServer.startAndInitialize({
workspaceFolders: [workspaceA.folder, workspaceB.folder],
});
child_process.execSync("dune clean", { cwd: workspaceA.path });
child_process.execSync("dune clean", { cwd: workspaceB.path });
type StarNotificationHandlerParams =
Parameters<Protocol.StarNotificationHandler>;
const receivedNotification: Promise<{
method: StarNotificationHandlerParams[0];
params: StarNotificationHandlerParams[1];
}> = new Promise((resolve, _reject) =>
languageServer.onNotification((method, params) =>
resolve({ method, params }),
),
);
await queryWorkspaceSymbol({
query: "",
});
const notification = await receivedNotification;
expect(notification.method).toBe(
Protocol.ShowMessageNotification.type.method,
);
// @ts-ignore
expect(notification.params.type).toEqual(Protocol.MessageType.Warning);
// @ts-ignore
expect(notification.params.message).toBe(
`No build directory found in workspace(s): ${workspaceA.name}, ${workspaceB.name}`,
);
});
});