diff --git a/CHANGES.md b/CHANGES.md index 8dc3f5588..8059e5c2d 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,8 +1,12 @@ # Unreleased +## Fixes + - Fix debouncing of document updates. It was essentially complicated broken in all but the most trivial cases. (#509 fixes #504) +- Fix completion when passing named and functional arguments (#512) + # 1.8.2 (09/14/2021) - Disable experimental dune support. It was accidentally left enabled. diff --git a/ocaml-lsp-server/src/compl.ml b/ocaml-lsp-server/src/compl.ml index e106803e4..20f1cc150 100644 --- a/ocaml-lsp-server/src/compl.ml +++ b/ocaml-lsp-server/src/compl.ml @@ -27,6 +27,7 @@ let completion_kind kind : CompletionItemKind.t option = | `MethodCall -> Some Method | `Keyword -> Some Keyword +(** @see https://ocaml.org/manual/lex.html reference *) let prefix_of_position ~short_path source position = match Msource.text source with | "" -> "" @@ -87,7 +88,18 @@ let prefix_of_position ~short_path source position = | Some pos -> pos + 1 in let len = from - pos + 1 in - String.sub text ~pos ~len + let reconstructed_prefix = String.sub text ~pos ~len in + (* if we reconstructed [~f:ignore] or [?f:ignore], we should take only + [ignore], so: *) + if + String.is_prefix reconstructed_prefix ~prefix:"~" + || String.is_prefix reconstructed_prefix ~prefix:"?" + then + match String.lsplit2 reconstructed_prefix ~on:':' with + | Some (_, s) -> s + | None -> reconstructed_prefix + else + reconstructed_prefix let suffix_of_position source position = match Msource.text source with diff --git a/ocaml-lsp-server/test/e2e/__tests__/textDocument-completion.test.ts b/ocaml-lsp-server/test/e2e/__tests__/textDocument-completion.test.ts index 2a69eda65..ff3883fd3 100644 --- a/ocaml-lsp-server/test/e2e/__tests__/textDocument-completion.test.ts +++ b/ocaml-lsp-server/test/e2e/__tests__/textDocument-completion.test.ts @@ -77,6 +77,124 @@ describe_opt("textDocument/completion", () => { expect(items).toMatchObject([{ label: "StringLabels" }]); }); + it("can complete symbol passed as a named argument", async () => { + await openDocument(outdent` +let g ~f = f 0 in +g ~f:ig + `); + + let items = await queryCompletion(Types.Position.create(1, 7)); + expect(items).toMatchInlineSnapshot(` + Array [ + Object { + "label": "ignore", + "textEdit": Object { + "newText": "ignore", + "range": Object { + "end": Object { + "character": 7, + "line": 1, + }, + "start": Object { + "character": 5, + "line": 1, + }, + }, + }, + }, + ] + `); + }); + + it("can complete symbol passed as a named argument - 2", async () => { + await openDocument(outdent` +module M = struct let igfoo _x = () end +let g ~f = f 0 in +g ~f:M.ig + `); + + let items = await queryCompletion(Types.Position.create(2, 9)); + expect(items).toMatchInlineSnapshot(` + Array [ + Object { + "label": "igfoo", + "textEdit": Object { + "newText": "igfoo", + "range": Object { + "end": Object { + "character": 9, + "line": 2, + }, + "start": Object { + "character": 7, + "line": 2, + }, + }, + }, + }, + ] + `); + }); + + it("can complete symbol passed as an optional argument", async () => { + await openDocument(outdent` +let g ?f = f in +g ?f:ig + `); + + let items = await queryCompletion(Types.Position.create(1, 7)); + expect(items).toMatchInlineSnapshot(` + Array [ + Object { + "label": "ignore", + "textEdit": Object { + "newText": "ignore", + "range": Object { + "end": Object { + "character": 7, + "line": 1, + }, + "start": Object { + "character": 5, + "line": 1, + }, + }, + }, + }, + ] + `); + }); + + it("can complete symbol passed as a optional argument - 2", async () => { + await openDocument(outdent` +module M = struct let igfoo _x = () end +let g ?f = f in +g ?f:M.ig + `); + + let items = await queryCompletion(Types.Position.create(2, 9)); + expect(items).toMatchInlineSnapshot(` + Array [ + Object { + "label": "igfoo", + "textEdit": Object { + "newText": "igfoo", + "range": Object { + "end": Object { + "character": 9, + "line": 2, + }, + "start": Object { + "character": 7, + "line": 2, + }, + }, + }, + }, + ] + `); + }); + it("completes identifier at top level", async () => { await openDocument(outdent` let somenum = 42