Skip to content

Commit 8aacdb2

Browse files
committed
Autocomplete for function calls: only suggest labeled args that were not yet assigned.
Fixes #187
1 parent 6a86641 commit 8aacdb2

File tree

5 files changed

+76
-15
lines changed

5 files changed

+76
-15
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
## master
22
- ppx autocomplete: handle more atomic expressions to the rhs of `=` without braces (variant, polymorphic variant, function call, list literal, nested component, characters, templates).
3+
- Autocomplete for function calls: only suggest labeled args that were not yet assigned.
34

45
## 1.1.1
56

analysis/src/NewCompletions.ml

+3-2
Original file line numberDiff line numberDiff line change
@@ -610,7 +610,7 @@ let processCompletable ~findItems ~package ~rawOpens
610610
in
611611
dec2)
612612
|> List.map mkDecorator
613-
| Clabel (funPath, prefix) ->
613+
| Clabel (funPath, prefix, identsSeen) ->
614614
let labels =
615615
match funPath |> findItems ~exact:true with
616616
| {SharedTypes.item = Value typ} :: _ ->
@@ -631,7 +631,8 @@ let processCompletable ~findItems ~package ~rawOpens
631631
~docstring:[]
632632
in
633633
labels
634-
|> List.filter (fun (name, _t) -> Utils.startsWith name prefix)
634+
|> List.filter (fun (name, _t) ->
635+
Utils.startsWith name prefix && not (List.mem name identsSeen))
635636
|> List.map mkLabel
636637

637638
let computeCompletions ~full ~maybeText ~pos =

analysis/src/PartialParser.ml

+38-12
Original file line numberDiff line numberDiff line change
@@ -36,20 +36,46 @@ let rec startOfLident text i =
3636

3737
(* foo(... ~arg) from ~arg find foo *)
3838
let findCallFromArgument text offset =
39-
let rec loop ~i ~nClosed =
39+
let none = ([], []) in
40+
let rec loop identsSeen i =
41+
let i = skipWhite text i in
4042
if i > 0 then
4143
match text.[i] with
42-
| '(' when nClosed > 0 -> loop ~i:(i - 1) ~nClosed:(nClosed - 1)
44+
| '}' ->
45+
let i1 = findBackSkippingCommentsAndStrings text '{' '}' (i - 1) 0 in
46+
if i1 > 0 then loop identsSeen i1 else none
47+
| ')' ->
48+
let i1 = findBackSkippingCommentsAndStrings text '(' ')' (i - 1) 0 in
49+
if i1 > 0 then loop identsSeen i1 else none
50+
| ']' ->
51+
let i1 = findBackSkippingCommentsAndStrings text '[' ']' (i - 1) 0 in
52+
if i1 > 0 then loop identsSeen i1 else none
53+
| '"' ->
54+
let i1 = findBack text '"' (i - 1) in
55+
if i1 > 0 then loop identsSeen i1 else none
56+
| '\'' ->
57+
let i1 = findBack text '\'' (i - 1) in
58+
if i1 > 0 then loop identsSeen i1 else none
59+
| '`' ->
60+
let i1 = findBack text '`' (i - 1) in
61+
if i1 > 0 then loop identsSeen i1 else none
4362
| '(' ->
4463
let i1 = skipWhite text (i - 1) in
4564
let i0 = startOfLident text i1 in
4665
let funLident = String.sub text i0 (i1 - i0 + 1) in
47-
Str.split (Str.regexp_string ".") funLident
48-
| ')' -> loop ~i:(i - 1) ~nClosed:(nClosed + 1)
49-
| _ -> loop ~i:(i - 1) ~nClosed
50-
else []
66+
(Str.split (Str.regexp_string ".") funLident, identsSeen)
67+
| 'a' .. 'z' | 'A' .. 'Z' | '0' .. '9' | '.' | '_' ->
68+
let i1 = startOfLident text i in
69+
let ident = String.sub text i1 (i - i1 + 1) in
70+
if i1 - 1 > 0 then
71+
match text.[i1 - 1] with
72+
| '~' -> loop (ident :: identsSeen) (i1 - 2)
73+
| _ -> loop identsSeen (i1 - 1)
74+
else none
75+
| _ -> loop identsSeen (i - 1)
76+
else none
5177
in
52-
loop ~i:offset ~nClosed:0
78+
loop [] offset
5379

5480
(* skip A or #A or %A if present *)
5581
let skipOptVariantExtension text i =
@@ -61,7 +87,7 @@ let skipOptVariantExtension text i =
6187
if i > 0 then match text.[i] with '#' | '%' -> i - 1 | _ -> i else i
6288
in
6389
i
64-
| _ -> i
90+
| _ -> i
6591
else i
6692

6793
(* Figure out whether id should be autocompleted as component prop.
@@ -143,8 +169,8 @@ type pipe = PipeId of string | PipeArray | PipeString
143169

144170
type completable =
145171
| Cdecorator of string (** e.g. @module *)
146-
| Clabel of string list * string
147-
(** e.g. (["M", "foo"], "label") for M.foo(...~label...) *)
172+
| Clabel of string list * string * string list
173+
(** e.g. (["M", "foo"], "label", ["l1", "l2"]) for M.foo(...~l1...~l2...~label...) *)
148174
| Cpath of string list (** e.g. ["M", "foo"] for M.foo *)
149175
| Cjsx of string list * string * string list
150176
(** E.g. (["M", "Comp"], "id", ["id1", "id2"]) for <M.Comp id1=... id2=... ... id *)
@@ -201,8 +227,8 @@ let findCompletable text offset =
201227
else Some (mkPath rest)
202228
| '~' ->
203229
let labelPrefix = suffix i in
204-
let funPath = findCallFromArgument text (i - 1) in
205-
Some (Clabel (funPath, labelPrefix))
230+
let funPath, identsSeen = findCallFromArgument text (i - 1) in
231+
Some (Clabel (funPath, labelPrefix, identsSeen))
206232
| '@' -> Some (Cdecorator (suffix i))
207233
| 'a' .. 'z' | 'A' .. 'Z' | '0' .. '9' | '.' | '_' -> loop (i - 1)
208234
| ' ' when i = offset - 1 -> (

analysis/tests/src/Completion.res

+7-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ module Lib = {
1818
let next = (~number=0, ~year) => number + year
1919
}
2020

21-
//^com let x = foo(~
21+
//^com let x = Lib.foo(~
2222

2323
//^com [1,2,3]->m
2424

@@ -58,3 +58,9 @@ let zzz = 11
5858
//^com @reac
5959

6060
//^com @react.
61+
62+
//^com let x = Lib.foo(~name, ~
63+
64+
//^com let x = Lib.foo(~age, ~
65+
66+
//^com let x = Lib.foo(~age={3+4}, ~

analysis/tests/src/expected/Completion.res.txt

+27
Original file line numberDiff line numberDiff line change
@@ -556,3 +556,30 @@ Complete tests/src/Completion.res 58:2
556556
"documentation": null
557557
}]
558558

559+
Complete tests/src/Completion.res 60:2
560+
[{
561+
"label": "age",
562+
"kind": 4,
563+
"tags": [],
564+
"detail": "int",
565+
"documentation": null
566+
}]
567+
568+
Complete tests/src/Completion.res 62:2
569+
[{
570+
"label": "name",
571+
"kind": 4,
572+
"tags": [],
573+
"detail": "string",
574+
"documentation": null
575+
}]
576+
577+
Complete tests/src/Completion.res 64:2
578+
[{
579+
"label": "name",
580+
"kind": 4,
581+
"tags": [],
582+
"detail": "string",
583+
"documentation": null
584+
}]
585+

0 commit comments

Comments
 (0)