From 0ae0cafca51e9c07114e4534b0664638da35253a Mon Sep 17 00:00:00 2001 From: PizieDust Date: Wed, 3 Jan 2024 17:38:51 +0100 Subject: [PATCH] let merlin work only if syntax doc is activated lint change from italics to code pill Completion for `in` keyword (#1217) feat(auto-completion): auto-complete `in` (in a hacky way) in .ml files Prepare release 1.17.0 (#1219) * chore: restore lost version number in changes (erased in c8c10966133cbbe88e6f23e5fd7bad3e125c198f) * chore: bump version number in changes before release Add flag to disable dune diagnostics (#1221) * add config to control dune diagnostics lint update changelog --- CHANGES.md | 16 ++ ocaml-lsp-server/docs/ocamllsp/config.md | 7 + ocaml-lsp-server/src/compl.ml | 26 ++- ocaml-lsp-server/src/config_data.ml | 187 ++++++++++++-------- ocaml-lsp-server/src/configuration.ml | 5 + ocaml-lsp-server/src/configuration.mli | 2 + ocaml-lsp-server/src/diagnostics.ml | 27 ++- ocaml-lsp-server/src/diagnostics.mli | 4 + ocaml-lsp-server/src/document.ml | 13 +- ocaml-lsp-server/src/env_vars.ml | 8 +- ocaml-lsp-server/src/hover_req.ml | 2 +- ocaml-lsp-server/src/ocaml_lsp_server.ml | 14 +- ocaml-lsp-server/test/e2e-new/completion.ml | 170 ++++++++++++++++-- 13 files changed, 375 insertions(+), 106 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 0b333f831..10114221f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,17 @@ # Unreleased +## Features + +- Introduce a configuration option to control dune diagnostics. The option is + called `duneDiganostics` and it may be set to `{ enable: false }` to disable + diagnostics. (#1221) + +- Includes a new optional/configurable option to toggle syntax documentation. If + toggled on, allows display of sytax documentation on hover tooltips. Can be + controlled via environment variables and by GUI for VS code. (#1218) + +# 1.17.0 + ## Fixes - Fix missing super & subscripts in markdown documentation. (#1170) @@ -28,6 +40,8 @@ match cases, rec, and constructors (#1141) - Add inlay hints for types on let bindings (#1159) +- Offer auto-completion for the keyword `in` (#1217) + # 1.16.2 ## Fixes @@ -106,6 +120,8 @@ [#941](https://github.com/ocaml/ocaml-lsp/issues/941), [#1003](https://github.com/ocaml/ocaml-lsp/issues/1003)) +# 1.15.0 + ## Features - Enable [semantic highlighting](https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_semanticTokens) diff --git a/ocaml-lsp-server/docs/ocamllsp/config.md b/ocaml-lsp-server/docs/ocamllsp/config.md index cebd291fa..e857874ae 100644 --- a/ocaml-lsp-server/docs/ocamllsp/config.md +++ b/ocaml-lsp-server/docs/ocamllsp/config.md @@ -22,6 +22,13 @@ interface config { */ codelens: { enable : boolean } + /** + * Enable/Disable Dune diagnostics + * @default true + * @since 1.18 + */ + duneDiagnostics: { enable : boolean } + /** * Enable/Disable Syntax Documentation * @default false diff --git a/ocaml-lsp-server/src/compl.ml b/ocaml-lsp-server/src/compl.ml index f0cf6e5bb..edc3b436f 100644 --- a/ocaml-lsp-server/src/compl.ml +++ b/ocaml-lsp-server/src/compl.ml @@ -158,6 +158,23 @@ module Complete_by_prefix = struct completion_entries ~f:(completionItem_of_completion_entry ~deprecated ~range ~compl_params) + let complete_keywords completion_position prefix = + match prefix with + | "" | "i" | "in" -> + let ci_for_in = + CompletionItem.create + ~label:"in" + ~textEdit: + (`TextEdit + (TextEdit.create + ~newText:"in" + ~range:(range_prefix completion_position prefix))) + ~kind:CompletionItemKind.Keyword + () + in + [ ci_for_in ] + | _ -> [] + let complete doc prefix pos ~deprecated ~resolve = let+ (completion : Query_protocol.completions) = let logical_pos = Position.logical pos in @@ -166,7 +183,14 @@ module Complete_by_prefix = struct doc (dispatch_cmd ~prefix logical_pos) in - process_dispatch_resp ~deprecated ~resolve doc pos completion + let keyword_completionItems = + (* we complete only keyword 'in' for now *) + match Document.Merlin.kind doc with + | Intf -> [] + | Impl -> complete_keywords pos prefix + in + keyword_completionItems + @ process_dispatch_resp ~deprecated ~resolve doc pos completion end module Complete_with_construct = struct diff --git a/ocaml-lsp-server/src/config_data.ml b/ocaml-lsp-server/src/config_data.ml index d36092a78..45a7f2546 100644 --- a/ocaml-lsp-server/src/config_data.ml +++ b/ocaml-lsp-server/src/config_data.ml @@ -365,23 +365,95 @@ module SyntaxDocumentation = struct | Ppx_yojson_conv_lib.Option.None -> false | Ppx_yojson_conv_lib.Option.Some v -> v) })) - | _ as yojson -> - Ppx_yojson_conv_lib.Yojson_conv_error.record_list_instead_atom - _tp_loc - yojson + | _ as yojson -> + Ppx_yojson_conv_lib.Yojson_conv_error.record_list_instead_atom + _tp_loc + yojson : Ppx_yojson_conv_lib.Yojson.Safe.t -> t) - let _ = t_of_yojson + let _ = t_of_yojson let yojson_of_t = (function - | { enable = v_enable } -> - let bnds : (string * Ppx_yojson_conv_lib.Yojson.Safe.t) list = [] in - let bnds = - let arg = yojson_of_bool v_enable in - ("enable", arg) :: bnds - in - `Assoc bnds + | { enable = v_enable } -> + let bnds : (string * Ppx_yojson_conv_lib.Yojson.Safe.t) list = [] in + let bnds = + let arg = yojson_of_bool v_enable in + ("enable", arg) :: bnds + in + `Assoc bnds + : t -> Ppx_yojson_conv_lib.Yojson.Safe.t) + + let _ = yojson_of_t + + [@@@end] +end + +module DuneDiagnostics = struct + type t = { enable : bool [@default true] } + [@@deriving_inline yojson] [@@yojson.allow_extra_fields] + + let _ = fun (_ : t) -> () + + let t_of_yojson = + (let _tp_loc = "ocaml-lsp-server/src/config_data.ml.DuneDiagnostics.t" in + function + | `Assoc field_yojsons as yojson -> ( + let enable_field = ref Ppx_yojson_conv_lib.Option.None + and duplicates = ref [] + and extra = ref [] in + let rec iter = function + | (field_name, _field_yojson) :: tail -> + (match field_name with + | "enable" -> ( + match Ppx_yojson_conv_lib.( ! ) enable_field with + | Ppx_yojson_conv_lib.Option.None -> + let fvalue = bool_of_yojson _field_yojson in + enable_field := Ppx_yojson_conv_lib.Option.Some fvalue + | Ppx_yojson_conv_lib.Option.Some _ -> + duplicates := field_name :: Ppx_yojson_conv_lib.( ! ) duplicates) + | _ -> ()); + iter tail + | [] -> () + in + iter field_yojsons; + match Ppx_yojson_conv_lib.( ! ) duplicates with + | _ :: _ -> + Ppx_yojson_conv_lib.Yojson_conv_error.record_duplicate_fields + _tp_loc + (Ppx_yojson_conv_lib.( ! ) duplicates) + yojson + | [] -> ( + match Ppx_yojson_conv_lib.( ! ) extra with + | _ :: _ -> + Ppx_yojson_conv_lib.Yojson_conv_error.record_extra_fields + _tp_loc + (Ppx_yojson_conv_lib.( ! ) extra) + yojson + | [] -> + let enable_value = Ppx_yojson_conv_lib.( ! ) enable_field in + { enable = + (match enable_value with + | Ppx_yojson_conv_lib.Option.None -> true + | Ppx_yojson_conv_lib.Option.Some v -> v) + })) + | _ as yojson -> + Ppx_yojson_conv_lib.Yojson_conv_error.record_list_instead_atom + _tp_loc + yojson + : Ppx_yojson_conv_lib.Yojson.Safe.t -> t) + + let _ = t_of_yojson + + let yojson_of_t = + (function + | { enable = v_enable } -> + let bnds : (string * Ppx_yojson_conv_lib.Yojson.Safe.t) list = [] in + let bnds = + let arg = yojson_of_bool v_enable in + ("enable", arg) :: bnds + in + `Assoc bnds : t -> Ppx_yojson_conv_lib.Yojson.Safe.t) let _ = yojson_of_t @@ -395,9 +467,10 @@ module SyntaxDocumentation = struct let _ = fun (_ : t) -> () - let t_of_yojson = + let t_of_yojson = (let _tp_loc = "ocaml-lsp-server/src/config_data.ml.SyntaxDocumentation.t" + in function | `Assoc field_yojsons as yojson -> ( @@ -485,6 +558,7 @@ let t_of_yojson = | `Assoc field_yojsons as yojson -> ( let codelens_field = ref Ppx_yojson_conv_lib.Option.None and extended_hover_field = ref Ppx_yojson_conv_lib.Option.None + and dune_diagnostics_field = ref Ppx_yojson_conv_lib.Option.None and syntax_documentation_field = ref Ppx_yojson_conv_lib.Option.None and inlay_hints_field = ref Ppx_yojson_conv_lib.Option.None and duplicates = ref [] @@ -512,28 +586,6 @@ let t_of_yojson = extended_hover_field := Ppx_yojson_conv_lib.Option.Some fvalue | Ppx_yojson_conv_lib.Option.Some _ -> duplicates := field_name :: Ppx_yojson_conv_lib.( ! ) duplicates) - | "syntaxDocumentation" -> ( - match Ppx_yojson_conv_lib.( ! ) syntax_documentation_field with - | Ppx_yojson_conv_lib.Option.None -> - let fvalue = - Json.Nullable_option.t_of_yojson - SyntaxDocumentation.t_of_yojson - _field_yojson - in - syntax_documentation_field := Ppx_yojson_conv_lib.Option.Some fvalue - | Ppx_yojson_conv_lib.Option.Some _ -> - duplicates := field_name :: Ppx_yojson_conv_lib.( ! ) duplicates) - | "inlayHints" -> ( - match Ppx_yojson_conv_lib.( ! ) inlay_hints_field with - | Ppx_yojson_conv_lib.Option.None -> - let fvalue = - Json.Nullable_option.t_of_yojson - InlayHints.t_of_yojson - _field_yojson - in - inlay_hints_field := Ppx_yojson_conv_lib.Option.Some fvalue - | Ppx_yojson_conv_lib.Option.Some _ -> - duplicates := field_name :: Ppx_yojson_conv_lib.( ! ) duplicates) | "duneDiagnostics" -> ( match Ppx_yojson_conv_lib.( ! ) dune_diagnostics_field with | Ppx_yojson_conv_lib.Option.None -> @@ -557,7 +609,18 @@ let t_of_yojson = Ppx_yojson_conv_lib.Option.Some fvalue | Ppx_yojson_conv_lib.Option.Some _ -> duplicates := field_name :: Ppx_yojson_conv_lib.( ! ) duplicates) - | _ -> ()); + | "duneDiagnostics" -> ( + match Ppx_yojson_conv_lib.( ! ) dune_diagnostics_field with + | Ppx_yojson_conv_lib.Option.None -> + let fvalue = + Json.Nullable_option.t_of_yojson + DuneDiagnostics.t_of_yojson + _field_yojson + in + dune_diagnostics_field := Ppx_yojson_conv_lib.Option.Some fvalue + | Ppx_yojson_conv_lib.Option.Some _ -> + duplicates := field_name :: Ppx_yojson_conv_lib.( ! ) duplicates) + | _ -> ()); iter tail | [] -> () in @@ -576,15 +639,12 @@ let t_of_yojson = (Ppx_yojson_conv_lib.( ! ) extra) yojson | [] -> - let ( ( codelens_value - , extended_hover_value, syntax_documentation_value - , inlay_hints_value + let ( codelens_value + , extended_hover_value , dune_diagnostics_value - , syntax_documentation_value ) ) = + , syntax_documentation_value ) = ( Ppx_yojson_conv_lib.( ! ) codelens_field , Ppx_yojson_conv_lib.( ! ) extended_hover_field - , Ppx_yojson_conv_lib.( ! ) syntax_documentation_field - , Ppx_yojson_conv_lib.( ! ) inlay_hints_field , Ppx_yojson_conv_lib.( ! ) dune_diagnostics_field , Ppx_yojson_conv_lib.( ! ) syntax_documentation_field ) in @@ -596,12 +656,12 @@ let t_of_yojson = (match extended_hover_value with | Ppx_yojson_conv_lib.Option.None -> None | Ppx_yojson_conv_lib.Option.Some v -> v) - ; syntax_documentation = - (match syntax_documentation_value with - | Ppx_yojson_conv_lib.Option.None -> None - | Ppx_yojson_conv_lib.Option.Some v -> v) - ; inlay_hints = - (match inlay_hints_value with + ; dune_diagnostics = + (match dune_diagnostics_value with + | Ppx_yojson_conv_lib.Option.None -> None + | Ppx_yojson_conv_lib.Option.Some v -> v) + ; syntax_documentation = + (match syntax_documentation_value with | Ppx_yojson_conv_lib.Option.None -> None | Ppx_yojson_conv_lib.Option.Some v -> v) })) @@ -617,25 +677,13 @@ let yojson_of_t = (function | { codelens = v_codelens - ; extended_hover = v_extended_hover; syntax_documentation = - v_syntax_documentation - ; inlay_hints = v_inlay_hints + ; extended_hover = v_extended_hover ; dune_diagnostics = v_dune_diagnostics - - + ; dune_diagnostics = v_dune_diagnostics + ; syntax_documentation = v_syntax_documentation } -> let bnds : (string * Ppx_yojson_conv_lib.Yojson.Safe.t) list = [] in - let bnds = - if None = v_syntax_documentation then bnds - else - let arg = - (Json.Nullable_option.yojson_of_t SyntaxDocumentation.yojson_of_t) - v_syntax_documentation - in - let bnd = ("syntaxDocumentation", arg) in - bnd :: bnds - in let bnds = if None = v_syntax_documentation then bnds else @@ -656,16 +704,6 @@ let yojson_of_t = let bnd = ("duneDiagnostics", arg) in bnd :: bnds in - let bnds = - if None = v_inlay_hints then bnds - else - let arg = - (Json.Nullable_option.yojson_of_t InlayHints.yojson_of_t) - v_inlay_hints - in - let bnd = ("inlayHints", arg) in - bnd :: bnds - in let bnds = if None = v_syntax_documentation then bnds else @@ -705,9 +743,6 @@ let _ = yojson_of_t let default = { codelens = Some { enable = false } ; extended_hover = Some { enable = false } - ; syntax_documentation = Some { enable = false } - ; inlay_hints = - Some { hint_pattern_variables = false; hint_let_bindings = false } ; dune_diagnostics = Some { enable = true } ; syntax_documentation = Some { enable = false } } diff --git a/ocaml-lsp-server/src/configuration.ml b/ocaml-lsp-server/src/configuration.ml index 4b8201568..41020b923 100644 --- a/ocaml-lsp-server/src/configuration.ml +++ b/ocaml-lsp-server/src/configuration.ml @@ -47,3 +47,8 @@ let update t { DidChangeConfigurationParams.settings } = in let data = Config_data.t_of_yojson settings in Fiber.return { wheel; data } + +let report_dune_diagnostics t = + match t.data.dune_diagnostics with + | Some { enable = true } | None -> true + | Some { enable = false } -> false diff --git a/ocaml-lsp-server/src/configuration.mli b/ocaml-lsp-server/src/configuration.mli index 16e01c56f..fb6d8e4e5 100644 --- a/ocaml-lsp-server/src/configuration.mli +++ b/ocaml-lsp-server/src/configuration.mli @@ -10,3 +10,5 @@ val default : unit -> t Fiber.t val wheel : t -> Lev_fiber.Timer.Wheel.t val update : t -> DidChangeConfigurationParams.t -> t Fiber.t + +val report_dune_diagnostics : t -> bool diff --git a/ocaml-lsp-server/src/diagnostics.ml b/ocaml-lsp-server/src/diagnostics.ml index 8c8d8adc7..3864cafc8 100644 --- a/ocaml-lsp-server/src/diagnostics.ml +++ b/ocaml-lsp-server/src/diagnostics.ml @@ -87,9 +87,11 @@ type t = ; mutable dirty_uris : Uri_set.t ; related_information : bool ; tags : DiagnosticTag.t list + ; mutable report_dune_diagnostics : bool } -let create (capabilities : PublishDiagnosticsClientCapabilities.t option) send = +let create (capabilities : PublishDiagnosticsClientCapabilities.t option) send + ~report_dune_diagnostics = let related_information, tags = match capabilities with | None -> (false, []) @@ -105,6 +107,7 @@ let create (capabilities : PublishDiagnosticsClientCapabilities.t option) send = ; send ; related_information ; tags + ; report_dune_diagnostics } let send = @@ -157,11 +160,12 @@ let send = { d with source } else fun _pid x -> x in - Table.foldi ~init:() t.dune ~f:(fun dune per_dune () -> - Table.iter per_dune ~f:(fun (uri, diagnostic) -> - if Uri_set.mem dirty_uris uri then - let diagnostic = set_dune_source dune.pid diagnostic in - add_dune_diagnostic pending uri diagnostic)); + if t.report_dune_diagnostics then + Table.foldi ~init:() t.dune ~f:(fun dune per_dune () -> + Table.iter per_dune ~f:(fun (uri, diagnostic) -> + if Uri_set.mem dirty_uris uri then + let diagnostic = set_dune_source dune.pid diagnostic in + add_dune_diagnostic pending uri diagnostic)); t.dirty_uris <- (match which with | `All -> Uri_set.empty @@ -359,3 +363,14 @@ let merlin_diagnostics diagnostics merlin = Range.compare d1.range d2.range)) in set diagnostics (`Merlin (uri, all_diagnostics)) + +let set_report_dune_diagnostics t ~report_dune_diagnostics = + let open Fiber.O in + let* () = Fiber.return () in + if t.report_dune_diagnostics = report_dune_diagnostics then Fiber.return () + else ( + t.report_dune_diagnostics <- report_dune_diagnostics; + Table.iter t.dune ~f:(fun per_dune -> + Table.iter per_dune ~f:(fun (uri, _diagnostic) -> + t.dirty_uris <- Uri_set.add t.dirty_uris uri)); + send t `All) diff --git a/ocaml-lsp-server/src/diagnostics.mli b/ocaml-lsp-server/src/diagnostics.mli index 479df31ac..4ae6af57e 100644 --- a/ocaml-lsp-server/src/diagnostics.mli +++ b/ocaml-lsp-server/src/diagnostics.mli @@ -9,6 +9,7 @@ type t val create : PublishDiagnosticsClientCapabilities.t option -> (PublishDiagnosticsParams.t list -> unit Fiber.t) + -> report_dune_diagnostics:bool -> t val send : t -> [ `All | `One of Uri.t ] -> unit Fiber.t @@ -36,6 +37,9 @@ val tags_of_message : val merlin_diagnostics : t -> Document.Merlin.t -> unit Fiber.t +val set_report_dune_diagnostics : + t -> report_dune_diagnostics:bool -> unit Fiber.t + (** Exposed for testing *) val equal_message : string -> string -> bool diff --git a/ocaml-lsp-server/src/document.ml b/ocaml-lsp-server/src/document.ml index b210ce8eb..cabd3e24a 100644 --- a/ocaml-lsp-server/src/document.ml +++ b/ocaml-lsp-server/src/document.ml @@ -294,8 +294,7 @@ module Merlin = struct { loc : Loc.t ; typ : string ; doc : string option - ; syntax_doc_result : [ `Found of Query_protocol.syntax_doc_result - | `No_documentation ] + ; syntax_doc : Query_protocol.syntax_doc_result option } let type_enclosing doc pos verbosity ~with_syntax_doc = @@ -319,8 +318,14 @@ module Merlin = struct | [] | (_, `Index _, _) :: _ -> None | (loc, `String typ, _) :: _ -> let doc = doc_comment pipeline pos in - let syntax_doc_result = syntax_doc pipeline pos in - Some { loc; typ; doc; syntax_doc_result }) + let syntax_doc = + match + (Env_vars._ENABLE_SYNTAX_DOCUMENTATION (), syntax_doc_state) + with + | _, true | Some true, _ -> syntax_doc pipeline pos + | _ -> None + in + Some { loc; typ; doc; syntax_doc }) let doc_comment doc pos = with_pipeline_exn doc (fun pipeline -> doc_comment pipeline pos) diff --git a/ocaml-lsp-server/src/env_vars.ml b/ocaml-lsp-server/src/env_vars.ml index 78982ddbd..bbad463e8 100644 --- a/ocaml-lsp-server/src/env_vars.ml +++ b/ocaml-lsp-server/src/env_vars.ml @@ -18,8 +18,8 @@ let _IS_HOVER_EXTENDED () : bool option = | "true" | "1" -> Some true | _ -> Some false -let _ENABLE_SYNTAX_DOCUMENTATION () : bool option = - let* f = Sys.getenv_opt "OCAMLLSP_ENABLE_SYNTAX_DOCUMENTATION" in - match f with - | "true" | "1" -> Some true +let _ENABLE_SYNTAX_DOCUMENTATION () : bool option = + let* f = Sys.getenv_opt "OCAMLLSP_ENABLE_SYNTAX_DOCUMENTATION" in + match f with + | "true" | "1" -> Some true | _ -> Some false diff --git a/ocaml-lsp-server/src/hover_req.ml b/ocaml-lsp-server/src/hover_req.ml index 06145bff6..f8d885e6a 100644 --- a/ocaml-lsp-server/src/hover_req.ml +++ b/ocaml-lsp-server/src/hover_req.ml @@ -24,7 +24,7 @@ let format_contents ~syntax ~markdown ~typ ~doc let syntax_doc = Option.map syntax_doc ~f:(fun syntax_doc -> sprintf - "_syntax_ %s: %s. See [Manual](%s)" + "`syntax` %s: %s. See [Manual](%s)" syntax_doc.name syntax_doc.description syntax_doc.documentation) diff --git a/ocaml-lsp-server/src/ocaml_lsp_server.ml b/ocaml-lsp-server/src/ocaml_lsp_server.ml index 448c7efba..f831f6734 100644 --- a/ocaml-lsp-server/src/ocaml_lsp_server.ml +++ b/ocaml-lsp-server/src/ocaml_lsp_server.ml @@ -211,7 +211,11 @@ let on_initialize server (ip : InitializeParams.t) = let state : State.t = Server.state server in let workspaces = Workspaces.create ip in let diagnostics = + let report_dune_diagnostics = + Configuration.report_dune_diagnostics state.configuration + in Diagnostics.create + ~report_dune_diagnostics (let open Option.O in let* td = ip.capabilities.textDocument in td.publishDiagnostics) @@ -705,7 +709,15 @@ let on_notification server (notification : Client_notification.t) : state | CancelRequest _ -> Fiber.return state | ChangeConfiguration req -> - let+ configuration = Configuration.update state.configuration req in + let* configuration = Configuration.update state.configuration req in + let+ () = + let report_dune_diagnostics = + Configuration.report_dune_diagnostics configuration + in + Diagnostics.set_report_dune_diagnostics + ~report_dune_diagnostics + (State.diagnostics state) + in { state with configuration } | DidSaveTextDocument { textDocument = { uri }; _ } -> ( let state = Server.state server in diff --git a/ocaml-lsp-server/test/e2e-new/completion.ml b/ocaml-lsp-server/test/e2e-new/completion.ml index e5a5dc3c7..edfb96273 100644 --- a/ocaml-lsp-server/test/e2e-new/completion.ml +++ b/ocaml-lsp-server/test/e2e-new/completion.ml @@ -595,6 +595,17 @@ let plus_42 (x:int) (y:int) = [%expect {| Completions: + { + "kind": 14, + "label": "in", + "textEdit": { + "newText": "in", + "range": { + "end": { "character": 12, "line": 5 }, + "start": { "character": 12, "line": 5 } + } + } + } { "detail": "int", "kind": 12, @@ -712,19 +723,6 @@ let plus_42 (x:int) (y:int) = } } } - { - "detail": "char -> int", - "kind": 12, - "label": "int_of_char", - "sortText": "0009", - "textEdit": { - "newText": "int_of_char", - "range": { - "end": { "character": 12, "line": 5 }, - "start": { "character": 12, "line": 5 } - } - } - } ............. |}] @@ -1162,3 +1160,149 @@ let%expect_test "completion doesn't autocomplete record fields" = (* 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 } + } + } + } + ............. |}]