Skip to content

Commit 5530307

Browse files
committed
Extend autocompletion to handle nested objects.
Such as `nestedObj["x"]["y"]`.
1 parent d8689fb commit 5530307

File tree

5 files changed

+111
-68
lines changed

5 files changed

+111
-68
lines changed

CHANGELOG.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
- Fix issue in JSX autocomplete when the component is declared external.
33
- Fix jump-to-definition for uncurried calls.
44
- Fix issue where values for autocomplete were pulled from implementations instead of interfaces.
5-
- Add autocompletion for object access of the form foo["bar"].
5+
- Add autocompletion for object access of the form `foo["x"]` and `foo["x"]["y"]["z"]`.
66
- Fix issue with autocomplete then punned props are used in JSX. E.g. `<M foo ...>`.
77
- Fix issue with JSX autocompletion not working after `foo=#variant`.
88

analysis/src/NewCompletions.ml

Lines changed: 29 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1145,28 +1145,38 @@ let processCompletable ~findItems ~full ~package ~rawOpens
11451145
|> List.filter (fun (name, _t) ->
11461146
Utils.startsWith name prefix && not (List.mem name identsSeen))
11471147
|> List.map mkLabel
1148-
| Cobj (lhs, prefix) ->
1149-
let labels =
1148+
| Cobj (lhs, path, prefix) ->
1149+
let rec getFields (texp : Types.type_expr) =
1150+
match texp.desc with
1151+
| Tfield (name, _, t1, t2) ->
1152+
let fields = t2 |> getFields in
1153+
(name, t1) :: fields
1154+
| Tlink te -> te |> getFields
1155+
| Tvar None -> []
1156+
| _ -> []
1157+
in
1158+
let rec getObj (t : Types.type_expr) =
1159+
match t.desc with
1160+
| Tlink t1 | Tsubst t1 | Tpoly (t1, []) -> getObj t1
1161+
| Tobject (tObj, _) -> getFields tObj
1162+
| _ -> []
1163+
in
1164+
let fields =
11501165
match [lhs] |> findItems ~exact:true with
1151-
| {SharedTypes.item = Value typ} :: _ ->
1152-
let rec getFields (texp : Types.type_expr) =
1153-
match texp.desc with
1154-
| Tfield (name, _, t1, t2) ->
1155-
let fields = t2 |> getFields in
1156-
(name, t1) :: fields
1157-
| Tlink te -> te |> getFields
1158-
| Tvar None -> []
1159-
| _ -> []
1160-
in
1161-
let rec getObj (t : Types.type_expr) =
1162-
match t.desc with
1163-
| Tlink t1 | Tsubst t1 -> getObj t1
1164-
| Tobject (tObj, _) -> getFields tObj
1165-
| _ -> []
1166-
in
1167-
getObj typ
1166+
| {SharedTypes.item = Value typ} :: _ -> getObj typ
11681167
| _ -> []
11691168
in
1169+
let rec resolvePath fields path =
1170+
match path with
1171+
| name :: restPath -> (
1172+
match fields |> List.find_opt (fun (n, _) -> n = name) with
1173+
| Some (_, fieldType) ->
1174+
let innerFields = getObj fieldType in
1175+
resolvePath innerFields restPath
1176+
| None -> [])
1177+
| [] -> fields
1178+
in
1179+
let labels = resolvePath fields path in
11701180
let mkLabel_ name typString =
11711181
mkItem ~name ~kind:4 ~deprecated:None ~detail:typString ~docstring:[]
11721182
in

analysis/src/PartialParser.ml

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,8 @@ type completable =
190190
| Cpath of string list (** e.g. ["M", "foo"] for M.foo *)
191191
| Cjsx of string list * string * string list
192192
(** E.g. (["M", "Comp"], "id", ["id1", "id2"]) for <M.Comp id1=... id2=... ... id *)
193-
| Cobj of string * string (** e.g. ("foo", "bar") for foo['bar *)
193+
| Cobj of string * string list * string
194+
(** e.g. ("foo", ["a", "b"], "bar") for foo["a"]["b"]["bar" *)
194195
| Cpipe of pipe * string (** E.g. ("x", "foo") for "x->foo" *)
195196

196197
let isLowercaseIdent id =
@@ -234,16 +235,23 @@ let findCompletable text offset =
234235
in
235236
let mkObj ~off ~partialName =
236237
let off = skipWhite text off in
237-
let rec loop i =
238-
if i < 0 then Some (String.sub text 0 (i - 1))
238+
let rec loop off path i =
239+
if i < 0 then Some ([], String.sub text 0 (i - 1))
239240
else
240241
match text.[i] with
241-
| 'a' .. 'z' | 'A' .. 'Z' | '0' .. '9' | '_' -> loop (i - 1)
242-
| _ -> Some (String.sub text (i + 1) (off - i))
242+
| 'a' .. 'z' | 'A' .. 'Z' | '0' .. '9' | '_' -> loop off path (i - 1)
243+
| ']' when i > 1 && text.[i - 1] = '"' ->
244+
let i0 = i - 2 in
245+
let i1 = startOfLident text i0 in
246+
let ident = String.sub text i1 (i0 - i1 + 1) in
247+
if ident <> "" && i1 > 1 && text.[i1 - 1] = '"' && text.[i1 - 2] = '['
248+
then loop (off - i + i1 - 3) (ident :: path) (i1 - 3)
249+
else None
250+
| _ -> Some (path, String.sub text (i + 1) (off - i))
243251
in
244-
match loop off with
252+
match loop off [] off with
245253
| None -> None
246-
| Some lhs -> Some (Cobj (lhs, partialName))
254+
| Some (path, lhs) -> Some (Cobj (lhs, path, partialName))
247255
in
248256

249257
let suffix i = String.sub text (i + 1) (offset - (i + 1)) in

analysis/tests/src/Completion.res

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ module MyList = Belt.List
33
//^com Array.
44
//^com Array.m
55

6-
76
module Dep: {
87
@ocaml.doc("Some doc comment") @deprecated("Use customDouble instead")
98
let customDouble: int => int
@@ -30,20 +29,19 @@ let op = Some(3)
3029

3130
module ForAuto = {
3231
type t = int
33-
let abc = (x:t, _y:int) => x
34-
let abd = (x:t, _y:int) => x
32+
let abc = (x: t, _y: int) => x
33+
let abd = (x: t, _y: int) => x
3534
}
3635

37-
let fa:ForAuto.t = 34
36+
let fa: ForAuto.t = 34
3837
//^com fa->
3938

4039
//^com "hello"->Js.Dict.u
4140

4241
module O = {
4342
module Comp = {
4443
@react.component
45-
let make = (~first="", ~zoo=3, ~second) =>
46-
React.string(first ++ second ++ string_of_int(zoo))
44+
let make = (~first="", ~zoo=3, ~second) => React.string(first ++ second ++ string_of_int(zoo))
4745
}
4846
}
4947

@@ -65,10 +63,17 @@ let zzz = 11
6563

6664
//^com let x = Lib.foo(~age={3+4}, ~
6765

68-
let _ = Lib.foo(//~age,
69-
//^com ~
70-
~age=3, ~name="")
66+
let _ = Lib.foo(
67+
//~age,
68+
//^com ~
69+
~age=3,
70+
~name="",
71+
)
7172

7273
let someObj = {"name": "a", "age": 32}
7374

7475
//^com someObj["a
76+
77+
let nestedObj = {"x": {"y": {"name": "a", "age": 32}}}
78+
79+
//^com nestedObj["x"]["y"]["

0 commit comments

Comments
 (0)