linol/ocaml-lsp-server/test/e2e/__tests__/textDocument-formatting.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

183 lines
4.4 KiB
TypeScript

import * as fs from "node:fs";
import * as os from "node:os";
import * as path from "node:path";
import * as Protocol from "vscode-languageserver-protocol";
import * as Types from "vscode-languageserver-types";
import * as LanguageServer from "../src/LanguageServer";
const ocamlFormat = `
break-cases=all
break-separators=before
break-sequences=true
cases-exp-indent=2
doc-comments=before
dock-collection-brackets=false
field-space=loose
if-then-else=k-r
indicate-nested-or-patterns=unsafe-no
let-and=sparse
sequence-style=terminator
space-around-arrays
space-around-lists
space-around-records
type-decl=sparse
wrap-comments=true
`;
function setupOcamlFormat(ocamlFormat: string) {
const tmpdir = fs.mkdtempSync(path.join(os.tmpdir(), "ocamllsp-test-"));
fs.writeFileSync(path.join(tmpdir, ".ocamlformat"), ocamlFormat);
return tmpdir;
}
function openDocument(
languageServer: LanguageServer.LanguageServer,
source: string,
name: string,
) {
languageServer.sendNotification(
Protocol.DidOpenTextDocumentNotification.type,
{
textDocument: Types.TextDocumentItem.create(name, "ocaml", 0, source),
},
);
}
async function query(
languageServer: LanguageServer.LanguageServer,
name: string,
) {
return await languageServer.sendRequest(
Protocol.DocumentFormattingRequest.type,
{
textDocument: Types.TextDocumentIdentifier.create(name),
options: Types.FormattingOptions.create(2, true),
},
);
}
const maybeDescribe = os.type() === "Windows_NT" ? describe.skip : describe;
maybeDescribe("textDocument/formatting", () => {
maybeDescribe("reformatter binary present", () => {
let languageServer: LanguageServer.LanguageServer;
afterEach(async () => {
await LanguageServer.exit(languageServer);
});
it("can format an ocaml impl file", async () => {
languageServer = await LanguageServer.startAndInitialize();
const name = path.join(setupOcamlFormat(ocamlFormat), "test.ml");
openDocument(
languageServer,
"let rec gcd a b =\n" +
" match (a, b) with\n" +
" | 0, n\n" +
" | n, 0 ->\n" +
" n\n" +
" | _, _ -> gcd a (b mod a)\n",
name,
);
const result = await query(languageServer, name);
expect(result).toMatchInlineSnapshot(`
[
{
"newText": " | 0, n
",
"range": {
"end": {
"character": 0,
"line": 3,
},
"start": {
"character": 0,
"line": 2,
},
},
},
]
`);
});
it("leaves unchanged files alone", async () => {
languageServer = await LanguageServer.startAndInitialize();
const name = path.join(setupOcamlFormat(ocamlFormat), "test.ml");
openDocument(
languageServer,
"let rec gcd a b =\n" +
" match (a, b) with\n" +
" | 0, n\n" +
" | n, 0 ->\n" +
" n\n" +
" | _, _ -> gcd a (b mod a)\n",
name,
);
const result = await query(languageServer, name);
expect(result).toMatchObject([]);
});
it("can format an ocaml intf file", async () => {
languageServer = await LanguageServer.startAndInitialize();
const name = path.join(setupOcamlFormat(ocamlFormat), "test.mli");
openDocument(
languageServer,
"module Test : sig\n type t =\n | Foo\n | Bar\n | Baz\nend\n",
name,
);
const result = await query(languageServer, name);
expect(result).toMatchInlineSnapshot(`
[
{
"newText": "module Test : sig
",
"range": {
"end": {
"character": 0,
"line": 1,
},
"start": {
"character": 0,
"line": 0,
},
},
},
]
`);
});
it("does not format ignored files", async () => {
languageServer = await LanguageServer.startAndInitialize();
const tmpdir = setupOcamlFormat(ocamlFormat);
const ocamlFormatIgnore = path.join(tmpdir, ".ocamlformat-ignore");
fs.writeFileSync(ocamlFormatIgnore, "test.ml\n");
const name = path.join(tmpdir, "test.ml");
openDocument(
languageServer,
"let rec gcd a b = match (a, b) with\n" +
" | 0, n\n" +
" | n, 0 ->\n" +
" n\n" +
" | _, _ -> gcd a (b mod a)\n",
name,
);
const result = await query(languageServer, name);
expect(result).toMatchObject([]);
});
});
});