diff --git a/CHANGELOG.md b/CHANGELOG.md index 9471238eb..776784277 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ ## master - ppx autocomplete: handle more atomic expressions to the rhs of `=` without braces (variant, polymorphic variant, function call, list literal, nested component, characters, templates). +- Autocomplete for function calls: only suggest labeled args that were not yet assigned. ## 1.1.1 diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml index c18fcd172..56ee34e2a 100644 --- a/analysis/src/NewCompletions.ml +++ b/analysis/src/NewCompletions.ml @@ -610,7 +610,7 @@ let processCompletable ~findItems ~package ~rawOpens in dec2) |> List.map mkDecorator - | Clabel (funPath, prefix) -> + | Clabel (funPath, prefix, identsSeen) -> let labels = match funPath |> findItems ~exact:true with | {SharedTypes.item = Value typ} :: _ -> @@ -631,7 +631,8 @@ let processCompletable ~findItems ~package ~rawOpens ~docstring:[] in labels - |> List.filter (fun (name, _t) -> Utils.startsWith name prefix) + |> List.filter (fun (name, _t) -> + Utils.startsWith name prefix && not (List.mem name identsSeen)) |> List.map mkLabel let computeCompletions ~full ~maybeText ~pos = diff --git a/analysis/src/PartialParser.ml b/analysis/src/PartialParser.ml index 3a48c53cb..11602e1f0 100644 --- a/analysis/src/PartialParser.ml +++ b/analysis/src/PartialParser.ml @@ -36,20 +36,46 @@ let rec startOfLident text i = (* foo(... ~arg) from ~arg find foo *) let findCallFromArgument text offset = - let rec loop ~i ~nClosed = + let none = ([], []) in + let rec loop identsSeen i = + let i = skipWhite text i in if i > 0 then match text.[i] with - | '(' when nClosed > 0 -> loop ~i:(i - 1) ~nClosed:(nClosed - 1) + | '}' -> + let i1 = findBackSkippingCommentsAndStrings text '{' '}' (i - 1) 0 in + if i1 > 0 then loop identsSeen i1 else none + | ')' -> + let i1 = findBackSkippingCommentsAndStrings text '(' ')' (i - 1) 0 in + if i1 > 0 then loop identsSeen i1 else none + | ']' -> + let i1 = findBackSkippingCommentsAndStrings text '[' ']' (i - 1) 0 in + if i1 > 0 then loop identsSeen i1 else none + | '"' -> + let i1 = findBack text '"' (i - 1) in + if i1 > 0 then loop identsSeen i1 else none + | '\'' -> + let i1 = findBack text '\'' (i - 1) in + if i1 > 0 then loop identsSeen i1 else none + | '`' -> + let i1 = findBack text '`' (i - 1) in + if i1 > 0 then loop identsSeen i1 else none | '(' -> let i1 = skipWhite text (i - 1) in let i0 = startOfLident text i1 in let funLident = String.sub text i0 (i1 - i0 + 1) in - Str.split (Str.regexp_string ".") funLident - | ')' -> loop ~i:(i - 1) ~nClosed:(nClosed + 1) - | _ -> loop ~i:(i - 1) ~nClosed - else [] + (Str.split (Str.regexp_string ".") funLident, identsSeen) + | 'a' .. 'z' | 'A' .. 'Z' | '0' .. '9' | '.' | '_' -> + let i1 = startOfLident text i in + let ident = String.sub text i1 (i - i1 + 1) in + if i1 - 1 > 0 then + match text.[i1 - 1] with + | '~' -> loop (ident :: identsSeen) (i1 - 2) + | _ -> loop identsSeen (i1 - 1) + else none + | _ -> loop identsSeen (i - 1) + else none in - loop ~i:offset ~nClosed:0 + loop [] offset (* skip A or #A or %A if present *) let skipOptVariantExtension text i = @@ -61,7 +87,7 @@ let skipOptVariantExtension text i = if i > 0 then match text.[i] with '#' | '%' -> i - 1 | _ -> i else i in i - | _ -> i + | _ -> i else i (* Figure out whether id should be autocompleted as component prop. @@ -143,8 +169,8 @@ type pipe = PipeId of string | PipeArray | PipeString type completable = | Cdecorator of string (** e.g. @module *) - | Clabel of string list * string - (** e.g. (["M", "foo"], "label") for M.foo(...~label...) *) + | Clabel of string list * string * string list + (** e.g. (["M", "foo"], "label", ["l1", "l2"]) for M.foo(...~l1...~l2...~label...) *) | Cpath of string list (** e.g. ["M", "foo"] for M.foo *) | Cjsx of string list * string * string list (** E.g. (["M", "Comp"], "id", ["id1", "id2"]) for let labelPrefix = suffix i in - let funPath = findCallFromArgument text (i - 1) in - Some (Clabel (funPath, labelPrefix)) + let funPath, identsSeen = findCallFromArgument text (i - 1) in + Some (Clabel (funPath, labelPrefix, identsSeen)) | '@' -> Some (Cdecorator (suffix i)) | 'a' .. 'z' | 'A' .. 'Z' | '0' .. '9' | '.' | '_' -> loop (i - 1) | ' ' when i = offset - 1 -> ( diff --git a/analysis/tests/src/Completion.res b/analysis/tests/src/Completion.res index 36cc8989e..06f31001e 100644 --- a/analysis/tests/src/Completion.res +++ b/analysis/tests/src/Completion.res @@ -18,7 +18,7 @@ module Lib = { let next = (~number=0, ~year) => number + year } -//^com let x = foo(~ +//^com let x = Lib.foo(~ //^com [1,2,3]->m @@ -58,3 +58,9 @@ let zzz = 11 //^com @reac //^com @react. + +//^com let x = Lib.foo(~name, ~ + +//^com let x = Lib.foo(~age, ~ + +//^com let x = Lib.foo(~age={3+4}, ~ diff --git a/analysis/tests/src/expected/Completion.res.txt b/analysis/tests/src/expected/Completion.res.txt index dd3c1bd8c..319600a0d 100644 --- a/analysis/tests/src/expected/Completion.res.txt +++ b/analysis/tests/src/expected/Completion.res.txt @@ -556,3 +556,30 @@ Complete tests/src/Completion.res 58:2 "documentation": null }] +Complete tests/src/Completion.res 60:2 +[{ + "label": "age", + "kind": 4, + "tags": [], + "detail": "int", + "documentation": null + }] + +Complete tests/src/Completion.res 62:2 +[{ + "label": "name", + "kind": 4, + "tags": [], + "detail": "string", + "documentation": null + }] + +Complete tests/src/Completion.res 64:2 +[{ + "label": "name", + "kind": 4, + "tags": [], + "detail": "string", + "documentation": null + }] +