@@ -15,18 +15,69 @@ let isPatternTuple pat =
15
15
| Ppat_tuple _ -> true
16
16
| _ -> false
17
17
18
- let traverseExpr (exp : Parsetree.expression ) ~exprPath ~pos =
18
+ let rec traverseExpr (exp : Parsetree.expression ) ~exprPath ~pos
19
+ ~firstCharBeforeCursorNoWhite =
19
20
let someIfHasCursor v =
20
21
if exp.pexp_loc |> CursorPosition. locHasCursor ~pos then Some v else None
21
22
in
22
23
match exp.pexp_desc with
24
+ | Pexp_ident {txt = Lident txt } when Utils. hasBraces exp.pexp_attributes ->
25
+ (* An ident with braces attribute corresponds to for example `{n}`.
26
+ Looks like a record but is parsed as an ident with braces. *)
27
+ someIfHasCursor (txt, [Completable. ERecordBody {seenFields = [] }] @ exprPath)
23
28
| Pexp_ident {txt = Lident txt } -> someIfHasCursor (txt, exprPath)
24
29
| Pexp_construct ({txt = Lident "()" } , _ ) -> someIfHasCursor (" " , exprPath)
25
30
| Pexp_construct ({txt = Lident txt } , None) -> someIfHasCursor (txt, exprPath)
26
31
| Pexp_variant (label , None) -> someIfHasCursor (" #" ^ label, exprPath)
27
32
| Pexp_record ([] , _ ) ->
28
33
(* Empty fields means we're in a record body `{}`. Complete for the fields. *)
29
34
someIfHasCursor (" " , [Completable. ERecordBody {seenFields = [] }] @ exprPath)
35
+ | Pexp_record (fields , _ ) -> (
36
+ let fieldWithCursor = ref None in
37
+ let fieldWithExprHole = ref None in
38
+ fields
39
+ |> List. iter (fun (fname , exp ) ->
40
+ match
41
+ ( fname.Location. txt,
42
+ exp.Parsetree. pexp_loc |> CursorPosition. classifyLoc ~pos )
43
+ with
44
+ | Longident. Lident fname , HasCursor ->
45
+ fieldWithCursor := Some (fname, exp)
46
+ | Lident fname , _ when isExprHole exp ->
47
+ fieldWithExprHole := Some (fname, exp)
48
+ | _ -> () );
49
+ let seenFields =
50
+ fields
51
+ |> List. filter_map (fun (fieldName , _f ) ->
52
+ match fieldName with
53
+ | {Location. txt = Longident. Lident fieldName } -> Some fieldName
54
+ | _ -> None )
55
+ in
56
+ match (! fieldWithCursor, ! fieldWithExprHole) with
57
+ | Some (fname , f ), _ | None , Some (fname , f ) -> (
58
+ match f.pexp_desc with
59
+ | Pexp_extension ({txt = "rescript.exprhole" } , _ ) ->
60
+ (* An expression hole means for example `{someField: <com>}`. We want to complete for the type of `someField`. *)
61
+ someIfHasCursor
62
+ (" " , [Completable. EFollowRecordField {fieldName = fname}] @ exprPath)
63
+ | Pexp_ident {txt = Lident txt } ->
64
+ (* A var means `{s}` or similar. Complete for fields. *)
65
+ someIfHasCursor (txt, [Completable. ERecordBody {seenFields}] @ exprPath)
66
+ | _ ->
67
+ f
68
+ |> traverseExpr ~first CharBeforeCursorNoWhite ~pos
69
+ ~expr Path:
70
+ ([Completable. EFollowRecordField {fieldName = fname}] @ exprPath)
71
+ )
72
+ | None , None -> (
73
+ (* Figure out if we're completing for a new field.
74
+ If the cursor is inside of the record body, but no field has the cursor,
75
+ and there's no pattern hole. Check the first char to the left of the cursor,
76
+ ignoring white space. If that's a comma, we assume you're completing for a new field. *)
77
+ match firstCharBeforeCursorNoWhite with
78
+ | Some ',' ->
79
+ someIfHasCursor (" " , [Completable. ERecordBody {seenFields}] @ exprPath)
80
+ | _ -> None ))
30
81
| _ -> None
31
82
32
83
type prop = {
@@ -42,8 +93,8 @@ type jsxProps = {
42
93
childrenStart : (int * int ) option ;
43
94
}
44
95
45
- let findJsxPropsCompletable ~jsxProps ~endPos ~posBeforeCursor ~ posAfterCompName
46
- =
96
+ let findJsxPropsCompletable ~jsxProps ~endPos ~posBeforeCursor
97
+ ~ firstCharBeforeCursorNoWhite ~ posAfterCompName =
47
98
let allLabels =
48
99
List. fold_right
49
100
(fun prop allLabels -> prop.name :: allLabels)
@@ -66,7 +117,10 @@ let findJsxPropsCompletable ~jsxProps ~endPos ~posBeforeCursor ~posAfterCompName
66
117
None
67
118
else if prop.exp.pexp_loc |> Loc. hasPos ~pos: posBeforeCursor then
68
119
(* Cursor on expr assigned *)
69
- match traverseExpr prop.exp ~expr Path:[] ~pos: posBeforeCursor with
120
+ match
121
+ traverseExpr prop.exp ~expr Path:[] ~pos: posBeforeCursor
122
+ ~first CharBeforeCursorNoWhite
123
+ with
70
124
| Some (prefix , nested ) ->
71
125
Some
72
126
(CjsxPropValue
@@ -148,8 +202,8 @@ let extractJsxProps ~(compName : Longident.t Location.loc) ~args =
148
202
args |> processProps ~acc: []
149
203
150
204
let findArgCompletables ~(args : arg list ) ~endPos ~posBeforeCursor
151
- ~(contextPath : Completable.contextPath ) ~posAfterFunExpr ~ charBeforeCursor
152
- ~isPipedExpr =
205
+ ~(contextPath : Completable.contextPath ) ~posAfterFunExpr
206
+ ~firstCharBeforeCursorNoWhite ~ charBeforeCursor ~ isPipedExpr =
153
207
let fnHasCursor =
154
208
posAfterFunExpr < = posBeforeCursor && posBeforeCursor < endPos
155
209
in
@@ -171,7 +225,10 @@ let findArgCompletables ~(args : arg list) ~endPos ~posBeforeCursor
171
225
then Some (Completable. CnamedArg (contextPath, labelled.name, allNames))
172
226
else if exp.pexp_loc |> Loc. hasPos ~pos: posBeforeCursor then
173
227
(* Completing in the assignment of labelled argument *)
174
- match traverseExpr exp ~expr Path:[] ~pos: posBeforeCursor with
228
+ match
229
+ traverseExpr exp ~expr Path:[] ~pos: posBeforeCursor
230
+ ~first CharBeforeCursorNoWhite
231
+ with
175
232
| None -> None
176
233
| Some (prefix , nested ) ->
177
234
Some
@@ -196,7 +253,10 @@ let findArgCompletables ~(args : arg list) ~endPos ~posBeforeCursor
196
253
if Res_parsetree_viewer. isTemplateLiteral exp then None
197
254
else if exp.pexp_loc |> Loc. hasPos ~pos: posBeforeCursor then
198
255
(* Completing in an unlabelled argument *)
199
- match traverseExpr exp ~pos: posBeforeCursor ~expr Path:[] with
256
+ match
257
+ traverseExpr exp ~pos: posBeforeCursor ~first CharBeforeCursorNoWhite
258
+ ~expr Path:[]
259
+ with
200
260
| None -> None
201
261
| Some (prefix , nested ) ->
202
262
Some
@@ -972,6 +1032,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text =
972
1032
let jsxCompletable =
973
1033
findJsxPropsCompletable ~jsx Props ~end Pos:(Loc. end_ expr.pexp_loc)
974
1034
~pos BeforeCursor ~pos AfterCompName:(Loc. end_ compName.loc)
1035
+ ~first CharBeforeCursorNoWhite
975
1036
in
976
1037
if jsxCompletable <> None then setResultOpt jsxCompletable
977
1038
else if compName.loc |> Loc. hasPos ~pos: posBeforeCursor then
@@ -1009,6 +1070,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text =
1009
1070
~end Pos:(Loc. end_ expr.pexp_loc) ~pos BeforeCursor
1010
1071
~pos AfterFunExpr:(Loc. end_ funExpr.pexp_loc)
1011
1072
~char BeforeCursor ~is PipedExpr:true
1073
+ ~first CharBeforeCursorNoWhite
1012
1074
| None -> None
1013
1075
in
1014
1076
@@ -1044,6 +1106,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text =
1044
1106
~end Pos:(Loc. end_ expr.pexp_loc) ~pos BeforeCursor
1045
1107
~pos AfterFunExpr:(Loc. end_ funExpr.pexp_loc)
1046
1108
~char BeforeCursor ~is PipedExpr:false
1109
+ ~first CharBeforeCursorNoWhite
1047
1110
| None -> None
1048
1111
in
1049
1112
0 commit comments