Skip to content

Commit 50bbbe8

Browse files
committed
Add autocompletion for object access of the form foo['bar']
1 parent 93fb4bf commit 50bbbe8

File tree

6 files changed

+70
-3
lines changed

6 files changed

+70
-3
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +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'].
56

67
## 1.1.3
78

analysis/src/NewCompletions.ml

+31
Original file line numberDiff line numberDiff line change
@@ -1145,6 +1145,37 @@ 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 =
1150+
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
1168+
| _ -> []
1169+
in
1170+
let mkLabel_ name typString =
1171+
mkItem ~name ~kind:4 ~deprecated:None ~detail:typString ~docstring:[]
1172+
in
1173+
let mkLabel (name, typ) = mkLabel_ name (typ |> Shared.typeToString) in
1174+
if labels = [] then []
1175+
else
1176+
labels
1177+
|> List.filter (fun (name, _t) -> Utils.startsWith name prefix)
1178+
|> List.map mkLabel
11481179

11491180
let getCompletable ~textOpt ~pos =
11501181
match textOpt with

analysis/src/PartialParser.ml

+18-1
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ let skipOptVariantExtension text i =
109109
arg ::= id | id = [?] atomicExpr
110110
atomicExpr ::= id | "abc" | 'a' | 42 | `...` | optVariant {...} | optVariant (...) | <...> | [...]
111111
optVariant ::= a | A | #a | #A | _nothing_
112-
*)
112+
*)
113113
let findJsxContext text offset =
114114
let rec loop identsSeen i =
115115
let i = skipWhite text i in
@@ -187,6 +187,7 @@ type completable =
187187
| Cpath of string list (** e.g. ["M", "foo"] for M.foo *)
188188
| Cjsx of string list * string * string list
189189
(** E.g. (["M", "Comp"], "id", ["id1", "id2"]) for <M.Comp id1=... id2=... ... id *)
190+
| Cobj of string * string (** e.g. ("foo", "bar") for foo['bar *)
190191
| Cpipe of pipe * string (** E.g. ("x", "foo") for "x->foo" *)
191192

192193
let isLowercaseIdent id =
@@ -228,6 +229,19 @@ let findCompletable text offset =
228229
| None -> None
229230
| Some lhs -> Some (Cpipe (lhs, partialName))
230231
in
232+
let mkObj ~off ~partialName =
233+
let off = skipWhite text off in
234+
let rec loop i =
235+
if i < 0 then Some (String.sub text 0 (i - 1))
236+
else
237+
match text.[i] with
238+
| 'a' .. 'z' | 'A' .. 'Z' | '0' .. '9' | '_' -> loop (i - 1)
239+
| _ -> Some (String.sub text (i + 1) (off - i))
240+
in
241+
match loop off with
242+
| None -> None
243+
| Some lhs -> Some (Cobj (lhs, partialName))
244+
in
231245

232246
let suffix i = String.sub text (i + 1) (offset - (i + 1)) in
233247
let rec loop i =
@@ -243,6 +257,9 @@ let findCompletable text offset =
243257
let funPath, identsSeen = findCallFromArgument text (i - 1) in
244258
Some (Clabel (funPath, labelPrefix, identsSeen))
245259
| '@' -> Some (Cdecorator (suffix i))
260+
| '"' when i > 0 && text.[i - 1] = '[' ->
261+
let partialName = suffix i in
262+
mkObj ~off:(i - 2) ~partialName
246263
| 'a' .. 'z' | 'A' .. 'Z' | '0' .. '9' | '.' | '_' -> loop (i - 1)
247264
| ' ' when i = offset - 1 -> (
248265
(* autocomplete with no id: check if inside JSX *)

analysis/tests/src/Completion.res

+5-1
Original file line numberDiff line numberDiff line change
@@ -67,4 +67,8 @@ let zzz = 11
6767

6868
let _ = Lib.foo(//~age,
6969
//^com ~
70-
~age=3, ~name="")
70+
~age=3, ~name="")
71+
72+
let someObj = {"name": "a", "age": 32}
73+
74+
//^com someObj["a

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

+14
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,11 @@ DocumentSymbol tests/src/Completion.res
535535
"name": "zzz",
536536
"kind": 13,
537537
"location": {"uri": "Completion.res", "range": {"start": {"line": 49, "character": 4}, "end": {"line": 49, "character": 7}}}
538+
},
539+
{
540+
"name": "someObj",
541+
"kind": 19,
542+
"location": {"uri": "Completion.res", "range": {"start": {"line": 71, "character": 4}, "end": {"line": 71, "character": 11}}}
538543
}
539544
]
540545

@@ -598,3 +603,12 @@ Complete tests/src/Completion.res 67:2
598603
"documentation": null
599604
}]
600605

606+
Complete tests/src/Completion.res 72:2
607+
[{
608+
"label": "age",
609+
"kind": 4,
610+
"tags": [],
611+
"detail": "int",
612+
"documentation": null
613+
}]
614+

server/src/server.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -637,7 +637,7 @@ function onMessage(msg: m.Message) {
637637
renameProvider: { prepareProvider: true },
638638
// disabled right now until we use the parser to show non-stale symbols per keystroke
639639
// documentSymbolProvider: true,
640-
completionProvider: { triggerCharacters: [".", ">", "@", "~"] },
640+
completionProvider: { triggerCharacters: [".", ">", "@", "~", '"'] },
641641
},
642642
};
643643
let response: m.ResponseMessage = {

0 commit comments

Comments
 (0)