linol/thirdparty/lsp/ocaml-lsp-server/test/e2e-new/completion.ml

1319 lines
29 KiB
OCaml

open Test.Import
let iter_completions
?prep
?path
?(triggerCharacter = "")
?(triggerKind = CompletionTriggerKind.Invoked)
~position
=
let makeRequest textDocument =
let context = CompletionContext.create ~triggerCharacter ~triggerKind () in
Lsp.Client_request.TextDocumentCompletion
(CompletionParams.create ~textDocument ~position ~context ())
in
Lsp_helpers.iter_lsp_response ?prep ?path ~makeRequest
;;
let print_completions
?(prep = fun _ -> Fiber.return ())
?(path = "foo.ml")
?(limit = 10)
?(pre_print = fun x -> x)
source
position
=
iter_completions ~prep ~path ~source ~position (function
| None -> print_endline "No completion Items"
| Some completions ->
let items =
match completions with
| `CompletionList comp -> comp.items
| `List comp -> comp
in
items
|> pre_print
|> (function
| [] -> print_endline "No completions"
| items ->
print_endline "Completions:";
let originalLength = List.length items in
items
|> List.take (min limit originalLength)
|> List.iter ~f:(fun item ->
item
|> CompletionItem.yojson_of_t
|> Yojson.Safe.pretty_to_string ~std:false
|> print_endline);
if originalLength > limit then print_endline "............."))
;;
let%expect_test "can start completion at arbitrary position (before the dot)" =
let source = {ocaml|Strin.func|ocaml} in
let position = Position.create ~line:0 ~character:5 in
print_completions source position;
[%expect
{|
Completions:
{
"detail": "",
"kind": 9,
"label": "String",
"sortText": "0000",
"textEdit": {
"newText": "String",
"range": {
"end": { "character": 5, "line": 0 },
"start": { "character": 0, "line": 0 }
}
}
}
{
"detail": "",
"kind": 9,
"label": "StringLabels",
"sortText": "0001",
"textEdit": {
"newText": "StringLabels",
"range": {
"end": { "character": 5, "line": 0 },
"start": { "character": 0, "line": 0 }
}
}
} |}]
;;
let%expect_test "can start completion at arbitrary position" =
let source = {ocaml|StringLabels|ocaml} in
let position = Position.create ~line:0 ~character:6 in
print_completions source position;
[%expect
{|
Completions:
{
"detail": "",
"kind": 9,
"label": "String",
"sortText": "0000",
"textEdit": {
"newText": "String",
"range": {
"end": { "character": 6, "line": 0 },
"start": { "character": 0, "line": 0 }
}
}
}
{
"detail": "",
"kind": 9,
"label": "StringLabels",
"sortText": "0001",
"textEdit": {
"newText": "StringLabels",
"range": {
"end": { "character": 6, "line": 0 },
"start": { "character": 0, "line": 0 }
}
}
} |}]
;;
let%expect_test "can start completion at arbitrary position 2" =
let source = {ocaml|StringLabels|ocaml} in
let position = Position.create ~line:0 ~character:7 in
print_completions source position;
[%expect
{|
Completions:
{
"detail": "",
"kind": 9,
"label": "StringLabels",
"sortText": "0000",
"textEdit": {
"newText": "StringLabels",
"range": {
"end": { "character": 7, "line": 0 },
"start": { "character": 0, "line": 0 }
}
}
} |}]
;;
let%expect_test "can start completion after operator without space" =
let source = {ocaml|[1;2]|>List.ma|ocaml} in
let position = Position.create ~line:0 ~character:14 in
print_completions source position;
[%expect
{|
Completions:
{
"detail": "('a -> 'b) -> 'a list -> 'b list",
"kind": 12,
"label": "map",
"sortText": "0000",
"textEdit": {
"newText": "map",
"range": {
"end": { "character": 14, "line": 0 },
"start": { "character": 12, "line": 0 }
}
}
}
{
"detail": "(int -> 'a -> 'b) -> 'a list -> 'b list",
"kind": 12,
"label": "mapi",
"sortText": "0001",
"textEdit": {
"newText": "mapi",
"range": {
"end": { "character": 14, "line": 0 },
"start": { "character": 12, "line": 0 }
}
}
}
{
"detail": "('a -> 'b -> 'c) -> 'a list -> 'b list -> 'c list",
"kind": 12,
"label": "map2",
"sortText": "0002",
"textEdit": {
"newText": "map2",
"range": {
"end": { "character": 14, "line": 0 },
"start": { "character": 12, "line": 0 }
}
}
} |}]
;;
let%expect_test "can start completion after operator with space" =
let source = {ocaml|[1;2] |> List.ma|ocaml} in
let position = Position.create ~line:0 ~character:16 in
print_completions source position;
[%expect
{|
Completions:
{
"detail": "('a -> 'b) -> 'a list -> 'b list",
"kind": 12,
"label": "map",
"sortText": "0000",
"textEdit": {
"newText": "map",
"range": {
"end": { "character": 16, "line": 0 },
"start": { "character": 14, "line": 0 }
}
}
}
{
"detail": "(int -> 'a -> 'b) -> 'a list -> 'b list",
"kind": 12,
"label": "mapi",
"sortText": "0001",
"textEdit": {
"newText": "mapi",
"range": {
"end": { "character": 16, "line": 0 },
"start": { "character": 14, "line": 0 }
}
}
}
{
"detail": "('a -> 'b -> 'c) -> 'a list -> 'b list -> 'c list",
"kind": 12,
"label": "map2",
"sortText": "0002",
"textEdit": {
"newText": "map2",
"range": {
"end": { "character": 16, "line": 0 },
"start": { "character": 14, "line": 0 }
}
}
}
|}]
;;
let%expect_test "can start completion in dot chain with tab" =
let source = {ocaml|[1;2] |> List. ma|ocaml} in
let position = Position.create ~line:0 ~character:17 in
print_completions source position;
[%expect
{|
Completions:
{
"detail": "('a -> 'b) -> 'a list -> 'b list",
"kind": 12,
"label": "map",
"sortText": "0000",
"textEdit": {
"newText": "map",
"range": {
"end": { "character": 17, "line": 0 },
"start": { "character": 15, "line": 0 }
}
}
}
{
"detail": "(int -> 'a -> 'b) -> 'a list -> 'b list",
"kind": 12,
"label": "mapi",
"sortText": "0001",
"textEdit": {
"newText": "mapi",
"range": {
"end": { "character": 17, "line": 0 },
"start": { "character": 15, "line": 0 }
}
}
}
{
"detail": "('a -> 'b -> 'c) -> 'a list -> 'b list -> 'c list",
"kind": 12,
"label": "map2",
"sortText": "0002",
"textEdit": {
"newText": "map2",
"range": {
"end": { "character": 17, "line": 0 },
"start": { "character": 15, "line": 0 }
}
}
}
|}]
;;
let%expect_test "can start completion in dot chain with newline" =
let source =
{ocaml|[1;2] |> List.
ma|ocaml}
in
let position = Position.create ~line:1 ~character:2 in
print_completions source position;
[%expect
{|
Completions:
{
"detail": "('a -> 'b) -> 'a list -> 'b list",
"kind": 12,
"label": "map",
"sortText": "0000",
"textEdit": {
"newText": "map",
"range": {
"end": { "character": 2, "line": 1 },
"start": { "character": 0, "line": 1 }
}
}
}
{
"detail": "(int -> 'a -> 'b) -> 'a list -> 'b list",
"kind": 12,
"label": "mapi",
"sortText": "0001",
"textEdit": {
"newText": "mapi",
"range": {
"end": { "character": 2, "line": 1 },
"start": { "character": 0, "line": 1 }
}
}
}
{
"detail": "('a -> 'b -> 'c) -> 'a list -> 'b list -> 'c list",
"kind": 12,
"label": "map2",
"sortText": "0002",
"textEdit": {
"newText": "map2",
"range": {
"end": { "character": 2, "line": 1 },
"start": { "character": 0, "line": 1 }
}
}
}
|}]
;;
let%expect_test "can start completion in dot chain with space" =
let source = {ocaml|[1;2] |> List. ma|ocaml} in
let position = Position.create ~line:0 ~character:17 in
print_completions source position;
[%expect
{|
Completions:
{
"detail": "('a -> 'b) -> 'a list -> 'b list",
"kind": 12,
"label": "map",
"sortText": "0000",
"textEdit": {
"newText": "map",
"range": {
"end": { "character": 17, "line": 0 },
"start": { "character": 15, "line": 0 }
}
}
}
{
"detail": "(int -> 'a -> 'b) -> 'a list -> 'b list",
"kind": 12,
"label": "mapi",
"sortText": "0001",
"textEdit": {
"newText": "mapi",
"range": {
"end": { "character": 17, "line": 0 },
"start": { "character": 15, "line": 0 }
}
}
}
{
"detail": "('a -> 'b -> 'c) -> 'a list -> 'b list -> 'c list",
"kind": 12,
"label": "map2",
"sortText": "0002",
"textEdit": {
"newText": "map2",
"range": {
"end": { "character": 17, "line": 0 },
"start": { "character": 15, "line": 0 }
}
}
}
|}]
;;
let%expect_test "can start completion after dereference" =
let source =
{ocaml|let apple=ref 10 in
!ap|ocaml}
in
let position = Position.create ~line:1 ~character:3 in
print_completions source position;
[%expect
{|
Completions:
{
"detail": "int ref",
"kind": 12,
"label": "apple",
"sortText": "0000",
"textEdit": {
"newText": "apple",
"range": {
"end": { "character": 3, "line": 1 },
"start": { "character": 1, "line": 1 }
}
}
}
|}]
;;
let%expect_test "can complete symbol passed as a named argument" =
let source =
{ocaml|let g ~f = f 0 in
g ~f:ig|ocaml}
in
let position = Position.create ~line:1 ~character:7 in
print_completions source position;
[%expect
{|
Completions:
{
"detail": "'a -> unit",
"kind": 12,
"label": "ignore",
"sortText": "0000",
"textEdit": {
"newText": "ignore",
"range": {
"end": { "character": 7, "line": 1 },
"start": { "character": 5, "line": 1 }
}
}
}
|}]
;;
let%expect_test "can complete symbol passed as a named argument - 2" =
let source =
{ocaml|module M = struct let igfoo _x = () end
let g ~f = f 0 in
g ~f:M.ig|ocaml}
in
let position = Position.create ~line:2 ~character:9 in
print_completions source position;
[%expect
{|
Completions:
{
"detail": "'a -> unit",
"kind": 12,
"label": "igfoo",
"sortText": "0000",
"textEdit": {
"newText": "igfoo",
"range": {
"end": { "character": 9, "line": 2 },
"start": { "character": 7, "line": 2 }
}
}
}
|}]
;;
let%expect_test "can complete symbol passed as an optional argument" =
let source =
{ocaml|
let g ?f = f in
g ?f:ig
|ocaml}
in
let position = Position.create ~line:2 ~character:7 in
print_completions source position;
[%expect
{|
Completions:
{
"detail": "'a -> unit",
"kind": 12,
"label": "ignore",
"sortText": "0000",
"textEdit": {
"newText": "ignore",
"range": {
"end": { "character": 7, "line": 2 },
"start": { "character": 5, "line": 2 }
}
}
}
|}]
;;
let%expect_test "can complete symbol passed as an optional argument - 2" =
let source =
{ocaml|module M = struct let igfoo _x = () end
let g ?f = f in
g ?f:M.ig|ocaml}
in
let position = Position.create ~line:2 ~character:9 in
print_completions source position;
[%expect
{|
Completions:
{
"detail": "'a -> unit",
"kind": 12,
"label": "igfoo",
"sortText": "0000",
"textEdit": {
"newText": "igfoo",
"range": {
"end": { "character": 9, "line": 2 },
"start": { "character": 7, "line": 2 }
}
}
}
|}]
;;
let%expect_test "completes identifier after completion-triggering character" =
let source =
{ocaml|
module Test = struct
let somenum = 42
let somestring = "hello"
end
let x = Test.
|ocaml}
in
let position = Position.create ~line:6 ~character:13 in
print_completions source position;
[%expect
{|
Completions:
{
"detail": "int",
"kind": 12,
"label": "somenum",
"sortText": "0000",
"textEdit": {
"newText": "somenum",
"range": {
"end": { "character": 13, "line": 6 },
"start": { "character": 13, "line": 6 }
}
}
}
{
"detail": "string",
"kind": 12,
"label": "somestring",
"sortText": "0001",
"textEdit": {
"newText": "somestring",
"range": {
"end": { "character": 13, "line": 6 },
"start": { "character": 13, "line": 6 }
}
}
}
|}]
;;
let%expect_test "completes infix operators" =
let source =
{ocaml|
let (>>|) = (+)
let y = 1 >
|ocaml}
in
let position = Position.create ~line:2 ~character:11 in
print_completions source position;
[%expect
{|
Completions:
{
"detail": "int -> int -> int",
"kind": 12,
"label": ">>|",
"sortText": "0000",
"textEdit": {
"newText": ">>|",
"range": {
"end": { "character": 11, "line": 2 },
"start": { "character": 10, "line": 2 }
}
}
}
{
"detail": "'a -> 'a -> bool",
"kind": 12,
"label": ">",
"sortText": "0001",
"textEdit": {
"newText": ">",
"range": {
"end": { "character": 11, "line": 2 },
"start": { "character": 10, "line": 2 }
}
}
}
{
"detail": "'a -> 'a -> bool",
"kind": 12,
"label": ">=",
"sortText": "0002",
"textEdit": {
"newText": ">=",
"range": {
"end": { "character": 11, "line": 2 },
"start": { "character": 10, "line": 2 }
}
}
}
|}]
;;
let%expect_test "completes without prefix" =
let source =
{ocaml|
let somenum = 42
let somestring = "hello"
let plus_42 (x:int) (y:int) =
somenum +
|ocaml}
in
let position = Position.create ~line:5 ~character:12 in
print_completions source position;
[%expect
{|
Completions:
{
"detail": "int -> int -> int",
"kind": 12,
"label": "+",
"sortText": "0000",
"textEdit": {
"newText": "+",
"range": {
"end": { "character": 12, "line": 5 },
"start": { "character": 11, "line": 5 }
}
}
}
{
"detail": "float -> float -> float",
"kind": 12,
"label": "+.",
"sortText": "0001",
"textEdit": {
"newText": "+.",
"range": {
"end": { "character": 12, "line": 5 },
"start": { "character": 11, "line": 5 }
}
}
}
|}]
;;
let%expect_test "completes labels" =
let source = {ocaml|let f = ListLabels.map ~|ocaml} in
let position = Position.create ~line:0 ~character:24 in
print_completions source position;
[%expect
{|
Completions:
{
"detail": "int -> int",
"kind": 12,
"label": "~+",
"sortText": "0000",
"textEdit": {
"newText": "~+",
"range": {
"end": { "character": 24, "line": 0 },
"start": { "character": 23, "line": 0 }
}
}
}
{
"detail": "float -> float",
"kind": 12,
"label": "~+.",
"sortText": "0001",
"textEdit": {
"newText": "~+.",
"range": {
"end": { "character": 24, "line": 0 },
"start": { "character": 23, "line": 0 }
}
}
}
{
"detail": "int -> int",
"kind": 12,
"label": "~-",
"sortText": "0002",
"textEdit": {
"newText": "~-",
"range": {
"end": { "character": 24, "line": 0 },
"start": { "character": 23, "line": 0 }
}
}
}
{
"detail": "float -> float",
"kind": 12,
"label": "~-.",
"sortText": "0003",
"textEdit": {
"newText": "~-.",
"range": {
"end": { "character": 24, "line": 0 },
"start": { "character": 23, "line": 0 }
}
}
}
{
"detail": "'a -> 'b",
"kind": 5,
"label": "~f",
"sortText": "0004",
"textEdit": {
"newText": "~f",
"range": {
"end": { "character": 24, "line": 0 },
"start": { "character": 23, "line": 0 }
}
}
}
|}]
;;
let%expect_test "works for polymorphic variants - function application context - 1" =
let source =
{ocaml|
let f (_a: [`String | `Int of int]) = ()
let u = f `Str
|ocaml}
in
let position = Position.create ~line:3 ~character:14 in
print_completions source position;
[%expect
{|
Completions:
{
"detail": "`String",
"kind": 20,
"label": "`String",
"sortText": "0000",
"textEdit": {
"newText": "`String",
"range": {
"end": { "character": 14, "line": 3 },
"start": { "character": 10, "line": 3 }
}
}
}
|}]
;;
let%expect_test "works for polymorphic variants - function application context - 2" =
let source =
{ocaml|
let f (_a: [`String | `Int of int]) = ()
let u = f `In
|ocaml}
in
let position = Position.create ~line:3 ~character:13 in
print_completions source position;
[%expect
{|
Completions:
{
"detail": "`Int of int",
"kind": 20,
"label": "`Int",
"sortText": "0000",
"textEdit": {
"newText": "`Int",
"range": {
"end": { "character": 13, "line": 3 },
"start": { "character": 10, "line": 3 }
}
}
}
|}]
;;
let%expect_test "works for polymorphic variants" =
let source =
{ocaml|
type t = [ `Int | `String ]
let x : t = `I
|ocaml}
in
let position = Position.create ~line:3 ~character:15 in
print_completions source position;
[%expect
{|
Completions:
{
"detail": "`Int",
"kind": 20,
"label": "`Int",
"sortText": "0000",
"textEdit": {
"newText": "`Int",
"range": {
"end": { "character": 15, "line": 3 },
"start": { "character": 13, "line": 3 }
}
}
}
|}]
;;
let%expect_test "completion for holes" =
let source = {ocaml|let u : int = _|ocaml} in
let position = Position.create ~line:0 ~character:15 in
let filter =
List.filter ~f:(fun (item : CompletionItem.t) ->
not (String.starts_with ~prefix:"__" item.label))
in
print_completions ~pre_print:filter source position;
[%expect
{|
Completions:
{
"filterText": "_0",
"kind": 1,
"label": "0",
"sortText": "0000",
"textEdit": {
"newText": "0",
"range": {
"end": { "character": 15, "line": 0 },
"start": { "character": 14, "line": 0 }
}
}
}
|}]
;;
let%expect_test "completes identifier at top level" =
let source =
{ocaml|
let somenum = 42
let somestring = "hello"
let () =
some
|ocaml}
in
let position = Position.create ~line:5 ~character:6 in
print_completions source position;
[%expect
{|
Completions:
{
"detail": "int",
"kind": 12,
"label": "somenum",
"sortText": "0000",
"textEdit": {
"newText": "somenum",
"range": {
"end": { "character": 6, "line": 5 },
"start": { "character": 2, "line": 5 }
}
}
}
{
"detail": "string",
"kind": 12,
"label": "somestring",
"sortText": "0001",
"textEdit": {
"newText": "somestring",
"range": {
"end": { "character": 6, "line": 5 },
"start": { "character": 2, "line": 5 }
}
}
}
|}]
;;
let%expect_test "completes from a module" =
let source = {ocaml|let f = List.m|ocaml} in
let position = Position.create ~line:0 ~character:14 in
print_completions source position;
[%expect
{|
Completions:
{
"detail": "('a -> 'b) -> 'a list -> 'b list",
"kind": 12,
"label": "map",
"sortText": "0000",
"textEdit": {
"newText": "map",
"range": {
"end": { "character": 14, "line": 0 },
"start": { "character": 13, "line": 0 }
}
}
}
{
"detail": "('a -> 'b -> 'c) -> 'a list -> 'b list -> 'c list",
"kind": 12,
"label": "map2",
"sortText": "0001",
"textEdit": {
"newText": "map2",
"range": {
"end": { "character": 14, "line": 0 },
"start": { "character": 13, "line": 0 }
}
}
}
{
"detail": "(int -> 'a -> 'b) -> 'a list -> 'b list",
"kind": 12,
"label": "mapi",
"sortText": "0002",
"textEdit": {
"newText": "mapi",
"range": {
"end": { "character": 14, "line": 0 },
"start": { "character": 13, "line": 0 }
}
}
}
{
"detail": "'a -> 'a list -> bool",
"kind": 12,
"label": "mem",
"sortText": "0003",
"textEdit": {
"newText": "mem",
"range": {
"end": { "character": 14, "line": 0 },
"start": { "character": 13, "line": 0 }
}
}
}
{
"detail": "'a -> ('a * 'b) list -> bool",
"kind": 12,
"label": "mem_assoc",
"sortText": "0004",
"textEdit": {
"newText": "mem_assoc",
"range": {
"end": { "character": 14, "line": 0 },
"start": { "character": 13, "line": 0 }
}
}
}
{
"detail": "'a -> ('a * 'b) list -> bool",
"kind": 12,
"label": "mem_assq",
"sortText": "0005",
"textEdit": {
"newText": "mem_assq",
"range": {
"end": { "character": 14, "line": 0 },
"start": { "character": 13, "line": 0 }
}
}
}
{
"detail": "'a -> 'a list -> bool",
"kind": 12,
"label": "memq",
"sortText": "0006",
"textEdit": {
"newText": "memq",
"range": {
"end": { "character": 14, "line": 0 },
"start": { "character": 13, "line": 0 }
}
}
}
{
"detail": "('a -> 'a -> int) -> 'a list -> 'a list -> 'a list",
"kind": 12,
"label": "merge",
"sortText": "0007",
"textEdit": {
"newText": "merge",
"range": {
"end": { "character": 14, "line": 0 },
"start": { "character": 13, "line": 0 }
}
}
}|}]
;;
let%expect_test "completes a module name" =
let source = {ocaml|let f = L|ocaml} in
let position = Position.create ~line:0 ~character:9 in
print_completions ~pre_print:(List.take 5) source position;
[%expect
{|
Completions:
{
"detail": "",
"kind": 9,
"label": "LargeFile",
"sortText": "0000",
"textEdit": {
"newText": "LargeFile",
"range": {
"end": { "character": 9, "line": 0 },
"start": { "character": 8, "line": 0 }
}
}
}
{
"detail": "",
"kind": 9,
"label": "Lazy",
"sortText": "0001",
"textEdit": {
"newText": "Lazy",
"range": {
"end": { "character": 9, "line": 0 },
"start": { "character": 8, "line": 0 }
}
}
}
{
"detail": "",
"kind": 9,
"label": "Lexing",
"sortText": "0002",
"textEdit": {
"newText": "Lexing",
"range": {
"end": { "character": 9, "line": 0 },
"start": { "character": 8, "line": 0 }
}
}
}
{
"detail": "",
"kind": 9,
"label": "List",
"sortText": "0003",
"textEdit": {
"newText": "List",
"range": {
"end": { "character": 9, "line": 0 },
"start": { "character": 8, "line": 0 }
}
}
}
{
"detail": "",
"kind": 9,
"label": "ListLabels",
"sortText": "0004",
"textEdit": {
"newText": "ListLabels",
"range": {
"end": { "character": 9, "line": 0 },
"start": { "character": 8, "line": 0 }
}
}
}
|}]
;;
let%expect_test "completion doesn't autocomplete record fields" =
let source =
{ocaml|
type r = {
x: int;
y: string
}
let _ =
|ocaml}
in
let position = Position.create ~line:5 ~character:8 in
print_completions
~pre_print:
(List.filter ~f:(fun (compl : CompletionItem.t) ->
compl.label = "x" || compl.label = "y"))
source
position;
(* We expect 0 completions*)
[%expect {| No completions |}]
;;
let%expect_test "completion for `in` keyword - no prefix" =
let source =
{ocaml|
let foo param1 =
let bar = param1 |ocaml}
in
let position = Position.create ~line:2 ~character:19 in
print_completions ~limit:3 source position;
[%expect
{|
Completions:
{
"kind": 14,
"label": "in",
"textEdit": {
"newText": "in",
"range": {
"end": { "character": 19, "line": 2 },
"start": { "character": 19, "line": 2 }
}
}
}
{
"detail": "'a -> 'b",
"kind": 12,
"label": "param1",
"sortText": "0000",
"textEdit": {
"newText": "param1",
"range": {
"end": { "character": 19, "line": 2 },
"start": { "character": 19, "line": 2 }
}
}
}
{
"detail": "'a ref -> 'a",
"kind": 12,
"label": "!",
"sortText": "0001",
"textEdit": {
"newText": "!",
"range": {
"end": { "character": 19, "line": 2 },
"start": { "character": 19, "line": 2 }
}
}
}
............. |}]
;;
let%expect_test "completion for `in` keyword - prefix i" =
let source =
{ocaml|
let foo param1 =
let bar = param1 i
|ocaml}
in
let position = Position.create ~line:2 ~character:20 in
print_completions ~limit:3 source position;
[%expect
{|
Completions:
{
"kind": 14,
"label": "in",
"textEdit": {
"newText": "in",
"range": {
"end": { "character": 20, "line": 2 },
"start": { "character": 19, "line": 2 }
}
}
}
{
"detail": "'a -> unit",
"kind": 12,
"label": "ignore",
"sortText": "0000",
"textEdit": {
"newText": "ignore",
"range": {
"end": { "character": 20, "line": 2 },
"start": { "character": 19, "line": 2 }
}
}
}
{
"detail": "in_channel -> int",
"kind": 12,
"label": "in_channel_length",
"sortText": "0001",
"textEdit": {
"newText": "in_channel_length",
"range": {
"end": { "character": 20, "line": 2 },
"start": { "character": 19, "line": 2 }
}
}
}
............. |}]
;;
let%expect_test "completion for `in` keyword - prefix in" =
let source =
{ocaml|
let foo param1 =
let bar = param1 in
|ocaml}
in
let position = Position.create ~line:2 ~character:21 in
print_completions ~limit:3 source position;
[%expect
{|
Completions:
{
"kind": 14,
"label": "in",
"textEdit": {
"newText": "in",
"range": {
"end": { "character": 21, "line": 2 },
"start": { "character": 19, "line": 2 }
}
}
}
{
"detail": "in_channel -> int",
"kind": 12,
"label": "in_channel_length",
"sortText": "0000",
"textEdit": {
"newText": "in_channel_length",
"range": {
"end": { "character": 21, "line": 2 },
"start": { "character": 19, "line": 2 }
}
}
}
{
"detail": "int ref -> unit",
"kind": 12,
"label": "incr",
"sortText": "0001",
"textEdit": {
"newText": "incr",
"range": {
"end": { "character": 21, "line": 2 },
"start": { "character": 19, "line": 2 }
}
}
}
............. |}]
;;
(* Test case was taken from issue #1358 *)
let%expect_test "completion for object methods" =
let source = {ocaml|let f (x : < a_method : 'a >) = x#|ocaml} in
let position = Position.create ~line:0 ~character:34 in
print_completions ~limit:3 source position;
[%expect
{|
Completions:
{
"kind": 14,
"label": "in",
"textEdit": {
"newText": "in",
"range": {
"end": { "character": 34, "line": 0 },
"start": { "character": 34, "line": 0 }
}
}
}
{
"detail": "'a",
"kind": 2,
"label": "a_method",
"sortText": "0000",
"textEdit": {
"newText": "a_method",
"range": {
"end": { "character": 34, "line": 0 },
"start": { "character": 34, "line": 0 }
}
}
} |}]
;;
let%expect_test "completion for object methods" =
let source = {ocaml|let f (x : < a_method : 'a; ab_m : 'b >) = x#ab|ocaml} in
let position = Position.create ~line:0 ~character:49 in
print_completions ~limit:3 source position;
[%expect
{|
Completions:
{
"detail": "'b",
"kind": 2,
"label": "ab_m",
"sortText": "0000",
"textEdit": {
"newText": "ab_m",
"range": {
"end": { "character": 49, "line": 0 },
"start": { "character": 47, "line": 0 }
}
}
} |}]
;;