mirror of
https://github.com/c-cube/linol.git
synced 2025-12-09 20:55:43 -05:00
599 lines
12 KiB
TypeScript
599 lines
12 KiB
TypeScript
import outdent from "outdent";
|
|
import * as Types from "vscode-languageserver-types";
|
|
import * as LanguageServer from "./../src/LanguageServer";
|
|
|
|
describe("textDocument/hover", () => {
|
|
let languageServer: LanguageServer.LanguageServer;
|
|
|
|
afterEach(async () => {
|
|
await LanguageServer.exit(languageServer);
|
|
});
|
|
|
|
it("returns type inferred under cursor", async () => {
|
|
languageServer = await LanguageServer.startAndInitialize();
|
|
await languageServer.sendNotification("textDocument/didOpen", {
|
|
textDocument: Types.TextDocumentItem.create(
|
|
"file:///test.ml",
|
|
"ocaml",
|
|
0,
|
|
"let x = 1\n",
|
|
),
|
|
});
|
|
|
|
const result = await languageServer.sendRequest("textDocument/hover", {
|
|
textDocument: Types.TextDocumentIdentifier.create("file:///test.ml"),
|
|
position: Types.Position.create(0, 4),
|
|
});
|
|
|
|
expect(result).toMatchInlineSnapshot(`
|
|
{
|
|
"contents": {
|
|
"kind": "plaintext",
|
|
"value": "int",
|
|
},
|
|
"range": {
|
|
"end": {
|
|
"character": 5,
|
|
"line": 0,
|
|
},
|
|
"start": {
|
|
"character": 4,
|
|
"line": 0,
|
|
},
|
|
},
|
|
}
|
|
`);
|
|
});
|
|
|
|
it("returns type inferred under cursor (markdown formatting)", async () => {
|
|
languageServer = await LanguageServer.startAndInitialize({
|
|
capabilities: {
|
|
textDocument: {
|
|
hover: {
|
|
dynamicRegistration: true,
|
|
contentFormat: ["markdown", "plaintext"],
|
|
},
|
|
moniker: {},
|
|
},
|
|
},
|
|
});
|
|
await languageServer.sendNotification("textDocument/didOpen", {
|
|
textDocument: Types.TextDocumentItem.create(
|
|
"file:///test.ml",
|
|
"ocaml",
|
|
0,
|
|
"let x = 1\n",
|
|
),
|
|
});
|
|
|
|
const result = await languageServer.sendRequest("textDocument/hover", {
|
|
textDocument: Types.TextDocumentIdentifier.create("file:///test.ml"),
|
|
position: Types.Position.create(0, 4),
|
|
});
|
|
|
|
expect(result).toMatchInlineSnapshot(`
|
|
{
|
|
"contents": {
|
|
"kind": "markdown",
|
|
"value": "\`\`\`ocaml
|
|
int
|
|
\`\`\`",
|
|
},
|
|
"range": {
|
|
"end": {
|
|
"character": 5,
|
|
"line": 0,
|
|
},
|
|
"start": {
|
|
"character": 4,
|
|
"line": 0,
|
|
},
|
|
},
|
|
}
|
|
`);
|
|
});
|
|
|
|
// checks for regression https://github.com/ocaml/merlin/issues/1540
|
|
// FIXME: when we can again fetch documentation, update this test
|
|
it("returns type inferred under cursor with documentation in let-definition", async () => {
|
|
languageServer = await LanguageServer.startAndInitialize({
|
|
capabilities: {
|
|
textDocument: {
|
|
hover: {
|
|
dynamicRegistration: true,
|
|
contentFormat: ["markdown", "plaintext"],
|
|
},
|
|
moniker: {},
|
|
},
|
|
},
|
|
});
|
|
await languageServer.sendNotification("textDocument/didOpen", {
|
|
textDocument: Types.TextDocumentItem.create(
|
|
"file:///test.ml",
|
|
"ocaml",
|
|
0,
|
|
outdent`
|
|
(** This function has a nice documentation *)
|
|
let id x = x
|
|
|
|
`,
|
|
),
|
|
});
|
|
|
|
const result = await languageServer.sendRequest("textDocument/hover", {
|
|
textDocument: Types.TextDocumentIdentifier.create("file:///test.ml"),
|
|
position: Types.Position.create(1, 4),
|
|
});
|
|
|
|
expect(result).toMatchInlineSnapshot(`
|
|
{
|
|
"contents": {
|
|
"kind": "markdown",
|
|
"value": "\`\`\`ocaml
|
|
'a -> 'a
|
|
\`\`\`
|
|
***
|
|
This function has a nice documentation",
|
|
},
|
|
"range": {
|
|
"end": {
|
|
"character": 6,
|
|
"line": 1,
|
|
},
|
|
"start": {
|
|
"character": 4,
|
|
"line": 1,
|
|
},
|
|
},
|
|
}
|
|
`);
|
|
});
|
|
|
|
it("returns type inferred under cursor with documentation", async () => {
|
|
languageServer = await LanguageServer.startAndInitialize({
|
|
capabilities: {
|
|
textDocument: {
|
|
hover: {
|
|
dynamicRegistration: true,
|
|
contentFormat: ["markdown", "plaintext"],
|
|
},
|
|
moniker: {},
|
|
},
|
|
},
|
|
});
|
|
await languageServer.sendNotification("textDocument/didOpen", {
|
|
textDocument: Types.TextDocumentItem.create(
|
|
"file:///test.ml",
|
|
"ocaml",
|
|
0,
|
|
outdent`
|
|
(** This function has a nice documentation *)
|
|
let id x = x
|
|
|
|
let () = id ()
|
|
`,
|
|
),
|
|
});
|
|
|
|
const result = await languageServer.sendRequest("textDocument/hover", {
|
|
textDocument: Types.TextDocumentIdentifier.create("file:///test.ml"),
|
|
position: Types.Position.create(3, 9),
|
|
});
|
|
|
|
expect(result).toMatchInlineSnapshot(`
|
|
{
|
|
"contents": {
|
|
"kind": "markdown",
|
|
"value": "\`\`\`ocaml
|
|
'a -> 'a
|
|
\`\`\`
|
|
***
|
|
This function has a nice documentation",
|
|
},
|
|
"range": {
|
|
"end": {
|
|
"character": 11,
|
|
"line": 3,
|
|
},
|
|
"start": {
|
|
"character": 9,
|
|
"line": 3,
|
|
},
|
|
},
|
|
}
|
|
`);
|
|
});
|
|
|
|
it("returns type inferred under cursor with documentation with tags (markdown formatting)", async () => {
|
|
languageServer = await LanguageServer.startAndInitialize({
|
|
capabilities: {
|
|
textDocument: {
|
|
hover: {
|
|
dynamicRegistration: true,
|
|
contentFormat: ["markdown", "plaintext"],
|
|
},
|
|
moniker: {},
|
|
},
|
|
},
|
|
});
|
|
await languageServer.sendNotification("textDocument/didOpen", {
|
|
textDocument: Types.TextDocumentItem.create(
|
|
"file:///test.ml",
|
|
"ocaml",
|
|
0,
|
|
outdent`
|
|
(** This function has a nice documentation.
|
|
|
|
It performs division of two integer numbers.
|
|
|
|
@param x dividend
|
|
@param divisor
|
|
|
|
@return {i quotient}, i.e. result of division
|
|
@raise Division_by_zero raised when divided by zero
|
|
|
|
@see <https://en.wikipedia.org/wiki/Arithmetic#Division_(%C3%B7,_or_/)> article
|
|
@see 'arithmetic.ml' for more context
|
|
|
|
@since 4.0.0
|
|
@before 4.4.0
|
|
|
|
@deprecated use [(/)]
|
|
|
|
@version 1.0.0
|
|
@author John Doe *)
|
|
let div x y =
|
|
x / y
|
|
|
|
let f = div 4 2
|
|
`,
|
|
),
|
|
});
|
|
|
|
const result = await languageServer.sendRequest("textDocument/hover", {
|
|
textDocument: Types.TextDocumentIdentifier.create("file:///test.ml"),
|
|
position: Types.Position.create(23, 9),
|
|
});
|
|
|
|
expect(result).toMatchInlineSnapshot(`
|
|
{
|
|
"contents": {
|
|
"kind": "markdown",
|
|
"value": "\`\`\`ocaml
|
|
int -> int -> int
|
|
\`\`\`
|
|
***
|
|
This function has a nice documentation.
|
|
|
|
It performs division of two integer numbers.
|
|
|
|
***@param*** \`x\`
|
|
dividend
|
|
|
|
***@param*** divisor
|
|
|
|
***@return***
|
|
*quotient*, i.e. result of division
|
|
|
|
***@raise*** \`Division_by_zero\`
|
|
raised when divided by zero
|
|
|
|
***@see*** [link](https://en.wikipedia.org/wiki/Arithmetic#Division_\\(%C3%B7,_or_/\\))
|
|
article
|
|
|
|
***@see*** \`arithmetic.ml\`
|
|
for more context
|
|
|
|
***@since*** \`4.0.0\`
|
|
|
|
***@before*** \`4.4.0\`
|
|
|
|
***@deprecated***
|
|
use \`(/)\`
|
|
|
|
***@version*** \`1.0.0\`
|
|
|
|
***@author*** John Doe",
|
|
},
|
|
"range": {
|
|
"end": {
|
|
"character": 11,
|
|
"line": 23,
|
|
},
|
|
"start": {
|
|
"character": 8,
|
|
"line": 23,
|
|
},
|
|
},
|
|
}
|
|
`);
|
|
});
|
|
|
|
it("returns good type when cursor is between values", async () => {
|
|
languageServer = await LanguageServer.startAndInitialize({
|
|
capabilities: {
|
|
textDocument: {
|
|
hover: {
|
|
dynamicRegistration: true,
|
|
contentFormat: ["markdown", "plaintext"],
|
|
},
|
|
moniker: {},
|
|
},
|
|
},
|
|
});
|
|
await languageServer.sendNotification("textDocument/didOpen", {
|
|
textDocument: Types.TextDocumentItem.create(
|
|
"file:///test.ml",
|
|
"ocaml",
|
|
0,
|
|
outdent`
|
|
let f i f = float_of_int i +. f
|
|
let i = 10
|
|
let f = 10.
|
|
let sum = f i f
|
|
`,
|
|
),
|
|
});
|
|
|
|
const result = await languageServer.sendRequest("textDocument/hover", {
|
|
textDocument: Types.TextDocumentIdentifier.create("file:///test.ml"),
|
|
position: Types.Position.create(3, 13),
|
|
});
|
|
|
|
expect(result).toMatchInlineSnapshot(`
|
|
{
|
|
"contents": {
|
|
"kind": "markdown",
|
|
"value": "\`\`\`ocaml
|
|
int
|
|
\`\`\`",
|
|
},
|
|
"range": {
|
|
"end": {
|
|
"character": 13,
|
|
"line": 3,
|
|
},
|
|
"start": {
|
|
"character": 12,
|
|
"line": 3,
|
|
},
|
|
},
|
|
}
|
|
`);
|
|
});
|
|
|
|
it("regression test for #343", async () => {
|
|
languageServer = await LanguageServer.startAndInitialize({
|
|
capabilities: {
|
|
textDocument: {
|
|
hover: {
|
|
dynamicRegistration: true,
|
|
contentFormat: ["markdown", "plaintext"],
|
|
},
|
|
moniker: {},
|
|
},
|
|
},
|
|
});
|
|
await languageServer.sendNotification("textDocument/didOpen", {
|
|
textDocument: Types.TextDocumentItem.create(
|
|
"file:///test.ml",
|
|
"ocaml",
|
|
0,
|
|
outdent`
|
|
type t = s
|
|
and s = string
|
|
type 'a fib = ('a -> unit) -> unit
|
|
`,
|
|
),
|
|
});
|
|
|
|
const hover1 = await languageServer.sendRequest("textDocument/hover", {
|
|
textDocument: Types.TextDocumentIdentifier.create("file:///test.ml"),
|
|
position: Types.Position.create(1, 4),
|
|
});
|
|
|
|
expect(hover1).toMatchInlineSnapshot(`
|
|
{
|
|
"contents": {
|
|
"kind": "markdown",
|
|
"value": "\`\`\`ocaml
|
|
type s = t
|
|
\`\`\`",
|
|
},
|
|
"range": {
|
|
"end": {
|
|
"character": 14,
|
|
"line": 1,
|
|
},
|
|
"start": {
|
|
"character": 0,
|
|
"line": 1,
|
|
},
|
|
},
|
|
}
|
|
`);
|
|
|
|
const hover2 = await languageServer.sendRequest("textDocument/hover", {
|
|
textDocument: Types.TextDocumentIdentifier.create("file:///test.ml"),
|
|
position: Types.Position.create(2, 9),
|
|
});
|
|
|
|
expect(hover2).toMatchInlineSnapshot(`
|
|
{
|
|
"contents": {
|
|
"kind": "markdown",
|
|
"value": "\`\`\`ocaml
|
|
type 'a fib = ('a -> unit) -> unit
|
|
\`\`\`",
|
|
},
|
|
"range": {
|
|
"end": {
|
|
"character": 34,
|
|
"line": 2,
|
|
},
|
|
"start": {
|
|
"character": 0,
|
|
"line": 2,
|
|
},
|
|
},
|
|
}
|
|
`);
|
|
});
|
|
|
|
it("regression test for #403", async () => {
|
|
languageServer = await LanguageServer.startAndInitialize();
|
|
await languageServer.sendNotification("textDocument/didOpen", {
|
|
textDocument: Types.TextDocumentItem.create(
|
|
"file:///test.ml",
|
|
"ocaml",
|
|
0,
|
|
outdent`
|
|
type foo = int
|
|
|
|
let x : foo = 1
|
|
`,
|
|
),
|
|
});
|
|
|
|
const result = await languageServer.sendRequest("textDocument/hover", {
|
|
textDocument: Types.TextDocumentIdentifier.create("file:///test.ml"),
|
|
position: Types.Position.create(2, 4),
|
|
});
|
|
|
|
expect(result).toMatchInlineSnapshot(`
|
|
{
|
|
"contents": {
|
|
"kind": "plaintext",
|
|
"value": "foo",
|
|
},
|
|
"range": {
|
|
"end": {
|
|
"character": 5,
|
|
"line": 2,
|
|
},
|
|
"start": {
|
|
"character": 4,
|
|
"line": 2,
|
|
},
|
|
},
|
|
}
|
|
`);
|
|
});
|
|
|
|
it("FIXME: reproduce [#344](https://github.com/ocaml/ocaml-lsp/issues/344)", async () => {
|
|
languageServer = await LanguageServer.startAndInitialize({
|
|
capabilities: {
|
|
textDocument: {
|
|
hover: {
|
|
dynamicRegistration: true,
|
|
contentFormat: ["markdown", "plaintext"],
|
|
},
|
|
moniker: {},
|
|
},
|
|
},
|
|
});
|
|
|
|
await languageServer.sendNotification("textDocument/didOpen", {
|
|
textDocument: Types.TextDocumentItem.create(
|
|
"file:///test.ml",
|
|
"ocaml",
|
|
0,
|
|
// the empty space below is necessary to reproduce the bug
|
|
outdent`
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
let k = ()
|
|
let m = List.map
|
|
`,
|
|
),
|
|
});
|
|
|
|
// here we see that all is ok
|
|
const hoverOverK = await languageServer.sendRequest("textDocument/hover", {
|
|
textDocument: Types.TextDocumentIdentifier.create("file:///test.ml"),
|
|
position: Types.Position.create(24, 4),
|
|
});
|
|
|
|
expect(hoverOverK).toMatchInlineSnapshot(`
|
|
{
|
|
"contents": {
|
|
"kind": "markdown",
|
|
"value": "\`\`\`ocaml
|
|
unit
|
|
\`\`\`",
|
|
},
|
|
"range": {
|
|
"end": {
|
|
"character": 5,
|
|
"line": 24,
|
|
},
|
|
"start": {
|
|
"character": 4,
|
|
"line": 24,
|
|
},
|
|
},
|
|
}
|
|
`);
|
|
|
|
// we trigger the bug
|
|
const autocompleteForListm = await languageServer.sendRequest(
|
|
"textDocument/hover",
|
|
{
|
|
textDocument: Types.TextDocumentIdentifier.create("file:///test.ml"),
|
|
position: Types.Position.create(25, 15),
|
|
},
|
|
);
|
|
|
|
const buggedHoverOverK = await languageServer.sendRequest(
|
|
"textDocument/hover",
|
|
{
|
|
textDocument: Types.TextDocumentIdentifier.create("file:///test.ml"),
|
|
position: Types.Position.create(24, 4),
|
|
},
|
|
);
|
|
|
|
// now the same hover as before comes with unrelated documentation
|
|
expect(buggedHoverOverK).toMatchInlineSnapshot(`
|
|
{
|
|
"contents": {
|
|
"kind": "markdown",
|
|
"value": "\`\`\`ocaml
|
|
unit
|
|
\`\`\`",
|
|
},
|
|
"range": {
|
|
"end": {
|
|
"character": 5,
|
|
"line": 24,
|
|
},
|
|
"start": {
|
|
"character": 4,
|
|
"line": 24,
|
|
},
|
|
},
|
|
}
|
|
`);
|
|
});
|
|
});
|