From d60d21c83c4f05497212bd2891fe6cab31130c6f Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Wed, 4 Jan 2023 19:08:18 +0100 Subject: [PATCH 1/3] add failing tests --- analysis/tests/src/SignatureHelp.res | 15 ++++++ .../tests/src/expected/SignatureHelp.res.txt | 51 +++++++++++++++++++ 2 files changed, 66 insertions(+) diff --git a/analysis/tests/src/SignatureHelp.res b/analysis/tests/src/SignatureHelp.res index 4011d4f89..8fa6c4020 100644 --- a/analysis/tests/src/SignatureHelp.res +++ b/analysis/tests/src/SignatureHelp.res @@ -53,3 +53,18 @@ let iAmSoSpecial = (iJustHaveOneArg: string) => { // let _ = "hello"->otherFunc(1 // ^she + +let fn = (age: int, name: string, year: int) => { + ignore(age) + ignore(name) + ignore(year) +} + +// let _ = fn(22, ) +// ^she + +// let _ = fn(22, , 2023) +// ^she + +// let _ = fn(12, "hello", ) +// ^she diff --git a/analysis/tests/src/expected/SignatureHelp.res.txt b/analysis/tests/src/expected/SignatureHelp.res.txt index 69786f386..4d8cb38a8 100644 --- a/analysis/tests/src/expected/SignatureHelp.res.txt +++ b/analysis/tests/src/expected/SignatureHelp.res.txt @@ -215,3 +215,54 @@ extracted params: "activeParameter": 1 } +Signature help src/SignatureHelp.res 62:17 +posCursor:[62:13] posNoWhite:[62:12] Found expr:[62:11->62:19] +Pexp_apply ...[62:11->62:13] (...[62:14->62:16]) +posCursor:[62:13] posNoWhite:[62:12] Found expr:[62:11->62:13] +Pexp_ident fn:[62:11->62:13] +argAtCursor: none +extracted params: +[(int, string, int] +{ + "signatures": [{ + "label": "let fn: (int, string, int) => unit", + "parameters": [{"label": [8, 12], "documentation": {"kind": "markdown", "value": "```rescript\nint\n```"}}, {"label": [14, 20], "documentation": {"kind": "markdown", "value": "```rescript\nint\n```"}}, {"label": [22, 25], "documentation": {"kind": "markdown", "value": "```rescript\nint\n```"}}] + }], + "activeSignature": 0, + "activeParameter": -1 +} + +Signature help src/SignatureHelp.res 65:17 +posCursor:[65:13] posNoWhite:[65:12] Found expr:[65:11->65:25] +Pexp_apply ...[65:11->65:13] (...[65:14->65:16], ...[65:20->65:24]) +posCursor:[65:13] posNoWhite:[65:12] Found expr:[65:11->65:13] +Pexp_ident fn:[65:11->65:13] +argAtCursor: none +extracted params: +[(int, string, int] +{ + "signatures": [{ + "label": "let fn: (int, string, int) => unit", + "parameters": [{"label": [8, 12], "documentation": {"kind": "markdown", "value": "```rescript\nint\n```"}}, {"label": [14, 20], "documentation": {"kind": "markdown", "value": "```rescript\nint\n```"}}, {"label": [22, 25], "documentation": {"kind": "markdown", "value": "```rescript\nint\n```"}}] + }], + "activeSignature": 0, + "activeParameter": -1 +} + +Signature help src/SignatureHelp.res 68:26 +posCursor:[68:13] posNoWhite:[68:12] Found expr:[68:11->68:28] +Pexp_apply ...[68:11->68:13] (...[68:14->68:16], ...[68:18->68:25]) +posCursor:[68:13] posNoWhite:[68:12] Found expr:[68:11->68:13] +Pexp_ident fn:[68:11->68:13] +argAtCursor: none +extracted params: +[(int, string, int] +{ + "signatures": [{ + "label": "let fn: (int, string, int) => unit", + "parameters": [{"label": [8, 12], "documentation": {"kind": "markdown", "value": "```rescript\nint\n```"}}, {"label": [14, 20], "documentation": {"kind": "markdown", "value": "```rescript\nint\n```"}}, {"label": [22, 25], "documentation": {"kind": "markdown", "value": "```rescript\nint\n```"}}] + }], + "activeSignature": 0, + "activeParameter": -1 +} + From 3a198572570cbc310c2fb5ab821c3a8eb151e6c9 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Wed, 4 Jan 2023 20:45:24 +0100 Subject: [PATCH 2/3] improve precision in signature help by accounting for commas (meaning intent to fill in the next argument) --- analysis/src/CompletionFrontEnd.ml | 9 +- analysis/src/SignatureHelp.ml | 351 ++++++++++-------- analysis/src/Utils.ml | 7 + .../tests/src/expected/SignatureHelp.res.txt | 22 +- 4 files changed, 218 insertions(+), 171 deletions(-) diff --git a/analysis/src/CompletionFrontEnd.ml b/analysis/src/CompletionFrontEnd.ml index aaeee4dd1..6f9eeed21 100644 --- a/analysis/src/CompletionFrontEnd.ml +++ b/analysis/src/CompletionFrontEnd.ml @@ -1,12 +1,5 @@ open SharedTypes -let rec skipWhite text i = - if i < 0 then 0 - else - match text.[i] with - | ' ' | '\n' | '\r' | '\t' -> skipWhite text (i - 1) - | _ -> i - let extractCompletableArgValueInfo exp = match exp.Parsetree.pexp_desc with | Pexp_ident {txt = Lident txt} -> Some txt @@ -302,7 +295,7 @@ let completePipeChain ~(lhs : Parsetree.expression) = | _ -> None let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = - let offsetNoWhite = skipWhite text (offset - 1) in + let offsetNoWhite = Utils.skipWhite text (offset - 1) in let posNoWhite = let line, col = posCursor in (line, max 0 col - offset + offsetNoWhite) diff --git a/analysis/src/SignatureHelp.ml b/analysis/src/SignatureHelp.ml index 69a0ff006..567b6155e 100644 --- a/analysis/src/SignatureHelp.ml +++ b/analysis/src/SignatureHelp.ml @@ -152,159 +152,206 @@ let docsForLabel typeExpr ~file ~package ~supportsMarkdownLinks = typeString :: typeDefinitions |> String.concat "\n" let signatureHelp ~path ~pos ~currentFile ~debug = - let posBeforeCursor = Pos.posBeforeCursor pos in - let supportsMarkdownLinks = true in - let foundFunctionApplicationExpr = ref None in - let setFound r = - if !foundFunctionApplicationExpr = None then - foundFunctionApplicationExpr := Some r - in - let searchForArgWithCursor ~isPipeExpr ~args ~exp = - let extractedArgs = extractExpApplyArgs ~args in - let argAtCursor = - let unlabelledArgCount = ref (if isPipeExpr then 1 else 0) in - extractedArgs - |> List.find_map (fun arg -> - match arg.label with - | None -> - let currentUnlabelledArgCount = !unlabelledArgCount in - unlabelledArgCount := currentUnlabelledArgCount + 1; - (* An argument without a label is just the expression, so we can use that. *) - if arg.exp.pexp_loc |> Loc.hasPos ~pos:posBeforeCursor then - Some (Unlabelled currentUnlabelledArgCount) - else None - | Some {name; posStart; posEnd} -> ( - (* Check for the label identifier itself having the cursor *) - match - pos |> CursorPosition.classifyPositions ~posStart ~posEnd - with - | HasCursor -> Some (Labelled name) - | NoCursor | EmptyLoc -> ( - (* If we're not in the label, check the exp. Either the exp - exists and has the cursor. Or the exp is a parser recovery - node, in which case we assume that the parser recovery - indicates that the cursor was here. *) - match - ( arg.exp.pexp_desc, - arg.exp.pexp_loc - |> CursorPosition.classifyLoc ~pos:posBeforeCursor ) - with - | Pexp_extension ({txt = "rescript.exprhole"}, _), _ - | _, HasCursor -> - Some (Labelled name) - | _ -> None))) - in - setFound (argAtCursor, exp, extractedArgs) - in - let expr (iterator : Ast_iterator.iterator) (expr : Parsetree.expression) = - (match expr with - (* Handle pipes, like someVar->someFunc(... *) - | { - pexp_desc = - Pexp_apply - ( {pexp_desc = Pexp_ident {txt = Lident "|."}}, - [ - _; - ( _, - { - pexp_desc = - Pexp_apply (({pexp_desc = Pexp_ident _} as exp), args); - pexp_loc; - } ); - ] ); - } - when pexp_loc - |> CursorPosition.classifyLoc ~pos:posBeforeCursor - == HasCursor -> - searchForArgWithCursor ~isPipeExpr:true ~args ~exp - (* Look for applying idents, like someIdent(...) *) - | { - pexp_desc = Pexp_apply (({pexp_desc = Pexp_ident _} as exp), args); - pexp_loc; - } - when pexp_loc - |> CursorPosition.classifyLoc ~pos:posBeforeCursor - == HasCursor -> - searchForArgWithCursor ~isPipeExpr:false ~args ~exp - | _ -> ()); - Ast_iterator.default_iterator.expr iterator expr - in - let iterator = {Ast_iterator.default_iterator with expr} in - let parser = Res_driver.parsingEngine.parseImplementation ~forPrinter:false in - let {Res_driver.parsetree = structure} = parser ~filename:currentFile in - iterator.structure iterator structure |> ignore; - match !foundFunctionApplicationExpr with - | Some (argAtCursor, exp, _extractedArgs) -> ( - (* Not looking for the cursor position after this, but rather the target function expression's loc. *) - let pos = exp.pexp_loc |> Loc.end_ in - match findFunctionType ~currentFile ~debug ~path ~pos with - | Some (args, name, docstring, type_expr, package, _env, file) -> - if debug then - Printf.printf "argAtCursor: %s\n" - (match argAtCursor with - | None -> "none" - | Some (Labelled name) -> "~" ^ name - | Some (Unlabelled index) -> "unlabelled<" ^ string_of_int index ^ ">"); - - (* The LS protocol wants us to send both the full type signature (label) that the end user sees as the signature help, and all parameters in that label - in the form of a list of start/end character offsets. We leverage the parser to figure the offsets out by parsing the label, and extract the - offsets from the parser. *) + let textOpt = Files.readFile currentFile in + match textOpt with + | None | Some "" -> None + | Some text -> ( + match Pos.positionToOffset text pos with + | None -> None + | Some offset -> ( + let posBeforeCursor = Pos.posBeforeCursor pos in + let offsetNoWhite = Utils.skipWhite text (offset - 1) in + let firstCharBeforeCursorNoWhite = + if offsetNoWhite < String.length text && offsetNoWhite >= 0 then + Some text.[offsetNoWhite] + else None + in + let supportsMarkdownLinks = true in + let foundFunctionApplicationExpr = ref None in + let setFound r = + if !foundFunctionApplicationExpr = None then + foundFunctionApplicationExpr := Some r + in + let searchForArgWithCursor ~isPipeExpr ~args ~exp = + let extractedArgs = extractExpApplyArgs ~args in + let argAtCursor = + let firstArgIndex = if isPipeExpr then 1 else 0 in + let unlabelledArgCount = ref firstArgIndex in + let lastUnlabelledArgBeforeCursor = ref firstArgIndex in + let argAtCursor_ = + extractedArgs + |> List.find_map (fun arg -> + match arg.label with + | None -> + let currentUnlabelledArgCount = !unlabelledArgCount in + unlabelledArgCount := currentUnlabelledArgCount + 1; + (* An argument without a label is just the expression, so we can use that. *) + if arg.exp.pexp_loc |> Loc.hasPos ~pos:posBeforeCursor then + Some (Unlabelled currentUnlabelledArgCount) + else ( + (* If this unlabelled arg doesn't have the cursor, record + it as the last seen unlabelled arg before the + cursor.*) + if posBeforeCursor >= (arg.exp.pexp_loc |> Loc.start) + then + lastUnlabelledArgBeforeCursor := + currentUnlabelledArgCount; + None) + | Some {name; posStart; posEnd} -> ( + (* Check for the label identifier itself having the cursor *) + match + pos |> CursorPosition.classifyPositions ~posStart ~posEnd + with + | HasCursor -> Some (Labelled name) + | NoCursor | EmptyLoc -> ( + (* If we're not in the label, check the exp. Either the exp + exists and has the cursor. Or the exp is a parser recovery + node, in which case we assume that the parser recovery + indicates that the cursor was here. *) + match + ( arg.exp.pexp_desc, + arg.exp.pexp_loc + |> CursorPosition.classifyLoc ~pos:posBeforeCursor ) + with + | Pexp_extension ({txt = "rescript.exprhole"}, _), _ + | _, HasCursor -> + Some (Labelled name) + | _ -> None))) + in - (* Put together a label here that both makes sense to show to the end user in the signature help, but also can be passed to the parser. *) - let label = "let " ^ name ^ ": " ^ Shared.typeToString type_expr in - let {Res_driver.parsetree = signature} = - Res_driver.parseInterfaceFromSource ~forPrinter:false - ~displayFilename:"" ~source:label + match argAtCursor_ with + | None -> + Some + (Unlabelled + (!lastUnlabelledArgBeforeCursor + + + if firstCharBeforeCursorNoWhite = Some ',' then 1 + (* If we found no argument with the cursor, we might still be + able to complete for an unlabelled argument, if the char + before the cursor is ',', like: `someFn(123, )` + complete for argument 2, or: `someFn(123, , true)` + complete for argument 2 as well. Adding 1 here accounts + for the comma telling us that the users intent is to fill + in the next argument. *) + else 0)) + | v -> v + in + setFound (argAtCursor, exp, extractedArgs) + in + let expr (iterator : Ast_iterator.iterator) (expr : Parsetree.expression) + = + (match expr with + (* Handle pipes, like someVar->someFunc(... *) + | { + pexp_desc = + Pexp_apply + ( {pexp_desc = Pexp_ident {txt = Lident "|."}}, + [ + _; + ( _, + { + pexp_desc = + Pexp_apply (({pexp_desc = Pexp_ident _} as exp), args); + pexp_loc; + } ); + ] ); + } + when pexp_loc + |> CursorPosition.classifyLoc ~pos:posBeforeCursor + == HasCursor -> + searchForArgWithCursor ~isPipeExpr:true ~args ~exp + (* Look for applying idents, like someIdent(...) *) + | { + pexp_desc = Pexp_apply (({pexp_desc = Pexp_ident _} as exp), args); + pexp_loc; + } + when pexp_loc + |> CursorPosition.classifyLoc ~pos:posBeforeCursor + == HasCursor -> + searchForArgWithCursor ~isPipeExpr:false ~args ~exp + | _ -> ()); + Ast_iterator.default_iterator.expr iterator expr + in + let iterator = {Ast_iterator.default_iterator with expr} in + let parser = + Res_driver.parsingEngine.parseImplementation ~forPrinter:false in + let {Res_driver.parsetree = structure} = parser ~filename:currentFile in + iterator.structure iterator structure |> ignore; + match !foundFunctionApplicationExpr with + | Some (argAtCursor, exp, _extractedArgs) -> ( + (* Not looking for the cursor position after this, but rather the target function expression's loc. *) + let pos = exp.pexp_loc |> Loc.end_ in + match findFunctionType ~currentFile ~debug ~path ~pos with + | Some (args, name, docstring, type_expr, package, _env, file) -> + if debug then + Printf.printf "argAtCursor: %s\n" + (match argAtCursor with + | None -> "none" + | Some (Labelled name) -> "~" ^ name + | Some (Unlabelled index) -> + "unlabelled<" ^ string_of_int index ^ ">"); - let parameters = extractParameters ~signature ~label in - if debug then - Printf.printf "extracted params: \n%s\n" - (parameters - |> List.map (fun (_, start, end_) -> - String.sub label start (end_ - start)) - |> list); + (* The LS protocol wants us to send both the full type signature (label) that the end user sees as the signature help, and all parameters in that label + in the form of a list of start/end character offsets. We leverage the parser to figure the offsets out by parsing the label, and extract the + offsets from the parser. *) - (* Figure out the active parameter *) - let activeParameter = findActiveParameter ~argAtCursor ~args in - Some - { - Protocol.signatures = - [ - { - label; - parameters = - parameters - |> List.map (fun (argLabel, start, end_) -> - { - Protocol.label = (start, end_); - documentation = - (match - args - |> List.find_opt (fun (lbl, _) -> - lbl = argLabel) - with - | None -> - {Protocol.kind = "markdown"; value = "Nope"} - | Some (_, labelTypExpr) -> - { - Protocol.kind = "markdown"; - value = - docsForLabel ~supportsMarkdownLinks ~file - ~package labelTypExpr; - }); - }); - documentation = - (match List.nth_opt docstring 0 with - | None -> None - | Some docs -> Some {Protocol.kind = "markdown"; value = docs}); - }; - ]; - activeSignature = Some 0; - activeParameter = - (match activeParameter with - | None -> Some (-1) - | activeParameter -> activeParameter); - } - | _ -> None) - | _ -> None + (* Put together a label here that both makes sense to show to the end user in the signature help, but also can be passed to the parser. *) + let label = "let " ^ name ^ ": " ^ Shared.typeToString type_expr in + let {Res_driver.parsetree = signature} = + Res_driver.parseInterfaceFromSource ~forPrinter:false + ~displayFilename:"" ~source:label + in + + let parameters = extractParameters ~signature ~label in + if debug then + Printf.printf "extracted params: \n%s\n" + (parameters + |> List.map (fun (_, start, end_) -> + String.sub label start (end_ - start)) + |> list); + + (* Figure out the active parameter *) + let activeParameter = findActiveParameter ~argAtCursor ~args in + Some + { + Protocol.signatures = + [ + { + label; + parameters = + parameters + |> List.map (fun (argLabel, start, end_) -> + { + Protocol.label = (start, end_); + documentation = + (match + args + |> List.find_opt (fun (lbl, _) -> + lbl = argLabel) + with + | None -> + {Protocol.kind = "markdown"; value = "Nope"} + | Some (_, labelTypExpr) -> + { + Protocol.kind = "markdown"; + value = + docsForLabel ~supportsMarkdownLinks ~file + ~package labelTypExpr; + }); + }); + documentation = + (match List.nth_opt docstring 0 with + | None -> None + | Some docs -> + Some {Protocol.kind = "markdown"; value = docs}); + }; + ]; + activeSignature = Some 0; + activeParameter = + (match activeParameter with + | None -> Some (-1) + | activeParameter -> activeParameter); + } + | _ -> None) + | _ -> None)) diff --git a/analysis/src/Utils.ml b/analysis/src/Utils.ml index 56533af89..5bca598c8 100644 --- a/analysis/src/Utils.ml +++ b/analysis/src/Utils.ml @@ -152,3 +152,10 @@ let identifyType type_desc = | Tunivar _ -> "Tunivar" | Tpoly _ -> "Tpoly" | Tpackage _ -> "Tpackage" + +let rec skipWhite text i = + if i < 0 then 0 + else + match text.[i] with + | ' ' | '\n' | '\r' | '\t' -> skipWhite text (i - 1) + | _ -> i diff --git a/analysis/tests/src/expected/SignatureHelp.res.txt b/analysis/tests/src/expected/SignatureHelp.res.txt index 4d8cb38a8..6e94ad824 100644 --- a/analysis/tests/src/expected/SignatureHelp.res.txt +++ b/analysis/tests/src/expected/SignatureHelp.res.txt @@ -3,7 +3,7 @@ posCursor:[16:19] posNoWhite:[16:18] Found expr:[16:11->16:20] Pexp_apply ...[16:11->16:19] (...[46:0->16:20]) posCursor:[16:19] posNoWhite:[16:18] Found expr:[16:11->16:19] Pexp_ident someFunc:[16:11->16:19] -argAtCursor: none +argAtCursor: unlabelled<0> extracted params: [( int, ~two: string=?, ~three: unit => unit, ~four: someVariant, unit] @@ -14,7 +14,7 @@ extracted params: "documentation": {"kind": "markdown", "value": " Does stuff. "} }], "activeSignature": 0, - "activeParameter": -1 + "activeParameter": 0 } Signature help src/SignatureHelp.res 19:21 @@ -117,7 +117,7 @@ posCursor:[34:20] posNoWhite:[34:19] Found expr:[34:11->34:21] Pexp_apply ...[34:11->34:20] (...[46:0->34:21]) posCursor:[34:20] posNoWhite:[34:19] Found expr:[34:11->34:20] Pexp_ident otherFunc:[34:11->34:20] -argAtCursor: none +argAtCursor: unlabelled<0> extracted params: [(string, int, float] { @@ -126,7 +126,7 @@ extracted params: "parameters": [{"label": [15, 22], "documentation": {"kind": "markdown", "value": "```rescript\nstring\n```"}}, {"label": [24, 27], "documentation": {"kind": "markdown", "value": "```rescript\nstring\n```"}}, {"label": [29, 34], "documentation": {"kind": "markdown", "value": "```rescript\nstring\n```"}}] }], "activeSignature": 0, - "activeParameter": -1 + "activeParameter": 0 } Signature help src/SignatureHelp.res 37:24 @@ -185,7 +185,7 @@ posCursor:[50:23] posNoWhite:[50:22] Found expr:[50:11->50:24] Pexp_apply ...[50:11->50:23] (...[56:0->50:24]) posCursor:[50:23] posNoWhite:[50:22] Found expr:[50:11->50:23] Pexp_ident iAmSoSpecial:[50:11->50:23] -argAtCursor: none +argAtCursor: unlabelled<0> extracted params: [string] { @@ -220,7 +220,7 @@ posCursor:[62:13] posNoWhite:[62:12] Found expr:[62:11->62:19] Pexp_apply ...[62:11->62:13] (...[62:14->62:16]) posCursor:[62:13] posNoWhite:[62:12] Found expr:[62:11->62:13] Pexp_ident fn:[62:11->62:13] -argAtCursor: none +argAtCursor: unlabelled<1> extracted params: [(int, string, int] { @@ -229,7 +229,7 @@ extracted params: "parameters": [{"label": [8, 12], "documentation": {"kind": "markdown", "value": "```rescript\nint\n```"}}, {"label": [14, 20], "documentation": {"kind": "markdown", "value": "```rescript\nint\n```"}}, {"label": [22, 25], "documentation": {"kind": "markdown", "value": "```rescript\nint\n```"}}] }], "activeSignature": 0, - "activeParameter": -1 + "activeParameter": 1 } Signature help src/SignatureHelp.res 65:17 @@ -237,7 +237,7 @@ posCursor:[65:13] posNoWhite:[65:12] Found expr:[65:11->65:25] Pexp_apply ...[65:11->65:13] (...[65:14->65:16], ...[65:20->65:24]) posCursor:[65:13] posNoWhite:[65:12] Found expr:[65:11->65:13] Pexp_ident fn:[65:11->65:13] -argAtCursor: none +argAtCursor: unlabelled<1> extracted params: [(int, string, int] { @@ -246,7 +246,7 @@ extracted params: "parameters": [{"label": [8, 12], "documentation": {"kind": "markdown", "value": "```rescript\nint\n```"}}, {"label": [14, 20], "documentation": {"kind": "markdown", "value": "```rescript\nint\n```"}}, {"label": [22, 25], "documentation": {"kind": "markdown", "value": "```rescript\nint\n```"}}] }], "activeSignature": 0, - "activeParameter": -1 + "activeParameter": 1 } Signature help src/SignatureHelp.res 68:26 @@ -254,7 +254,7 @@ posCursor:[68:13] posNoWhite:[68:12] Found expr:[68:11->68:28] Pexp_apply ...[68:11->68:13] (...[68:14->68:16], ...[68:18->68:25]) posCursor:[68:13] posNoWhite:[68:12] Found expr:[68:11->68:13] Pexp_ident fn:[68:11->68:13] -argAtCursor: none +argAtCursor: unlabelled<2> extracted params: [(int, string, int] { @@ -263,6 +263,6 @@ extracted params: "parameters": [{"label": [8, 12], "documentation": {"kind": "markdown", "value": "```rescript\nint\n```"}}, {"label": [14, 20], "documentation": {"kind": "markdown", "value": "```rescript\nint\n```"}}, {"label": [22, 25], "documentation": {"kind": "markdown", "value": "```rescript\nint\n```"}}] }], "activeSignature": 0, - "activeParameter": -1 + "activeParameter": 2 } From 897443a7639059d414b3719a748815a722a8124b Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Wed, 4 Jan 2023 20:51:18 +0100 Subject: [PATCH 3/3] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 46181e0ef..c3cf3ad05 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,6 +22,7 @@ #### :nail_care: Polish - Prefer opened `Belt` modules in autocomplete when `-open Belt` is detected in `bsconfig`. https://github.com/rescript-lang/rescript-vscode/pull/673 +- Improve precision in signature help. You now do not need to type anything into the argument for it to highlight. https://github.com/rescript-lang/rescript-vscode/pull/675 ## v1.10.0