@@ -13,6 +13,9 @@ module PositionContext = struct
13
13
(* * The char before the cursor, not excluding whitespace *)
14
14
whitespaceAfterCursor : char option ;
15
15
(* * The type of whitespace after the cursor, if any *)
16
+ locHasPos : Location .t -> bool ;
17
+ (* * A helper for checking whether a loc has the cursor (beforeCursor).
18
+ This is the most natural position to check when figuring out if the user has the cursor in something. *)
16
19
}
17
20
18
21
let make ~offset ~posCursor text =
@@ -40,6 +43,9 @@ module PositionContext = struct
40
43
| _ -> (Some charBeforeCursor, None ))
41
44
| _ -> (None , None )
42
45
in
46
+ let locHasPos loc =
47
+ loc |> CursorPosition. locHasCursor ~pos: posBeforeCursor
48
+ in
43
49
{
44
50
offset;
45
51
beforeCursor = posBeforeCursor;
@@ -48,6 +54,7 @@ module PositionContext = struct
48
54
cursor = posCursor;
49
55
charBeforeCursor;
50
56
whitespaceAfterCursor;
57
+ locHasPos;
51
58
}
52
59
end
53
60
@@ -114,14 +121,19 @@ module CompletionInstruction = struct
114
121
end
115
122
116
123
type currentlyExpecting =
117
- | Unit
118
- | Type of ctxPath
124
+ | Unit (* * Unit, (). Is what we reset to. *)
125
+ | Type of ctxPath (* * A type at a context path. *)
126
+ | TypeAtLoc of Location .t (* * A type at a location. *)
119
127
| FunctionReturnType of ctxPath
128
+ (* * An instruction to resolve the return type of the type at the
129
+ provided context path, if it's a function (it should always be,
130
+ but you know...) *)
120
131
121
132
let currentlyExpectingToString (c : currentlyExpecting ) =
122
133
match c with
123
134
| Unit -> " Unit"
124
135
| Type ctxPath -> Printf. sprintf " Type<%s>" (ctxPathToString ctxPath)
136
+ | TypeAtLoc loc -> Printf. sprintf " TypeAtLoc: %s" (Loc. toString loc)
125
137
| FunctionReturnType ctxPath ->
126
138
Printf. sprintf " FunctionReturnType<%s>" (ctxPathToString ctxPath)
127
139
@@ -153,6 +165,41 @@ module CompletionContext = struct
153
165
currentlyExpecting :: completionContext .currentlyExpecting;
154
166
}
155
167
168
+ let addCurrentlyExpectingOpt currentlyExpecting completionContext =
169
+ match currentlyExpecting with
170
+ | None -> completionContext
171
+ | Some currentlyExpecting ->
172
+ {
173
+ completionContext with
174
+ currentlyExpecting =
175
+ currentlyExpecting :: completionContext .currentlyExpecting;
176
+ }
177
+
178
+ let currentlyExpectingOrReset currentlyExpecting completionContext =
179
+ match currentlyExpecting with
180
+ | None -> {completionContext with currentlyExpecting = [] }
181
+ | Some currentlyExpecting ->
182
+ {
183
+ completionContext with
184
+ currentlyExpecting =
185
+ currentlyExpecting :: completionContext .currentlyExpecting;
186
+ }
187
+
188
+ let currentlyExpectingOrTypeAtLoc ~loc currentlyExpecting completionContext =
189
+ match currentlyExpecting with
190
+ | None ->
191
+ {
192
+ completionContext with
193
+ currentlyExpecting =
194
+ TypeAtLoc loc :: completionContext .currentlyExpecting;
195
+ }
196
+ | Some currentlyExpecting ->
197
+ {
198
+ completionContext with
199
+ currentlyExpecting =
200
+ currentlyExpecting :: completionContext .currentlyExpecting;
201
+ }
202
+
156
203
let withResetCurrentlyExpecting completionContext =
157
204
{completionContext with currentlyExpecting = [Unit ]}
158
205
@@ -292,6 +339,13 @@ let rec scopePattern ~scope (pat : Parsetree.pattern) =
292
339
let scopeValueBinding ~scope (vb : Parsetree.value_binding ) =
293
340
scopePattern ~scope vb.pvb_pat
294
341
342
+ let scopeValueBindings ~scope (valueBindings : Parsetree.value_binding list ) =
343
+ let newScope = ref scope in
344
+ valueBindings
345
+ |> List. iter (fun (vb : Parsetree.value_binding ) ->
346
+ newScope := scopeValueBinding vb ~scope: ! newScope);
347
+ ! newScope
348
+
295
349
let scopeTypeKind ~scope (tk : Parsetree.type_kind ) =
296
350
match tk with
297
351
| Ptype_variant constrDecls ->
@@ -327,31 +381,11 @@ let rec completeFromStructure ~completionContext
327
381
328
382
and completeStructureItem ~(completionContext : CompletionContext.t )
329
383
(item : Parsetree.structure_item ) : CompletionResult.t =
384
+ let locHasPos = completionContext.positionContext.locHasPos in
330
385
match item.pstr_desc with
331
386
| Pstr_value (recFlag , valueBindings ) ->
332
- let scopeFromBindings =
333
- valueBindings
334
- |> List. map (fun (vb : Parsetree.value_binding ) ->
335
- scopeValueBinding vb ~scope: completionContext.scope)
336
- |> List. concat
337
- in
338
- if
339
- item.pstr_loc
340
- |> CursorPosition. classifyLoc
341
- ~pos: completionContext.positionContext.beforeCursor
342
- = HasCursor
343
- then
344
- valueBindings
345
- |> Utils. findMap (fun (vb : Parsetree.value_binding ) ->
346
- (* TODO: This will create duplicate scope entries for the current binding. Does it matter? *)
347
- completeValueBinding
348
- ~completion Context:
349
- (if recFlag = Recursive then
350
- completionContext
351
- |> contextWithNewScope scopeFromBindings
352
- |> CompletionContext. withResetCtx
353
- else completionContext)
354
- vb)
387
+ if locHasPos item.pstr_loc then
388
+ completeValueBindings ~completion Context ~rec Flag valueBindings
355
389
else None
356
390
| Pstr_eval _ | Pstr_primitive _ | Pstr_type _ | Pstr_typext _
357
391
| Pstr_exception _ | Pstr_module _ | Pstr_recmodule _ | Pstr_modtype _
@@ -360,47 +394,44 @@ and completeStructureItem ~(completionContext : CompletionContext.t)
360
394
| Pstr_class _ | Pstr_class_type _ ->
361
395
(* These aren't relevant for ReScript *) None
362
396
363
- and completeValueBinding ~completionContext (vb : Parsetree.value_binding ) :
364
- CompletionResult. t =
365
- let scopeWithPattern =
366
- scopePattern ~scope: completionContext.scope vb.pvb_pat
367
- in
368
- if
369
- vb.pvb_pat.ppat_loc
370
- |> CursorPosition. classifyLoc
371
- ~pos: completionContext.positionContext.beforeCursor
372
- = HasCursor
373
- then (
397
+ and completeValueBinding ~(completionContext : CompletionContext.t )
398
+ (vb : Parsetree.value_binding ) : CompletionResult.t =
399
+ let locHasPos = completionContext.positionContext.locHasPos in
400
+ if locHasPos vb.pvb_pat.ppat_loc then (
374
401
print_endline " complete pattern" ;
375
402
None )
376
- else if
377
- vb.pvb_expr.pexp_loc
378
- |> CursorPosition. classifyLoc
379
- ~pos: completionContext.positionContext.beforeCursor
380
- = HasCursor
381
- then
382
- let currentlyExpecting =
403
+ else if locHasPos vb.pvb_expr.pexp_loc then
404
+ let bindingConstraint =
383
405
findCurrentlyLookingForInPattern ~completion Context vb.pvb_pat
384
406
in
385
- completeExpr
386
- ~completion Context:
387
- {
388
- completionContext with
389
- scope = scopeWithPattern;
390
- currentlyExpecting =
391
- mergeCurrentlyLookingFor currentlyExpecting
392
- completionContext.currentlyExpecting;
393
- }
394
- vb.pvb_expr
407
+ (* A let binding expression either has the constraint of the binding,
408
+ or an inferred constraint (if it has been compiled), or no constraint. *)
409
+ let completionContext =
410
+ completionContext
411
+ |> CompletionContext. currentlyExpectingOrTypeAtLoc
412
+ ~loc: vb.pvb_pat.ppat_loc bindingConstraint
413
+ in
414
+ completeExpr ~completion Context vb.pvb_expr
395
415
else None
396
416
417
+ and completeValueBindings ~(completionContext : CompletionContext.t )
418
+ ~(recFlag : Asttypes.rec_flag )
419
+ (valueBindings : Parsetree.value_binding list ) : CompletionResult.t =
420
+ let completionContext =
421
+ if recFlag = Recursive then
422
+ let scopeFromBindings =
423
+ scopeValueBindings valueBindings ~scope: completionContext.scope
424
+ in
425
+ CompletionContext. withScope scopeFromBindings completionContext
426
+ else completionContext
427
+ in
428
+ valueBindings
429
+ |> Utils. findMap (fun (vb : Parsetree.value_binding ) ->
430
+ completeValueBinding ~completion Context vb)
431
+
397
432
and completeExpr ~completionContext (expr : Parsetree.expression ) :
398
433
CompletionResult. t =
399
- let locHasPos loc =
400
- loc
401
- |> CursorPosition. locHasCursor
402
- ~pos: completionContext.positionContext.beforeCursor
403
- in
434
+ let locHasPos = completionContext.positionContext.locHasPos in
404
435
match expr.pexp_desc with
405
436
(* == VARIANTS == *)
406
437
| Pexp_construct (_id, Some {pexp_desc = Pexp_tuple args; pexp_loc})
@@ -520,10 +551,22 @@ and completeExpr ~completionContext (expr : Parsetree.expression) :
520
551
if lid.loc |> locHasPos then
521
552
CompletionResult. ctxPath (CId (lidPath, Value )) completionContext
522
553
else None
523
- | Pexp_let (_recFlag , _valueBindings , nextExpr ) ->
554
+ | Pexp_let (recFlag , valueBindings , nextExpr ) ->
524
555
(* A let binding. `let a = b` *)
525
- (* TODO: Handle recflag, scope, and complete in value bindings *)
526
- if locHasPos nextExpr.pexp_loc then completeExpr ~completion Context nextExpr
556
+ let scopeFromBindings =
557
+ scopeValueBindings valueBindings ~scope: completionContext.scope
558
+ in
559
+ let completionContextWithScopeFromBindings =
560
+ CompletionContext. withScope scopeFromBindings completionContext
561
+ in
562
+ (* First check if the next expr is the thing with the cursor *)
563
+ if locHasPos nextExpr.pexp_loc then
564
+ completeExpr ~completion Context:completionContextWithScopeFromBindings
565
+ nextExpr
566
+ else if locHasPos expr.pexp_loc then
567
+ (* The cursor is in the expression, but not in the next expression.
568
+ Check the value bindings.*)
569
+ completeValueBindings ~rec Flag ~completion Context valueBindings
527
570
else None
528
571
| Pexp_ifthenelse (condition , then_ , maybeElse ) -> (
529
572
if locHasPos condition.pexp_loc then
0 commit comments