Skip to content

Commit 61b196d

Browse files
committed
moar work
1 parent d7910e9 commit 61b196d

File tree

4 files changed

+139
-78
lines changed

4 files changed

+139
-78
lines changed

.vscode/settings.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@
88
},
99
"ocaml.sandbox": {
1010
"kind": "opam",
11-
"switch": "${workspaceFolder:rescript-vscode}/analysis"
11+
"switch": "4.14.0"
1212
}
1313
}

analysis/src/CompletionFrontEndNew.ml

+104-61
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ module PositionContext = struct
1313
(** The char before the cursor, not excluding whitespace *)
1414
whitespaceAfterCursor: char option;
1515
(** 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. *)
1619
}
1720

1821
let make ~offset ~posCursor text =
@@ -40,6 +43,9 @@ module PositionContext = struct
4043
| _ -> (Some charBeforeCursor, None))
4144
| _ -> (None, None)
4245
in
46+
let locHasPos loc =
47+
loc |> CursorPosition.locHasCursor ~pos:posBeforeCursor
48+
in
4349
{
4450
offset;
4551
beforeCursor = posBeforeCursor;
@@ -48,6 +54,7 @@ module PositionContext = struct
4854
cursor = posCursor;
4955
charBeforeCursor;
5056
whitespaceAfterCursor;
57+
locHasPos;
5158
}
5259
end
5360

@@ -114,14 +121,19 @@ module CompletionInstruction = struct
114121
end
115122

116123
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. *)
119127
| 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...) *)
120131

121132
let currentlyExpectingToString (c : currentlyExpecting) =
122133
match c with
123134
| Unit -> "Unit"
124135
| Type ctxPath -> Printf.sprintf "Type<%s>" (ctxPathToString ctxPath)
136+
| TypeAtLoc loc -> Printf.sprintf "TypeAtLoc: %s" (Loc.toString loc)
125137
| FunctionReturnType ctxPath ->
126138
Printf.sprintf "FunctionReturnType<%s>" (ctxPathToString ctxPath)
127139

@@ -153,6 +165,41 @@ module CompletionContext = struct
153165
currentlyExpecting :: completionContext.currentlyExpecting;
154166
}
155167

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+
156203
let withResetCurrentlyExpecting completionContext =
157204
{completionContext with currentlyExpecting = [Unit]}
158205

@@ -292,6 +339,13 @@ let rec scopePattern ~scope (pat : Parsetree.pattern) =
292339
let scopeValueBinding ~scope (vb : Parsetree.value_binding) =
293340
scopePattern ~scope vb.pvb_pat
294341

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+
295349
let scopeTypeKind ~scope (tk : Parsetree.type_kind) =
296350
match tk with
297351
| Ptype_variant constrDecls ->
@@ -327,31 +381,11 @@ let rec completeFromStructure ~completionContext
327381

328382
and completeStructureItem ~(completionContext : CompletionContext.t)
329383
(item : Parsetree.structure_item) : CompletionResult.t =
384+
let locHasPos = completionContext.positionContext.locHasPos in
330385
match item.pstr_desc with
331386
| 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-
~completionContext:
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 ~completionContext ~recFlag valueBindings
355389
else None
356390
| Pstr_eval _ | Pstr_primitive _ | Pstr_type _ | Pstr_typext _
357391
| Pstr_exception _ | Pstr_module _ | Pstr_recmodule _ | Pstr_modtype _
@@ -360,47 +394,44 @@ and completeStructureItem ~(completionContext : CompletionContext.t)
360394
| Pstr_class _ | Pstr_class_type _ ->
361395
(* These aren't relevant for ReScript *) None
362396

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 (
374401
print_endline "complete pattern";
375402
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 =
383405
findCurrentlyLookingForInPattern ~completionContext vb.pvb_pat
384406
in
385-
completeExpr
386-
~completionContext:
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 ~completionContext vb.pvb_expr
395415
else None
396416

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 ~completionContext vb)
431+
397432
and completeExpr ~completionContext (expr : Parsetree.expression) :
398433
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
404435
match expr.pexp_desc with
405436
(* == VARIANTS == *)
406437
| Pexp_construct (_id, Some {pexp_desc = Pexp_tuple args; pexp_loc})
@@ -520,10 +551,22 @@ and completeExpr ~completionContext (expr : Parsetree.expression) :
520551
if lid.loc |> locHasPos then
521552
CompletionResult.ctxPath (CId (lidPath, Value)) completionContext
522553
else None
523-
| Pexp_let (_recFlag, _valueBindings, nextExpr) ->
554+
| Pexp_let (recFlag, valueBindings, nextExpr) ->
524555
(* A let binding. `let a = b` *)
525-
(* TODO: Handle recflag, scope, and complete in value bindings *)
526-
if locHasPos nextExpr.pexp_loc then completeExpr ~completionContext 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 ~completionContext: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 ~recFlag ~completionContext valueBindings
527570
else None
528571
| Pexp_ifthenelse (condition, then_, maybeElse) -> (
529572
if locHasPos condition.pexp_loc then

analysis/tests/src/CompletionNew.res

+8
Original file line numberDiff line numberDiff line change
@@ -72,3 +72,11 @@ type fn = (~name: string=?, string) => bool
7272

7373
// let someFun: fn = (str, ~name) => {let whatever = true; if whatever {}}
7474
// ^co2
75+
76+
// A let binding with an annotation. Reset to annotated constraint.
77+
// let someFun: fn = (str, ~name) => {let whatever: bool = t}
78+
// ^co2
79+
80+
// A let binding without annotation. Point to inferred type if it has compiled.
81+
// let someFun: fn = (str, ~name) => {let whatever = t}
82+
// ^co2
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,61 @@
11
Complete2 src/CompletionNew.res 2:17
22
Result: CId(Value)=m
3-
Scope: 1 items
4-
Looking for type:
3+
Scope: 0 items
4+
Looking for type: TypeAtLoc: [2:7->2:13]
55

66
Complete2 src/CompletionNew.res 7:30
77
Result: CId(Module)=O
8-
Scope: 1 items
8+
Scope: 0 items
99
Looking for type: Type<CId(Type)=someVariant>
1010

1111
Complete2 src/CompletionNew.res 10:36
1212
Result: CVariantPayload($0)->CId(Value)=t
13-
Scope: 1 items
13+
Scope: 0 items
1414
Looking for type: Type<CId(Type)=someVariant>
1515

1616
Complete2 src/CompletionNew.res 13:42
1717
Result: CVariantPayload($1)->CId(Module)=S
18-
Scope: 1 items
18+
Scope: 0 items
1919
Looking for type: Type<CId(Type)=someVariant>
2020

2121
Complete2 src/CompletionNew.res 16:47
2222
Result: CVariantPayload($1)->CVariantPayload($0)->CId(Module)=O
23-
Scope: 1 items
23+
Scope: 0 items
2424
Looking for type: Type<CId(Type)=someVariant>
2525

2626
Complete2 src/CompletionNew.res 27:29
2727
Result: CRecordField=
28-
Scope: 1 items
28+
Scope: 0 items
2929
Looking for type: Type<CId(Type)=someRecord>
3030

3131
Complete2 src/CompletionNew.res 30:30
3232
Result: CRecordField=n
33-
Scope: 1 items
33+
Scope: 0 items
3434
Looking for type: Type<CId(Type)=someRecord>
3535

3636
Complete2 src/CompletionNew.res 33:39
3737
Result: CRecordField=variant->CId(Module)=O
38-
Scope: 1 items
38+
Scope: 0 items
3939
Looking for type: Type<CId(Type)=someRecord>
4040

4141
Complete2 src/CompletionNew.res 36:66
4242
Result: CRecordField=nested->CRecordField=maybeVariant->CVariantPayload($1)->CId(Value)=t
43-
Scope: 1 items
43+
Scope: 0 items
4444
Looking for type: Type<CId(Type)=someRecord>
4545

4646
Complete2 src/CompletionNew.res 39:66
4747
Result: CRecordField=variant
48-
Scope: 1 items
48+
Scope: 0 items
4949
Looking for type: Type<CId(Type)=someRecord>
5050

5151
Complete2 src/CompletionNew.res 42:56
5252
Result: CRecordField=nested->CRecordField=
53-
Scope: 1 items
53+
Scope: 0 items
5454
Looking for type: Type<CId(Type)=someRecord>
5555

5656
Complete2 src/CompletionNew.res 45:57
5757
Result: CRecordField=
58-
Scope: 1 items
58+
Scope: 0 items
5959
Looking for type: Type<CId(Type)=someRecord>
6060

6161
Complete2 src/CompletionNew.res 49:71
@@ -75,21 +75,31 @@ Looking for type: Type<CId(Type)=someRecord>
7575

7676
Complete2 src/CompletionNew.res 61:58
7777
Result: CId(Value)=doStuff
78-
Scope: 1 items
78+
Scope: 0 items
7979
Looking for type:
8080

8181
Complete2 src/CompletionNew.res 66:32
8282
Result: CId(Value)=
83-
Scope: 2 items
83+
Scope: 1 items
8484
Looking for type: Type<CBool>
8585

8686
Complete2 src/CompletionNew.res 69:38
8787
Result: CRecordField=
88-
Scope: 3 items
88+
Scope: 2 items
8989
Looking for type: FunctionReturnType<CId(Type)=fn>
9090

9191
Complete2 src/CompletionNew.res 72:72
9292
Result: CId(Value)=
9393
Scope: 3 items
9494
Looking for type: FunctionReturnType<CId(Type)=fn>
9595

96+
Complete2 src/CompletionNew.res 76:60
97+
Result: CId(Value)=t
98+
Scope: 2 items
99+
Looking for type: Type<CBool>
100+
101+
Complete2 src/CompletionNew.res 80:54
102+
Result: CId(Value)=t
103+
Scope: 2 items
104+
Looking for type: TypeAtLoc: [80:42->80:50]
105+

0 commit comments

Comments
 (0)