diff --git a/analysis/src/SignatureHelp.ml b/analysis/src/SignatureHelp.ml index fdefee2a1..c903aab6e 100644 --- a/analysis/src/SignatureHelp.ml +++ b/analysis/src/SignatureHelp.ml @@ -154,9 +154,68 @@ let signatureHelp ~path ~pos ~currentFile ~debug = let posBeforeCursor = Pos.posBeforeCursor pos in let supportsMarkdownLinks = true in let foundFunctionApplicationExpr = ref None in - let setFound r = foundFunctionApplicationExpr := Some r 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); @@ -165,41 +224,7 @@ let signatureHelp ~path ~pos ~currentFile ~debug = when pexp_loc |> CursorPosition.classifyLoc ~pos:posBeforeCursor == HasCursor -> - let extractedArgs = extractExpApplyArgs ~args in - let argAtCursor = - let unlabelledArgCount = ref 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) + searchForArgWithCursor ~isPipeExpr:false ~args ~exp | _ -> ()); Ast_iterator.default_iterator.expr iterator expr in diff --git a/analysis/tests/src/SignatureHelp.res b/analysis/tests/src/SignatureHelp.res index 6d2e81b9b..4011d4f89 100644 --- a/analysis/tests/src/SignatureHelp.res +++ b/analysis/tests/src/SignatureHelp.res @@ -50,3 +50,6 @@ let iAmSoSpecial = (iJustHaveOneArg: string) => { // let _ = iAmSoSpecial( // ^she + +// let _ = "hello"->otherFunc(1 +// ^she diff --git a/analysis/tests/src/expected/SignatureHelp.res.txt b/analysis/tests/src/expected/SignatureHelp.res.txt index 897a350a3..69786f386 100644 --- a/analysis/tests/src/expected/SignatureHelp.res.txt +++ b/analysis/tests/src/expected/SignatureHelp.res.txt @@ -182,7 +182,7 @@ extracted params: Signature help src/SignatureHelp.res 50:24 posCursor:[50:23] posNoWhite:[50:22] Found expr:[50:11->50:24] -Pexp_apply ...[50:11->50:23] (...[53:0->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 @@ -197,3 +197,21 @@ extracted params: "activeParameter": 0 } +Signature help src/SignatureHelp.res 53:31 +posCursor:[53:29] posNoWhite:[53:28] Found expr:[53:11->53:31] +posCursor:[53:29] posNoWhite:[53:28] Found expr:[53:20->53:31] +Pexp_apply ...[53:20->53:29] (...[53:30->53:31]) +posCursor:[53:29] posNoWhite:[53:28] Found expr:[53:20->53:29] +Pexp_ident otherFunc:[53:20->53:29] +argAtCursor: unlabelled<1> +extracted params: +[(string, int, float] +{ + "signatures": [{ + "label": "let otherFunc: (string, int, float) => unit", + "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 +} +