From 9a63a84ea67412669020bc542540815b75c88ffe Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Fri, 30 Dec 2022 22:16:20 +0100 Subject: [PATCH 01/31] basic setup of some sort of structure, and complete tuple patterns --- analysis/src/CompletionBackEnd.ml | 40 +++++++ analysis/src/CompletionFrontEnd.ml | 101 +++++++++++++++++- analysis/src/SharedTypes.ml | 23 ++++ analysis/tests/src/CompletionPattern.res | 18 ++++ .../tests/src/expected/Completion.res.txt | 3 + .../src/expected/CompletionPattern.res.txt | 48 +++++++++ 6 files changed, 232 insertions(+), 1 deletion(-) create mode 100644 analysis/tests/src/CompletionPattern.res create mode 100644 analysis/tests/src/expected/CompletionPattern.res.txt diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index 3568d3581..6dba2ec2a 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -1729,6 +1729,17 @@ let getJsxLabels ~componentPath ~findTypeOfValue ~package = typ |> getLabels | None -> [] +let rec resolveNestedPattern typ ~env ~package ~nested = + match nested with + | [] -> Some (typ, env) + | patternPath :: nested -> ( + match (patternPath, typ |> extractType ~env ~package) with + | Completable.PTupleItem {itemNum}, Some (Tuple (env, tupleItems, _)) -> ( + match List.nth_opt tupleItems itemNum with + | None -> None + | Some typ -> typ |> resolveNestedPattern ~env ~package ~nested) + | _ -> None) + let processCompletable ~debug ~full ~scope ~env ~pos ~forHover (completable : Completable.t) = let package = full.package in @@ -2091,3 +2102,32 @@ Note: The `@react.component` decorator requires the react-jsx config to be set i Utils.startsWith name prefix && (forHover || not (List.mem name identsSeen))) |> List.map mkLabel + | Cpattern {typ; prefix; nested = None} -> ( + let envWhereCompletionStarted = env in + match + typ + |> getCompletionsForContextPath ~full ~opens ~rawOpens ~allFiles ~pos ~env + ~exact:true ~scope + |> completionsGetTypeEnv + with + | Some (typ, env) -> + typ + |> completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix + ~expandOption:false + | None -> []) + | Cpattern {typ; prefix; nested = Some nested} -> ( + let envWhereCompletionStarted = env in + match + typ + |> getCompletionsForContextPath ~full ~opens ~rawOpens ~allFiles ~pos ~env + ~exact:true ~scope + |> completionsGetTypeEnv + with + | Some (typ, env) -> ( + match typ |> resolveNestedPattern ~env ~package:full.package ~nested with + | None -> [] + | Some (typ, env) -> + typ + |> completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix + ~expandOption:false) + | None -> []) diff --git a/analysis/src/CompletionFrontEnd.ml b/analysis/src/CompletionFrontEnd.ml index aaeee4dd1..ea12c7f4c 100644 --- a/analysis/src/CompletionFrontEnd.ml +++ b/analysis/src/CompletionFrontEnd.ml @@ -20,6 +20,11 @@ let isExprHole exp = | Pexp_extension ({txt = "rescript.exprhole"}, _) -> true | _ -> false +let isPatternHole pat = + match pat.Parsetree.ppat_desc with + | Ppat_extension ({txt = "rescript.patternhole"}, _) -> true + | _ -> false + type prop = { name: string; posStart: int * int; @@ -410,6 +415,96 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = !scope |> Scope.addModule ~name:md.pmd_name.txt ~loc:md.pmd_name.loc in + let lookingForPat = ref None in + + let setLookingForPat ctxPath = + lookingForPat := + Some (Completable.Cpattern {typ = ctxPath; prefix = ""; nested = None}); + Printf.printf "looking for: %s \n" (Completable.toString (Cpath ctxPath)) + in + let appendNestedPat patternPat = + match !lookingForPat with + | Some (Completable.Cpattern ({nested = None} as p)) -> + lookingForPat := Some (Cpattern {p with nested = Some [patternPat]}) + | Some (Completable.Cpattern ({nested = Some nested} as p)) -> + lookingForPat := + Some (Cpattern {p with nested = Some (nested @ [patternPat])}) + | _ -> () + in + let commitFoundPat ?prefix () = + match !lookingForPat with + | Some (Completable.Cpattern p as cpattern) -> + let cpattern = + match prefix with + | None -> cpattern + | Some prefix -> Cpattern {p with prefix} + in + setResult cpattern + | _ -> () + in + + let rec typedCompletionExpr (exp : Parsetree.expression) = + if + exp.pexp_loc + |> CursorPosition.classifyLoc ~pos:posBeforeCursor + = HasCursor + then + match exp.pexp_desc with + | Pexp_match (_exp, []) -> + (* No cases means there's no `|` yet in the switch *) () + | Pexp_match + ( exp, + [ + { + pc_lhs = + { + ppat_desc = + Ppat_extension ({txt = "rescript.patternhole"}, _); + }; + }; + ] ) -> ( + (* A single case that's a pattern hole typically means `switch x { | }`. Complete as the pattern itself with nothing nested. *) + match exprToContextPath exp with + | None -> () + | Some ctxPath -> + setResult + (Completable.Cpattern {typ = ctxPath; nested = None; prefix = ""})) + | Pexp_match (exp, _cases) -> ( + (* If there's more than one case, or the case isn't a pattern hole, set that we're looking for this path currently. *) + match exp |> exprToContextPath with + | None -> () + | Some ctxPath -> setLookingForPat ctxPath) + | _ -> () + in + + let rec typedCompletionPat (pat : Parsetree.pattern) = + if + pat.ppat_loc + |> CursorPosition.classifyLoc ~pos:posBeforeCursor + = HasCursor + then + match pat.ppat_desc with + | Ppat_var {txt} -> commitFoundPat ~prefix:txt () + | Ppat_tuple patterns -> ( + let patCount = ref (-1) in + let patCountWithPatHole = ref None in + patterns + |> List.iteri (fun index p -> + match + p.Parsetree.ppat_loc + |> CursorPosition.classifyLoc ~pos:posBeforeCursor + with + | HasCursor -> patCount := index + | EmptyLoc -> patCountWithPatHole := Some index + | _ -> ()); + match (!patCount, !patCountWithPatHole) with + | patCount, _ when patCount > -1 -> + appendNestedPat (Completable.PTupleItem {itemNum = patCount}) + | _, Some patHoleCount -> + appendNestedPat (Completable.PTupleItem {itemNum = patHoleCount}) + | _ -> ()) + | _ -> () + in let case (iterator : Ast_iterator.iterator) (case : Parsetree.case) = let oldScope = !scope in scopePattern case.pc_lhs; @@ -548,6 +643,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = setResult (Cpath (CPPipe {contextPath = pipe; id; lhsLoc})); true in + typedCompletionExpr expr; match expr.pexp_desc with | Pexp_apply ( {pexp_desc = Pexp_ident {txt = Lident "|."; loc = opLoc}}, @@ -807,7 +903,10 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = (Loc.toString lid.loc); setResult (Cpath (CPId (lidPath, Value))) | _ -> ()); - Ast_iterator.default_iterator.pat iterator pat) + let oldLookingForPat = !lookingForPat in + typedCompletionPat pat; + Ast_iterator.default_iterator.pat iterator pat; + lookingForPat := oldLookingForPat) in let module_expr (iterator : Ast_iterator.iterator) (me : Parsetree.module_expr) = diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index ef2808427..4e4f5ac61 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -550,6 +550,12 @@ module Completable = struct (** The loc item for the left hand side of the pipe. *) } + type patternPath = PTupleItem of {itemNum: int} + + let patternPathToString p = + match p with + | PTupleItem {itemNum} -> "tuple($" ^ string_of_int itemNum ^ ")" + type t = | Cdecorator of string (** e.g. @module *) | CnamedArg of contextPath * string * string list @@ -569,6 +575,11 @@ module Completable = struct propName: string; prefix: string; } + | Cpattern of { + typ: contextPath; + nested: patternPath list option; + prefix: string; + } (** An extracted type from a type expr *) type extractedType = @@ -614,6 +625,7 @@ module Completable = struct | CPObj (cp, s) -> contextPathToString cp ^ "[\"" ^ s ^ "\"]" | CPPipe {contextPath; id} -> contextPathToString contextPath ^ "->" ^ id in + function | Cpath cp -> "Cpath " ^ contextPathToString cp | Cdecorator s -> "Cdecorator(" ^ str s ^ ")" @@ -637,6 +649,17 @@ module Completable = struct | CjsxPropValue {prefix; pathToComponent; propName} -> "CjsxPropValue " ^ (pathToComponent |> list) ^ " " ^ propName ^ "=" ^ prefix + | Cpattern {typ; nested; prefix} -> ( + "Cpattern " ^ contextPathToString typ + ^ (if prefix = "" then "" else "=" ^ prefix) + ^ + match nested with + | None -> "" + | Some patternPaths -> + "->" + ^ (patternPaths + |> List.map (fun patternPath -> patternPathToString patternPath) + |> String.concat ", ")) end module CursorPosition = struct diff --git a/analysis/tests/src/CompletionPattern.res b/analysis/tests/src/CompletionPattern.res new file mode 100644 index 000000000..1b8e1e411 --- /dev/null +++ b/analysis/tests/src/CompletionPattern.res @@ -0,0 +1,18 @@ +let v = (true, Some(false)) + +// switch v { +// ^com + +// switch v { | } +// ^com + +// switch v { | (t, _) } +// ^com + +let x = true + +// switch x { | +// ^com + +// switch x { | t +// ^com diff --git a/analysis/tests/src/expected/Completion.res.txt b/analysis/tests/src/expected/Completion.res.txt index 56d4cd5a6..cdd084bbd 100644 --- a/analysis/tests/src/expected/Completion.res.txt +++ b/analysis/tests/src/expected/Completion.res.txt @@ -965,6 +965,7 @@ Resolved opens 2 Completion.res Completion.res [] Complete src/Completion.res 243:8 +looking for: Cpath Value[someR] posCursor:[243:8] posNoWhite:[243:7] Found expr:[241:8->246:1] posCursor:[243:8] posNoWhite:[243:7] Found expr:[242:14->243:8] Pexp_apply ...[243:3->243:4] (...[242:14->242:15], ...[243:5->243:8]) @@ -1442,6 +1443,7 @@ posCursor:[355:23] posNoWhite:[355:22] Found expr:[355:12->355:23] Complete src/Completion.res 362:8 posCursor:[362:8] posNoWhite:[362:7] Found expr:[360:8->365:3] +looking for: Cpath Value[x] posCursor:[362:8] posNoWhite:[362:7] Found expr:[361:2->365:3] posCursor:[362:8] posNoWhite:[362:7] Found pattern:[362:7->364:5] posCursor:[362:8] posNoWhite:[362:7] Found pattern:[362:7->362:8] @@ -1477,6 +1479,7 @@ Resolved opens 2 Completion.res Completion.res Complete src/Completion.res 373:21 posCursor:[373:21] posNoWhite:[373:20] Found expr:[371:8->376:3] +looking for: Cpath Value[x] posCursor:[373:21] posNoWhite:[373:20] Found expr:[372:2->376:3] posCursor:[373:21] posNoWhite:[373:20] Found pattern:[373:7->375:5] posCursor:[373:21] posNoWhite:[373:20] Found pattern:[373:7->373:21] diff --git a/analysis/tests/src/expected/CompletionPattern.res.txt b/analysis/tests/src/expected/CompletionPattern.res.txt new file mode 100644 index 000000000..38157efb5 --- /dev/null +++ b/analysis/tests/src/expected/CompletionPattern.res.txt @@ -0,0 +1,48 @@ +Complete src/CompletionPattern.res 2:13 +posCursor:[2:13] posNoWhite:[2:12] Found expr:[2:3->2:13] +[] + +Complete src/CompletionPattern.res 5:15 +XXX Not found! +Completable: Cpattern Value[v] +[{ + "label": "(_, _)", + "kind": 12, + "tags": [], + "detail": "(bool, option)", + "documentation": null, + "insertText": "(${1:_}, ${2:_})", + "insertTextFormat": 2 + }] + +Complete src/CompletionPattern.res 8:18 +looking for: Cpath Value[v] +posCursor:[8:18] posNoWhite:[8:17] Found expr:[8:3->8:24] +posCursor:[8:18] posNoWhite:[8:17] Found pattern:[8:16->8:22] +posCursor:[8:18] posNoWhite:[8:17] Found pattern:[8:17->8:18] +Completable: Cpattern Value[v]=t->tuple($0) +[{ + "label": "true", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }] + +Complete src/CompletionPattern.res 13:16 +posCursor:[13:16] posNoWhite:[13:14] Found expr:[13:3->13:15] +[] + +Complete src/CompletionPattern.res 16:17 +looking for: Cpath Value[x] +posCursor:[16:17] posNoWhite:[16:16] Found expr:[16:3->16:17] +posCursor:[16:17] posNoWhite:[16:16] Found pattern:[16:16->16:17] +Completable: Cpattern Value[x]=t +[{ + "label": "true", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }] + From 112ee62423e100f36a48e676037e3100a2eb0e5f Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Fri, 30 Dec 2022 22:20:30 +0100 Subject: [PATCH 02/31] nested tuple --- analysis/tests/src/CompletionPattern.res | 10 ++++- .../src/expected/CompletionPattern.res.txt | 45 ++++++++++++------- 2 files changed, 39 insertions(+), 16 deletions(-) diff --git a/analysis/tests/src/CompletionPattern.res b/analysis/tests/src/CompletionPattern.res index 1b8e1e411..287352a54 100644 --- a/analysis/tests/src/CompletionPattern.res +++ b/analysis/tests/src/CompletionPattern.res @@ -1,4 +1,9 @@ -let v = (true, Some(false)) +let v = (true, Some(false), (true, true)) + +let _ = switch v { +| (true, _, _) => 1 +| _ => 2 +} // switch v { // ^com @@ -9,6 +14,9 @@ let v = (true, Some(false)) // switch v { | (t, _) } // ^com +// switch v { | (_, _, (f, _)) } +// ^com + let x = true // switch x { | diff --git a/analysis/tests/src/expected/CompletionPattern.res.txt b/analysis/tests/src/expected/CompletionPattern.res.txt index 38157efb5..d6d25648b 100644 --- a/analysis/tests/src/expected/CompletionPattern.res.txt +++ b/analysis/tests/src/expected/CompletionPattern.res.txt @@ -1,25 +1,25 @@ -Complete src/CompletionPattern.res 2:13 -posCursor:[2:13] posNoWhite:[2:12] Found expr:[2:3->2:13] +Complete src/CompletionPattern.res 7:13 +posCursor:[7:13] posNoWhite:[7:12] Found expr:[7:3->7:13] [] -Complete src/CompletionPattern.res 5:15 +Complete src/CompletionPattern.res 10:15 XXX Not found! Completable: Cpattern Value[v] [{ - "label": "(_, _)", + "label": "(_, _, _)", "kind": 12, "tags": [], - "detail": "(bool, option)", + "detail": "(bool, option, (bool, bool))", "documentation": null, - "insertText": "(${1:_}, ${2:_})", + "insertText": "(${1:_}, ${2:_}, ${3:_})", "insertTextFormat": 2 }] -Complete src/CompletionPattern.res 8:18 +Complete src/CompletionPattern.res 13:18 looking for: Cpath Value[v] -posCursor:[8:18] posNoWhite:[8:17] Found expr:[8:3->8:24] -posCursor:[8:18] posNoWhite:[8:17] Found pattern:[8:16->8:22] -posCursor:[8:18] posNoWhite:[8:17] Found pattern:[8:17->8:18] +posCursor:[13:18] posNoWhite:[13:17] Found expr:[13:3->13:24] +posCursor:[13:18] posNoWhite:[13:17] Found pattern:[13:16->13:22] +posCursor:[13:18] posNoWhite:[13:17] Found pattern:[13:17->13:18] Completable: Cpattern Value[v]=t->tuple($0) [{ "label": "true", @@ -29,14 +29,29 @@ Completable: Cpattern Value[v]=t->tuple($0) "documentation": null }] -Complete src/CompletionPattern.res 13:16 -posCursor:[13:16] posNoWhite:[13:14] Found expr:[13:3->13:15] +Complete src/CompletionPattern.res 16:25 +looking for: Cpath Value[v] +posCursor:[16:25] posNoWhite:[16:24] Found expr:[16:3->16:32] +posCursor:[16:25] posNoWhite:[16:24] Found pattern:[16:16->16:30] +posCursor:[16:25] posNoWhite:[16:24] Found pattern:[16:23->16:29] +posCursor:[16:25] posNoWhite:[16:24] Found pattern:[16:24->16:25] +Completable: Cpattern Value[v]=f->tuple($2), tuple($0) +[{ + "label": "false", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }] + +Complete src/CompletionPattern.res 21:16 +posCursor:[21:16] posNoWhite:[21:14] Found expr:[21:3->21:15] [] -Complete src/CompletionPattern.res 16:17 +Complete src/CompletionPattern.res 24:17 looking for: Cpath Value[x] -posCursor:[16:17] posNoWhite:[16:16] Found expr:[16:3->16:17] -posCursor:[16:17] posNoWhite:[16:16] Found pattern:[16:16->16:17] +posCursor:[24:17] posNoWhite:[24:16] Found expr:[24:3->24:17] +posCursor:[24:17] posNoWhite:[24:16] Found pattern:[24:16->24:17] Completable: Cpattern Value[x]=t [{ "label": "true", From c18e7bf5e25e216230bcf0d4e700f041a44ede3b Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Fri, 30 Dec 2022 23:17:00 +0100 Subject: [PATCH 03/31] start integrating completion for records --- analysis/src/CompletionBackEnd.ml | 30 ++++-- analysis/src/CompletionFrontEnd.ml | 36 ++++++- analysis/src/SharedTypes.ml | 10 +- analysis/tests/src/CompletionPattern.res | 32 ++++++- .../src/expected/CompletionPattern.res.txt | 93 ++++++++++++++++++- 5 files changed, 186 insertions(+), 15 deletions(-) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index 6dba2ec2a..a41993136 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -1528,6 +1528,8 @@ let rec extractType ~env ~package (t : Types.type_expr) = Some (Tvariant {env; constructors; variantName = name.txt; variantDecl = decl}) + | Some (env, {item = {kind = Record fields}}) -> + Some (Trecord {env; fields; typeExpr = t}) | _ -> None) | Ttuple expressions -> Some (Tuple (env, expressions, t)) | Tvariant {row_fields} -> @@ -1566,7 +1568,7 @@ let printConstructorArgs argsLen ~asSnippet = else "" let completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix - ~expandOption = + ~expandOption ~includeLocalValues = let namesUsed = Hashtbl.create 10 in let rec completeTypedValueInner t ~env ~full ~prefix ~expandOption = let items = @@ -1641,10 +1643,17 @@ let completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix ~insertText:(printConstructorArgs numExprs ~asSnippet:true) ~kind:(Value typ) ~env (); ] + | Some (Trecord {env; fields; typeExpr}) -> + fields + |> List.map (fun (field : field) -> + Completion.create ~name:field.fname.txt + ~kind:(Field (field, typeExpr |> Shared.typeToString)) + ~env) + |> filterItems ~prefix | _ -> [] in (* Include all values and modules in completion if there's a prefix, not otherwise *) - if prefix = "" then items + if prefix = "" || includeLocalValues = false then items else items @ completionForExportedValues ~env:envWhereCompletionStarted ~prefix @@ -1738,6 +1747,13 @@ let rec resolveNestedPattern typ ~env ~package ~nested = match List.nth_opt tupleItems itemNum with | None -> None | Some typ -> typ |> resolveNestedPattern ~env ~package ~nested) + | Completable.PRecordField {fieldName}, Some (Trecord {env; fields}) -> ( + match + fields + |> List.find_opt (fun (field : field) -> field.fname.txt = fieldName) + with + | None -> None + | Some {typ} -> typ |> resolveNestedPattern ~env ~package ~nested) | _ -> None) let processCompletable ~debug ~full ~scope ~env ~pos ~forHover @@ -1803,7 +1819,7 @@ let processCompletable ~debug ~full ~scope ~env ~pos ~forHover | Some (_, typ, env) -> typ |> completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix - ~expandOption:true) + ~expandOption:true ~includeLocalValues:true) | Cdecorator prefix -> let mkDecorator (name, docstring) = {(Completion.create ~name ~kind:(Label "") ~env) with docstring} @@ -2068,11 +2084,11 @@ Note: The `@react.component` decorator requires the react-jsx config to be set i | Some (Optional _, typ) -> typ |> completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix - ~expandOption:true + ~expandOption:true ~includeLocalValues:true | Some ((Unlabelled _ | Labelled _), typ) -> typ |> completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix - ~expandOption:false) + ~expandOption:false ~includeLocalValues:true) | CnamedArg (cp, prefix, identsSeen) -> let labels = match @@ -2113,7 +2129,7 @@ Note: The `@react.component` decorator requires the react-jsx config to be set i | Some (typ, env) -> typ |> completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix - ~expandOption:false + ~expandOption:false ~includeLocalValues:false | None -> []) | Cpattern {typ; prefix; nested = Some nested} -> ( let envWhereCompletionStarted = env in @@ -2129,5 +2145,5 @@ Note: The `@react.component` decorator requires the react-jsx config to be set i | Some (typ, env) -> typ |> completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix - ~expandOption:false) + ~expandOption:false ~includeLocalValues:false) | None -> []) diff --git a/analysis/src/CompletionFrontEnd.ml b/analysis/src/CompletionFrontEnd.ml index ea12c7f4c..d909e2739 100644 --- a/analysis/src/CompletionFrontEnd.ml +++ b/analysis/src/CompletionFrontEnd.ml @@ -420,7 +420,8 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = let setLookingForPat ctxPath = lookingForPat := Some (Completable.Cpattern {typ = ctxPath; prefix = ""; nested = None}); - Printf.printf "looking for: %s \n" (Completable.toString (Cpath ctxPath)) + if debug then + Printf.printf "looking for: %s \n" (Completable.toString (Cpath ctxPath)) in let appendNestedPat patternPat = match !lookingForPat with @@ -443,7 +444,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = | _ -> () in - let rec typedCompletionExpr (exp : Parsetree.expression) = + let typedCompletionExpr (exp : Parsetree.expression) = if exp.pexp_loc |> CursorPosition.classifyLoc ~pos:posBeforeCursor @@ -477,7 +478,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = | _ -> () in - let rec typedCompletionPat (pat : Parsetree.pattern) = + let typedCompletionPat (pat : Parsetree.pattern) = if pat.ppat_loc |> CursorPosition.classifyLoc ~pos:posBeforeCursor @@ -503,6 +504,35 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = | _, Some patHoleCount -> appendNestedPat (Completable.PTupleItem {itemNum = patHoleCount}) | _ -> ()) + | Ppat_record ([], _) -> commitFoundPat ~prefix:"" () + | Ppat_record (fields, _) -> ( + let fieldWithCursor = ref None in + let fieldWithPatHole = ref None in + fields + |> List.filter (fun (_, f) -> + (* Ensure we only include fields with patterns we can continue into. *) + match f.Parsetree.ppat_desc with + | Ppat_tuple _ | Ppat_construct _ | Ppat_variant _ + | Ppat_record _ -> + true + | _ -> false) + |> List.iter (fun (fname, f) -> + match + ( fname.Location.txt, + f.Parsetree.ppat_loc + |> CursorPosition.classifyLoc ~pos:posBeforeCursor ) + with + | Longident.Lident fname, HasCursor -> + fieldWithCursor := Some fname + | Lident fname, EmptyLoc when isPatternHole f -> + fieldWithPatHole := Some fname + | _ -> ()); + match (!fieldWithCursor, !fieldWithPatHole) with + | Some fname, _ -> + appendNestedPat (Completable.PRecordField {fieldName = fname}) + | None, Some fname -> + appendNestedPat (Completable.PRecordField {fieldName = fname}) + | _ -> ()) | _ -> () in let case (iterator : Ast_iterator.iterator) (case : Parsetree.case) = diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index 4e4f5ac61..9214d1c22 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -550,11 +550,14 @@ module Completable = struct (** The loc item for the left hand side of the pipe. *) } - type patternPath = PTupleItem of {itemNum: int} + type patternPath = + | PTupleItem of {itemNum: int} + | PRecordField of {fieldName: string} let patternPathToString p = match p with | PTupleItem {itemNum} -> "tuple($" ^ string_of_int itemNum ^ ")" + | PRecordField {fieldName} -> "recordField(" ^ fieldName ^ ")" type t = | Cdecorator of string (** e.g. @module *) @@ -597,6 +600,11 @@ module Completable = struct constructors: polyVariantConstructor list; typeExpr: Types.type_expr; } + | Trecord of { + env: QueryEnv.t; + fields: field list; + typeExpr: Types.type_expr; + } let toString = let completionContextToString = function diff --git a/analysis/tests/src/CompletionPattern.res b/analysis/tests/src/CompletionPattern.res index 287352a54..52e1ddb5b 100644 --- a/analysis/tests/src/CompletionPattern.res +++ b/analysis/tests/src/CompletionPattern.res @@ -20,7 +20,37 @@ let _ = switch v { let x = true // switch x { | -// ^com +// ^com // switch x { | t // ^com + +type nestedRecord = {nested: bool} + +type rec someRecord = { + first: int, + second: (bool, option), + optThird: option<[#first | #second(someRecord)]>, + nest: nestedRecord, +} + +let f: someRecord = { + first: 123, + second: (true, None), + optThird: None, + nest: {nested: true}, +} + +let z = (f, true) + +// switch f { | {}} +// ^com + +// switch f { | {fi}} +// ^com + +// switch z { | ({o}, _)} +// ^com + +// switch f { | {nest: {}}} +// ^com diff --git a/analysis/tests/src/expected/CompletionPattern.res.txt b/analysis/tests/src/expected/CompletionPattern.res.txt index d6d25648b..19126bff0 100644 --- a/analysis/tests/src/expected/CompletionPattern.res.txt +++ b/analysis/tests/src/expected/CompletionPattern.res.txt @@ -44,9 +44,22 @@ Completable: Cpattern Value[v]=f->tuple($2), tuple($0) "documentation": null }] -Complete src/CompletionPattern.res 21:16 -posCursor:[21:16] posNoWhite:[21:14] Found expr:[21:3->21:15] -[] +Complete src/CompletionPattern.res 21:15 +XXX Not found! +Completable: Cpattern Value[x] +[{ + "label": "true", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }, { + "label": "false", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }] Complete src/CompletionPattern.res 24:17 looking for: Cpath Value[x] @@ -61,3 +74,77 @@ Completable: Cpattern Value[x]=t "documentation": null }] +Complete src/CompletionPattern.res 45:17 +looking for: Cpath Value[f] +posCursor:[45:17] posNoWhite:[45:16] Found expr:[45:3->45:19] +posCursor:[45:17] posNoWhite:[45:16] Found pattern:[45:16->45:18] +Completable: Cpattern Value[f] +[{ + "label": "first", + "kind": 5, + "tags": [], + "detail": "first: int\n\nsomeRecord", + "documentation": null + }, { + "label": "second", + "kind": 5, + "tags": [], + "detail": "second: (bool, option)\n\nsomeRecord", + "documentation": null + }, { + "label": "optThird", + "kind": 5, + "tags": [], + "detail": "optThird: option<[#second(someRecord) | #first]>\n\nsomeRecord", + "documentation": null + }, { + "label": "nest", + "kind": 5, + "tags": [], + "detail": "nest: nestedRecord\n\nsomeRecord", + "documentation": null + }] + +Complete src/CompletionPattern.res 48:19 +looking for: Cpath Value[f] +posCursor:[48:19] posNoWhite:[48:18] Found expr:[48:3->48:21] +posCursor:[48:19] posNoWhite:[48:18] Found pattern:[48:16->48:20] +posCursor:[48:19] posNoWhite:[48:18] Found pattern:[48:17->48:19] +Completable: Cpattern Value[f]=fi +[{ + "label": "first", + "kind": 5, + "tags": [], + "detail": "first: int\n\nsomeRecord", + "documentation": null + }] + +Complete src/CompletionPattern.res 51:19 +looking for: Cpath Value[z] +posCursor:[51:19] posNoWhite:[51:18] Found expr:[51:3->51:25] +posCursor:[51:19] posNoWhite:[51:18] Found pattern:[51:16->51:24] +posCursor:[51:19] posNoWhite:[51:18] Found pattern:[51:17->51:20] +posCursor:[51:19] posNoWhite:[51:18] Found pattern:[51:18->51:19] +Completable: Cpattern Value[z]=o->tuple($0) +[{ + "label": "optThird", + "kind": 5, + "tags": [], + "detail": "optThird: option<[#second(someRecord) | #first]>\n\nsomeRecord", + "documentation": null + }] + +Complete src/CompletionPattern.res 54:24 +looking for: Cpath Value[f] +posCursor:[54:24] posNoWhite:[54:23] Found expr:[54:3->54:27] +posCursor:[54:24] posNoWhite:[54:23] Found pattern:[54:16->54:26] +posCursor:[54:24] posNoWhite:[54:23] Found pattern:[54:23->54:25] +Completable: Cpattern Value[f]->recordField(nest) +[{ + "label": "nested", + "kind": 5, + "tags": [], + "detail": "nested: bool\n\nnestedRecord", + "documentation": null + }] + From 352872fd7300e5a45790f2e71326c24694e9ace8 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Sun, 1 Jan 2023 10:09:40 +0100 Subject: [PATCH 04/31] complete record bodies/fields in patterns --- analysis/src/CompletionBackEnd.ml | 42 +++++++----- analysis/src/CompletionFrontEnd.ml | 36 ++++++---- analysis/src/SharedTypes.ml | 9 ++- analysis/tests/src/CompletionPattern.res | 6 ++ .../src/expected/CompletionPattern.res.txt | 68 +++++++++++++------ 5 files changed, 109 insertions(+), 52 deletions(-) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index a41993136..2d82b8c5b 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -1568,7 +1568,7 @@ let printConstructorArgs argsLen ~asSnippet = else "" let completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix - ~expandOption ~includeLocalValues = + ~expandOption ~includeLocalValues ~completionContext = let namesUsed = Hashtbl.create 10 in let rec completeTypedValueInner t ~env ~full ~prefix ~expandOption = let items = @@ -1643,13 +1643,21 @@ let completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix ~insertText:(printConstructorArgs numExprs ~asSnippet:true) ~kind:(Value typ) ~env (); ] - | Some (Trecord {env; fields; typeExpr}) -> - fields - |> List.map (fun (field : field) -> - Completion.create ~name:field.fname.txt - ~kind:(Field (field, typeExpr |> Shared.typeToString)) - ~env) - |> filterItems ~prefix + | Some (Trecord {env; fields; typeExpr}) -> ( + match completionContext with + | Some Completable.RecordField -> + fields + |> List.map (fun (field : field) -> + Completion.create ~name:field.fname.txt + ~kind:(Field (field, typeExpr |> Shared.typeToString)) + ~env) + |> filterItems ~prefix + | None -> + [ + Completion.createWithSnippet ~name:"{}" + ~insertText:(if !Cfg.supportsSnippets then "{$0}" else "{}") + ~sortText:"a" ~kind:(Value typeExpr) ~env (); + ]) | _ -> [] in (* Include all values and modules in completion if there's a prefix, not otherwise *) @@ -1740,20 +1748,22 @@ let getJsxLabels ~componentPath ~findTypeOfValue ~package = let rec resolveNestedPattern typ ~env ~package ~nested = match nested with - | [] -> Some (typ, env) + | [] -> Some (typ, env, None) | patternPath :: nested -> ( match (patternPath, typ |> extractType ~env ~package) with | Completable.PTupleItem {itemNum}, Some (Tuple (env, tupleItems, _)) -> ( match List.nth_opt tupleItems itemNum with | None -> None | Some typ -> typ |> resolveNestedPattern ~env ~package ~nested) - | Completable.PRecordField {fieldName}, Some (Trecord {env; fields}) -> ( + | PFollowRecordField {fieldName}, Some (Trecord {env; fields}) -> ( match fields |> List.find_opt (fun (field : field) -> field.fname.txt = fieldName) with | None -> None | Some {typ} -> typ |> resolveNestedPattern ~env ~package ~nested) + | PRecordBody _, Some (Trecord {env; typeExpr}) -> + Some (typeExpr, env, Some Completable.RecordField) | _ -> None) let processCompletable ~debug ~full ~scope ~env ~pos ~forHover @@ -1819,7 +1829,7 @@ let processCompletable ~debug ~full ~scope ~env ~pos ~forHover | Some (_, typ, env) -> typ |> completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix - ~expandOption:true ~includeLocalValues:true) + ~expandOption:true ~includeLocalValues:true ~completionContext:None) | Cdecorator prefix -> let mkDecorator (name, docstring) = {(Completion.create ~name ~kind:(Label "") ~env) with docstring} @@ -2084,11 +2094,11 @@ Note: The `@react.component` decorator requires the react-jsx config to be set i | Some (Optional _, typ) -> typ |> completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix - ~expandOption:true ~includeLocalValues:true + ~expandOption:true ~includeLocalValues:true ~completionContext:None | Some ((Unlabelled _ | Labelled _), typ) -> typ |> completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix - ~expandOption:false ~includeLocalValues:true) + ~expandOption:false ~includeLocalValues:true ~completionContext:None) | CnamedArg (cp, prefix, identsSeen) -> let labels = match @@ -2129,7 +2139,7 @@ Note: The `@react.component` decorator requires the react-jsx config to be set i | Some (typ, env) -> typ |> completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix - ~expandOption:false ~includeLocalValues:false + ~expandOption:false ~includeLocalValues:false ~completionContext:None | None -> []) | Cpattern {typ; prefix; nested = Some nested} -> ( let envWhereCompletionStarted = env in @@ -2142,8 +2152,8 @@ Note: The `@react.component` decorator requires the react-jsx config to be set i | Some (typ, env) -> ( match typ |> resolveNestedPattern ~env ~package:full.package ~nested with | None -> [] - | Some (typ, env) -> + | Some (typ, env, completionContext) -> typ |> completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix - ~expandOption:false ~includeLocalValues:false) + ~expandOption:false ~includeLocalValues:false ~completionContext) | None -> []) diff --git a/analysis/src/CompletionFrontEnd.ml b/analysis/src/CompletionFrontEnd.ml index d909e2739..ba8facbb6 100644 --- a/analysis/src/CompletionFrontEnd.ml +++ b/analysis/src/CompletionFrontEnd.ml @@ -504,18 +504,14 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = | _, Some patHoleCount -> appendNestedPat (Completable.PTupleItem {itemNum = patHoleCount}) | _ -> ()) - | Ppat_record ([], _) -> commitFoundPat ~prefix:"" () + | Ppat_record ([], _) -> + appendNestedPat (Completable.PRecordBody {seenFields = []}); + commitFoundPat ~prefix:"" () | Ppat_record (fields, _) -> ( + (* TODO: Identify seen fields. *) let fieldWithCursor = ref None in let fieldWithPatHole = ref None in fields - |> List.filter (fun (_, f) -> - (* Ensure we only include fields with patterns we can continue into. *) - match f.Parsetree.ppat_desc with - | Ppat_tuple _ | Ppat_construct _ | Ppat_variant _ - | Ppat_record _ -> - true - | _ -> false) |> List.iter (fun (fname, f) -> match ( fname.Location.txt, @@ -523,15 +519,25 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = |> CursorPosition.classifyLoc ~pos:posBeforeCursor ) with | Longident.Lident fname, HasCursor -> - fieldWithCursor := Some fname - | Lident fname, EmptyLoc when isPatternHole f -> - fieldWithPatHole := Some fname + fieldWithCursor := Some (fname, f) + | Lident fname, _ when isPatternHole f -> + fieldWithPatHole := Some (fname, f) | _ -> ()); match (!fieldWithCursor, !fieldWithPatHole) with - | Some fname, _ -> - appendNestedPat (Completable.PRecordField {fieldName = fname}) - | None, Some fname -> - appendNestedPat (Completable.PRecordField {fieldName = fname}) + | Some (fname, f), _ | None, Some (fname, f) -> ( + match f.ppat_desc with + | Ppat_record _ | Ppat_construct _ | Ppat_variant _ | Ppat_tuple _ -> + (* These are things we can continue into in the pattern. *) + appendNestedPat (Completable.PFollowRecordField {fieldName = fname}) + | Ppat_extension ({txt = "rescript.patternhole"}, _) -> + (* A pattern hole means for example `{someField: }`. We want to complete for the type of `someField`. *) + appendNestedPat (Completable.PFollowRecordField {fieldName = fname}); + commitFoundPat ~prefix:"" () + | Ppat_var {txt} -> + (* A var means `{s}` or similar. Complete for fields. *) + appendNestedPat (Completable.PRecordBody {seenFields = []}); + commitFoundPat ~prefix:txt () + | _ -> ()) | _ -> ()) | _ -> () in diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index 9214d1c22..399cb2667 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -550,14 +550,19 @@ module Completable = struct (** The loc item for the left hand side of the pipe. *) } + (** Additional context for a pattern completion where needed. *) + type patternContext = RecordField + type patternPath = | PTupleItem of {itemNum: int} - | PRecordField of {fieldName: string} + | PFollowRecordField of {fieldName: string} + | PRecordBody of {seenFields: string list} let patternPathToString p = match p with | PTupleItem {itemNum} -> "tuple($" ^ string_of_int itemNum ^ ")" - | PRecordField {fieldName} -> "recordField(" ^ fieldName ^ ")" + | PFollowRecordField {fieldName} -> "recordField(" ^ fieldName ^ ")" + | PRecordBody _ -> "recordBody" type t = | Cdecorator of string (** e.g. @module *) diff --git a/analysis/tests/src/CompletionPattern.res b/analysis/tests/src/CompletionPattern.res index 52e1ddb5b..dbc0c2135 100644 --- a/analysis/tests/src/CompletionPattern.res +++ b/analysis/tests/src/CompletionPattern.res @@ -43,6 +43,9 @@ let f: someRecord = { let z = (f, true) +// switch f { | } +// ^com + // switch f { | {}} // ^com @@ -52,5 +55,8 @@ let z = (f, true) // switch z { | ({o}, _)} // ^com +// switch f { | {nest: }} +// ^com + // switch f { | {nest: {}}} // ^com diff --git a/analysis/tests/src/expected/CompletionPattern.res.txt b/analysis/tests/src/expected/CompletionPattern.res.txt index 19126bff0..6bf9e3401 100644 --- a/analysis/tests/src/expected/CompletionPattern.res.txt +++ b/analysis/tests/src/expected/CompletionPattern.res.txt @@ -74,11 +74,25 @@ Completable: Cpattern Value[x]=t "documentation": null }] -Complete src/CompletionPattern.res 45:17 -looking for: Cpath Value[f] -posCursor:[45:17] posNoWhite:[45:16] Found expr:[45:3->45:19] -posCursor:[45:17] posNoWhite:[45:16] Found pattern:[45:16->45:18] +Complete src/CompletionPattern.res 45:15 +XXX Not found! Completable: Cpattern Value[f] +[{ + "label": "{}", + "kind": 12, + "tags": [], + "detail": "someRecord", + "documentation": null, + "sortText": "a", + "insertText": "{$0}", + "insertTextFormat": 2 + }] + +Complete src/CompletionPattern.res 48:17 +looking for: Cpath Value[f] +posCursor:[48:17] posNoWhite:[48:16] Found expr:[48:3->48:19] +posCursor:[48:17] posNoWhite:[48:16] Found pattern:[48:16->48:18] +Completable: Cpattern Value[f]->recordBody [{ "label": "first", "kind": 5, @@ -105,12 +119,12 @@ Completable: Cpattern Value[f] "documentation": null }] -Complete src/CompletionPattern.res 48:19 +Complete src/CompletionPattern.res 51:19 looking for: Cpath Value[f] -posCursor:[48:19] posNoWhite:[48:18] Found expr:[48:3->48:21] -posCursor:[48:19] posNoWhite:[48:18] Found pattern:[48:16->48:20] -posCursor:[48:19] posNoWhite:[48:18] Found pattern:[48:17->48:19] -Completable: Cpattern Value[f]=fi +posCursor:[51:19] posNoWhite:[51:18] Found expr:[51:3->51:21] +posCursor:[51:19] posNoWhite:[51:18] Found pattern:[51:16->51:20] +posCursor:[51:19] posNoWhite:[51:18] Found pattern:[51:17->51:19] +Completable: Cpattern Value[f]=fi->recordBody [{ "label": "first", "kind": 5, @@ -119,13 +133,13 @@ Completable: Cpattern Value[f]=fi "documentation": null }] -Complete src/CompletionPattern.res 51:19 +Complete src/CompletionPattern.res 54:19 looking for: Cpath Value[z] -posCursor:[51:19] posNoWhite:[51:18] Found expr:[51:3->51:25] -posCursor:[51:19] posNoWhite:[51:18] Found pattern:[51:16->51:24] -posCursor:[51:19] posNoWhite:[51:18] Found pattern:[51:17->51:20] -posCursor:[51:19] posNoWhite:[51:18] Found pattern:[51:18->51:19] -Completable: Cpattern Value[z]=o->tuple($0) +posCursor:[54:19] posNoWhite:[54:18] Found expr:[54:3->54:25] +posCursor:[54:19] posNoWhite:[54:18] Found pattern:[54:16->54:24] +posCursor:[54:19] posNoWhite:[54:18] Found pattern:[54:17->54:20] +posCursor:[54:19] posNoWhite:[54:18] Found pattern:[54:18->54:19] +Completable: Cpattern Value[z]=o->tuple($0), recordBody [{ "label": "optThird", "kind": 5, @@ -134,12 +148,28 @@ Completable: Cpattern Value[z]=o->tuple($0) "documentation": null }] -Complete src/CompletionPattern.res 54:24 +Complete src/CompletionPattern.res 57:22 looking for: Cpath Value[f] -posCursor:[54:24] posNoWhite:[54:23] Found expr:[54:3->54:27] -posCursor:[54:24] posNoWhite:[54:23] Found pattern:[54:16->54:26] -posCursor:[54:24] posNoWhite:[54:23] Found pattern:[54:23->54:25] +posCursor:[57:22] posNoWhite:[57:21] Found expr:[57:3->57:25] +posCursor:[57:22] posNoWhite:[57:21] Found pattern:[57:16->57:25] Completable: Cpattern Value[f]->recordField(nest) +[{ + "label": "{}", + "kind": 12, + "tags": [], + "detail": "nestedRecord", + "documentation": null, + "sortText": "a", + "insertText": "{$0}", + "insertTextFormat": 2 + }] + +Complete src/CompletionPattern.res 60:24 +looking for: Cpath Value[f] +posCursor:[60:24] posNoWhite:[60:23] Found expr:[60:3->60:27] +posCursor:[60:24] posNoWhite:[60:23] Found pattern:[60:16->60:26] +posCursor:[60:24] posNoWhite:[60:23] Found pattern:[60:23->60:25] +Completable: Cpattern Value[f]->recordField(nest), recordBody [{ "label": "nested", "kind": 5, From 01516a65c4ec28371be953a854c4eead2bbbde53 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Sun, 1 Jan 2023 10:29:35 +0100 Subject: [PATCH 05/31] handle completing for new record fields, and filtering already seen fields --- analysis/src/CompletionBackEnd.ml | 8 +-- analysis/src/CompletionFrontEnd.ml | 31 +++++++++-- analysis/src/SharedTypes.ml | 2 +- analysis/tests/src/CompletionPattern.res | 3 ++ .../src/expected/CompletionPattern.res.txt | 51 +++++++++++++------ 5 files changed, 72 insertions(+), 23 deletions(-) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index 2d82b8c5b..8adbee45f 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -1645,8 +1645,10 @@ let completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix ] | Some (Trecord {env; fields; typeExpr}) -> ( match completionContext with - | Some Completable.RecordField -> + | Some (Completable.RecordField {seenFields}) -> fields + |> List.filter (fun (field : field) -> + List.mem field.fname.txt seenFields = false) |> List.map (fun (field : field) -> Completion.create ~name:field.fname.txt ~kind:(Field (field, typeExpr |> Shared.typeToString)) @@ -1762,8 +1764,8 @@ let rec resolveNestedPattern typ ~env ~package ~nested = with | None -> None | Some {typ} -> typ |> resolveNestedPattern ~env ~package ~nested) - | PRecordBody _, Some (Trecord {env; typeExpr}) -> - Some (typeExpr, env, Some Completable.RecordField) + | PRecordBody {seenFields}, Some (Trecord {env; typeExpr}) -> + Some (typeExpr, env, Some (Completable.RecordField {seenFields})) | _ -> None) let processCompletable ~debug ~full ~scope ~env ~pos ~forHover diff --git a/analysis/src/CompletionFrontEnd.ml b/analysis/src/CompletionFrontEnd.ml index ba8facbb6..c6a90bc5b 100644 --- a/analysis/src/CompletionFrontEnd.ml +++ b/analysis/src/CompletionFrontEnd.ml @@ -312,6 +312,15 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = let line, col = posCursor in (line, max 0 col - offset + offsetNoWhite) in + (* Identifies the first character before the cursor that's not white space. + Should be used very sparingly, but can be used to drive completion triggering + in scenarios where the parser eats things we'd need to complete. + Example: let {whatever, }, char is ','. *) + let firstCharBeforeCursorNoWhite = + if offsetNoWhite < String.length text && offsetNoWhite >= 0 then + Some text.[offsetNoWhite] + else None + in let posBeforeCursor = Pos.posBeforeCursor posCursor in let charBeforeCursor, blankAfterCursor = match Pos.positionToOffset text posCursor with @@ -505,10 +514,10 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = appendNestedPat (Completable.PTupleItem {itemNum = patHoleCount}) | _ -> ()) | Ppat_record ([], _) -> + (* Empty fields means we're in a record body `{}`. Complete for the fields. *) appendNestedPat (Completable.PRecordBody {seenFields = []}); commitFoundPat ~prefix:"" () | Ppat_record (fields, _) -> ( - (* TODO: Identify seen fields. *) let fieldWithCursor = ref None in let fieldWithPatHole = ref None in fields @@ -523,6 +532,13 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = | Lident fname, _ when isPatternHole f -> fieldWithPatHole := Some (fname, f) | _ -> ()); + let seenFields = + fields + |> List.filter_map (fun (fieldName, _f) -> + match fieldName with + | {Location.txt = Longident.Lident fieldName} -> Some fieldName + | _ -> None) + in match (!fieldWithCursor, !fieldWithPatHole) with | Some (fname, f), _ | None, Some (fname, f) -> ( match f.ppat_desc with @@ -535,10 +551,19 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = commitFoundPat ~prefix:"" () | Ppat_var {txt} -> (* A var means `{s}` or similar. Complete for fields. *) - appendNestedPat (Completable.PRecordBody {seenFields = []}); + appendNestedPat (Completable.PRecordBody {seenFields}); commitFoundPat ~prefix:txt () | _ -> ()) - | _ -> ()) + | None, None -> ( + (* Figure out if we're completing for a new field. + If the cursor is inside of the record body, but no field has the cursor, + and there's no pattern hole. Check the first char to the left of the cursor, + ignoring white space. If that's a comma, we assume you're completing for a new field. *) + match firstCharBeforeCursorNoWhite with + | Some ',' -> + appendNestedPat (Completable.PRecordBody {seenFields}); + commitFoundPat ~prefix:"" () + | _ -> ())) | _ -> () in let case (iterator : Ast_iterator.iterator) (case : Parsetree.case) = diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index 399cb2667..6f48f5f0d 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -551,7 +551,7 @@ module Completable = struct } (** Additional context for a pattern completion where needed. *) - type patternContext = RecordField + type patternContext = RecordField of {seenFields: string list} type patternPath = | PTupleItem of {itemNum: int} diff --git a/analysis/tests/src/CompletionPattern.res b/analysis/tests/src/CompletionPattern.res index dbc0c2135..96acfb918 100644 --- a/analysis/tests/src/CompletionPattern.res +++ b/analysis/tests/src/CompletionPattern.res @@ -49,6 +49,9 @@ let z = (f, true) // switch f { | {}} // ^com +// switch f { | {first, , second }} +// ^com + // switch f { | {fi}} // ^com diff --git a/analysis/tests/src/expected/CompletionPattern.res.txt b/analysis/tests/src/expected/CompletionPattern.res.txt index 6bf9e3401..11d01e7a3 100644 --- a/analysis/tests/src/expected/CompletionPattern.res.txt +++ b/analysis/tests/src/expected/CompletionPattern.res.txt @@ -119,11 +119,30 @@ Completable: Cpattern Value[f]->recordBody "documentation": null }] -Complete src/CompletionPattern.res 51:19 +Complete src/CompletionPattern.res 51:24 looking for: Cpath Value[f] -posCursor:[51:19] posNoWhite:[51:18] Found expr:[51:3->51:21] -posCursor:[51:19] posNoWhite:[51:18] Found pattern:[51:16->51:20] -posCursor:[51:19] posNoWhite:[51:18] Found pattern:[51:17->51:19] +posCursor:[51:24] posNoWhite:[51:22] Found expr:[51:3->51:36] +posCursor:[51:24] posNoWhite:[51:22] Found pattern:[51:16->51:35] +Completable: Cpattern Value[f]->recordBody +[{ + "label": "optThird", + "kind": 5, + "tags": [], + "detail": "optThird: option<[#second(someRecord) | #first]>\n\nsomeRecord", + "documentation": null + }, { + "label": "nest", + "kind": 5, + "tags": [], + "detail": "nest: nestedRecord\n\nsomeRecord", + "documentation": null + }] + +Complete src/CompletionPattern.res 54:19 +looking for: Cpath Value[f] +posCursor:[54:19] posNoWhite:[54:18] Found expr:[54:3->54:21] +posCursor:[54:19] posNoWhite:[54:18] Found pattern:[54:16->54:20] +posCursor:[54:19] posNoWhite:[54:18] Found pattern:[54:17->54:19] Completable: Cpattern Value[f]=fi->recordBody [{ "label": "first", @@ -133,12 +152,12 @@ Completable: Cpattern Value[f]=fi->recordBody "documentation": null }] -Complete src/CompletionPattern.res 54:19 +Complete src/CompletionPattern.res 57:19 looking for: Cpath Value[z] -posCursor:[54:19] posNoWhite:[54:18] Found expr:[54:3->54:25] -posCursor:[54:19] posNoWhite:[54:18] Found pattern:[54:16->54:24] -posCursor:[54:19] posNoWhite:[54:18] Found pattern:[54:17->54:20] -posCursor:[54:19] posNoWhite:[54:18] Found pattern:[54:18->54:19] +posCursor:[57:19] posNoWhite:[57:18] Found expr:[57:3->57:25] +posCursor:[57:19] posNoWhite:[57:18] Found pattern:[57:16->57:24] +posCursor:[57:19] posNoWhite:[57:18] Found pattern:[57:17->57:20] +posCursor:[57:19] posNoWhite:[57:18] Found pattern:[57:18->57:19] Completable: Cpattern Value[z]=o->tuple($0), recordBody [{ "label": "optThird", @@ -148,10 +167,10 @@ Completable: Cpattern Value[z]=o->tuple($0), recordBody "documentation": null }] -Complete src/CompletionPattern.res 57:22 +Complete src/CompletionPattern.res 60:22 looking for: Cpath Value[f] -posCursor:[57:22] posNoWhite:[57:21] Found expr:[57:3->57:25] -posCursor:[57:22] posNoWhite:[57:21] Found pattern:[57:16->57:25] +posCursor:[60:22] posNoWhite:[60:21] Found expr:[60:3->60:25] +posCursor:[60:22] posNoWhite:[60:21] Found pattern:[60:16->60:25] Completable: Cpattern Value[f]->recordField(nest) [{ "label": "{}", @@ -164,11 +183,11 @@ Completable: Cpattern Value[f]->recordField(nest) "insertTextFormat": 2 }] -Complete src/CompletionPattern.res 60:24 +Complete src/CompletionPattern.res 63:24 looking for: Cpath Value[f] -posCursor:[60:24] posNoWhite:[60:23] Found expr:[60:3->60:27] -posCursor:[60:24] posNoWhite:[60:23] Found pattern:[60:16->60:26] -posCursor:[60:24] posNoWhite:[60:23] Found pattern:[60:23->60:25] +posCursor:[63:24] posNoWhite:[63:23] Found expr:[63:3->63:27] +posCursor:[63:24] posNoWhite:[63:23] Found pattern:[63:16->63:26] +posCursor:[63:24] posNoWhite:[63:23] Found pattern:[63:23->63:25] Completable: Cpattern Value[f]->recordField(nest), recordBody [{ "label": "nested", From 5f4d7e02fa27046bca91d01cde7c44013189aab5 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Sun, 1 Jan 2023 10:35:30 +0100 Subject: [PATCH 06/31] add test for patterns under other patterns, and handle unsetting what pattern we're currently looking for --- analysis/src/CompletionFrontEnd.ml | 5 ++--- analysis/tests/src/CompletionPattern.res | 9 +++++++++ .../src/expected/CompletionPattern.res.txt | 19 ++++++++++++++++++- 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/analysis/src/CompletionFrontEnd.ml b/analysis/src/CompletionFrontEnd.ml index c6a90bc5b..63f1abd00 100644 --- a/analysis/src/CompletionFrontEnd.ml +++ b/analysis/src/CompletionFrontEnd.ml @@ -423,15 +423,14 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = scope := !scope |> Scope.addModule ~name:md.pmd_name.txt ~loc:md.pmd_name.loc in - let lookingForPat = ref None in - let setLookingForPat ctxPath = lookingForPat := Some (Completable.Cpattern {typ = ctxPath; prefix = ""; nested = None}); if debug then Printf.printf "looking for: %s \n" (Completable.toString (Cpath ctxPath)) in + let unsetLookingForPat = lookingForPat := None in let appendNestedPat patternPat = match !lookingForPat with | Some (Completable.Cpattern ({nested = None} as p)) -> @@ -484,7 +483,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = match exp |> exprToContextPath with | None -> () | Some ctxPath -> setLookingForPat ctxPath) - | _ -> () + | _ -> unsetLookingForPat in let typedCompletionPat (pat : Parsetree.pattern) = diff --git a/analysis/tests/src/CompletionPattern.res b/analysis/tests/src/CompletionPattern.res index 96acfb918..33e463d49 100644 --- a/analysis/tests/src/CompletionPattern.res +++ b/analysis/tests/src/CompletionPattern.res @@ -63,3 +63,12 @@ let z = (f, true) // switch f { | {nest: {}}} // ^com + +let _ = switch f { +| {first: 123, nest} => + () + // switch nest { | {}} + // ^com + nest.nested +| _ => false +} diff --git a/analysis/tests/src/expected/CompletionPattern.res.txt b/analysis/tests/src/expected/CompletionPattern.res.txt index 11d01e7a3..0c30fc96f 100644 --- a/analysis/tests/src/expected/CompletionPattern.res.txt +++ b/analysis/tests/src/expected/CompletionPattern.res.txt @@ -169,7 +169,7 @@ Completable: Cpattern Value[z]=o->tuple($0), recordBody Complete src/CompletionPattern.res 60:22 looking for: Cpath Value[f] -posCursor:[60:22] posNoWhite:[60:21] Found expr:[60:3->60:25] +posCursor:[60:22] posNoWhite:[60:21] Found expr:[60:3->73:1] posCursor:[60:22] posNoWhite:[60:21] Found pattern:[60:16->60:25] Completable: Cpattern Value[f]->recordField(nest) [{ @@ -197,3 +197,20 @@ Completable: Cpattern Value[f]->recordField(nest), recordBody "documentation": null }] +Complete src/CompletionPattern.res 69:22 +looking for: Cpath Value[f] +posCursor:[69:22] posNoWhite:[69:21] Found expr:[66:8->73:1] +posCursor:[69:22] posNoWhite:[69:21] Found expr:[68:2->71:13] +posCursor:[69:22] posNoWhite:[69:21] Found expr:[69:5->71:13] +looking for: Cpath Value[nest] +posCursor:[69:22] posNoWhite:[69:21] Found expr:[69:5->69:24] +posCursor:[69:22] posNoWhite:[69:21] Found pattern:[69:21->69:23] +Completable: Cpattern Value[nest]->recordBody +[{ + "label": "nested", + "kind": 5, + "tags": [], + "detail": "nested: bool\n\nnestedRecord", + "documentation": null + }] + From 3c7562cef16d273fdee42511a49e89c2da5bbc06 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Sun, 1 Jan 2023 10:43:53 +0100 Subject: [PATCH 07/31] identify and handle completing destructuring patterns --- analysis/src/CompletionFrontEnd.ml | 8 +++++ analysis/tests/src/CompletionPattern.res | 3 ++ .../src/expected/CompletionPattern.res.txt | 30 +++++++++++++++++++ 3 files changed, 41 insertions(+) diff --git a/analysis/src/CompletionFrontEnd.ml b/analysis/src/CompletionFrontEnd.ml index 63f1abd00..c6f117ad9 100644 --- a/analysis/src/CompletionFrontEnd.ml +++ b/analysis/src/CompletionFrontEnd.ml @@ -579,12 +579,20 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = in let structure_item (iterator : Ast_iterator.iterator) (item : Parsetree.structure_item) = + unsetLookingForPat; let processed = ref false in (match item.pstr_desc with | Pstr_open {popen_lid} -> scope := !scope |> Scope.addOpen ~lid:popen_lid.txt | Pstr_primitive vd -> scopeValueDescription vd | Pstr_value (recFlag, bindings) -> + (* Identify relevant destructures for completion, like `let {} = someVar` or `let (true, false) = someFn()`. *) + (match bindings with + | [{pvb_pat = {ppat_desc = Ppat_record _ | Ppat_tuple _}; pvb_expr}] -> ( + match exprToContextPath pvb_expr with + | None -> () + | Some ctxPath -> setLookingForPat ctxPath) + | _ -> ()); if recFlag = Recursive then bindings |> List.iter scopeValueBinding; bindings |> List.iter (fun vb -> iterator.value_binding iterator vb); if recFlag = Nonrecursive then bindings |> List.iter scopeValueBinding; diff --git a/analysis/tests/src/CompletionPattern.res b/analysis/tests/src/CompletionPattern.res index 33e463d49..8c01c16f1 100644 --- a/analysis/tests/src/CompletionPattern.res +++ b/analysis/tests/src/CompletionPattern.res @@ -72,3 +72,6 @@ let _ = switch f { nest.nested | _ => false } + +// let {} = f +// ^com diff --git a/analysis/tests/src/expected/CompletionPattern.res.txt b/analysis/tests/src/expected/CompletionPattern.res.txt index 0c30fc96f..286a0a316 100644 --- a/analysis/tests/src/expected/CompletionPattern.res.txt +++ b/analysis/tests/src/expected/CompletionPattern.res.txt @@ -214,3 +214,33 @@ Completable: Cpattern Value[nest]->recordBody "documentation": null }] +Complete src/CompletionPattern.res 75:8 +looking for: Cpath Value[f] +posCursor:[75:8] posNoWhite:[75:7] Found pattern:[75:7->75:9] +Completable: Cpattern Value[f]->recordBody +[{ + "label": "first", + "kind": 5, + "tags": [], + "detail": "first: int\n\nsomeRecord", + "documentation": null + }, { + "label": "second", + "kind": 5, + "tags": [], + "detail": "second: (bool, option)\n\nsomeRecord", + "documentation": null + }, { + "label": "optThird", + "kind": 5, + "tags": [], + "detail": "optThird: option<[#second(someRecord) | #first]>\n\nsomeRecord", + "documentation": null + }, { + "label": "nest", + "kind": 5, + "tags": [], + "detail": "nest: nestedRecord\n\nsomeRecord", + "documentation": null + }] + From cc8412f80842b483a81da521adaabf59277a38b1 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Sun, 1 Jan 2023 10:56:33 +0100 Subject: [PATCH 08/31] comments and cleanup --- analysis/src/CompletionBackEnd.ml | 4 ++++ analysis/src/CompletionFrontEnd.ml | 14 +++++++------- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index 8adbee45f..35fccdbb3 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -1644,6 +1644,9 @@ let completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix ~kind:(Value typ) ~env (); ] | Some (Trecord {env; fields; typeExpr}) -> ( + (* As we're completing for a record, we'll need a hint (completionContext) + here to figure out whether we should complete for a record field, or + the record body itself. *) match completionContext with | Some (Completable.RecordField {seenFields}) -> fields @@ -1748,6 +1751,7 @@ let getJsxLabels ~componentPath ~findTypeOfValue ~package = typ |> getLabels | None -> [] +(** This moves through a pattern via a set of instructions, trying to resolve the type at the end of the pattern. *) let rec resolveNestedPattern typ ~env ~package ~nested = match nested with | [] -> Some (typ, env, None) diff --git a/analysis/src/CompletionFrontEnd.ml b/analysis/src/CompletionFrontEnd.ml index c6f117ad9..8356fad56 100644 --- a/analysis/src/CompletionFrontEnd.ml +++ b/analysis/src/CompletionFrontEnd.ml @@ -435,7 +435,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = match !lookingForPat with | Some (Completable.Cpattern ({nested = None} as p)) -> lookingForPat := Some (Cpattern {p with nested = Some [patternPat]}) - | Some (Completable.Cpattern ({nested = Some nested} as p)) -> + | Some (Cpattern ({nested = Some nested} as p)) -> lookingForPat := Some (Cpattern {p with nested = Some (nested @ [patternPat])}) | _ -> () @@ -451,7 +451,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = setResult cpattern | _ -> () in - + (* Identifies expressions where we can do typed pattern or expr completion. *) let typedCompletionExpr (exp : Parsetree.expression) = if exp.pexp_loc @@ -485,7 +485,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = | Some ctxPath -> setLookingForPat ctxPath) | _ -> unsetLookingForPat in - + (* Tracks the path through a pattern for where the cursor is. *) let typedCompletionPat (pat : Parsetree.pattern) = if pat.ppat_loc @@ -495,7 +495,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = match pat.ppat_desc with | Ppat_var {txt} -> commitFoundPat ~prefix:txt () | Ppat_tuple patterns -> ( - let patCount = ref (-1) in + let patCount = ref None in let patCountWithPatHole = ref None in patterns |> List.iteri (fun index p -> @@ -503,13 +503,13 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = p.Parsetree.ppat_loc |> CursorPosition.classifyLoc ~pos:posBeforeCursor with - | HasCursor -> patCount := index + | HasCursor -> patCount := Some index | EmptyLoc -> patCountWithPatHole := Some index | _ -> ()); match (!patCount, !patCountWithPatHole) with - | patCount, _ when patCount > -1 -> + | Some patCount, _ -> appendNestedPat (Completable.PTupleItem {itemNum = patCount}) - | _, Some patHoleCount -> + | None, Some patHoleCount -> appendNestedPat (Completable.PTupleItem {itemNum = patHoleCount}) | _ -> ()) | Ppat_record ([], _) -> From 4df242fda54f79f2af8e3747a24db6cc9d06ad83 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Sun, 1 Jan 2023 10:59:09 +0100 Subject: [PATCH 09/31] add nested destructure test --- analysis/tests/src/CompletionPattern.res | 3 +++ .../tests/src/expected/CompletionPattern.res.txt | 14 ++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/analysis/tests/src/CompletionPattern.res b/analysis/tests/src/CompletionPattern.res index 8c01c16f1..0079eb7bd 100644 --- a/analysis/tests/src/CompletionPattern.res +++ b/analysis/tests/src/CompletionPattern.res @@ -75,3 +75,6 @@ let _ = switch f { // let {} = f // ^com + +// let {nest: {n}}} = f +// ^com diff --git a/analysis/tests/src/expected/CompletionPattern.res.txt b/analysis/tests/src/expected/CompletionPattern.res.txt index 286a0a316..68dfd3f4b 100644 --- a/analysis/tests/src/expected/CompletionPattern.res.txt +++ b/analysis/tests/src/expected/CompletionPattern.res.txt @@ -244,3 +244,17 @@ Completable: Cpattern Value[f]->recordBody "documentation": null }] +Complete src/CompletionPattern.res 78:16 +looking for: Cpath Value[f] +posCursor:[78:16] posNoWhite:[78:15] Found pattern:[78:7->78:18] +posCursor:[78:16] posNoWhite:[78:15] Found pattern:[78:14->78:17] +posCursor:[78:16] posNoWhite:[78:15] Found pattern:[78:15->78:16] +Completable: Cpattern Value[f]=n->recordField(nest), recordBody +[{ + "label": "nested", + "kind": 5, + "tags": [], + "detail": "nested: bool\n\nnestedRecord", + "documentation": null + }] + From 26b0e687d5616f328e2814abf46a9968f5ec0fcb Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Sun, 1 Jan 2023 18:40:51 +0100 Subject: [PATCH 10/31] fix --- analysis/src/CompletionFrontEnd.ml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/analysis/src/CompletionFrontEnd.ml b/analysis/src/CompletionFrontEnd.ml index 8356fad56..c2307a568 100644 --- a/analysis/src/CompletionFrontEnd.ml +++ b/analysis/src/CompletionFrontEnd.ml @@ -430,7 +430,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = if debug then Printf.printf "looking for: %s \n" (Completable.toString (Cpath ctxPath)) in - let unsetLookingForPat = lookingForPat := None in + let unsetLookingForPat () = lookingForPat := None in let appendNestedPat patternPat = match !lookingForPat with | Some (Completable.Cpattern ({nested = None} as p)) -> @@ -483,7 +483,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = match exp |> exprToContextPath with | None -> () | Some ctxPath -> setLookingForPat ctxPath) - | _ -> unsetLookingForPat + | _ -> unsetLookingForPat () in (* Tracks the path through a pattern for where the cursor is. *) let typedCompletionPat (pat : Parsetree.pattern) = @@ -579,7 +579,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = in let structure_item (iterator : Ast_iterator.iterator) (item : Parsetree.structure_item) = - unsetLookingForPat; + unsetLookingForPat (); let processed = ref false in (match item.pstr_desc with | Pstr_open {popen_lid} -> From 779e1be848424126753c399d0e598f510e7c5429 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Sun, 1 Jan 2023 21:46:11 +0100 Subject: [PATCH 11/31] descend through variant payloads --- analysis/src/CompletionBackEnd.ml | 12 +++ analysis/src/CompletionFrontEnd.ml | 93 +++++++++++++++---- analysis/src/SharedTypes.ml | 4 + analysis/tests/src/CompletionPattern.res | 14 +++ .../tests/src/expected/Completion.res.txt | 41 +------- .../src/expected/CompletionPattern.res.txt | 66 +++++++++++++ 6 files changed, 172 insertions(+), 58 deletions(-) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index 35fccdbb3..e8c313fab 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -1770,6 +1770,18 @@ let rec resolveNestedPattern typ ~env ~package ~nested = | Some {typ} -> typ |> resolveNestedPattern ~env ~package ~nested) | PRecordBody {seenFields}, Some (Trecord {env; typeExpr}) -> Some (typeExpr, env, Some (Completable.RecordField {seenFields})) + | ( PVariantPayload {constructorName; payloadNum}, + Some (Tvariant {env; constructors}) ) -> ( + match + constructors + |> List.find_opt (fun (c : Constructor.t) -> + c.cname.txt = constructorName) + with + | None -> None + | Some constructor -> ( + match List.nth_opt constructor.args payloadNum with + | None -> None + | Some (typ, _) -> typ |> resolveNestedPattern ~env ~package ~nested)) | _ -> None) let processCompletable ~debug ~full ~scope ~env ~pos ~forHover diff --git a/analysis/src/CompletionFrontEnd.ml b/analysis/src/CompletionFrontEnd.ml index c2307a568..ebe60e458 100644 --- a/analysis/src/CompletionFrontEnd.ml +++ b/analysis/src/CompletionFrontEnd.ml @@ -264,6 +264,12 @@ let rec exprToContextPath (e : Parsetree.expression) = | Some contexPath -> Some (CPApply (contexPath, args |> List.map fst))) | _ -> None +let rec getUnqualifiedName txt = + match txt with + | Longident.Lident fieldName -> fieldName + | Ldot (t, _) -> getUnqualifiedName t + | _ -> "" + let completePipeChain ~(lhs : Parsetree.expression) = (* Complete the end of pipe chains by reconstructing the pipe chain as a single pipe, so it can be completed. @@ -306,6 +312,20 @@ let completePipeChain ~(lhs : Parsetree.expression) = |> Option.map (fun ctxPath -> (ctxPath, pexp_loc)) | _ -> None +let findPatTupleItemWithCursor patterns ~pos = + let patCount = ref None in + let patCountWithPatHole = ref None in + patterns + |> List.iteri (fun index p -> + match p.Parsetree.ppat_loc |> CursorPosition.classifyLoc ~pos with + | HasCursor -> patCount := Some index + | EmptyLoc -> patCountWithPatHole := Some index + | _ -> ()); + match (!patCount, !patCountWithPatHole) with + | Some patCount, _ -> Some patCount + | None, Some patHoleCount -> Some patHoleCount + | _ -> None + let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = let offsetNoWhite = skipWhite text (offset - 1) in let posNoWhite = @@ -494,23 +514,11 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = then match pat.ppat_desc with | Ppat_var {txt} -> commitFoundPat ~prefix:txt () + | Ppat_construct ({txt = Lident prefix}, None) -> + commitFoundPat ~prefix () | Ppat_tuple patterns -> ( - let patCount = ref None in - let patCountWithPatHole = ref None in - patterns - |> List.iteri (fun index p -> - match - p.Parsetree.ppat_loc - |> CursorPosition.classifyLoc ~pos:posBeforeCursor - with - | HasCursor -> patCount := Some index - | EmptyLoc -> patCountWithPatHole := Some index - | _ -> ()); - match (!patCount, !patCountWithPatHole) with - | Some patCount, _ -> - appendNestedPat (Completable.PTupleItem {itemNum = patCount}) - | None, Some patHoleCount -> - appendNestedPat (Completable.PTupleItem {itemNum = patHoleCount}) + match patterns |> findPatTupleItemWithCursor ~pos:posBeforeCursor with + | Some itemNum -> appendNestedPat (Completable.PTupleItem {itemNum}) | _ -> ()) | Ppat_record ([], _) -> (* Empty fields means we're in a record body `{}`. Complete for the fields. *) @@ -563,6 +571,46 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = appendNestedPat (Completable.PRecordBody {seenFields}); commitFoundPat ~prefix:"" () | _ -> ())) + | Ppat_construct + ( {txt}, + Some {ppat_loc; ppat_desc = Ppat_construct ({txt = Lident "()"}, _)} + ) + when ppat_loc + |> CursorPosition.classifyLoc ~pos:posBeforeCursor + = HasCursor -> + (* Empty payload *) + appendNestedPat + (Completable.PVariantPayload + {constructorName = getUnqualifiedName txt; payloadNum = 0}); + commitFoundPat ~prefix:"" () + | Ppat_construct + ( {txt}, + Some + { + ppat_loc; + ppat_desc = + Ppat_var _ | Ppat_record _ | Ppat_construct _ | Ppat_variant _; + } ) + when ppat_loc + |> CursorPosition.classifyLoc ~pos:posBeforeCursor + = HasCursor -> + (* Single payload *) + appendNestedPat + (Completable.PVariantPayload + {constructorName = getUnqualifiedName txt; payloadNum = 0}) + | Ppat_construct + ({txt}, Some {ppat_loc; ppat_desc = Ppat_tuple tupleItems}) + when ppat_loc + |> CursorPosition.classifyLoc ~pos:posBeforeCursor + = HasCursor -> ( + (* Multiple payloads with cursor in item *) + (* TODO: New item with comma *) + match tupleItems |> findPatTupleItemWithCursor ~pos:posBeforeCursor with + | None -> () + | Some payloadNum -> + appendNestedPat + (Completable.PVariantPayload + {constructorName = getUnqualifiedName txt; payloadNum})) | _ -> () in let case (iterator : Ast_iterator.iterator) (case : Parsetree.case) = @@ -958,12 +1006,19 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = let pat (iterator : Ast_iterator.iterator) (pat : Parsetree.pattern) = if pat.ppat_loc |> Loc.hasPos ~pos:posNoWhite then ( found := true; + let oldLookingForPat = !lookingForPat in + typedCompletionPat pat; if debug then Printf.printf "posCursor:[%s] posNoWhite:[%s] Found pattern:%s\n" (Pos.toString posCursor) (Pos.toString posNoWhite) (Loc.toString pat.ppat_loc); - (match pat.ppat_desc with - | Ppat_construct (lid, _) -> + (* TODO: + This change breaks old behavior of completing constructors in scope. + Either be fine with it, fix it somehow, or incorporate completing + constructors in scope when regular completion for variants can't + be done. *) + (match (!lookingForPat, pat.ppat_desc) with + | None, Ppat_construct (lid, _) -> let lidPath = flattenLidCheckDot lid in if debug then Printf.printf "Ppat_construct %s:%s\n" @@ -971,8 +1026,6 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = (Loc.toString lid.loc); setResult (Cpath (CPId (lidPath, Value))) | _ -> ()); - let oldLookingForPat = !lookingForPat in - typedCompletionPat pat; Ast_iterator.default_iterator.pat iterator pat; lookingForPat := oldLookingForPat) in diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index 6f48f5f0d..a2974774b 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -557,12 +557,16 @@ module Completable = struct | PTupleItem of {itemNum: int} | PFollowRecordField of {fieldName: string} | PRecordBody of {seenFields: string list} + | PVariantPayload of {constructorName: string; payloadNum: int} let patternPathToString p = match p with | PTupleItem {itemNum} -> "tuple($" ^ string_of_int itemNum ^ ")" | PFollowRecordField {fieldName} -> "recordField(" ^ fieldName ^ ")" | PRecordBody _ -> "recordBody" + | PVariantPayload {constructorName; payloadNum} -> + "variantPayload::" ^ constructorName ^ "($" ^ string_of_int payloadNum + ^ ")" type t = | Cdecorator of string (** e.g. @module *) diff --git a/analysis/tests/src/CompletionPattern.res b/analysis/tests/src/CompletionPattern.res index 0079eb7bd..51adc8028 100644 --- a/analysis/tests/src/CompletionPattern.res +++ b/analysis/tests/src/CompletionPattern.res @@ -78,3 +78,17 @@ let _ = switch f { // let {nest: {n}}} = f // ^com + +type someVariant = One | Two(bool) | Three(someRecord, bool) + +let z = Two(true) +ignore(z) + +// switch z { | Two()} +// ^com + +// switch z { | Two(t)} +// ^com + +// switch z { | Three({})} +// ^com diff --git a/analysis/tests/src/expected/Completion.res.txt b/analysis/tests/src/expected/Completion.res.txt index cdd084bbd..3c3f27ab0 100644 --- a/analysis/tests/src/expected/Completion.res.txt +++ b/analysis/tests/src/expected/Completion.res.txt @@ -1447,35 +1447,10 @@ looking for: Cpath Value[x] posCursor:[362:8] posNoWhite:[362:7] Found expr:[361:2->365:3] posCursor:[362:8] posNoWhite:[362:7] Found pattern:[362:7->364:5] posCursor:[362:8] posNoWhite:[362:7] Found pattern:[362:7->362:8] -Ppat_construct T:[362:7->362:8] -Completable: Cpath Value[T] +Completable: Cpattern Value[x]=T Raw opens: 2 Shadow.B.place holder ... Shadow.A.place holder Resolved opens 2 Completion.res Completion.res -[{ - "label": "That", - "kind": 4, - "tags": [], - "detail": "That\n\ntype v = This | That", - "documentation": null - }, { - "label": "This", - "kind": 4, - "tags": [], - "detail": "This\n\ntype v = This | That", - "documentation": null - }, { - "label": "TableclothMap", - "kind": 9, - "tags": [], - "detail": "file module", - "documentation": null - }, { - "label": "TypeDefinition", - "kind": 9, - "tags": [], - "detail": "file module", - "documentation": null - }] +[] Complete src/Completion.res 373:21 posCursor:[373:21] posNoWhite:[373:20] Found expr:[371:8->376:3] @@ -1483,17 +1458,7 @@ looking for: Cpath Value[x] posCursor:[373:21] posNoWhite:[373:20] Found expr:[372:2->376:3] posCursor:[373:21] posNoWhite:[373:20] Found pattern:[373:7->375:5] posCursor:[373:21] posNoWhite:[373:20] Found pattern:[373:7->373:21] -Ppat_construct AndThatOther.T:[373:7->373:21] -Completable: Cpath Value[AndThatOther, T] -Raw opens: 2 Shadow.B.place holder ... Shadow.A.place holder -Resolved opens 2 Completion.res Completion.res -[{ - "label": "ThatOther", - "kind": 4, - "tags": [], - "detail": "ThatOther\n\ntype v = And | ThatOther", - "documentation": null - }] +[] Complete src/Completion.res 378:24 posCursor:[378:24] posNoWhite:[378:23] Found expr:[378:12->378:26] diff --git a/analysis/tests/src/expected/CompletionPattern.res.txt b/analysis/tests/src/expected/CompletionPattern.res.txt index 68dfd3f4b..99aed954a 100644 --- a/analysis/tests/src/expected/CompletionPattern.res.txt +++ b/analysis/tests/src/expected/CompletionPattern.res.txt @@ -258,3 +258,69 @@ Completable: Cpattern Value[f]=n->recordField(nest), recordBody "documentation": null }] +Complete src/CompletionPattern.res 86:20 +looking for: Cpath Value[z] +posCursor:[86:20] posNoWhite:[86:19] Found expr:[86:3->86:22] +posCursor:[86:20] posNoWhite:[86:19] Found pattern:[86:16->86:21] +posCursor:[86:20] posNoWhite:[86:19] Found pattern:[86:19->86:21] +Completable: Cpattern Value[z]->variantPayload::Two($0) +[{ + "label": "true", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }, { + "label": "false", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }] + +Complete src/CompletionPattern.res 89:21 +looking for: Cpath Value[z] +posCursor:[89:21] posNoWhite:[89:20] Found expr:[89:3->89:23] +posCursor:[89:21] posNoWhite:[89:20] Found pattern:[89:16->89:22] +posCursor:[89:21] posNoWhite:[89:20] Found pattern:[89:20->89:21] +Completable: Cpattern Value[z]=t->variantPayload::Two($0) +[{ + "label": "true", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }] + +Complete src/CompletionPattern.res 92:23 +looking for: Cpath Value[z] +posCursor:[92:23] posNoWhite:[92:22] Found expr:[92:3->92:26] +posCursor:[92:23] posNoWhite:[92:22] Found pattern:[92:16->92:25] +posCursor:[92:23] posNoWhite:[92:22] Found pattern:[92:22->92:24] +Completable: Cpattern Value[z]->variantPayload::Three($0), recordBody +[{ + "label": "first", + "kind": 5, + "tags": [], + "detail": "first: int\n\nsomeRecord", + "documentation": null + }, { + "label": "second", + "kind": 5, + "tags": [], + "detail": "second: (bool, option)\n\nsomeRecord", + "documentation": null + }, { + "label": "optThird", + "kind": 5, + "tags": [], + "detail": "optThird: option<[#second(someRecord) | #first]>\n\nsomeRecord", + "documentation": null + }, { + "label": "nest", + "kind": 5, + "tags": [], + "detail": "nest: nestedRecord\n\nsomeRecord", + "documentation": null + }] + From d349f43331ebdd5ecc86a244beaee69c16fc901d Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Sun, 1 Jan 2023 21:54:10 +0100 Subject: [PATCH 12/31] handle polyvariants --- analysis/src/CompletionBackEnd.ml | 12 ++++ analysis/src/CompletionFrontEnd.ml | 40 +++++++++++ analysis/src/SharedTypes.ml | 4 ++ analysis/tests/src/CompletionPattern.res | 13 ++++ .../src/expected/CompletionPattern.res.txt | 66 +++++++++++++++++++ 5 files changed, 135 insertions(+) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index e8c313fab..cb3361dad 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -1782,6 +1782,18 @@ let rec resolveNestedPattern typ ~env ~package ~nested = match List.nth_opt constructor.args payloadNum with | None -> None | Some (typ, _) -> typ |> resolveNestedPattern ~env ~package ~nested)) + | ( PPolyvariantPayload {constructorName; payloadNum}, + Some (Tpolyvariant {env; constructors}) ) -> ( + match + constructors + |> List.find_opt (fun (c : polyVariantConstructor) -> + c.name = constructorName) + with + | None -> None + | Some constructor -> ( + match List.nth_opt constructor.args payloadNum with + | None -> None + | Some typ -> typ |> resolveNestedPattern ~env ~package ~nested)) | _ -> None) let processCompletable ~debug ~full ~scope ~env ~pos ~forHover diff --git a/analysis/src/CompletionFrontEnd.ml b/analysis/src/CompletionFrontEnd.ml index ebe60e458..c54fbfa18 100644 --- a/analysis/src/CompletionFrontEnd.ml +++ b/analysis/src/CompletionFrontEnd.ml @@ -516,6 +516,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = | Ppat_var {txt} -> commitFoundPat ~prefix:txt () | Ppat_construct ({txt = Lident prefix}, None) -> commitFoundPat ~prefix () + | Ppat_variant (prefix, None) -> commitFoundPat ~prefix:("#" ^ prefix) () | Ppat_tuple patterns -> ( match patterns |> findPatTupleItemWithCursor ~pos:posBeforeCursor with | Some itemNum -> appendNestedPat (Completable.PTupleItem {itemNum}) @@ -611,6 +612,45 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = appendNestedPat (Completable.PVariantPayload {constructorName = getUnqualifiedName txt; payloadNum})) + | Ppat_variant + ( txt, + Some {ppat_loc; ppat_desc = Ppat_construct ({txt = Lident "()"}, _)} + ) + when ppat_loc + |> CursorPosition.classifyLoc ~pos:posBeforeCursor + = HasCursor -> + (* Empty payload *) + appendNestedPat + (Completable.PPolyvariantPayload + {constructorName = txt; payloadNum = 0}); + commitFoundPat ~prefix:"" () + | Ppat_variant + ( txt, + Some + { + ppat_loc; + ppat_desc = + Ppat_var _ | Ppat_record _ | Ppat_construct _ | Ppat_variant _; + } ) + when ppat_loc + |> CursorPosition.classifyLoc ~pos:posBeforeCursor + = HasCursor -> + (* Single payload *) + appendNestedPat + (Completable.PPolyvariantPayload + {constructorName = txt; payloadNum = 0}) + | Ppat_variant (txt, Some {ppat_loc; ppat_desc = Ppat_tuple tupleItems}) + when ppat_loc + |> CursorPosition.classifyLoc ~pos:posBeforeCursor + = HasCursor -> ( + (* Multiple payloads with cursor in item *) + (* TODO: New item with comma *) + match tupleItems |> findPatTupleItemWithCursor ~pos:posBeforeCursor with + | None -> () + | Some payloadNum -> + appendNestedPat + (Completable.PPolyvariantPayload {constructorName = txt; payloadNum}) + ) | _ -> () in let case (iterator : Ast_iterator.iterator) (case : Parsetree.case) = diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index a2974774b..64a53682b 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -558,6 +558,7 @@ module Completable = struct | PFollowRecordField of {fieldName: string} | PRecordBody of {seenFields: string list} | PVariantPayload of {constructorName: string; payloadNum: int} + | PPolyvariantPayload of {constructorName: string; payloadNum: int} let patternPathToString p = match p with @@ -567,6 +568,9 @@ module Completable = struct | PVariantPayload {constructorName; payloadNum} -> "variantPayload::" ^ constructorName ^ "($" ^ string_of_int payloadNum ^ ")" + | PPolyvariantPayload {constructorName; payloadNum} -> + "polyvariantPayload::" ^ constructorName ^ "($" ^ string_of_int payloadNum + ^ ")" type t = | Cdecorator of string (** e.g. @module *) diff --git a/analysis/tests/src/CompletionPattern.res b/analysis/tests/src/CompletionPattern.res index 51adc8028..0bc5b7aa6 100644 --- a/analysis/tests/src/CompletionPattern.res +++ b/analysis/tests/src/CompletionPattern.res @@ -92,3 +92,16 @@ ignore(z) // switch z { | Three({})} // ^com + +type somePolyVariant = [#one | #two(bool) | #three(someRecord, bool)] +let b: somePolyVariant = #two(true) +ignore(b) + +// switch b { | #two()} +// ^com + +// switch b { | #two(t)} +// ^com + +// switch b { | #three({})} +// ^com diff --git a/analysis/tests/src/expected/CompletionPattern.res.txt b/analysis/tests/src/expected/CompletionPattern.res.txt index 99aed954a..5b25bf12b 100644 --- a/analysis/tests/src/expected/CompletionPattern.res.txt +++ b/analysis/tests/src/expected/CompletionPattern.res.txt @@ -324,3 +324,69 @@ Completable: Cpattern Value[z]->variantPayload::Three($0), recordBody "documentation": null }] +Complete src/CompletionPattern.res 99:21 +looking for: Cpath Value[b] +posCursor:[99:21] posNoWhite:[99:20] Found expr:[99:3->99:23] +posCursor:[99:21] posNoWhite:[99:20] Found pattern:[99:16->99:22] +posCursor:[99:21] posNoWhite:[99:20] Found pattern:[99:20->99:21] +Completable: Cpattern Value[b]->polyvariantPayload::two($0) +[{ + "label": "true", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }, { + "label": "false", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }] + +Complete src/CompletionPattern.res 102:22 +looking for: Cpath Value[b] +posCursor:[102:22] posNoWhite:[102:21] Found expr:[102:3->102:24] +posCursor:[102:22] posNoWhite:[102:21] Found pattern:[102:16->102:23] +posCursor:[102:22] posNoWhite:[102:21] Found pattern:[102:21->102:22] +Completable: Cpattern Value[b]=t->polyvariantPayload::two($0) +[{ + "label": "true", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }] + +Complete src/CompletionPattern.res 105:24 +looking for: Cpath Value[b] +posCursor:[105:24] posNoWhite:[105:23] Found expr:[105:3->105:27] +posCursor:[105:24] posNoWhite:[105:23] Found pattern:[105:16->105:26] +posCursor:[105:24] posNoWhite:[105:23] Found pattern:[105:23->105:25] +Completable: Cpattern Value[b]->polyvariantPayload::three($0), recordBody +[{ + "label": "first", + "kind": 5, + "tags": [], + "detail": "first: int\n\nsomeRecord", + "documentation": null + }, { + "label": "second", + "kind": 5, + "tags": [], + "detail": "second: (bool, option)\n\nsomeRecord", + "documentation": null + }, { + "label": "optThird", + "kind": 5, + "tags": [], + "detail": "optThird: option<[#second(someRecord) | #first]>\n\nsomeRecord", + "documentation": null + }, { + "label": "nest", + "kind": 5, + "tags": [], + "detail": "nest: nestedRecord\n\nsomeRecord", + "documentation": null + }] + From 72039e2138adc46f32d3bf584f445538beb7a280 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Sun, 1 Jan 2023 22:04:49 +0100 Subject: [PATCH 13/31] complete arrays in patterns --- analysis/src/CompletionBackEnd.ml | 10 ++++++ analysis/src/CompletionFrontEnd.ml | 3 ++ analysis/src/SharedTypes.ml | 3 ++ analysis/tests/src/CompletionPattern.res | 9 +++++ .../src/expected/CompletionPattern.res.txt | 33 +++++++++++++++++++ 5 files changed, 58 insertions(+) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index cb3361dad..776d41a7e 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -1519,6 +1519,8 @@ let rec extractType ~env ~package (t : Types.type_expr) = | Tlink t1 | Tsubst t1 | Tpoly (t1, []) -> extractType ~env ~package t1 | Tconstr (Path.Pident {name = "option"}, [payloadTypeExpr], _) -> Some (Completable.Toption (env, payloadTypeExpr)) + | Tconstr (Path.Pident {name = "array"}, [payloadTypeExpr], _) -> + Some (Tarray (env, payloadTypeExpr)) | Tconstr (Path.Pident {name = "bool"}, [], _) -> Some (Tbool env) | Tconstr (path, _, _) -> ( match References.digConstructor ~env ~package path with @@ -1663,6 +1665,12 @@ let completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix ~insertText:(if !Cfg.supportsSnippets then "{$0}" else "{}") ~sortText:"a" ~kind:(Value typeExpr) ~env (); ]) + | Some (Tarray (env, typeExpr)) -> + [ + Completion.createWithSnippet ~name:"[]" + ~insertText:(if !Cfg.supportsSnippets then "[$0]" else "[]") + ~sortText:"a" ~kind:(Value typeExpr) ~env (); + ] | _ -> [] in (* Include all values and modules in completion if there's a prefix, not otherwise *) @@ -1794,6 +1802,8 @@ let rec resolveNestedPattern typ ~env ~package ~nested = match List.nth_opt constructor.args payloadNum with | None -> None | Some typ -> typ |> resolveNestedPattern ~env ~package ~nested)) + | PArray, Some (Tarray (env, typ)) -> + typ |> resolveNestedPattern ~env ~package ~nested | _ -> None) let processCompletable ~debug ~full ~scope ~env ~pos ~forHover diff --git a/analysis/src/CompletionFrontEnd.ml b/analysis/src/CompletionFrontEnd.ml index c54fbfa18..c601c2068 100644 --- a/analysis/src/CompletionFrontEnd.ml +++ b/analysis/src/CompletionFrontEnd.ml @@ -517,6 +517,9 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = | Ppat_construct ({txt = Lident prefix}, None) -> commitFoundPat ~prefix () | Ppat_variant (prefix, None) -> commitFoundPat ~prefix:("#" ^ prefix) () + | Ppat_array arrayPatterns -> + appendNestedPat Completable.PArray; + if List.length arrayPatterns = 0 then commitFoundPat ~prefix:"" () | Ppat_tuple patterns -> ( match patterns |> findPatTupleItemWithCursor ~pos:posBeforeCursor with | Some itemNum -> appendNestedPat (Completable.PTupleItem {itemNum}) diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index 64a53682b..6594f40fc 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -559,6 +559,7 @@ module Completable = struct | PRecordBody of {seenFields: string list} | PVariantPayload of {constructorName: string; payloadNum: int} | PPolyvariantPayload of {constructorName: string; payloadNum: int} + | PArray let patternPathToString p = match p with @@ -571,6 +572,7 @@ module Completable = struct | PPolyvariantPayload {constructorName; payloadNum} -> "polyvariantPayload::" ^ constructorName ^ "($" ^ string_of_int payloadNum ^ ")" + | PArray -> "array" type t = | Cdecorator of string (** e.g. @module *) @@ -602,6 +604,7 @@ module Completable = struct | Tuple of QueryEnv.t * Types.type_expr list * Types.type_expr | Toption of QueryEnv.t * Types.type_expr | Tbool of QueryEnv.t + | Tarray of QueryEnv.t * Types.type_expr | Tvariant of { env: QueryEnv.t; constructors: Constructor.t list; diff --git a/analysis/tests/src/CompletionPattern.res b/analysis/tests/src/CompletionPattern.res index 0bc5b7aa6..645017173 100644 --- a/analysis/tests/src/CompletionPattern.res +++ b/analysis/tests/src/CompletionPattern.res @@ -105,3 +105,12 @@ ignore(b) // switch b { | #three({})} // ^com + +let c: array = [] +ignore(c) + +// switch c { | } +// ^com + +// switch c { | [] } +// ^com diff --git a/analysis/tests/src/expected/CompletionPattern.res.txt b/analysis/tests/src/expected/CompletionPattern.res.txt index 5b25bf12b..bda65b726 100644 --- a/analysis/tests/src/expected/CompletionPattern.res.txt +++ b/analysis/tests/src/expected/CompletionPattern.res.txt @@ -390,3 +390,36 @@ Completable: Cpattern Value[b]->polyvariantPayload::three($0), recordBody "documentation": null }] +Complete src/CompletionPattern.res 111:15 +XXX Not found! +Completable: Cpattern Value[c] +[{ + "label": "[]", + "kind": 12, + "tags": [], + "detail": "bool", + "documentation": null, + "sortText": "a", + "insertText": "[$0]", + "insertTextFormat": 2 + }] + +Complete src/CompletionPattern.res 114:17 +looking for: Cpath Value[c] +posCursor:[114:17] posNoWhite:[114:16] Found expr:[114:3->114:20] +posCursor:[114:17] posNoWhite:[114:16] Found pattern:[114:16->114:18] +Completable: Cpattern Value[c]->array +[{ + "label": "true", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }, { + "label": "false", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }] + From 57ba325d3233ca41697c9aaa5237637ea130d396 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Mon, 2 Jan 2023 09:23:40 +0100 Subject: [PATCH 14/31] handle multiple payloads in variants/polyvariants via regular tuple handling --- analysis/src/CompletionBackEnd.ml | 16 +++- analysis/src/CompletionFrontEnd.ml | 19 ++--- analysis/src/SharedTypes.ml | 14 ++-- analysis/tests/src/CompletionPattern.res | 6 ++ .../src/expected/CompletionPattern.res.txt | 74 +++++++++++++------ 5 files changed, 85 insertions(+), 44 deletions(-) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index 776d41a7e..cb5e4922e 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -1778,8 +1778,8 @@ let rec resolveNestedPattern typ ~env ~package ~nested = | Some {typ} -> typ |> resolveNestedPattern ~env ~package ~nested) | PRecordBody {seenFields}, Some (Trecord {env; typeExpr}) -> Some (typeExpr, env, Some (Completable.RecordField {seenFields})) - | ( PVariantPayload {constructorName; payloadNum}, - Some (Tvariant {env; constructors}) ) -> ( + | PVariantPayload {constructorName}, Some (Tvariant {env; constructors}) + -> ( match constructors |> List.find_opt (fun (c : Constructor.t) -> @@ -1787,10 +1787,15 @@ let rec resolveNestedPattern typ ~env ~package ~nested = with | None -> None | Some constructor -> ( + let payloadNum, nested = + match nested with + | PTupleItem {itemNum} :: nested -> (itemNum, nested) + | _ -> (0, nested) + in match List.nth_opt constructor.args payloadNum with | None -> None | Some (typ, _) -> typ |> resolveNestedPattern ~env ~package ~nested)) - | ( PPolyvariantPayload {constructorName; payloadNum}, + | ( PPolyvariantPayload {constructorName}, Some (Tpolyvariant {env; constructors}) ) -> ( match constructors @@ -1799,6 +1804,11 @@ let rec resolveNestedPattern typ ~env ~package ~nested = with | None -> None | Some constructor -> ( + let payloadNum, nested = + match nested with + | PTupleItem {itemNum} :: nested -> (itemNum, nested) + | _ -> (0, nested) + in match List.nth_opt constructor.args payloadNum with | None -> None | Some typ -> typ |> resolveNestedPattern ~env ~package ~nested)) diff --git a/analysis/src/CompletionFrontEnd.ml b/analysis/src/CompletionFrontEnd.ml index c601c2068..38efe956b 100644 --- a/analysis/src/CompletionFrontEnd.ml +++ b/analysis/src/CompletionFrontEnd.ml @@ -585,7 +585,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = (* Empty payload *) appendNestedPat (Completable.PVariantPayload - {constructorName = getUnqualifiedName txt; payloadNum = 0}); + {constructorName = getUnqualifiedName txt}); commitFoundPat ~prefix:"" () | Ppat_construct ( {txt}, @@ -601,7 +601,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = (* Single payload *) appendNestedPat (Completable.PVariantPayload - {constructorName = getUnqualifiedName txt; payloadNum = 0}) + {constructorName = getUnqualifiedName txt}) | Ppat_construct ({txt}, Some {ppat_loc; ppat_desc = Ppat_tuple tupleItems}) when ppat_loc @@ -611,10 +611,10 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = (* TODO: New item with comma *) match tupleItems |> findPatTupleItemWithCursor ~pos:posBeforeCursor with | None -> () - | Some payloadNum -> + | Some _ -> appendNestedPat (Completable.PVariantPayload - {constructorName = getUnqualifiedName txt; payloadNum})) + {constructorName = getUnqualifiedName txt})) | Ppat_variant ( txt, Some {ppat_loc; ppat_desc = Ppat_construct ({txt = Lident "()"}, _)} @@ -624,8 +624,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = = HasCursor -> (* Empty payload *) appendNestedPat - (Completable.PPolyvariantPayload - {constructorName = txt; payloadNum = 0}); + (Completable.PPolyvariantPayload {constructorName = txt}); commitFoundPat ~prefix:"" () | Ppat_variant ( txt, @@ -640,8 +639,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = = HasCursor -> (* Single payload *) appendNestedPat - (Completable.PPolyvariantPayload - {constructorName = txt; payloadNum = 0}) + (Completable.PPolyvariantPayload {constructorName = txt}) | Ppat_variant (txt, Some {ppat_loc; ppat_desc = Ppat_tuple tupleItems}) when ppat_loc |> CursorPosition.classifyLoc ~pos:posBeforeCursor @@ -650,10 +648,9 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = (* TODO: New item with comma *) match tupleItems |> findPatTupleItemWithCursor ~pos:posBeforeCursor with | None -> () - | Some payloadNum -> + | Some _ -> appendNestedPat - (Completable.PPolyvariantPayload {constructorName = txt; payloadNum}) - ) + (Completable.PPolyvariantPayload {constructorName = txt})) | _ -> () in let case (iterator : Ast_iterator.iterator) (case : Parsetree.case) = diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index 6594f40fc..6bf515476 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -557,8 +557,8 @@ module Completable = struct | PTupleItem of {itemNum: int} | PFollowRecordField of {fieldName: string} | PRecordBody of {seenFields: string list} - | PVariantPayload of {constructorName: string; payloadNum: int} - | PPolyvariantPayload of {constructorName: string; payloadNum: int} + | PVariantPayload of {constructorName: string} + | PPolyvariantPayload of {constructorName: string} | PArray let patternPathToString p = @@ -566,12 +566,10 @@ module Completable = struct | PTupleItem {itemNum} -> "tuple($" ^ string_of_int itemNum ^ ")" | PFollowRecordField {fieldName} -> "recordField(" ^ fieldName ^ ")" | PRecordBody _ -> "recordBody" - | PVariantPayload {constructorName; payloadNum} -> - "variantPayload::" ^ constructorName ^ "($" ^ string_of_int payloadNum - ^ ")" - | PPolyvariantPayload {constructorName; payloadNum} -> - "polyvariantPayload::" ^ constructorName ^ "($" ^ string_of_int payloadNum - ^ ")" + | PVariantPayload {constructorName} -> + "variantPayload(" ^ constructorName ^ ")" + | PPolyvariantPayload {constructorName} -> + "polyvariantPayload(" ^ constructorName ^ ")" | PArray -> "array" type t = diff --git a/analysis/tests/src/CompletionPattern.res b/analysis/tests/src/CompletionPattern.res index 645017173..de4a8b3e3 100644 --- a/analysis/tests/src/CompletionPattern.res +++ b/analysis/tests/src/CompletionPattern.res @@ -93,6 +93,9 @@ ignore(z) // switch z { | Three({})} // ^com +// switch z { | Three({}, t)} +// ^com + type somePolyVariant = [#one | #two(bool) | #three(someRecord, bool)] let b: somePolyVariant = #two(true) ignore(b) @@ -106,6 +109,9 @@ ignore(b) // switch b { | #three({})} // ^com +// switch b { | #three({}, t)} +// ^com + let c: array = [] ignore(c) diff --git a/analysis/tests/src/expected/CompletionPattern.res.txt b/analysis/tests/src/expected/CompletionPattern.res.txt index bda65b726..bd6220f4d 100644 --- a/analysis/tests/src/expected/CompletionPattern.res.txt +++ b/analysis/tests/src/expected/CompletionPattern.res.txt @@ -263,7 +263,7 @@ looking for: Cpath Value[z] posCursor:[86:20] posNoWhite:[86:19] Found expr:[86:3->86:22] posCursor:[86:20] posNoWhite:[86:19] Found pattern:[86:16->86:21] posCursor:[86:20] posNoWhite:[86:19] Found pattern:[86:19->86:21] -Completable: Cpattern Value[z]->variantPayload::Two($0) +Completable: Cpattern Value[z]->variantPayload(Two) [{ "label": "true", "kind": 4, @@ -283,7 +283,7 @@ looking for: Cpath Value[z] posCursor:[89:21] posNoWhite:[89:20] Found expr:[89:3->89:23] posCursor:[89:21] posNoWhite:[89:20] Found pattern:[89:16->89:22] posCursor:[89:21] posNoWhite:[89:20] Found pattern:[89:20->89:21] -Completable: Cpattern Value[z]=t->variantPayload::Two($0) +Completable: Cpattern Value[z]=t->variantPayload(Two) [{ "label": "true", "kind": 4, @@ -297,7 +297,7 @@ looking for: Cpath Value[z] posCursor:[92:23] posNoWhite:[92:22] Found expr:[92:3->92:26] posCursor:[92:23] posNoWhite:[92:22] Found pattern:[92:16->92:25] posCursor:[92:23] posNoWhite:[92:22] Found pattern:[92:22->92:24] -Completable: Cpattern Value[z]->variantPayload::Three($0), recordBody +Completable: Cpattern Value[z]->variantPayload(Three), recordBody [{ "label": "first", "kind": 5, @@ -324,12 +324,27 @@ Completable: Cpattern Value[z]->variantPayload::Three($0), recordBody "documentation": null }] -Complete src/CompletionPattern.res 99:21 +Complete src/CompletionPattern.res 95:27 +looking for: Cpath Value[z] +posCursor:[95:27] posNoWhite:[95:26] Found expr:[95:3->95:29] +posCursor:[95:27] posNoWhite:[95:26] Found pattern:[95:16->95:28] +posCursor:[95:27] posNoWhite:[95:26] Found pattern:[95:21->95:29] +posCursor:[95:27] posNoWhite:[95:26] Found pattern:[95:26->95:27] +Completable: Cpattern Value[z]=t->variantPayload(Three), tuple($1) +[{ + "label": "true", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }] + +Complete src/CompletionPattern.res 102:21 looking for: Cpath Value[b] -posCursor:[99:21] posNoWhite:[99:20] Found expr:[99:3->99:23] -posCursor:[99:21] posNoWhite:[99:20] Found pattern:[99:16->99:22] -posCursor:[99:21] posNoWhite:[99:20] Found pattern:[99:20->99:21] -Completable: Cpattern Value[b]->polyvariantPayload::two($0) +posCursor:[102:21] posNoWhite:[102:20] Found expr:[102:3->102:23] +posCursor:[102:21] posNoWhite:[102:20] Found pattern:[102:16->102:22] +posCursor:[102:21] posNoWhite:[102:20] Found pattern:[102:20->102:21] +Completable: Cpattern Value[b]->polyvariantPayload(two) [{ "label": "true", "kind": 4, @@ -344,12 +359,12 @@ Completable: Cpattern Value[b]->polyvariantPayload::two($0) "documentation": null }] -Complete src/CompletionPattern.res 102:22 +Complete src/CompletionPattern.res 105:22 looking for: Cpath Value[b] -posCursor:[102:22] posNoWhite:[102:21] Found expr:[102:3->102:24] -posCursor:[102:22] posNoWhite:[102:21] Found pattern:[102:16->102:23] -posCursor:[102:22] posNoWhite:[102:21] Found pattern:[102:21->102:22] -Completable: Cpattern Value[b]=t->polyvariantPayload::two($0) +posCursor:[105:22] posNoWhite:[105:21] Found expr:[105:3->105:24] +posCursor:[105:22] posNoWhite:[105:21] Found pattern:[105:16->105:23] +posCursor:[105:22] posNoWhite:[105:21] Found pattern:[105:21->105:22] +Completable: Cpattern Value[b]=t->polyvariantPayload(two) [{ "label": "true", "kind": 4, @@ -358,12 +373,12 @@ Completable: Cpattern Value[b]=t->polyvariantPayload::two($0) "documentation": null }] -Complete src/CompletionPattern.res 105:24 +Complete src/CompletionPattern.res 108:24 looking for: Cpath Value[b] -posCursor:[105:24] posNoWhite:[105:23] Found expr:[105:3->105:27] -posCursor:[105:24] posNoWhite:[105:23] Found pattern:[105:16->105:26] -posCursor:[105:24] posNoWhite:[105:23] Found pattern:[105:23->105:25] -Completable: Cpattern Value[b]->polyvariantPayload::three($0), recordBody +posCursor:[108:24] posNoWhite:[108:23] Found expr:[108:3->108:27] +posCursor:[108:24] posNoWhite:[108:23] Found pattern:[108:16->108:26] +posCursor:[108:24] posNoWhite:[108:23] Found pattern:[108:23->108:25] +Completable: Cpattern Value[b]->polyvariantPayload(three), recordBody [{ "label": "first", "kind": 5, @@ -390,7 +405,22 @@ Completable: Cpattern Value[b]->polyvariantPayload::three($0), recordBody "documentation": null }] -Complete src/CompletionPattern.res 111:15 +Complete src/CompletionPattern.res 111:28 +looking for: Cpath Value[b] +posCursor:[111:28] posNoWhite:[111:27] Found expr:[111:3->111:30] +posCursor:[111:28] posNoWhite:[111:27] Found pattern:[111:16->111:29] +posCursor:[111:28] posNoWhite:[111:27] Found pattern:[111:22->111:29] +posCursor:[111:28] posNoWhite:[111:27] Found pattern:[111:27->111:28] +Completable: Cpattern Value[b]=t->polyvariantPayload(three), tuple($1) +[{ + "label": "true", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }] + +Complete src/CompletionPattern.res 117:15 XXX Not found! Completable: Cpattern Value[c] [{ @@ -404,10 +434,10 @@ Completable: Cpattern Value[c] "insertTextFormat": 2 }] -Complete src/CompletionPattern.res 114:17 +Complete src/CompletionPattern.res 120:17 looking for: Cpath Value[c] -posCursor:[114:17] posNoWhite:[114:16] Found expr:[114:3->114:20] -posCursor:[114:17] posNoWhite:[114:16] Found pattern:[114:16->114:18] +posCursor:[120:17] posNoWhite:[120:16] Found expr:[120:3->120:20] +posCursor:[120:17] posNoWhite:[120:16] Found pattern:[120:16->120:18] Completable: Cpattern Value[c]->array [{ "label": "true", From 3eb59e2ebfacb1f1d0f15be74f069fc72afc7cdd Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Tue, 3 Jan 2023 18:19:53 +0100 Subject: [PATCH 15/31] move from ast iterator to traversal function for finding completables in patterns --- analysis/src/CompletionBackEnd.ml | 18 +- analysis/src/CompletionFrontEnd.ml | 355 ++++++++++-------- analysis/src/SharedTypes.ml | 13 +- .../src/expected/CompletionPattern.res.txt | 16 +- 4 files changed, 220 insertions(+), 182 deletions(-) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index cb5e4922e..ae84b8f46 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -1778,7 +1778,7 @@ let rec resolveNestedPattern typ ~env ~package ~nested = | Some {typ} -> typ |> resolveNestedPattern ~env ~package ~nested) | PRecordBody {seenFields}, Some (Trecord {env; typeExpr}) -> Some (typeExpr, env, Some (Completable.RecordField {seenFields})) - | PVariantPayload {constructorName}, Some (Tvariant {env; constructors}) + | PVariantPayload {constructorName; itemNum}, Some (Tvariant {env; constructors}) -> ( match constructors @@ -1787,15 +1787,10 @@ let rec resolveNestedPattern typ ~env ~package ~nested = with | None -> None | Some constructor -> ( - let payloadNum, nested = - match nested with - | PTupleItem {itemNum} :: nested -> (itemNum, nested) - | _ -> (0, nested) - in - match List.nth_opt constructor.args payloadNum with + match List.nth_opt constructor.args itemNum with | None -> None | Some (typ, _) -> typ |> resolveNestedPattern ~env ~package ~nested)) - | ( PPolyvariantPayload {constructorName}, + | ( PPolyvariantPayload {constructorName; itemNum}, Some (Tpolyvariant {env; constructors}) ) -> ( match constructors @@ -1804,12 +1799,7 @@ let rec resolveNestedPattern typ ~env ~package ~nested = with | None -> None | Some constructor -> ( - let payloadNum, nested = - match nested with - | PTupleItem {itemNum} :: nested -> (itemNum, nested) - | _ -> (0, nested) - in - match List.nth_opt constructor.args payloadNum with + match List.nth_opt constructor.args itemNum with | None -> None | Some typ -> typ |> resolveNestedPattern ~env ~package ~nested)) | PArray, Some (Tarray (env, typ)) -> diff --git a/analysis/src/CompletionFrontEnd.ml b/analysis/src/CompletionFrontEnd.ml index 38efe956b..03bf56e59 100644 --- a/analysis/src/CompletionFrontEnd.ml +++ b/analysis/src/CompletionFrontEnd.ml @@ -411,123 +411,48 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = | Ppat_open (_, p) -> scopePattern p in - let scopeValueBinding (vb : Parsetree.value_binding) = - scopePattern vb.pvb_pat - in - - let scopeTypeKind (tk : Parsetree.type_kind) = - match tk with - | Ptype_variant constrDecls -> - constrDecls - |> List.iter (fun (cd : Parsetree.constructor_declaration) -> - scope := - !scope - |> Scope.addConstructor ~name:cd.pcd_name.txt ~loc:cd.pcd_loc) - | Ptype_record labelDecls -> - labelDecls - |> List.iter (fun (ld : Parsetree.label_declaration) -> - scope := - !scope |> Scope.addField ~name:ld.pld_name.txt ~loc:ld.pld_loc) - | _ -> () - in - let scopeTypeDeclaration (td : Parsetree.type_declaration) = - scope := - !scope |> Scope.addType ~name:td.ptype_name.txt ~loc:td.ptype_name.loc; - scopeTypeKind td.ptype_kind - in - let scopeModuleBinding (mb : Parsetree.module_binding) = - scope := - !scope |> Scope.addModule ~name:mb.pmb_name.txt ~loc:mb.pmb_name.loc - in - let scopeModuleDeclaration (md : Parsetree.module_declaration) = - scope := - !scope |> Scope.addModule ~name:md.pmd_name.txt ~loc:md.pmd_name.loc - in let lookingForPat = ref None in - let setLookingForPat ctxPath = - lookingForPat := - Some (Completable.Cpattern {typ = ctxPath; prefix = ""; nested = None}); - if debug then - Printf.printf "looking for: %s \n" (Completable.toString (Cpath ctxPath)) - in - let unsetLookingForPat () = lookingForPat := None in - let appendNestedPat patternPat = - match !lookingForPat with - | Some (Completable.Cpattern ({nested = None} as p)) -> - lookingForPat := Some (Cpattern {p with nested = Some [patternPat]}) - | Some (Cpattern ({nested = Some nested} as p)) -> - lookingForPat := - Some (Cpattern {p with nested = Some (nested @ [patternPat])}) - | _ -> () - in - let commitFoundPat ?prefix () = - match !lookingForPat with - | Some (Completable.Cpattern p as cpattern) -> - let cpattern = - match prefix with - | None -> cpattern - | Some prefix -> Cpattern {p with prefix} - in - setResult cpattern - | _ -> () - in - (* Identifies expressions where we can do typed pattern or expr completion. *) - let typedCompletionExpr (exp : Parsetree.expression) = - if - exp.pexp_loc - |> CursorPosition.classifyLoc ~pos:posBeforeCursor - = HasCursor - then - match exp.pexp_desc with - | Pexp_match (_exp, []) -> - (* No cases means there's no `|` yet in the switch *) () - | Pexp_match - ( exp, - [ - { - pc_lhs = - { - ppat_desc = - Ppat_extension ({txt = "rescript.patternhole"}, _); - }; - }; - ] ) -> ( - (* A single case that's a pattern hole typically means `switch x { | }`. Complete as the pattern itself with nothing nested. *) - match exprToContextPath exp with - | None -> () - | Some ctxPath -> - setResult - (Completable.Cpattern {typ = ctxPath; nested = None; prefix = ""})) - | Pexp_match (exp, _cases) -> ( - (* If there's more than one case, or the case isn't a pattern hole, set that we're looking for this path currently. *) - match exp |> exprToContextPath with - | None -> () - | Some ctxPath -> setLookingForPat ctxPath) - | _ -> unsetLookingForPat () - in - (* Tracks the path through a pattern for where the cursor is. *) - let typedCompletionPat (pat : Parsetree.pattern) = + + let rec traversePattern (pat : Parsetree.pattern) ~patternPath = if pat.ppat_loc |> CursorPosition.classifyLoc ~pos:posBeforeCursor = HasCursor then match pat.ppat_desc with - | Ppat_var {txt} -> commitFoundPat ~prefix:txt () + | Ppat_any | Ppat_constant _ | Ppat_interval _ -> None + | Ppat_lazy p + | Ppat_constraint (p, _) + | Ppat_alias (p, _) + | Ppat_exception p + | Ppat_open (_, p) -> + p |> traversePattern ~patternPath + | Ppat_or (p1, p2) -> + [p1; p2] |> List.find_map (fun p -> p |> traversePattern ~patternPath) + | Ppat_var {txt} -> Some (txt, patternPath) | Ppat_construct ({txt = Lident prefix}, None) -> - commitFoundPat ~prefix () - | Ppat_variant (prefix, None) -> commitFoundPat ~prefix:("#" ^ prefix) () + Some (prefix, patternPath) + | Ppat_variant (prefix, None) -> Some ("#" ^ prefix, patternPath) | Ppat_array arrayPatterns -> - appendNestedPat Completable.PArray; - if List.length arrayPatterns = 0 then commitFoundPat ~prefix:"" () - | Ppat_tuple patterns -> ( - match patterns |> findPatTupleItemWithCursor ~pos:posBeforeCursor with - | Some itemNum -> appendNestedPat (Completable.PTupleItem {itemNum}) - | _ -> ()) + let nextPatternPath = [Completable.PArray] @ patternPath in + if List.length arrayPatterns = 0 then Some ("", nextPatternPath) + else + arrayPatterns + |> List.find_map (fun pat -> + pat |> traversePattern ~patternPath:nextPatternPath) + | Ppat_tuple patterns -> + let itemNum = ref (-1) in + patterns + |> List.find_map (fun pat -> + itemNum := !itemNum + 1; + pat + |> traversePattern + ~patternPath: + ([Completable.PTupleItem {itemNum = !itemNum}] + @ patternPath)) | Ppat_record ([], _) -> (* Empty fields means we're in a record body `{}`. Complete for the fields. *) - appendNestedPat (Completable.PRecordBody {seenFields = []}); - commitFoundPat ~prefix:"" () + Some ("", [Completable.PRecordBody {seenFields = []}] @ patternPath) | Ppat_record (fields, _) -> ( let fieldWithCursor = ref None in let fieldWithPatHole = ref None in @@ -555,16 +480,21 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = match f.ppat_desc with | Ppat_record _ | Ppat_construct _ | Ppat_variant _ | Ppat_tuple _ -> (* These are things we can continue into in the pattern. *) - appendNestedPat (Completable.PFollowRecordField {fieldName = fname}) + f + |> traversePattern + ~patternPath: + ([Completable.PFollowRecordField {fieldName = fname}] + @ patternPath) | Ppat_extension ({txt = "rescript.patternhole"}, _) -> (* A pattern hole means for example `{someField: }`. We want to complete for the type of `someField`. *) - appendNestedPat (Completable.PFollowRecordField {fieldName = fname}); - commitFoundPat ~prefix:"" () + Some + ( "", + [Completable.PFollowRecordField {fieldName = fname}] + @ patternPath ) | Ppat_var {txt} -> (* A var means `{s}` or similar. Complete for fields. *) - appendNestedPat (Completable.PRecordBody {seenFields}); - commitFoundPat ~prefix:txt () - | _ -> ()) + Some (txt, [Completable.PRecordBody {seenFields}] @ patternPath) + | _ -> None) | None, None -> ( (* Figure out if we're completing for a new field. If the cursor is inside of the record body, but no field has the cursor, @@ -572,9 +502,8 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = ignoring white space. If that's a comma, we assume you're completing for a new field. *) match firstCharBeforeCursorNoWhite with | Some ',' -> - appendNestedPat (Completable.PRecordBody {seenFields}); - commitFoundPat ~prefix:"" () - | _ -> ())) + Some ("", [Completable.PRecordBody {seenFields}] @ patternPath) + | _ -> None)) | Ppat_construct ( {txt}, Some {ppat_loc; ppat_desc = Ppat_construct ({txt = Lident "()"}, _)} @@ -583,38 +512,56 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = |> CursorPosition.classifyLoc ~pos:posBeforeCursor = HasCursor -> (* Empty payload *) - appendNestedPat - (Completable.PVariantPayload - {constructorName = getUnqualifiedName txt}); - commitFoundPat ~prefix:"" () + Some + ( "", + [ + Completable.PVariantPayload + {constructorName = getUnqualifiedName txt; itemNum = 0}; + ] + @ patternPath ) | Ppat_construct ( {txt}, Some - { - ppat_loc; - ppat_desc = - Ppat_var _ | Ppat_record _ | Ppat_construct _ | Ppat_variant _; - } ) + ({ + ppat_loc; + ppat_desc = + ( Ppat_var _ | Ppat_record _ | Ppat_construct _ + | Ppat_variant _ ); + } as pat) ) when ppat_loc |> CursorPosition.classifyLoc ~pos:posBeforeCursor = HasCursor -> (* Single payload *) - appendNestedPat - (Completable.PVariantPayload - {constructorName = getUnqualifiedName txt}) + pat + |> traversePattern + ~patternPath: + ([ + Completable.PVariantPayload + {constructorName = getUnqualifiedName txt; itemNum = 0}; + ] + @ patternPath) | Ppat_construct ({txt}, Some {ppat_loc; ppat_desc = Ppat_tuple tupleItems}) when ppat_loc |> CursorPosition.classifyLoc ~pos:posBeforeCursor - = HasCursor -> ( + = HasCursor -> (* Multiple payloads with cursor in item *) (* TODO: New item with comma *) - match tupleItems |> findPatTupleItemWithCursor ~pos:posBeforeCursor with - | None -> () - | Some _ -> - appendNestedPat - (Completable.PVariantPayload - {constructorName = getUnqualifiedName txt})) + let itemNum = ref (-1) in + tupleItems + |> List.find_map (fun pat -> + itemNum := !itemNum + 1; + pat + |> traversePattern + ~patternPath: + ([ + Completable.PVariantPayload + { + constructorName = getUnqualifiedName txt; + itemNum = !itemNum; + }; + ] + @ patternPath)) | Ppat_variant ( txt, Some {ppat_loc; ppat_desc = Ppat_construct ({txt = Lident "()"}, _)} @@ -623,39 +570,141 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = |> CursorPosition.classifyLoc ~pos:posBeforeCursor = HasCursor -> (* Empty payload *) - appendNestedPat - (Completable.PPolyvariantPayload {constructorName = txt}); - commitFoundPat ~prefix:"" () + Some + ( "", + [ + Completable.PPolyvariantPayload + {constructorName = txt; itemNum = 0}; + ] + @ patternPath ) | Ppat_variant ( txt, Some - { - ppat_loc; - ppat_desc = - Ppat_var _ | Ppat_record _ | Ppat_construct _ | Ppat_variant _; - } ) + ({ + ppat_loc; + ppat_desc = + ( Ppat_var _ | Ppat_record _ | Ppat_construct _ + | Ppat_variant _ ); + } as pat) ) when ppat_loc |> CursorPosition.classifyLoc ~pos:posBeforeCursor = HasCursor -> (* Single payload *) - appendNestedPat - (Completable.PPolyvariantPayload {constructorName = txt}) + pat + |> traversePattern + ~patternPath: + ([ + Completable.PPolyvariantPayload + {constructorName = txt; itemNum = 0}; + ] + @ patternPath) | Ppat_variant (txt, Some {ppat_loc; ppat_desc = Ppat_tuple tupleItems}) when ppat_loc |> CursorPosition.classifyLoc ~pos:posBeforeCursor - = HasCursor -> ( + = HasCursor -> (* Multiple payloads with cursor in item *) (* TODO: New item with comma *) - match tupleItems |> findPatTupleItemWithCursor ~pos:posBeforeCursor with + let itemNum = ref (-1) in + tupleItems + |> List.find_map (fun pat -> + itemNum := !itemNum + 1; + pat + |> traversePattern + ~patternPath: + ([ + Completable.PPolyvariantPayload + {constructorName = txt; itemNum = !itemNum}; + ] + @ patternPath)) + | _ -> None + else None + in + let completePattern (pat : Parsetree.pattern) = + match (pat |> traversePattern ~patternPath:[], !lookingForPat) with + | Some (prefix, []), Some (Completable.Cpattern p) -> + setResult (Completable.Cpattern {p with prefix; nested = None}) + | Some (prefix, nestedPattern), Some (Cpattern p) -> + setResult + (Cpattern {p with prefix; nested = Some (List.rev nestedPattern)}) + | _ -> () + in + let scopeValueBinding (vb : Parsetree.value_binding) = + scopePattern vb.pvb_pat; + completePattern vb.pvb_pat + in + let scopeTypeKind (tk : Parsetree.type_kind) = + match tk with + | Ptype_variant constrDecls -> + constrDecls + |> List.iter (fun (cd : Parsetree.constructor_declaration) -> + scope := + !scope + |> Scope.addConstructor ~name:cd.pcd_name.txt ~loc:cd.pcd_loc) + | Ptype_record labelDecls -> + labelDecls + |> List.iter (fun (ld : Parsetree.label_declaration) -> + scope := + !scope |> Scope.addField ~name:ld.pld_name.txt ~loc:ld.pld_loc) + | _ -> () + in + let scopeTypeDeclaration (td : Parsetree.type_declaration) = + scope := + !scope |> Scope.addType ~name:td.ptype_name.txt ~loc:td.ptype_name.loc; + scopeTypeKind td.ptype_kind + in + let scopeModuleBinding (mb : Parsetree.module_binding) = + scope := + !scope |> Scope.addModule ~name:mb.pmb_name.txt ~loc:mb.pmb_name.loc + in + let scopeModuleDeclaration (md : Parsetree.module_declaration) = + scope := + !scope |> Scope.addModule ~name:md.pmd_name.txt ~loc:md.pmd_name.loc + in + let setLookingForPat ctxPath = + lookingForPat := + Some (Completable.Cpattern {typ = ctxPath; prefix = ""; nested = None}); + if debug then + Printf.printf "looking for: %s \n" (Completable.toString (Cpath ctxPath)) + in + let unsetLookingForPat () = lookingForPat := None in + (* Identifies expressions where we can do typed pattern or expr completion. *) + let typedCompletionExpr (exp : Parsetree.expression) = + if + exp.pexp_loc + |> CursorPosition.classifyLoc ~pos:posBeforeCursor + = HasCursor + then + match exp.pexp_desc with + | Pexp_match (_exp, []) -> + (* No cases means there's no `|` yet in the switch *) () + | Pexp_match + ( exp, + [ + { + pc_lhs = + { + ppat_desc = + Ppat_extension ({txt = "rescript.patternhole"}, _); + }; + }; + ] ) -> ( + (* A single case that's a pattern hole typically means `switch x { | }`. Complete as the pattern itself with nothing nested. *) + match exprToContextPath exp with + | None -> () + | Some ctxPath -> + setResult + (Completable.Cpattern {typ = ctxPath; nested = None; prefix = ""})) + | Pexp_match (exp, _cases) -> ( + (* If there's more than one case, or the case isn't a pattern hole, set that we're looking for this path currently. *) + match exp |> exprToContextPath with | None -> () - | Some _ -> - appendNestedPat - (Completable.PPolyvariantPayload {constructorName = txt})) - | _ -> () + | Some ctxPath -> setLookingForPat ctxPath) + | _ -> unsetLookingForPat () in let case (iterator : Ast_iterator.iterator) (case : Parsetree.case) = let oldScope = !scope in scopePattern case.pc_lhs; + completePattern case.pc_lhs; Ast_iterator.default_iterator.case iterator case; scope := oldScope in @@ -994,6 +1043,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = | None -> () | Some defaultExp -> iterator.expr iterator defaultExp); scopePattern pat; + completePattern pat; iterator.pat iterator pat; iterator.expr iterator e; scope := oldScope; @@ -1046,8 +1096,6 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = let pat (iterator : Ast_iterator.iterator) (pat : Parsetree.pattern) = if pat.ppat_loc |> Loc.hasPos ~pos:posNoWhite then ( found := true; - let oldLookingForPat = !lookingForPat in - typedCompletionPat pat; if debug then Printf.printf "posCursor:[%s] posNoWhite:[%s] Found pattern:%s\n" (Pos.toString posCursor) (Pos.toString posNoWhite) @@ -1066,8 +1114,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = (Loc.toString lid.loc); setResult (Cpath (CPId (lidPath, Value))) | _ -> ()); - Ast_iterator.default_iterator.pat iterator pat; - lookingForPat := oldLookingForPat) + Ast_iterator.default_iterator.pat iterator pat) in let module_expr (iterator : Ast_iterator.iterator) (me : Parsetree.module_expr) = diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index 6bf515476..e0c2b0cd8 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -557,8 +557,8 @@ module Completable = struct | PTupleItem of {itemNum: int} | PFollowRecordField of {fieldName: string} | PRecordBody of {seenFields: string list} - | PVariantPayload of {constructorName: string} - | PPolyvariantPayload of {constructorName: string} + | PVariantPayload of {constructorName: string; itemNum: int} + | PPolyvariantPayload of {constructorName: string; itemNum: int} | PArray let patternPathToString p = @@ -566,10 +566,11 @@ module Completable = struct | PTupleItem {itemNum} -> "tuple($" ^ string_of_int itemNum ^ ")" | PFollowRecordField {fieldName} -> "recordField(" ^ fieldName ^ ")" | PRecordBody _ -> "recordBody" - | PVariantPayload {constructorName} -> - "variantPayload(" ^ constructorName ^ ")" - | PPolyvariantPayload {constructorName} -> - "polyvariantPayload(" ^ constructorName ^ ")" + | PVariantPayload {constructorName; itemNum} -> + "variantPayload::" ^ constructorName ^ "($" ^ string_of_int itemNum ^ ")" + | PPolyvariantPayload {constructorName; itemNum} -> + "polyvariantPayload::" ^ constructorName ^ "($" ^ string_of_int itemNum + ^ ")" | PArray -> "array" type t = diff --git a/analysis/tests/src/expected/CompletionPattern.res.txt b/analysis/tests/src/expected/CompletionPattern.res.txt index bd6220f4d..f912a9d6c 100644 --- a/analysis/tests/src/expected/CompletionPattern.res.txt +++ b/analysis/tests/src/expected/CompletionPattern.res.txt @@ -263,7 +263,7 @@ looking for: Cpath Value[z] posCursor:[86:20] posNoWhite:[86:19] Found expr:[86:3->86:22] posCursor:[86:20] posNoWhite:[86:19] Found pattern:[86:16->86:21] posCursor:[86:20] posNoWhite:[86:19] Found pattern:[86:19->86:21] -Completable: Cpattern Value[z]->variantPayload(Two) +Completable: Cpattern Value[z]->variantPayload::Two($0) [{ "label": "true", "kind": 4, @@ -283,7 +283,7 @@ looking for: Cpath Value[z] posCursor:[89:21] posNoWhite:[89:20] Found expr:[89:3->89:23] posCursor:[89:21] posNoWhite:[89:20] Found pattern:[89:16->89:22] posCursor:[89:21] posNoWhite:[89:20] Found pattern:[89:20->89:21] -Completable: Cpattern Value[z]=t->variantPayload(Two) +Completable: Cpattern Value[z]=t->variantPayload::Two($0) [{ "label": "true", "kind": 4, @@ -297,7 +297,7 @@ looking for: Cpath Value[z] posCursor:[92:23] posNoWhite:[92:22] Found expr:[92:3->92:26] posCursor:[92:23] posNoWhite:[92:22] Found pattern:[92:16->92:25] posCursor:[92:23] posNoWhite:[92:22] Found pattern:[92:22->92:24] -Completable: Cpattern Value[z]->variantPayload(Three), recordBody +Completable: Cpattern Value[z]->variantPayload::Three($0), recordBody [{ "label": "first", "kind": 5, @@ -330,7 +330,7 @@ posCursor:[95:27] posNoWhite:[95:26] Found expr:[95:3->95:29] posCursor:[95:27] posNoWhite:[95:26] Found pattern:[95:16->95:28] posCursor:[95:27] posNoWhite:[95:26] Found pattern:[95:21->95:29] posCursor:[95:27] posNoWhite:[95:26] Found pattern:[95:26->95:27] -Completable: Cpattern Value[z]=t->variantPayload(Three), tuple($1) +Completable: Cpattern Value[z]=t->variantPayload::Three($1) [{ "label": "true", "kind": 4, @@ -344,7 +344,7 @@ looking for: Cpath Value[b] posCursor:[102:21] posNoWhite:[102:20] Found expr:[102:3->102:23] posCursor:[102:21] posNoWhite:[102:20] Found pattern:[102:16->102:22] posCursor:[102:21] posNoWhite:[102:20] Found pattern:[102:20->102:21] -Completable: Cpattern Value[b]->polyvariantPayload(two) +Completable: Cpattern Value[b]->polyvariantPayload::two($0) [{ "label": "true", "kind": 4, @@ -364,7 +364,7 @@ looking for: Cpath Value[b] posCursor:[105:22] posNoWhite:[105:21] Found expr:[105:3->105:24] posCursor:[105:22] posNoWhite:[105:21] Found pattern:[105:16->105:23] posCursor:[105:22] posNoWhite:[105:21] Found pattern:[105:21->105:22] -Completable: Cpattern Value[b]=t->polyvariantPayload(two) +Completable: Cpattern Value[b]=t->polyvariantPayload::two($0) [{ "label": "true", "kind": 4, @@ -378,7 +378,7 @@ looking for: Cpath Value[b] posCursor:[108:24] posNoWhite:[108:23] Found expr:[108:3->108:27] posCursor:[108:24] posNoWhite:[108:23] Found pattern:[108:16->108:26] posCursor:[108:24] posNoWhite:[108:23] Found pattern:[108:23->108:25] -Completable: Cpattern Value[b]->polyvariantPayload(three), recordBody +Completable: Cpattern Value[b]->polyvariantPayload::three($0), recordBody [{ "label": "first", "kind": 5, @@ -411,7 +411,7 @@ posCursor:[111:28] posNoWhite:[111:27] Found expr:[111:3->111:30] posCursor:[111:28] posNoWhite:[111:27] Found pattern:[111:16->111:29] posCursor:[111:28] posNoWhite:[111:27] Found pattern:[111:22->111:29] posCursor:[111:28] posNoWhite:[111:27] Found pattern:[111:27->111:28] -Completable: Cpattern Value[b]=t->polyvariantPayload(three), tuple($1) +Completable: Cpattern Value[b]=t->polyvariantPayload::three($1) [{ "label": "true", "kind": 4, From 8e3baf5be09782a434063ed6bfe4fb5dc86e8715 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Tue, 3 Jan 2023 18:26:36 +0100 Subject: [PATCH 16/31] ignore unused var --- analysis/tests/src/CompletionPattern.res | 1 + .../src/expected/CompletionPattern.res.txt | 146 +++++++++--------- 2 files changed, 74 insertions(+), 73 deletions(-) diff --git a/analysis/tests/src/CompletionPattern.res b/analysis/tests/src/CompletionPattern.res index de4a8b3e3..aec7b14a0 100644 --- a/analysis/tests/src/CompletionPattern.res +++ b/analysis/tests/src/CompletionPattern.res @@ -42,6 +42,7 @@ let f: someRecord = { } let z = (f, true) +ignore(z) // switch f { | } // ^com diff --git a/analysis/tests/src/expected/CompletionPattern.res.txt b/analysis/tests/src/expected/CompletionPattern.res.txt index f912a9d6c..96deeaec6 100644 --- a/analysis/tests/src/expected/CompletionPattern.res.txt +++ b/analysis/tests/src/expected/CompletionPattern.res.txt @@ -74,7 +74,7 @@ Completable: Cpattern Value[x]=t "documentation": null }] -Complete src/CompletionPattern.res 45:15 +Complete src/CompletionPattern.res 46:15 XXX Not found! Completable: Cpattern Value[f] [{ @@ -88,10 +88,10 @@ Completable: Cpattern Value[f] "insertTextFormat": 2 }] -Complete src/CompletionPattern.res 48:17 +Complete src/CompletionPattern.res 49:17 looking for: Cpath Value[f] -posCursor:[48:17] posNoWhite:[48:16] Found expr:[48:3->48:19] -posCursor:[48:17] posNoWhite:[48:16] Found pattern:[48:16->48:18] +posCursor:[49:17] posNoWhite:[49:16] Found expr:[49:3->49:19] +posCursor:[49:17] posNoWhite:[49:16] Found pattern:[49:16->49:18] Completable: Cpattern Value[f]->recordBody [{ "label": "first", @@ -119,10 +119,10 @@ Completable: Cpattern Value[f]->recordBody "documentation": null }] -Complete src/CompletionPattern.res 51:24 +Complete src/CompletionPattern.res 52:24 looking for: Cpath Value[f] -posCursor:[51:24] posNoWhite:[51:22] Found expr:[51:3->51:36] -posCursor:[51:24] posNoWhite:[51:22] Found pattern:[51:16->51:35] +posCursor:[52:24] posNoWhite:[52:22] Found expr:[52:3->52:36] +posCursor:[52:24] posNoWhite:[52:22] Found pattern:[52:16->52:35] Completable: Cpattern Value[f]->recordBody [{ "label": "optThird", @@ -138,11 +138,11 @@ Completable: Cpattern Value[f]->recordBody "documentation": null }] -Complete src/CompletionPattern.res 54:19 +Complete src/CompletionPattern.res 55:19 looking for: Cpath Value[f] -posCursor:[54:19] posNoWhite:[54:18] Found expr:[54:3->54:21] -posCursor:[54:19] posNoWhite:[54:18] Found pattern:[54:16->54:20] -posCursor:[54:19] posNoWhite:[54:18] Found pattern:[54:17->54:19] +posCursor:[55:19] posNoWhite:[55:18] Found expr:[55:3->55:21] +posCursor:[55:19] posNoWhite:[55:18] Found pattern:[55:16->55:20] +posCursor:[55:19] posNoWhite:[55:18] Found pattern:[55:17->55:19] Completable: Cpattern Value[f]=fi->recordBody [{ "label": "first", @@ -152,12 +152,12 @@ Completable: Cpattern Value[f]=fi->recordBody "documentation": null }] -Complete src/CompletionPattern.res 57:19 +Complete src/CompletionPattern.res 58:19 looking for: Cpath Value[z] -posCursor:[57:19] posNoWhite:[57:18] Found expr:[57:3->57:25] -posCursor:[57:19] posNoWhite:[57:18] Found pattern:[57:16->57:24] -posCursor:[57:19] posNoWhite:[57:18] Found pattern:[57:17->57:20] -posCursor:[57:19] posNoWhite:[57:18] Found pattern:[57:18->57:19] +posCursor:[58:19] posNoWhite:[58:18] Found expr:[58:3->58:25] +posCursor:[58:19] posNoWhite:[58:18] Found pattern:[58:16->58:24] +posCursor:[58:19] posNoWhite:[58:18] Found pattern:[58:17->58:20] +posCursor:[58:19] posNoWhite:[58:18] Found pattern:[58:18->58:19] Completable: Cpattern Value[z]=o->tuple($0), recordBody [{ "label": "optThird", @@ -167,10 +167,10 @@ Completable: Cpattern Value[z]=o->tuple($0), recordBody "documentation": null }] -Complete src/CompletionPattern.res 60:22 +Complete src/CompletionPattern.res 61:22 looking for: Cpath Value[f] -posCursor:[60:22] posNoWhite:[60:21] Found expr:[60:3->73:1] -posCursor:[60:22] posNoWhite:[60:21] Found pattern:[60:16->60:25] +posCursor:[61:22] posNoWhite:[61:21] Found expr:[61:3->74:1] +posCursor:[61:22] posNoWhite:[61:21] Found pattern:[61:16->61:25] Completable: Cpattern Value[f]->recordField(nest) [{ "label": "{}", @@ -183,11 +183,11 @@ Completable: Cpattern Value[f]->recordField(nest) "insertTextFormat": 2 }] -Complete src/CompletionPattern.res 63:24 +Complete src/CompletionPattern.res 64:24 looking for: Cpath Value[f] -posCursor:[63:24] posNoWhite:[63:23] Found expr:[63:3->63:27] -posCursor:[63:24] posNoWhite:[63:23] Found pattern:[63:16->63:26] -posCursor:[63:24] posNoWhite:[63:23] Found pattern:[63:23->63:25] +posCursor:[64:24] posNoWhite:[64:23] Found expr:[64:3->64:27] +posCursor:[64:24] posNoWhite:[64:23] Found pattern:[64:16->64:26] +posCursor:[64:24] posNoWhite:[64:23] Found pattern:[64:23->64:25] Completable: Cpattern Value[f]->recordField(nest), recordBody [{ "label": "nested", @@ -197,14 +197,14 @@ Completable: Cpattern Value[f]->recordField(nest), recordBody "documentation": null }] -Complete src/CompletionPattern.res 69:22 +Complete src/CompletionPattern.res 70:22 looking for: Cpath Value[f] -posCursor:[69:22] posNoWhite:[69:21] Found expr:[66:8->73:1] -posCursor:[69:22] posNoWhite:[69:21] Found expr:[68:2->71:13] -posCursor:[69:22] posNoWhite:[69:21] Found expr:[69:5->71:13] +posCursor:[70:22] posNoWhite:[70:21] Found expr:[67:8->74:1] +posCursor:[70:22] posNoWhite:[70:21] Found expr:[69:2->72:13] +posCursor:[70:22] posNoWhite:[70:21] Found expr:[70:5->72:13] looking for: Cpath Value[nest] -posCursor:[69:22] posNoWhite:[69:21] Found expr:[69:5->69:24] -posCursor:[69:22] posNoWhite:[69:21] Found pattern:[69:21->69:23] +posCursor:[70:22] posNoWhite:[70:21] Found expr:[70:5->70:24] +posCursor:[70:22] posNoWhite:[70:21] Found pattern:[70:21->70:23] Completable: Cpattern Value[nest]->recordBody [{ "label": "nested", @@ -214,9 +214,9 @@ Completable: Cpattern Value[nest]->recordBody "documentation": null }] -Complete src/CompletionPattern.res 75:8 +Complete src/CompletionPattern.res 76:8 looking for: Cpath Value[f] -posCursor:[75:8] posNoWhite:[75:7] Found pattern:[75:7->75:9] +posCursor:[76:8] posNoWhite:[76:7] Found pattern:[76:7->76:9] Completable: Cpattern Value[f]->recordBody [{ "label": "first", @@ -244,11 +244,11 @@ Completable: Cpattern Value[f]->recordBody "documentation": null }] -Complete src/CompletionPattern.res 78:16 +Complete src/CompletionPattern.res 79:16 looking for: Cpath Value[f] -posCursor:[78:16] posNoWhite:[78:15] Found pattern:[78:7->78:18] -posCursor:[78:16] posNoWhite:[78:15] Found pattern:[78:14->78:17] -posCursor:[78:16] posNoWhite:[78:15] Found pattern:[78:15->78:16] +posCursor:[79:16] posNoWhite:[79:15] Found pattern:[79:7->79:18] +posCursor:[79:16] posNoWhite:[79:15] Found pattern:[79:14->79:17] +posCursor:[79:16] posNoWhite:[79:15] Found pattern:[79:15->79:16] Completable: Cpattern Value[f]=n->recordField(nest), recordBody [{ "label": "nested", @@ -258,11 +258,11 @@ Completable: Cpattern Value[f]=n->recordField(nest), recordBody "documentation": null }] -Complete src/CompletionPattern.res 86:20 +Complete src/CompletionPattern.res 87:20 looking for: Cpath Value[z] -posCursor:[86:20] posNoWhite:[86:19] Found expr:[86:3->86:22] -posCursor:[86:20] posNoWhite:[86:19] Found pattern:[86:16->86:21] -posCursor:[86:20] posNoWhite:[86:19] Found pattern:[86:19->86:21] +posCursor:[87:20] posNoWhite:[87:19] Found expr:[87:3->87:22] +posCursor:[87:20] posNoWhite:[87:19] Found pattern:[87:16->87:21] +posCursor:[87:20] posNoWhite:[87:19] Found pattern:[87:19->87:21] Completable: Cpattern Value[z]->variantPayload::Two($0) [{ "label": "true", @@ -278,11 +278,11 @@ Completable: Cpattern Value[z]->variantPayload::Two($0) "documentation": null }] -Complete src/CompletionPattern.res 89:21 +Complete src/CompletionPattern.res 90:21 looking for: Cpath Value[z] -posCursor:[89:21] posNoWhite:[89:20] Found expr:[89:3->89:23] -posCursor:[89:21] posNoWhite:[89:20] Found pattern:[89:16->89:22] -posCursor:[89:21] posNoWhite:[89:20] Found pattern:[89:20->89:21] +posCursor:[90:21] posNoWhite:[90:20] Found expr:[90:3->90:23] +posCursor:[90:21] posNoWhite:[90:20] Found pattern:[90:16->90:22] +posCursor:[90:21] posNoWhite:[90:20] Found pattern:[90:20->90:21] Completable: Cpattern Value[z]=t->variantPayload::Two($0) [{ "label": "true", @@ -292,11 +292,11 @@ Completable: Cpattern Value[z]=t->variantPayload::Two($0) "documentation": null }] -Complete src/CompletionPattern.res 92:23 +Complete src/CompletionPattern.res 93:23 looking for: Cpath Value[z] -posCursor:[92:23] posNoWhite:[92:22] Found expr:[92:3->92:26] -posCursor:[92:23] posNoWhite:[92:22] Found pattern:[92:16->92:25] -posCursor:[92:23] posNoWhite:[92:22] Found pattern:[92:22->92:24] +posCursor:[93:23] posNoWhite:[93:22] Found expr:[93:3->93:26] +posCursor:[93:23] posNoWhite:[93:22] Found pattern:[93:16->93:25] +posCursor:[93:23] posNoWhite:[93:22] Found pattern:[93:22->93:24] Completable: Cpattern Value[z]->variantPayload::Three($0), recordBody [{ "label": "first", @@ -324,12 +324,12 @@ Completable: Cpattern Value[z]->variantPayload::Three($0), recordBody "documentation": null }] -Complete src/CompletionPattern.res 95:27 +Complete src/CompletionPattern.res 96:27 looking for: Cpath Value[z] -posCursor:[95:27] posNoWhite:[95:26] Found expr:[95:3->95:29] -posCursor:[95:27] posNoWhite:[95:26] Found pattern:[95:16->95:28] -posCursor:[95:27] posNoWhite:[95:26] Found pattern:[95:21->95:29] -posCursor:[95:27] posNoWhite:[95:26] Found pattern:[95:26->95:27] +posCursor:[96:27] posNoWhite:[96:26] Found expr:[96:3->96:29] +posCursor:[96:27] posNoWhite:[96:26] Found pattern:[96:16->96:28] +posCursor:[96:27] posNoWhite:[96:26] Found pattern:[96:21->96:29] +posCursor:[96:27] posNoWhite:[96:26] Found pattern:[96:26->96:27] Completable: Cpattern Value[z]=t->variantPayload::Three($1) [{ "label": "true", @@ -339,11 +339,11 @@ Completable: Cpattern Value[z]=t->variantPayload::Three($1) "documentation": null }] -Complete src/CompletionPattern.res 102:21 +Complete src/CompletionPattern.res 103:21 looking for: Cpath Value[b] -posCursor:[102:21] posNoWhite:[102:20] Found expr:[102:3->102:23] -posCursor:[102:21] posNoWhite:[102:20] Found pattern:[102:16->102:22] -posCursor:[102:21] posNoWhite:[102:20] Found pattern:[102:20->102:21] +posCursor:[103:21] posNoWhite:[103:20] Found expr:[103:3->103:23] +posCursor:[103:21] posNoWhite:[103:20] Found pattern:[103:16->103:22] +posCursor:[103:21] posNoWhite:[103:20] Found pattern:[103:20->103:21] Completable: Cpattern Value[b]->polyvariantPayload::two($0) [{ "label": "true", @@ -359,11 +359,11 @@ Completable: Cpattern Value[b]->polyvariantPayload::two($0) "documentation": null }] -Complete src/CompletionPattern.res 105:22 +Complete src/CompletionPattern.res 106:22 looking for: Cpath Value[b] -posCursor:[105:22] posNoWhite:[105:21] Found expr:[105:3->105:24] -posCursor:[105:22] posNoWhite:[105:21] Found pattern:[105:16->105:23] -posCursor:[105:22] posNoWhite:[105:21] Found pattern:[105:21->105:22] +posCursor:[106:22] posNoWhite:[106:21] Found expr:[106:3->106:24] +posCursor:[106:22] posNoWhite:[106:21] Found pattern:[106:16->106:23] +posCursor:[106:22] posNoWhite:[106:21] Found pattern:[106:21->106:22] Completable: Cpattern Value[b]=t->polyvariantPayload::two($0) [{ "label": "true", @@ -373,11 +373,11 @@ Completable: Cpattern Value[b]=t->polyvariantPayload::two($0) "documentation": null }] -Complete src/CompletionPattern.res 108:24 +Complete src/CompletionPattern.res 109:24 looking for: Cpath Value[b] -posCursor:[108:24] posNoWhite:[108:23] Found expr:[108:3->108:27] -posCursor:[108:24] posNoWhite:[108:23] Found pattern:[108:16->108:26] -posCursor:[108:24] posNoWhite:[108:23] Found pattern:[108:23->108:25] +posCursor:[109:24] posNoWhite:[109:23] Found expr:[109:3->109:27] +posCursor:[109:24] posNoWhite:[109:23] Found pattern:[109:16->109:26] +posCursor:[109:24] posNoWhite:[109:23] Found pattern:[109:23->109:25] Completable: Cpattern Value[b]->polyvariantPayload::three($0), recordBody [{ "label": "first", @@ -405,12 +405,12 @@ Completable: Cpattern Value[b]->polyvariantPayload::three($0), recordBody "documentation": null }] -Complete src/CompletionPattern.res 111:28 +Complete src/CompletionPattern.res 112:28 looking for: Cpath Value[b] -posCursor:[111:28] posNoWhite:[111:27] Found expr:[111:3->111:30] -posCursor:[111:28] posNoWhite:[111:27] Found pattern:[111:16->111:29] -posCursor:[111:28] posNoWhite:[111:27] Found pattern:[111:22->111:29] -posCursor:[111:28] posNoWhite:[111:27] Found pattern:[111:27->111:28] +posCursor:[112:28] posNoWhite:[112:27] Found expr:[112:3->112:30] +posCursor:[112:28] posNoWhite:[112:27] Found pattern:[112:16->112:29] +posCursor:[112:28] posNoWhite:[112:27] Found pattern:[112:22->112:29] +posCursor:[112:28] posNoWhite:[112:27] Found pattern:[112:27->112:28] Completable: Cpattern Value[b]=t->polyvariantPayload::three($1) [{ "label": "true", @@ -420,7 +420,7 @@ Completable: Cpattern Value[b]=t->polyvariantPayload::three($1) "documentation": null }] -Complete src/CompletionPattern.res 117:15 +Complete src/CompletionPattern.res 118:15 XXX Not found! Completable: Cpattern Value[c] [{ @@ -434,10 +434,10 @@ Completable: Cpattern Value[c] "insertTextFormat": 2 }] -Complete src/CompletionPattern.res 120:17 +Complete src/CompletionPattern.res 121:17 looking for: Cpath Value[c] -posCursor:[120:17] posNoWhite:[120:16] Found expr:[120:3->120:20] -posCursor:[120:17] posNoWhite:[120:16] Found pattern:[120:16->120:18] +posCursor:[121:17] posNoWhite:[121:16] Found expr:[121:3->121:20] +posCursor:[121:17] posNoWhite:[121:16] Found pattern:[121:16->121:18] Completable: Cpattern Value[c]->array [{ "label": "true", From dda7fd4f0748646b81b53e600a6f05445743a5a4 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Tue, 3 Jan 2023 18:30:06 +0100 Subject: [PATCH 17/31] handle options --- analysis/src/CompletionBackEnd.ml | 7 +++++-- analysis/tests/src/CompletionPattern.res | 6 ++++++ .../src/expected/CompletionPattern.res.txt | 20 +++++++++++++++++++ 3 files changed, 31 insertions(+), 2 deletions(-) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index ae84b8f46..94e3e4fa4 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -1778,8 +1778,11 @@ let rec resolveNestedPattern typ ~env ~package ~nested = | Some {typ} -> typ |> resolveNestedPattern ~env ~package ~nested) | PRecordBody {seenFields}, Some (Trecord {env; typeExpr}) -> Some (typeExpr, env, Some (Completable.RecordField {seenFields})) - | PVariantPayload {constructorName; itemNum}, Some (Tvariant {env; constructors}) - -> ( + | ( PVariantPayload {constructorName = "Some"; itemNum = 0}, + Some (Toption (env, typ)) ) -> + typ |> resolveNestedPattern ~env ~package ~nested + | ( PVariantPayload {constructorName; itemNum}, + Some (Tvariant {env; constructors}) ) -> ( match constructors |> List.find_opt (fun (c : Constructor.t) -> diff --git a/analysis/tests/src/CompletionPattern.res b/analysis/tests/src/CompletionPattern.res index aec7b14a0..174265610 100644 --- a/analysis/tests/src/CompletionPattern.res +++ b/analysis/tests/src/CompletionPattern.res @@ -121,3 +121,9 @@ ignore(c) // switch c { | [] } // ^com + +let o = Some(true) +ignore(o) + +// switch o { | Some() } +// ^com diff --git a/analysis/tests/src/expected/CompletionPattern.res.txt b/analysis/tests/src/expected/CompletionPattern.res.txt index 96deeaec6..b10158694 100644 --- a/analysis/tests/src/expected/CompletionPattern.res.txt +++ b/analysis/tests/src/expected/CompletionPattern.res.txt @@ -453,3 +453,23 @@ Completable: Cpattern Value[c]->array "documentation": null }] +Complete src/CompletionPattern.res 127:21 +looking for: Cpath Value[o] +posCursor:[127:21] posNoWhite:[127:20] Found expr:[127:3->127:24] +posCursor:[127:21] posNoWhite:[127:20] Found pattern:[127:16->127:22] +posCursor:[127:21] posNoWhite:[127:20] Found pattern:[127:20->127:22] +Completable: Cpattern Value[o]->variantPayload::Some($0) +[{ + "label": "true", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }, { + "label": "false", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }] + From 3ea5168ac08f6d0c4507ff3207f4f4492f06a756 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Tue, 3 Jan 2023 20:51:38 +0100 Subject: [PATCH 18/31] handle completing new items in constructor/variant payloads with multiple items --- analysis/src/CompletionFrontEnd.ml | 166 ++++++++++++------ analysis/tests/src/CompletionPattern.res | 32 ++++ .../src/expected/CompletionPattern.res.txt | 156 ++++++++++++++++ 3 files changed, 299 insertions(+), 55 deletions(-) diff --git a/analysis/src/CompletionFrontEnd.ml b/analysis/src/CompletionFrontEnd.ml index 03bf56e59..c2714ec70 100644 --- a/analysis/src/CompletionFrontEnd.ml +++ b/analysis/src/CompletionFrontEnd.ml @@ -25,6 +25,11 @@ let isPatternHole pat = | Ppat_extension ({txt = "rescript.patternhole"}, _) -> true | _ -> false +let isPatternTuple pat = + match pat.Parsetree.ppat_desc with + | Ppat_tuple _ -> true + | _ -> false + type prop = { name: string; posStart: int * int; @@ -240,6 +245,12 @@ let findArgCompletables ~(args : arg list) ~endPos ~posBeforeCursor }) | _ -> loop args +let lastLocIndexBeforePos locs ~pos = + let posNum = ref (-1) in + locs + |> List.iteri (fun index loc -> if pos >= Loc.start loc then posNum := index); + if !posNum > -1 then Some !posNum else None + let rec exprToContextPath (e : Parsetree.expression) = match e.pexp_desc with | Pexp_constant (Pconst_string _) -> Some Completable.CPString @@ -511,7 +522,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = when ppat_loc |> CursorPosition.classifyLoc ~pos:posBeforeCursor = HasCursor -> - (* Empty payload *) + (* Empty payload with cursor, like: Test() *) Some ( "", [ @@ -519,18 +530,23 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = {constructorName = getUnqualifiedName txt; itemNum = 0}; ] @ patternPath ) - | Ppat_construct - ( {txt}, - Some - ({ - ppat_loc; - ppat_desc = - ( Ppat_var _ | Ppat_record _ | Ppat_construct _ - | Ppat_variant _ ); - } as pat) ) - when ppat_loc + | Ppat_construct ({txt}, Some pat) + when posBeforeCursor >= (pat.ppat_loc |> Loc.end_) + && firstCharBeforeCursorNoWhite = Some ',' + && isPatternTuple pat = false -> + (* Empty payload with trailing ',', like: Test(true, ) *) + Some + ( "", + [ + Completable.PVariantPayload + {constructorName = getUnqualifiedName txt; itemNum = 1}; + ] + @ patternPath ) + | Ppat_construct ({txt}, Some pat) + when pat.ppat_loc |> CursorPosition.classifyLoc ~pos:posBeforeCursor - = HasCursor -> + = HasCursor + && isPatternTuple pat = false -> (* Single payload *) pat |> traversePattern @@ -544,24 +560,43 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = ({txt}, Some {ppat_loc; ppat_desc = Ppat_tuple tupleItems}) when ppat_loc |> CursorPosition.classifyLoc ~pos:posBeforeCursor - = HasCursor -> - (* Multiple payloads with cursor in item *) - (* TODO: New item with comma *) + = HasCursor -> ( let itemNum = ref (-1) in - tupleItems - |> List.find_map (fun pat -> - itemNum := !itemNum + 1; - pat - |> traversePattern - ~patternPath: - ([ - Completable.PVariantPayload - { - constructorName = getUnqualifiedName txt; - itemNum = !itemNum; - }; - ] - @ patternPath)) + let itemWithCursor = + tupleItems + |> List.find_map (fun pat -> + itemNum := !itemNum + 1; + pat + |> traversePattern + ~patternPath: + ([ + Completable.PVariantPayload + { + constructorName = getUnqualifiedName txt; + itemNum = !itemNum; + }; + ] + @ patternPath)) + in + match (itemWithCursor, firstCharBeforeCursorNoWhite) with + | None, Some ',' -> ( + (* No tuple item has the cursor, but there's a comma before the cursor. + Figure out what arg we're trying to complete. Example: Test(true, , None) *) + let locs = tupleItems |> List.map (fun p -> p.Parsetree.ppat_loc) in + match locs |> lastLocIndexBeforePos ~pos:posBeforeCursor with + | None -> None + | Some itemNum -> + Some + ( "", + [ + Completable.PVariantPayload + { + constructorName = getUnqualifiedName txt; + itemNum = itemNum + 1; + }; + ] + @ patternPath )) + | v, _ -> v) | Ppat_variant ( txt, Some {ppat_loc; ppat_desc = Ppat_construct ({txt = Lident "()"}, _)} @@ -569,7 +604,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = when ppat_loc |> CursorPosition.classifyLoc ~pos:posBeforeCursor = HasCursor -> - (* Empty payload *) + (* Empty payload with cursor, like: #test() *) Some ( "", [ @@ -577,18 +612,23 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = {constructorName = txt; itemNum = 0}; ] @ patternPath ) - | Ppat_variant - ( txt, - Some - ({ - ppat_loc; - ppat_desc = - ( Ppat_var _ | Ppat_record _ | Ppat_construct _ - | Ppat_variant _ ); - } as pat) ) - when ppat_loc + | Ppat_variant (txt, Some pat) + when posBeforeCursor >= (pat.ppat_loc |> Loc.end_) + && firstCharBeforeCursorNoWhite = Some ',' + && isPatternTuple pat = false -> + (* Empty payload with trailing ',', like: #test(true, ) *) + Some + ( "", + [ + Completable.PPolyvariantPayload + {constructorName = txt; itemNum = 1}; + ] + @ patternPath ) + | Ppat_variant (txt, Some pat) + when pat.ppat_loc |> CursorPosition.classifyLoc ~pos:posBeforeCursor - = HasCursor -> + = HasCursor + && isPatternTuple pat = false -> (* Single payload *) pat |> traversePattern @@ -601,21 +641,37 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = | Ppat_variant (txt, Some {ppat_loc; ppat_desc = Ppat_tuple tupleItems}) when ppat_loc |> CursorPosition.classifyLoc ~pos:posBeforeCursor - = HasCursor -> - (* Multiple payloads with cursor in item *) - (* TODO: New item with comma *) + = HasCursor -> ( let itemNum = ref (-1) in - tupleItems - |> List.find_map (fun pat -> - itemNum := !itemNum + 1; - pat - |> traversePattern - ~patternPath: - ([ - Completable.PPolyvariantPayload - {constructorName = txt; itemNum = !itemNum}; - ] - @ patternPath)) + let itemWithCursor = + tupleItems + |> List.find_map (fun pat -> + itemNum := !itemNum + 1; + pat + |> traversePattern + ~patternPath: + ([ + Completable.PPolyvariantPayload + {constructorName = txt; itemNum = !itemNum}; + ] + @ patternPath)) + in + match (itemWithCursor, firstCharBeforeCursorNoWhite) with + | None, Some ',' -> ( + (* No tuple item has the cursor, but there's a comma before the cursor. + Figure out what arg we're trying to complete. Example: #test(true, , None) *) + let locs = tupleItems |> List.map (fun p -> p.Parsetree.ppat_loc) in + match locs |> lastLocIndexBeforePos ~pos:posBeforeCursor with + | None -> None + | Some itemNum -> + Some + ( "", + [ + Completable.PPolyvariantPayload + {constructorName = txt; itemNum = itemNum + 1}; + ] + @ patternPath )) + | v, _ -> v) | _ -> None else None in diff --git a/analysis/tests/src/CompletionPattern.res b/analysis/tests/src/CompletionPattern.res index 174265610..8bd44ac0b 100644 --- a/analysis/tests/src/CompletionPattern.res +++ b/analysis/tests/src/CompletionPattern.res @@ -127,3 +127,35 @@ ignore(o) // switch o { | Some() } // ^com + +type multiPayloadVariant = Test(int, bool, option, array) + +let p = Test(1, true, Some(false), []) + +// switch p { | Test(1, )} +// ^com + +// switch p { | Test(1, true, )} +// ^com + +// switch p { | Test(1, , None)} +// ^com + +// switch p { | Test(1, true, None, )} +// ^com + +type multiPayloadPolyVariant = [#test(int, bool, option, array)] + +let v: multiPayloadPolyVariant = #test(1, true, Some(false), []) + +// switch v { | #test(1, )} +// ^com + +// switch v { | #test(1, true, )} +// ^com + +// switch v { | #test(1, , None)} +// ^com + +// switch v { | #test(1, true, None, )} +// ^com diff --git a/analysis/tests/src/expected/CompletionPattern.res.txt b/analysis/tests/src/expected/CompletionPattern.res.txt index b10158694..133e93a35 100644 --- a/analysis/tests/src/expected/CompletionPattern.res.txt +++ b/analysis/tests/src/expected/CompletionPattern.res.txt @@ -473,3 +473,159 @@ Completable: Cpattern Value[o]->variantPayload::Some($0) "documentation": null }] +Complete src/CompletionPattern.res 134:23 +looking for: Cpath Value[p] +posCursor:[134:23] posNoWhite:[134:22] Found expr:[134:3->134:26] +posCursor:[134:23] posNoWhite:[134:22] Found pattern:[134:16->134:25] +Completable: Cpattern Value[p]->variantPayload::Test($1) +[{ + "label": "true", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }, { + "label": "false", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }] + +Complete src/CompletionPattern.res 137:29 +looking for: Cpath Value[p] +posCursor:[137:29] posNoWhite:[137:28] Found expr:[137:3->137:32] +posCursor:[137:29] posNoWhite:[137:28] Found pattern:[137:16->137:31] +posCursor:[137:29] posNoWhite:[137:28] Found pattern:[137:20->137:32] +Completable: Cpattern Value[p]->variantPayload::Test($2) +[{ + "label": "None", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }, { + "label": "Some(_)", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null, + "insertText": "Some(${1:_})", + "insertTextFormat": 2 + }] + +Complete src/CompletionPattern.res 140:23 +looking for: Cpath Value[p] +posCursor:[140:23] posNoWhite:[140:22] Found expr:[140:3->140:32] +posCursor:[140:23] posNoWhite:[140:22] Found pattern:[140:16->140:31] +posCursor:[140:23] posNoWhite:[140:22] Found pattern:[140:20->140:32] +Completable: Cpattern Value[p]->variantPayload::Test($1) +[{ + "label": "true", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }, { + "label": "false", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }] + +Complete src/CompletionPattern.res 143:35 +looking for: Cpath Value[p] +posCursor:[143:35] posNoWhite:[143:34] Found expr:[143:3->143:38] +posCursor:[143:35] posNoWhite:[143:34] Found pattern:[143:16->143:37] +posCursor:[143:35] posNoWhite:[143:34] Found pattern:[143:20->143:38] +Completable: Cpattern Value[p]->variantPayload::Test($3) +[{ + "label": "[]", + "kind": 12, + "tags": [], + "detail": "bool", + "documentation": null, + "sortText": "a", + "insertText": "[$0]", + "insertTextFormat": 2 + }] + +Complete src/CompletionPattern.res 150:24 +looking for: Cpath Value[v] +posCursor:[150:24] posNoWhite:[150:23] Found expr:[150:3->150:27] +posCursor:[150:24] posNoWhite:[150:23] Found pattern:[150:16->150:26] +Completable: Cpattern Value[v]->polyvariantPayload::test($1) +[{ + "label": "true", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }, { + "label": "false", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }] + +Complete src/CompletionPattern.res 153:30 +looking for: Cpath Value[v] +posCursor:[153:30] posNoWhite:[153:29] Found expr:[153:3->153:33] +posCursor:[153:30] posNoWhite:[153:29] Found pattern:[153:16->153:32] +posCursor:[153:30] posNoWhite:[153:29] Found pattern:[153:21->153:32] +Completable: Cpattern Value[v]->polyvariantPayload::test($2) +[{ + "label": "None", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }, { + "label": "Some(_)", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null, + "insertText": "Some(${1:_})", + "insertTextFormat": 2 + }] + +Complete src/CompletionPattern.res 156:24 +looking for: Cpath Value[v] +posCursor:[156:24] posNoWhite:[156:23] Found expr:[156:3->156:33] +posCursor:[156:24] posNoWhite:[156:23] Found pattern:[156:16->156:32] +posCursor:[156:24] posNoWhite:[156:23] Found pattern:[156:21->156:32] +Completable: Cpattern Value[v]->polyvariantPayload::test($1) +[{ + "label": "true", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }, { + "label": "false", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }] + +Complete src/CompletionPattern.res 159:36 +looking for: Cpath Value[v] +posCursor:[159:36] posNoWhite:[159:35] Found expr:[159:3->159:39] +posCursor:[159:36] posNoWhite:[159:35] Found pattern:[159:16->159:38] +posCursor:[159:36] posNoWhite:[159:35] Found pattern:[159:21->159:38] +Completable: Cpattern Value[v]->polyvariantPayload::test($3) +[{ + "label": "[]", + "kind": 12, + "tags": [], + "detail": "bool", + "documentation": null, + "sortText": "a", + "insertText": "[$0]", + "insertTextFormat": 2 + }] + From 124509eb41174ad855f00f7c24666cace59768d3 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Tue, 3 Jan 2023 20:58:47 +0100 Subject: [PATCH 19/31] handle completing new items in tuples --- analysis/src/CompletionFrontEnd.ml | 36 ++++++++--- analysis/tests/src/CompletionPattern.res | 11 ++++ .../src/expected/CompletionPattern.res.txt | 61 +++++++++++++++++++ 3 files changed, 99 insertions(+), 9 deletions(-) diff --git a/analysis/src/CompletionFrontEnd.ml b/analysis/src/CompletionFrontEnd.ml index c2714ec70..e19b5facb 100644 --- a/analysis/src/CompletionFrontEnd.ml +++ b/analysis/src/CompletionFrontEnd.ml @@ -441,6 +441,9 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = | Ppat_or (p1, p2) -> [p1; p2] |> List.find_map (fun p -> p |> traversePattern ~patternPath) | Ppat_var {txt} -> Some (txt, patternPath) + | Ppat_construct ({txt = Lident "()"}, None) -> + (* switch s { | () }*) + Some ("", patternPath @ [Completable.PTupleItem {itemNum = 0}]) | Ppat_construct ({txt = Lident prefix}, None) -> Some (prefix, patternPath) | Ppat_variant (prefix, None) -> Some ("#" ^ prefix, patternPath) @@ -451,16 +454,31 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = arrayPatterns |> List.find_map (fun pat -> pat |> traversePattern ~patternPath:nextPatternPath) - | Ppat_tuple patterns -> + | Ppat_tuple tupleItems -> ( let itemNum = ref (-1) in - patterns - |> List.find_map (fun pat -> - itemNum := !itemNum + 1; - pat - |> traversePattern - ~patternPath: - ([Completable.PTupleItem {itemNum = !itemNum}] - @ patternPath)) + let itemWithCursor = + tupleItems + |> List.find_map (fun pat -> + itemNum := !itemNum + 1; + pat + |> traversePattern + ~patternPath: + ([Completable.PTupleItem {itemNum = !itemNum}] + @ patternPath)) + in + match (itemWithCursor, firstCharBeforeCursorNoWhite) with + | None, Some ',' -> ( + (* No tuple item has the cursor, but there's a comma before the cursor. + Figure out what arg we're trying to complete. Example: #test(true, , None) *) + let locs = tupleItems |> List.map (fun p -> p.Parsetree.ppat_loc) in + match locs |> lastLocIndexBeforePos ~pos:posBeforeCursor with + | None -> None + | Some itemNum -> + Some + ( "", + [Completable.PTupleItem {itemNum = itemNum + 1}] @ patternPath + )) + | v, _ -> v) | Ppat_record ([], _) -> (* Empty fields means we're in a record body `{}`. Complete for the fields. *) Some ("", [Completable.PRecordBody {seenFields = []}] @ patternPath) diff --git a/analysis/tests/src/CompletionPattern.res b/analysis/tests/src/CompletionPattern.res index 8bd44ac0b..1f68ef814 100644 --- a/analysis/tests/src/CompletionPattern.res +++ b/analysis/tests/src/CompletionPattern.res @@ -159,3 +159,14 @@ let v: multiPayloadPolyVariant = #test(1, true, Some(false), []) // switch v { | #test(1, true, None, )} // ^com + +let s = (true, Some(true), [false]) + +// switch s { | () } +// ^com + +// switch s { | (true, ) } +// ^com + +// switch s { | (true, , []) } +// ^com diff --git a/analysis/tests/src/expected/CompletionPattern.res.txt b/analysis/tests/src/expected/CompletionPattern.res.txt index 133e93a35..b1f770510 100644 --- a/analysis/tests/src/expected/CompletionPattern.res.txt +++ b/analysis/tests/src/expected/CompletionPattern.res.txt @@ -629,3 +629,64 @@ Completable: Cpattern Value[v]->polyvariantPayload::test($3) "insertTextFormat": 2 }] +Complete src/CompletionPattern.res 164:17 +looking for: Cpath Value[s] +posCursor:[164:17] posNoWhite:[164:16] Found expr:[164:3->164:20] +posCursor:[164:17] posNoWhite:[164:16] Found pattern:[164:16->164:18] +Completable: Cpattern Value[s]->tuple($0) +[{ + "label": "true", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }, { + "label": "false", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }] + +Complete src/CompletionPattern.res 167:23 +looking for: Cpath Value[s] +posCursor:[167:23] posNoWhite:[167:21] Found expr:[167:3->167:26] +posCursor:[167:23] posNoWhite:[167:21] Found pattern:[167:16->167:24] +Completable: Cpattern Value[s]->tuple($1) +[{ + "label": "None", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }, { + "label": "Some(_)", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null, + "insertText": "Some(${1:_})", + "insertTextFormat": 2 + }] + +Complete src/CompletionPattern.res 170:22 +looking for: Cpath Value[s] +posCursor:[170:22] posNoWhite:[170:21] Found expr:[170:3->170:30] +posCursor:[170:22] posNoWhite:[170:21] Found pattern:[170:16->170:28] +Completable: Cpattern Value[s]->tuple($1) +[{ + "label": "None", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }, { + "label": "Some(_)", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null, + "insertText": "Some(${1:_})", + "insertTextFormat": 2 + }] + From 68e22db0e78327328d9eb1f039f2d37060717e4d Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Tue, 3 Jan 2023 21:00:32 +0100 Subject: [PATCH 20/31] add broken parser case --- analysis/tests/src/BrokenParserCases.res | 4 ++++ analysis/tests/src/expected/BrokenParserCases.res.txt | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/analysis/tests/src/BrokenParserCases.res b/analysis/tests/src/BrokenParserCases.res index 877b7b0e4..f748ed0f5 100644 --- a/analysis/tests/src/BrokenParserCases.res +++ b/analysis/tests/src/BrokenParserCases.res @@ -3,3 +3,7 @@ // let _ = someFn(~isOff=, ()) // ^com +// This should parse as a single item tuple when in a pattern? +// switch s { | (t) } +// ^com + diff --git a/analysis/tests/src/expected/BrokenParserCases.res.txt b/analysis/tests/src/expected/BrokenParserCases.res.txt index 7e4f89500..ffa42e128 100644 --- a/analysis/tests/src/expected/BrokenParserCases.res.txt +++ b/analysis/tests/src/expected/BrokenParserCases.res.txt @@ -4,3 +4,10 @@ Pexp_apply ...[2:11->2:17] (~isOff2:19->2:24=...[2:27->2:29]) Completable: Cargument Value[someFn]($0) [] +Complete src/BrokenParserCases.res 6:18 +looking for: Cpath Value[s] +posCursor:[6:18] posNoWhite:[6:17] Found expr:[6:3->6:21] +posCursor:[6:18] posNoWhite:[6:17] Found pattern:[6:16->6:19] +Completable: Cpattern Value[s]=t +[] + From 23b783338cd89b3a8697f899df3bf360107e88e8 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Tue, 3 Jan 2023 21:01:19 +0100 Subject: [PATCH 21/31] remove unused --- analysis/src/CompletionFrontEnd.ml | 14 -------------- 1 file changed, 14 deletions(-) diff --git a/analysis/src/CompletionFrontEnd.ml b/analysis/src/CompletionFrontEnd.ml index e19b5facb..ed9a242ba 100644 --- a/analysis/src/CompletionFrontEnd.ml +++ b/analysis/src/CompletionFrontEnd.ml @@ -323,20 +323,6 @@ let completePipeChain ~(lhs : Parsetree.expression) = |> Option.map (fun ctxPath -> (ctxPath, pexp_loc)) | _ -> None -let findPatTupleItemWithCursor patterns ~pos = - let patCount = ref None in - let patCountWithPatHole = ref None in - patterns - |> List.iteri (fun index p -> - match p.Parsetree.ppat_loc |> CursorPosition.classifyLoc ~pos with - | HasCursor -> patCount := Some index - | EmptyLoc -> patCountWithPatHole := Some index - | _ -> ()); - match (!patCount, !patCountWithPatHole) with - | Some patCount, _ -> Some patCount - | None, Some patHoleCount -> Some patHoleCount - | _ -> None - let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = let offsetNoWhite = skipWhite text (offset - 1) in let posNoWhite = From 59762347920e04fdea118a5540e703dc845639ce Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Tue, 3 Jan 2023 21:13:25 +0100 Subject: [PATCH 22/31] refactor and unify how pat tuple items are traversed --- analysis/src/CompletionFrontEnd.ml | 160 +++++++++++------------------ 1 file changed, 60 insertions(+), 100 deletions(-) diff --git a/analysis/src/CompletionFrontEnd.ml b/analysis/src/CompletionFrontEnd.ml index ed9a242ba..0e0e55615 100644 --- a/analysis/src/CompletionFrontEnd.ml +++ b/analysis/src/CompletionFrontEnd.ml @@ -245,12 +245,6 @@ let findArgCompletables ~(args : arg list) ~endPos ~posBeforeCursor }) | _ -> loop args -let lastLocIndexBeforePos locs ~pos = - let posNum = ref (-1) in - locs - |> List.iteri (fun index loc -> if pos >= Loc.start loc then posNum := index); - if !posNum > -1 then Some !posNum else None - let rec exprToContextPath (e : Parsetree.expression) = match e.pexp_desc with | Pexp_constant (Pconst_string _) -> Some Completable.CPString @@ -410,7 +404,27 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = let lookingForPat = ref None in - let rec traversePattern (pat : Parsetree.pattern) ~patternPath = + let rec traverseTupleItems tupleItems ~nextPatternPath ~resultFromFoundItemNum + = + let itemNum = ref (-1) in + let itemWithCursor = + tupleItems + |> List.find_map (fun pat -> + itemNum := !itemNum + 1; + pat |> traversePattern ~patternPath:(nextPatternPath !itemNum)) + in + match (itemWithCursor, firstCharBeforeCursorNoWhite) with + | None, Some ',' -> + (* No tuple item has the cursor, but there's a comma before the cursor. + Figure out what arg we're trying to complete. Example: (true, , None) *) + let posNum = ref (-1) in + tupleItems + |> List.iteri (fun index pat -> + if posBeforeCursor >= Loc.start pat.Parsetree.ppat_loc then + posNum := index); + if !posNum > -1 then Some ("", resultFromFoundItemNum !posNum) else None + | v, _ -> v + and traversePattern (pat : Parsetree.pattern) ~patternPath = if pat.ppat_loc |> CursorPosition.classifyLoc ~pos:posBeforeCursor @@ -440,31 +454,13 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = arrayPatterns |> List.find_map (fun pat -> pat |> traversePattern ~patternPath:nextPatternPath) - | Ppat_tuple tupleItems -> ( - let itemNum = ref (-1) in - let itemWithCursor = - tupleItems - |> List.find_map (fun pat -> - itemNum := !itemNum + 1; - pat - |> traversePattern - ~patternPath: - ([Completable.PTupleItem {itemNum = !itemNum}] - @ patternPath)) - in - match (itemWithCursor, firstCharBeforeCursorNoWhite) with - | None, Some ',' -> ( - (* No tuple item has the cursor, but there's a comma before the cursor. - Figure out what arg we're trying to complete. Example: #test(true, , None) *) - let locs = tupleItems |> List.map (fun p -> p.Parsetree.ppat_loc) in - match locs |> lastLocIndexBeforePos ~pos:posBeforeCursor with - | None -> None - | Some itemNum -> - Some - ( "", - [Completable.PTupleItem {itemNum = itemNum + 1}] @ patternPath - )) - | v, _ -> v) + | Ppat_tuple tupleItems -> + tupleItems + |> traverseTupleItems + ~nextPatternPath:(fun itemNum -> + [Completable.PTupleItem {itemNum}] @ patternPath) + ~resultFromFoundItemNum:(fun itemNum -> + [Completable.PTupleItem {itemNum = itemNum + 1}] @ patternPath) | Ppat_record ([], _) -> (* Empty fields means we're in a record body `{}`. Complete for the fields. *) Some ("", [Completable.PRecordBody {seenFields = []}] @ patternPath) @@ -564,43 +560,24 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = ({txt}, Some {ppat_loc; ppat_desc = Ppat_tuple tupleItems}) when ppat_loc |> CursorPosition.classifyLoc ~pos:posBeforeCursor - = HasCursor -> ( - let itemNum = ref (-1) in - let itemWithCursor = - tupleItems - |> List.find_map (fun pat -> - itemNum := !itemNum + 1; - pat - |> traversePattern - ~patternPath: - ([ - Completable.PVariantPayload - { - constructorName = getUnqualifiedName txt; - itemNum = !itemNum; - }; - ] - @ patternPath)) - in - match (itemWithCursor, firstCharBeforeCursorNoWhite) with - | None, Some ',' -> ( - (* No tuple item has the cursor, but there's a comma before the cursor. - Figure out what arg we're trying to complete. Example: Test(true, , None) *) - let locs = tupleItems |> List.map (fun p -> p.Parsetree.ppat_loc) in - match locs |> lastLocIndexBeforePos ~pos:posBeforeCursor with - | None -> None - | Some itemNum -> - Some - ( "", - [ - Completable.PVariantPayload - { - constructorName = getUnqualifiedName txt; - itemNum = itemNum + 1; - }; - ] - @ patternPath )) - | v, _ -> v) + = HasCursor -> + tupleItems + |> traverseTupleItems + ~nextPatternPath:(fun itemNum -> + [ + Completable.PVariantPayload + {constructorName = getUnqualifiedName txt; itemNum}; + ] + @ patternPath) + ~resultFromFoundItemNum:(fun itemNum -> + [ + Completable.PVariantPayload + { + constructorName = getUnqualifiedName txt; + itemNum = itemNum + 1; + }; + ] + @ patternPath) | Ppat_variant ( txt, Some {ppat_loc; ppat_desc = Ppat_construct ({txt = Lident "()"}, _)} @@ -645,37 +622,20 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = | Ppat_variant (txt, Some {ppat_loc; ppat_desc = Ppat_tuple tupleItems}) when ppat_loc |> CursorPosition.classifyLoc ~pos:posBeforeCursor - = HasCursor -> ( - let itemNum = ref (-1) in - let itemWithCursor = - tupleItems - |> List.find_map (fun pat -> - itemNum := !itemNum + 1; - pat - |> traversePattern - ~patternPath: - ([ - Completable.PPolyvariantPayload - {constructorName = txt; itemNum = !itemNum}; - ] - @ patternPath)) - in - match (itemWithCursor, firstCharBeforeCursorNoWhite) with - | None, Some ',' -> ( - (* No tuple item has the cursor, but there's a comma before the cursor. - Figure out what arg we're trying to complete. Example: #test(true, , None) *) - let locs = tupleItems |> List.map (fun p -> p.Parsetree.ppat_loc) in - match locs |> lastLocIndexBeforePos ~pos:posBeforeCursor with - | None -> None - | Some itemNum -> - Some - ( "", - [ - Completable.PPolyvariantPayload - {constructorName = txt; itemNum = itemNum + 1}; - ] - @ patternPath )) - | v, _ -> v) + = HasCursor -> + tupleItems + |> traverseTupleItems + ~nextPatternPath:(fun itemNum -> + [ + Completable.PPolyvariantPayload {constructorName = txt; itemNum}; + ] + @ patternPath) + ~resultFromFoundItemNum:(fun itemNum -> + [ + Completable.PPolyvariantPayload + {constructorName = txt; itemNum = itemNum + 1}; + ] + @ patternPath) | _ -> None else None in From c601823370597e4649dad0ed581784a502fde905 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Tue, 3 Jan 2023 21:19:28 +0100 Subject: [PATCH 23/31] simplify setting completion pattern --- analysis/src/CompletionBackEnd.ml | 4 ++-- analysis/src/CompletionFrontEnd.ml | 12 +++++------- analysis/src/SharedTypes.ml | 10 +++------- 3 files changed, 10 insertions(+), 16 deletions(-) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index 94e3e4fa4..cb765976a 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -2171,7 +2171,7 @@ Note: The `@react.component` decorator requires the react-jsx config to be set i Utils.startsWith name prefix && (forHover || not (List.mem name identsSeen))) |> List.map mkLabel - | Cpattern {typ; prefix; nested = None} -> ( + | Cpattern {typ; prefix; nested = []} -> ( let envWhereCompletionStarted = env in match typ @@ -2184,7 +2184,7 @@ Note: The `@react.component` decorator requires the react-jsx config to be set i |> completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix ~expandOption:false ~includeLocalValues:false ~completionContext:None | None -> []) - | Cpattern {typ; prefix; nested = Some nested} -> ( + | Cpattern {typ; prefix; nested} -> ( let envWhereCompletionStarted = env in match typ diff --git a/analysis/src/CompletionFrontEnd.ml b/analysis/src/CompletionFrontEnd.ml index 0e0e55615..8efa428ad 100644 --- a/analysis/src/CompletionFrontEnd.ml +++ b/analysis/src/CompletionFrontEnd.ml @@ -641,11 +641,10 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = in let completePattern (pat : Parsetree.pattern) = match (pat |> traversePattern ~patternPath:[], !lookingForPat) with - | Some (prefix, []), Some (Completable.Cpattern p) -> - setResult (Completable.Cpattern {p with prefix; nested = None}) - | Some (prefix, nestedPattern), Some (Cpattern p) -> + | Some (prefix, nestedPattern), Some ctxPath -> setResult - (Cpattern {p with prefix; nested = Some (List.rev nestedPattern)}) + (Completable.Cpattern + {typ = ctxPath; prefix; nested = List.rev nestedPattern}) | _ -> () in let scopeValueBinding (vb : Parsetree.value_binding) = @@ -681,8 +680,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = !scope |> Scope.addModule ~name:md.pmd_name.txt ~loc:md.pmd_name.loc in let setLookingForPat ctxPath = - lookingForPat := - Some (Completable.Cpattern {typ = ctxPath; prefix = ""; nested = None}); + lookingForPat := Some ctxPath; if debug then Printf.printf "looking for: %s \n" (Completable.toString (Cpath ctxPath)) in @@ -713,7 +711,7 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = | None -> () | Some ctxPath -> setResult - (Completable.Cpattern {typ = ctxPath; nested = None; prefix = ""})) + (Completable.Cpattern {typ = ctxPath; nested = []; prefix = ""})) | Pexp_match (exp, _cases) -> ( (* If there's more than one case, or the case isn't a pattern hole, set that we're looking for this path currently. *) match exp |> exprToContextPath with diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index e0c2b0cd8..78271d81d 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -592,11 +592,7 @@ module Completable = struct propName: string; prefix: string; } - | Cpattern of { - typ: contextPath; - nested: patternPath list option; - prefix: string; - } + | Cpattern of {typ: contextPath; nested: patternPath list; prefix: string} (** An extracted type from a type expr *) type extractedType = @@ -677,8 +673,8 @@ module Completable = struct ^ (if prefix = "" then "" else "=" ^ prefix) ^ match nested with - | None -> "" - | Some patternPaths -> + | [] -> "" + | patternPaths -> "->" ^ (patternPaths |> List.map (fun patternPath -> patternPathToString patternPath) From a828e79a9363a27d7ae182db6abd39d128ea6b43 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Tue, 3 Jan 2023 21:58:17 +0100 Subject: [PATCH 24/31] handle completing root switch case when theres more than one case --- analysis/src/CompletionFrontEnd.ml | 28 +++++++++++++-- analysis/tests/src/CompletionPattern.res | 6 ++++ .../tests/src/expected/Completion.res.txt | 1 - .../src/expected/CompletionPattern.res.txt | 35 ++++++++++++++++++- 4 files changed, 65 insertions(+), 5 deletions(-) diff --git a/analysis/src/CompletionFrontEnd.ml b/analysis/src/CompletionFrontEnd.ml index 8efa428ad..2eadb535b 100644 --- a/analysis/src/CompletionFrontEnd.ml +++ b/analysis/src/CompletionFrontEnd.ml @@ -712,13 +712,35 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = | Some ctxPath -> setResult (Completable.Cpattern {typ = ctxPath; nested = []; prefix = ""})) - | Pexp_match (exp, _cases) -> ( - (* If there's more than one case, or the case isn't a pattern hole, set that we're looking for this path currently. *) + | Pexp_match (exp, cases) -> ( + (* If there's more than one case, or the case isn't a pattern hole, figure out if we're completing another + broken parser case (`switch x { | true => () | }` for example). *) match exp |> exprToContextPath with | None -> () - | Some ctxPath -> setLookingForPat ctxPath) + | Some ctxPath -> ( + let caseWithCursor = + cases + |> List.find_opt (fun case -> + case.Parsetree.pc_lhs.ppat_loc + |> CursorPosition.classifyLoc ~pos:posBeforeCursor + = HasCursor) + in + let caseWithPatHole = + cases + |> List.find_opt (fun case -> isPatternHole case.Parsetree.pc_lhs) + in + match (caseWithPatHole, caseWithCursor) with + | _, Some _ -> + (* Always continue if there's a case with the cursor *) + setLookingForPat ctxPath + | Some _, None -> + (* If there's no case with the cursor, but a broken parser case, complete for the top level. *) + setResult + (Completable.Cpattern {typ = ctxPath; nested = []; prefix = ""}) + | None, None -> ())) | _ -> unsetLookingForPat () in + let case (iterator : Ast_iterator.iterator) (case : Parsetree.case) = let oldScope = !scope in scopePattern case.pc_lhs; diff --git a/analysis/tests/src/CompletionPattern.res b/analysis/tests/src/CompletionPattern.res index 1f68ef814..7c1ea20be 100644 --- a/analysis/tests/src/CompletionPattern.res +++ b/analysis/tests/src/CompletionPattern.res @@ -170,3 +170,9 @@ let s = (true, Some(true), [false]) // switch s { | (true, , []) } // ^com + +// switch s { | (true, []) => () | } +// ^com + +// switch s { | (true, []) => () | (true, , []) } +// ^com diff --git a/analysis/tests/src/expected/Completion.res.txt b/analysis/tests/src/expected/Completion.res.txt index 3c3f27ab0..42869a07c 100644 --- a/analysis/tests/src/expected/Completion.res.txt +++ b/analysis/tests/src/expected/Completion.res.txt @@ -965,7 +965,6 @@ Resolved opens 2 Completion.res Completion.res [] Complete src/Completion.res 243:8 -looking for: Cpath Value[someR] posCursor:[243:8] posNoWhite:[243:7] Found expr:[241:8->246:1] posCursor:[243:8] posNoWhite:[243:7] Found expr:[242:14->243:8] Pexp_apply ...[243:3->243:4] (...[242:14->242:15], ...[243:5->243:8]) diff --git a/analysis/tests/src/expected/CompletionPattern.res.txt b/analysis/tests/src/expected/CompletionPattern.res.txt index b1f770510..6a88e1188 100644 --- a/analysis/tests/src/expected/CompletionPattern.res.txt +++ b/analysis/tests/src/expected/CompletionPattern.res.txt @@ -198,7 +198,6 @@ Completable: Cpattern Value[f]->recordField(nest), recordBody }] Complete src/CompletionPattern.res 70:22 -looking for: Cpath Value[f] posCursor:[70:22] posNoWhite:[70:21] Found expr:[67:8->74:1] posCursor:[70:22] posNoWhite:[70:21] Found expr:[69:2->72:13] posCursor:[70:22] posNoWhite:[70:21] Found expr:[70:5->72:13] @@ -690,3 +689,37 @@ Completable: Cpattern Value[s]->tuple($1) "insertTextFormat": 2 }] +Complete src/CompletionPattern.res 173:35 +XXX Not found! +Completable: Cpattern Value[s] +[{ + "label": "(_, _, _)", + "kind": 12, + "tags": [], + "detail": "(bool, option, array)", + "documentation": null, + "insertText": "(${1:_}, ${2:_}, ${3:_})", + "insertTextFormat": 2 + }] + +Complete src/CompletionPattern.res 176:41 +looking for: Cpath Value[s] +posCursor:[176:41] posNoWhite:[176:40] Found expr:[176:3->176:50] +posCursor:[176:41] posNoWhite:[176:40] Found pattern:[176:35->176:47] +Completable: Cpattern Value[s]->tuple($1) +[{ + "label": "None", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }, { + "label": "Some(_)", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null, + "insertText": "Some(${1:_})", + "insertTextFormat": 2 + }] + From 11cb6aea7248ea7330bc05138f06258e2d889f3a Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Tue, 3 Jan 2023 21:59:46 +0100 Subject: [PATCH 25/31] refactor --- analysis/src/CompletionFrontEnd.ml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/analysis/src/CompletionFrontEnd.ml b/analysis/src/CompletionFrontEnd.ml index 2eadb535b..ee7bc85c9 100644 --- a/analysis/src/CompletionFrontEnd.ml +++ b/analysis/src/CompletionFrontEnd.ml @@ -718,26 +718,28 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = match exp |> exprToContextPath with | None -> () | Some ctxPath -> ( - let caseWithCursor = + let hasCaseWithCursor = cases |> List.find_opt (fun case -> case.Parsetree.pc_lhs.ppat_loc |> CursorPosition.classifyLoc ~pos:posBeforeCursor = HasCursor) + |> Option.is_some in - let caseWithPatHole = + let hasCaseWithPatHole = cases |> List.find_opt (fun case -> isPatternHole case.Parsetree.pc_lhs) + |> Option.is_some in - match (caseWithPatHole, caseWithCursor) with - | _, Some _ -> + match (hasCaseWithPatHole, hasCaseWithCursor) with + | _, true -> (* Always continue if there's a case with the cursor *) setLookingForPat ctxPath - | Some _, None -> + | true, false -> (* If there's no case with the cursor, but a broken parser case, complete for the top level. *) setResult (Completable.Cpattern {typ = ctxPath; nested = []; prefix = ""}) - | None, None -> ())) + | false, false -> ())) | _ -> unsetLookingForPat () in From 942966413c2f890f70feaf6ad3e0e72be6d18192 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Tue, 3 Jan 2023 23:13:10 +0100 Subject: [PATCH 26/31] handle ppat_or --- analysis/src/CompletionFrontEnd.ml | 417 +++++++++--------- analysis/src/SharedTypes.ml | 4 + analysis/tests/src/CompletionPattern.res | 3 + .../src/expected/CompletionPattern.res.txt | 29 ++ 4 files changed, 236 insertions(+), 217 deletions(-) diff --git a/analysis/src/CompletionFrontEnd.ml b/analysis/src/CompletionFrontEnd.ml index ee7bc85c9..0537f82c7 100644 --- a/analysis/src/CompletionFrontEnd.ml +++ b/analysis/src/CompletionFrontEnd.ml @@ -404,6 +404,9 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = let lookingForPat = ref None in + let locHasCursor = CursorPosition.locHasCursor ~pos:posBeforeCursor in + let locIsEmpty = CursorPosition.locIsEmpty ~pos:posBeforeCursor in + let rec traverseTupleItems tupleItems ~nextPatternPath ~resultFromFoundItemNum = let itemNum = ref (-1) in @@ -425,219 +428,200 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = if !posNum > -1 then Some ("", resultFromFoundItemNum !posNum) else None | v, _ -> v and traversePattern (pat : Parsetree.pattern) ~patternPath = - if - pat.ppat_loc - |> CursorPosition.classifyLoc ~pos:posBeforeCursor - = HasCursor - then - match pat.ppat_desc with - | Ppat_any | Ppat_constant _ | Ppat_interval _ -> None - | Ppat_lazy p - | Ppat_constraint (p, _) - | Ppat_alias (p, _) - | Ppat_exception p - | Ppat_open (_, p) -> - p |> traversePattern ~patternPath - | Ppat_or (p1, p2) -> + let someIfHasCursor v = + if locHasCursor pat.Parsetree.ppat_loc then Some v else None + in + match pat.ppat_desc with + | Ppat_any | Ppat_constant _ | Ppat_interval _ -> None + | Ppat_lazy p + | Ppat_constraint (p, _) + | Ppat_alias (p, _) + | Ppat_exception p + | Ppat_open (_, p) -> + p |> traversePattern ~patternPath + | Ppat_or (p1, p2) -> ( + let orPatWithItem = [p1; p2] |> List.find_map (fun p -> p |> traversePattern ~patternPath) - | Ppat_var {txt} -> Some (txt, patternPath) - | Ppat_construct ({txt = Lident "()"}, None) -> - (* switch s { | () }*) - Some ("", patternPath @ [Completable.PTupleItem {itemNum = 0}]) - | Ppat_construct ({txt = Lident prefix}, None) -> - Some (prefix, patternPath) - | Ppat_variant (prefix, None) -> Some ("#" ^ prefix, patternPath) - | Ppat_array arrayPatterns -> - let nextPatternPath = [Completable.PArray] @ patternPath in - if List.length arrayPatterns = 0 then Some ("", nextPatternPath) - else - arrayPatterns - |> List.find_map (fun pat -> - pat |> traversePattern ~patternPath:nextPatternPath) - | Ppat_tuple tupleItems -> - tupleItems - |> traverseTupleItems - ~nextPatternPath:(fun itemNum -> - [Completable.PTupleItem {itemNum}] @ patternPath) - ~resultFromFoundItemNum:(fun itemNum -> - [Completable.PTupleItem {itemNum = itemNum + 1}] @ patternPath) - | Ppat_record ([], _) -> - (* Empty fields means we're in a record body `{}`. Complete for the fields. *) - Some ("", [Completable.PRecordBody {seenFields = []}] @ patternPath) - | Ppat_record (fields, _) -> ( - let fieldWithCursor = ref None in - let fieldWithPatHole = ref None in + in + match orPatWithItem with + | None when isPatternHole p1 || isPatternHole p2 -> Some ("", patternPath) + | v -> v) + | Ppat_var {txt} -> someIfHasCursor (txt, patternPath) + | Ppat_construct ({txt = Lident "()"}, None) -> + (* switch s { | () }*) + someIfHasCursor ("", patternPath @ [Completable.PTupleItem {itemNum = 0}]) + | Ppat_construct ({txt = Lident prefix}, None) -> + someIfHasCursor (prefix, patternPath) + | Ppat_variant (prefix, None) -> someIfHasCursor ("#" ^ prefix, patternPath) + | Ppat_array arrayPatterns -> + let nextPatternPath = [Completable.PArray] @ patternPath in + if List.length arrayPatterns = 0 && locHasCursor pat.ppat_loc then + Some ("", nextPatternPath) + else + arrayPatterns + |> List.find_map (fun pat -> + pat |> traversePattern ~patternPath:nextPatternPath) + | Ppat_tuple tupleItems when locHasCursor pat.ppat_loc -> + tupleItems + |> traverseTupleItems + ~nextPatternPath:(fun itemNum -> + [Completable.PTupleItem {itemNum}] @ patternPath) + ~resultFromFoundItemNum:(fun itemNum -> + [Completable.PTupleItem {itemNum = itemNum + 1}] @ patternPath) + | Ppat_record ([], _) -> + (* Empty fields means we're in a record body `{}`. Complete for the fields. *) + someIfHasCursor + ("", [Completable.PRecordBody {seenFields = []}] @ patternPath) + | Ppat_record (fields, _) -> ( + let fieldWithCursor = ref None in + let fieldWithPatHole = ref None in + fields + |> List.iter (fun (fname, f) -> + match + ( fname.Location.txt, + f.Parsetree.ppat_loc + |> CursorPosition.classifyLoc ~pos:posBeforeCursor ) + with + | Longident.Lident fname, HasCursor -> + fieldWithCursor := Some (fname, f) + | Lident fname, _ when isPatternHole f -> + fieldWithPatHole := Some (fname, f) + | _ -> ()); + let seenFields = fields - |> List.iter (fun (fname, f) -> - match - ( fname.Location.txt, - f.Parsetree.ppat_loc - |> CursorPosition.classifyLoc ~pos:posBeforeCursor ) - with - | Longident.Lident fname, HasCursor -> - fieldWithCursor := Some (fname, f) - | Lident fname, _ when isPatternHole f -> - fieldWithPatHole := Some (fname, f) - | _ -> ()); - let seenFields = - fields - |> List.filter_map (fun (fieldName, _f) -> - match fieldName with - | {Location.txt = Longident.Lident fieldName} -> Some fieldName - | _ -> None) - in - match (!fieldWithCursor, !fieldWithPatHole) with - | Some (fname, f), _ | None, Some (fname, f) -> ( - match f.ppat_desc with - | Ppat_record _ | Ppat_construct _ | Ppat_variant _ | Ppat_tuple _ -> - (* These are things we can continue into in the pattern. *) - f - |> traversePattern - ~patternPath: - ([Completable.PFollowRecordField {fieldName = fname}] - @ patternPath) - | Ppat_extension ({txt = "rescript.patternhole"}, _) -> - (* A pattern hole means for example `{someField: }`. We want to complete for the type of `someField`. *) - Some - ( "", - [Completable.PFollowRecordField {fieldName = fname}] - @ patternPath ) - | Ppat_var {txt} -> - (* A var means `{s}` or similar. Complete for fields. *) - Some (txt, [Completable.PRecordBody {seenFields}] @ patternPath) - | _ -> None) - | None, None -> ( - (* Figure out if we're completing for a new field. - If the cursor is inside of the record body, but no field has the cursor, - and there's no pattern hole. Check the first char to the left of the cursor, - ignoring white space. If that's a comma, we assume you're completing for a new field. *) - match firstCharBeforeCursorNoWhite with - | Some ',' -> - Some ("", [Completable.PRecordBody {seenFields}] @ patternPath) - | _ -> None)) - | Ppat_construct - ( {txt}, - Some {ppat_loc; ppat_desc = Ppat_construct ({txt = Lident "()"}, _)} - ) - when ppat_loc - |> CursorPosition.classifyLoc ~pos:posBeforeCursor - = HasCursor -> - (* Empty payload with cursor, like: Test() *) - Some - ( "", - [ - Completable.PVariantPayload - {constructorName = getUnqualifiedName txt; itemNum = 0}; - ] - @ patternPath ) - | Ppat_construct ({txt}, Some pat) - when posBeforeCursor >= (pat.ppat_loc |> Loc.end_) - && firstCharBeforeCursorNoWhite = Some ',' - && isPatternTuple pat = false -> - (* Empty payload with trailing ',', like: Test(true, ) *) - Some - ( "", - [ - Completable.PVariantPayload - {constructorName = getUnqualifiedName txt; itemNum = 1}; - ] - @ patternPath ) - | Ppat_construct ({txt}, Some pat) - when pat.ppat_loc - |> CursorPosition.classifyLoc ~pos:posBeforeCursor - = HasCursor - && isPatternTuple pat = false -> - (* Single payload *) - pat - |> traversePattern - ~patternPath: - ([ - Completable.PVariantPayload - {constructorName = getUnqualifiedName txt; itemNum = 0}; - ] - @ patternPath) - | Ppat_construct - ({txt}, Some {ppat_loc; ppat_desc = Ppat_tuple tupleItems}) - when ppat_loc - |> CursorPosition.classifyLoc ~pos:posBeforeCursor - = HasCursor -> - tupleItems - |> traverseTupleItems - ~nextPatternPath:(fun itemNum -> - [ - Completable.PVariantPayload - {constructorName = getUnqualifiedName txt; itemNum}; - ] - @ patternPath) - ~resultFromFoundItemNum:(fun itemNum -> - [ - Completable.PVariantPayload - { - constructorName = getUnqualifiedName txt; - itemNum = itemNum + 1; - }; - ] - @ patternPath) - | Ppat_variant - ( txt, - Some {ppat_loc; ppat_desc = Ppat_construct ({txt = Lident "()"}, _)} - ) - when ppat_loc - |> CursorPosition.classifyLoc ~pos:posBeforeCursor - = HasCursor -> - (* Empty payload with cursor, like: #test() *) - Some - ( "", - [ - Completable.PPolyvariantPayload - {constructorName = txt; itemNum = 0}; - ] - @ patternPath ) - | Ppat_variant (txt, Some pat) - when posBeforeCursor >= (pat.ppat_loc |> Loc.end_) - && firstCharBeforeCursorNoWhite = Some ',' - && isPatternTuple pat = false -> - (* Empty payload with trailing ',', like: #test(true, ) *) - Some - ( "", - [ - Completable.PPolyvariantPayload - {constructorName = txt; itemNum = 1}; - ] - @ patternPath ) - | Ppat_variant (txt, Some pat) - when pat.ppat_loc - |> CursorPosition.classifyLoc ~pos:posBeforeCursor - = HasCursor - && isPatternTuple pat = false -> - (* Single payload *) - pat - |> traversePattern - ~patternPath: - ([ - Completable.PPolyvariantPayload - {constructorName = txt; itemNum = 0}; - ] - @ patternPath) - | Ppat_variant (txt, Some {ppat_loc; ppat_desc = Ppat_tuple tupleItems}) - when ppat_loc - |> CursorPosition.classifyLoc ~pos:posBeforeCursor - = HasCursor -> - tupleItems - |> traverseTupleItems - ~nextPatternPath:(fun itemNum -> - [ - Completable.PPolyvariantPayload {constructorName = txt; itemNum}; - ] - @ patternPath) - ~resultFromFoundItemNum:(fun itemNum -> - [ - Completable.PPolyvariantPayload - {constructorName = txt; itemNum = itemNum + 1}; - ] - @ patternPath) - | _ -> None - else None + |> List.filter_map (fun (fieldName, _f) -> + match fieldName with + | {Location.txt = Longident.Lident fieldName} -> Some fieldName + | _ -> None) + in + match (!fieldWithCursor, !fieldWithPatHole) with + | Some (fname, f), _ | None, Some (fname, f) -> ( + match f.ppat_desc with + | Ppat_extension ({txt = "rescript.patternhole"}, _) -> + (* A pattern hole means for example `{someField: }`. We want to complete for the type of `someField`. *) + someIfHasCursor + ( "", + [Completable.PFollowRecordField {fieldName = fname}] @ patternPath + ) + | Ppat_var {txt} -> + (* A var means `{s}` or similar. Complete for fields. *) + someIfHasCursor + (txt, [Completable.PRecordBody {seenFields}] @ patternPath) + | _ -> + f + |> traversePattern + ~patternPath: + ([Completable.PFollowRecordField {fieldName = fname}] + @ patternPath)) + | None, None -> ( + (* Figure out if we're completing for a new field. + If the cursor is inside of the record body, but no field has the cursor, + and there's no pattern hole. Check the first char to the left of the cursor, + ignoring white space. If that's a comma, we assume you're completing for a new field. *) + match firstCharBeforeCursorNoWhite with + | Some ',' -> + someIfHasCursor + ("", [Completable.PRecordBody {seenFields}] @ patternPath) + | _ -> None)) + | Ppat_construct + ( {txt}, + Some {ppat_loc; ppat_desc = Ppat_construct ({txt = Lident "()"}, _)} + ) + when locHasCursor ppat_loc -> + (* Empty payload with cursor, like: Test() *) + Some + ( "", + [ + Completable.PVariantPayload + {constructorName = getUnqualifiedName txt; itemNum = 0}; + ] + @ patternPath ) + | Ppat_construct ({txt}, Some pat) + when posBeforeCursor >= (pat.ppat_loc |> Loc.end_) + && firstCharBeforeCursorNoWhite = Some ',' + && isPatternTuple pat = false -> + (* Empty payload with trailing ',', like: Test(true, ) *) + Some + ( "", + [ + Completable.PVariantPayload + {constructorName = getUnqualifiedName txt; itemNum = 1}; + ] + @ patternPath ) + | Ppat_construct ({txt}, Some pat) + when locHasCursor pat.ppat_loc && isPatternTuple pat = false -> + (* Single payload *) + pat + |> traversePattern + ~patternPath: + ([ + Completable.PVariantPayload + {constructorName = getUnqualifiedName txt; itemNum = 0}; + ] + @ patternPath) + | Ppat_construct ({txt}, Some {ppat_loc; ppat_desc = Ppat_tuple tupleItems}) + when locHasCursor ppat_loc -> + tupleItems + |> traverseTupleItems + ~nextPatternPath:(fun itemNum -> + [ + Completable.PVariantPayload + {constructorName = getUnqualifiedName txt; itemNum}; + ] + @ patternPath) + ~resultFromFoundItemNum:(fun itemNum -> + [ + Completable.PVariantPayload + { + constructorName = getUnqualifiedName txt; + itemNum = itemNum + 1; + }; + ] + @ patternPath) + | Ppat_variant + ( txt, + Some {ppat_loc; ppat_desc = Ppat_construct ({txt = Lident "()"}, _)} + ) + when locHasCursor ppat_loc -> + (* Empty payload with cursor, like: #test() *) + Some + ( "", + [Completable.PPolyvariantPayload {constructorName = txt; itemNum = 0}] + @ patternPath ) + | Ppat_variant (txt, Some pat) + when posBeforeCursor >= (pat.ppat_loc |> Loc.end_) + && firstCharBeforeCursorNoWhite = Some ',' + && isPatternTuple pat = false -> + (* Empty payload with trailing ',', like: #test(true, ) *) + Some + ( "", + [Completable.PPolyvariantPayload {constructorName = txt; itemNum = 1}] + @ patternPath ) + | Ppat_variant (txt, Some pat) + when locHasCursor pat.ppat_loc && isPatternTuple pat = false -> + (* Single payload *) + pat + |> traversePattern + ~patternPath: + ([ + Completable.PPolyvariantPayload + {constructorName = txt; itemNum = 0}; + ] + @ patternPath) + | Ppat_variant (txt, Some {ppat_loc; ppat_desc = Ppat_tuple tupleItems}) + when locHasCursor ppat_loc -> + tupleItems + |> traverseTupleItems + ~nextPatternPath:(fun itemNum -> + [Completable.PPolyvariantPayload {constructorName = txt; itemNum}] + @ patternPath) + ~resultFromFoundItemNum:(fun itemNum -> + [ + Completable.PPolyvariantPayload + {constructorName = txt; itemNum = itemNum + 1}; + ] + @ patternPath) + | _ -> None in let completePattern (pat : Parsetree.pattern) = match (pat |> traversePattern ~patternPath:[], !lookingForPat) with @@ -721,17 +705,16 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = let hasCaseWithCursor = cases |> List.find_opt (fun case -> - case.Parsetree.pc_lhs.ppat_loc - |> CursorPosition.classifyLoc ~pos:posBeforeCursor - = HasCursor) + locHasCursor case.Parsetree.pc_lhs.ppat_loc) |> Option.is_some in - let hasCaseWithPatHole = + let hasCaseWithEmptyLoc = cases - |> List.find_opt (fun case -> isPatternHole case.Parsetree.pc_lhs) + |> List.find_opt (fun case -> + locIsEmpty case.Parsetree.pc_lhs.ppat_loc) |> Option.is_some in - match (hasCaseWithPatHole, hasCaseWithCursor) with + match (hasCaseWithEmptyLoc, hasCaseWithCursor) with | _, true -> (* Always continue if there's a case with the cursor *) setLookingForPat ctxPath diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index 78271d81d..ebdea286f 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -699,6 +699,10 @@ module CursorPosition = struct if posStart <= pos && pos <= posEnd then HasCursor else if posEnd = (Location.none |> Loc.end_) then EmptyLoc else NoCursor + + let locHasCursor loc ~pos = loc |> classifyLoc ~pos = HasCursor + + let locIsEmpty loc ~pos = loc |> classifyLoc ~pos = EmptyLoc end type labelled = { diff --git a/analysis/tests/src/CompletionPattern.res b/analysis/tests/src/CompletionPattern.res index 7c1ea20be..4e48a99db 100644 --- a/analysis/tests/src/CompletionPattern.res +++ b/analysis/tests/src/CompletionPattern.res @@ -176,3 +176,6 @@ let s = (true, Some(true), [false]) // switch s { | (true, []) => () | (true, , []) } // ^com + +// switch z { | One | } +// ^com diff --git a/analysis/tests/src/expected/CompletionPattern.res.txt b/analysis/tests/src/expected/CompletionPattern.res.txt index 6a88e1188..3acce5f6c 100644 --- a/analysis/tests/src/expected/CompletionPattern.res.txt +++ b/analysis/tests/src/expected/CompletionPattern.res.txt @@ -723,3 +723,32 @@ Completable: Cpattern Value[s]->tuple($1) "insertTextFormat": 2 }] +Complete src/CompletionPattern.res 179:21 +XXX Not found! +Completable: Cpattern Value[z] +[{ + "label": "One", + "kind": 4, + "tags": [], + "detail": "One\n\ntype someVariant = One | Two(bool) | Three(someRecord, bool)", + "documentation": null, + "insertText": "One", + "insertTextFormat": 2 + }, { + "label": "Two(_)", + "kind": 4, + "tags": [], + "detail": "Two(bool)\n\ntype someVariant = One | Two(bool) | Three(someRecord, bool)", + "documentation": null, + "insertText": "Two(${1:_})", + "insertTextFormat": 2 + }, { + "label": "Three(_, _)", + "kind": 4, + "tags": [], + "detail": "Three(someRecord, bool)\n\ntype someVariant = One | Two(bool) | Three(someRecord, bool)", + "documentation": null, + "insertText": "Three(${1:_}, ${2:_})", + "insertTextFormat": 2 + }] + From a61f06b3781d69da1ef19532082dcb3601fd7ae1 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Wed, 4 Jan 2023 14:43:57 +0100 Subject: [PATCH 27/31] handle or patterns (including broken patterns) in variant/polyvariant payloads --- analysis/src/CompletionFrontEnd.ml | 40 ++++----- analysis/tests/src/CompletionPattern.res | 12 +++ .../src/expected/CompletionPattern.res.txt | 82 +++++++++++++++++++ 3 files changed, 112 insertions(+), 22 deletions(-) diff --git a/analysis/src/CompletionFrontEnd.ml b/analysis/src/CompletionFrontEnd.ml index 0537f82c7..c1f746c26 100644 --- a/analysis/src/CompletionFrontEnd.ml +++ b/analysis/src/CompletionFrontEnd.ml @@ -548,17 +548,6 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = {constructorName = getUnqualifiedName txt; itemNum = 1}; ] @ patternPath ) - | Ppat_construct ({txt}, Some pat) - when locHasCursor pat.ppat_loc && isPatternTuple pat = false -> - (* Single payload *) - pat - |> traversePattern - ~patternPath: - ([ - Completable.PVariantPayload - {constructorName = getUnqualifiedName txt; itemNum = 0}; - ] - @ patternPath) | Ppat_construct ({txt}, Some {ppat_loc; ppat_desc = Ppat_tuple tupleItems}) when locHasCursor ppat_loc -> tupleItems @@ -578,6 +567,15 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = }; ] @ patternPath) + | Ppat_construct ({txt}, Some p) when locHasCursor pat.ppat_loc -> + p + |> traversePattern + ~patternPath: + ([ + Completable.PVariantPayload + {constructorName = getUnqualifiedName txt; itemNum = 0}; + ] + @ patternPath) | Ppat_variant ( txt, Some {ppat_loc; ppat_desc = Ppat_construct ({txt = Lident "()"}, _)} @@ -597,17 +595,6 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = ( "", [Completable.PPolyvariantPayload {constructorName = txt; itemNum = 1}] @ patternPath ) - | Ppat_variant (txt, Some pat) - when locHasCursor pat.ppat_loc && isPatternTuple pat = false -> - (* Single payload *) - pat - |> traversePattern - ~patternPath: - ([ - Completable.PPolyvariantPayload - {constructorName = txt; itemNum = 0}; - ] - @ patternPath) | Ppat_variant (txt, Some {ppat_loc; ppat_desc = Ppat_tuple tupleItems}) when locHasCursor ppat_loc -> tupleItems @@ -621,6 +608,15 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = {constructorName = txt; itemNum = itemNum + 1}; ] @ patternPath) + | Ppat_variant (txt, Some p) when locHasCursor pat.ppat_loc -> + p + |> traversePattern + ~patternPath: + ([ + Completable.PPolyvariantPayload + {constructorName = txt; itemNum = 0}; + ] + @ patternPath) | _ -> None in let completePattern (pat : Parsetree.pattern) = diff --git a/analysis/tests/src/CompletionPattern.res b/analysis/tests/src/CompletionPattern.res index 4e48a99db..26cba1167 100644 --- a/analysis/tests/src/CompletionPattern.res +++ b/analysis/tests/src/CompletionPattern.res @@ -179,3 +179,15 @@ let s = (true, Some(true), [false]) // switch z { | One | } // ^com + +// switch z { | One | Two(true | ) } +// ^com + +// switch z { | One | Three({test: true}, true | ) } +// ^com + +// switch b { | #one | #two(true | ) } +// ^com + +// switch b { | #one | #three({test: true}, true | ) } +// ^com diff --git a/analysis/tests/src/expected/CompletionPattern.res.txt b/analysis/tests/src/expected/CompletionPattern.res.txt index 3acce5f6c..f9e0b4de3 100644 --- a/analysis/tests/src/expected/CompletionPattern.res.txt +++ b/analysis/tests/src/expected/CompletionPattern.res.txt @@ -752,3 +752,85 @@ Completable: Cpattern Value[z] "insertTextFormat": 2 }] +Complete src/CompletionPattern.res 182:32 +looking for: Cpath Value[z] +posCursor:[182:32] posNoWhite:[182:31] Found expr:[182:3->182:37] +posCursor:[182:32] posNoWhite:[182:31] Found pattern:[182:16->182:34] +posCursor:[182:32] posNoWhite:[182:31] Found pattern:[182:22->182:34] +Completable: Cpattern Value[z]->variantPayload::Two($0) +[{ + "label": "true", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }, { + "label": "false", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }] + +Complete src/CompletionPattern.res 185:48 +looking for: Cpath Value[z] +posCursor:[185:48] posNoWhite:[185:47] Found expr:[185:3->185:53] +posCursor:[185:48] posNoWhite:[185:47] Found pattern:[185:16->185:50] +posCursor:[185:48] posNoWhite:[185:47] Found pattern:[185:22->185:50] +posCursor:[185:48] posNoWhite:[185:47] Found pattern:[185:27->185:53] +Completable: Cpattern Value[z]->variantPayload::Three($1) +[{ + "label": "true", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }, { + "label": "false", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }] + +Complete src/CompletionPattern.res 188:34 +looking for: Cpath Value[b] +posCursor:[188:34] posNoWhite:[188:33] Found expr:[188:3->188:39] +posCursor:[188:34] posNoWhite:[188:33] Found pattern:[188:16->188:36] +posCursor:[188:34] posNoWhite:[188:33] Found pattern:[188:23->188:36] +Completable: Cpattern Value[b]->polyvariantPayload::two($0) +[{ + "label": "true", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }, { + "label": "false", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }] + +Complete src/CompletionPattern.res 191:50 +looking for: Cpath Value[b] +posCursor:[191:50] posNoWhite:[191:49] Found expr:[191:3->191:55] +posCursor:[191:50] posNoWhite:[191:49] Found pattern:[191:16->191:52] +posCursor:[191:50] posNoWhite:[191:49] Found pattern:[191:23->191:52] +posCursor:[191:50] posNoWhite:[191:49] Found pattern:[191:29->191:52] +Completable: Cpattern Value[b]->polyvariantPayload::three($1) +[{ + "label": "true", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }, { + "label": "false", + "kind": 4, + "tags": [], + "detail": "bool", + "documentation": null + }] + From 36590132de8a259d613c78496b49f752ca92295c Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Wed, 4 Jan 2023 21:08:49 +0100 Subject: [PATCH 28/31] changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 46181e0ef..23e37dadd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,7 @@ - Add autocomplete for JSX prop values. https://github.com/rescript-lang/rescript-vscode/pull/667 - Add snippet support in completion items. https://github.com/rescript-lang/rescript-vscode/pull/668 - Add support from completing polyvariants as values. https://github.com/rescript-lang/rescript-vscode/pull/669 +- Add support for completion in patterns. https://github.com/rescript-lang/rescript-vscode/pull/670 #### :nail_care: Polish From bb630931b0259773589c9ec439fae4c011080ae0 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Thu, 5 Jan 2023 18:53:37 +0100 Subject: [PATCH 29/31] add way to use fallbacks when completing patterns, restoring old behavior that was broken by pattern completion --- analysis/src/CompletionBackEnd.ml | 35 +++++++++++----- analysis/src/CompletionFrontEnd.ml | 28 ++++++++----- analysis/src/SharedTypes.ml | 7 +++- .../tests/src/expected/Completion.res.txt | 41 ++++++++++++++++++- .../src/expected/CompletionPattern.res.txt | 15 +++++++ 5 files changed, 101 insertions(+), 25 deletions(-) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index cb765976a..e0934e11e 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -1809,7 +1809,7 @@ let rec resolveNestedPattern typ ~env ~package ~nested = typ |> resolveNestedPattern ~env ~package ~nested | _ -> None) -let processCompletable ~debug ~full ~scope ~env ~pos ~forHover +let rec processCompletable ~debug ~full ~scope ~env ~pos ~forHover (completable : Completable.t) = let package = full.package in let rawOpens = Scope.getRawOpens scope in @@ -2171,7 +2171,7 @@ Note: The `@react.component` decorator requires the react-jsx config to be set i Utils.startsWith name prefix && (forHover || not (List.mem name identsSeen))) |> List.map mkLabel - | Cpattern {typ; prefix; nested = []} -> ( + | Cpattern {typ; prefix; nested = []; fallback} -> ( let envWhereCompletionStarted = env in match typ @@ -2179,12 +2179,19 @@ Note: The `@react.component` decorator requires the react-jsx config to be set i ~exact:true ~scope |> completionsGetTypeEnv with - | Some (typ, env) -> - typ - |> completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix - ~expandOption:false ~includeLocalValues:false ~completionContext:None + | Some (typ, env) -> ( + let items = + typ + |> completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix + ~expandOption:false ~includeLocalValues:false + ~completionContext:None + in + match (items, fallback) with + | [], Some fallback -> + fallback |> processCompletable ~debug ~full ~scope ~env ~pos ~forHover + | items, _ -> items) | None -> []) - | Cpattern {typ; prefix; nested} -> ( + | Cpattern {typ; prefix; nested; fallback} -> ( let envWhereCompletionStarted = env in match typ @@ -2195,8 +2202,14 @@ Note: The `@react.component` decorator requires the react-jsx config to be set i | Some (typ, env) -> ( match typ |> resolveNestedPattern ~env ~package:full.package ~nested with | None -> [] - | Some (typ, env, completionContext) -> - typ - |> completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix - ~expandOption:false ~includeLocalValues:false ~completionContext) + | Some (typ, env, completionContext) -> ( + let items = + typ + |> completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix + ~expandOption:false ~includeLocalValues:false ~completionContext + in + match (items, fallback) with + | [], Some fallback -> + fallback |> processCompletable ~debug ~full ~scope ~env ~pos ~forHover + | items, _ -> items)) | None -> []) diff --git a/analysis/src/CompletionFrontEnd.ml b/analysis/src/CompletionFrontEnd.ml index c1f746c26..6be8b8b45 100644 --- a/analysis/src/CompletionFrontEnd.ml +++ b/analysis/src/CompletionFrontEnd.ml @@ -624,7 +624,12 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = | Some (prefix, nestedPattern), Some ctxPath -> setResult (Completable.Cpattern - {typ = ctxPath; prefix; nested = List.rev nestedPattern}) + { + typ = ctxPath; + prefix; + nested = List.rev nestedPattern; + fallback = None; + }) | _ -> () in let scopeValueBinding (vb : Parsetree.value_binding) = @@ -691,7 +696,8 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = | None -> () | Some ctxPath -> setResult - (Completable.Cpattern {typ = ctxPath; nested = []; prefix = ""})) + (Completable.Cpattern + {typ = ctxPath; nested = []; prefix = ""; fallback = None})) | Pexp_match (exp, cases) -> ( (* If there's more than one case, or the case isn't a pattern hole, figure out if we're completing another broken parser case (`switch x { | true => () | }` for example). *) @@ -717,7 +723,8 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = | true, false -> (* If there's no case with the cursor, but a broken parser case, complete for the top level. *) setResult - (Completable.Cpattern {typ = ctxPath; nested = []; prefix = ""}) + (Completable.Cpattern + {typ = ctxPath; nested = []; prefix = ""; fallback = None}) | false, false -> ())) | _ -> unsetLookingForPat () in @@ -1121,19 +1128,18 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = Printf.printf "posCursor:[%s] posNoWhite:[%s] Found pattern:%s\n" (Pos.toString posCursor) (Pos.toString posNoWhite) (Loc.toString pat.ppat_loc); - (* TODO: - This change breaks old behavior of completing constructors in scope. - Either be fine with it, fix it somehow, or incorporate completing - constructors in scope when regular completion for variants can't - be done. *) - (match (!lookingForPat, pat.ppat_desc) with - | None, Ppat_construct (lid, _) -> + (match pat.ppat_desc with + | Ppat_construct (lid, _) -> ( let lidPath = flattenLidCheckDot lid in if debug then Printf.printf "Ppat_construct %s:%s\n" (lidPath |> String.concat ".") (Loc.toString lid.loc); - setResult (Cpath (CPId (lidPath, Value))) + let completion = Completable.Cpath (CPId (lidPath, Value)) in + match !result with + | Some (Completable.Cpattern p, scope) -> + result := Some (Cpattern {p with fallback = Some completion}, scope) + | _ -> setResult completion) | _ -> ()); Ast_iterator.default_iterator.pat iterator pat) in diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index ebdea286f..1da08ec8c 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -592,7 +592,12 @@ module Completable = struct propName: string; prefix: string; } - | Cpattern of {typ: contextPath; nested: patternPath list; prefix: string} + | Cpattern of { + typ: contextPath; + nested: patternPath list; + prefix: string; + fallback: t option; + } (** An extracted type from a type expr *) type extractedType = diff --git a/analysis/tests/src/expected/Completion.res.txt b/analysis/tests/src/expected/Completion.res.txt index 42869a07c..6601f01ac 100644 --- a/analysis/tests/src/expected/Completion.res.txt +++ b/analysis/tests/src/expected/Completion.res.txt @@ -1446,10 +1446,37 @@ looking for: Cpath Value[x] posCursor:[362:8] posNoWhite:[362:7] Found expr:[361:2->365:3] posCursor:[362:8] posNoWhite:[362:7] Found pattern:[362:7->364:5] posCursor:[362:8] posNoWhite:[362:7] Found pattern:[362:7->362:8] +Ppat_construct T:[362:7->362:8] Completable: Cpattern Value[x]=T Raw opens: 2 Shadow.B.place holder ... Shadow.A.place holder Resolved opens 2 Completion.res Completion.res -[] +Raw opens: 2 Shadow.B.place holder ... Shadow.A.place holder +Resolved opens 2 Completion.res Completion.res +[{ + "label": "That", + "kind": 4, + "tags": [], + "detail": "That\n\ntype v = This | That", + "documentation": null + }, { + "label": "This", + "kind": 4, + "tags": [], + "detail": "This\n\ntype v = This | That", + "documentation": null + }, { + "label": "TableclothMap", + "kind": 9, + "tags": [], + "detail": "file module", + "documentation": null + }, { + "label": "TypeDefinition", + "kind": 9, + "tags": [], + "detail": "file module", + "documentation": null + }] Complete src/Completion.res 373:21 posCursor:[373:21] posNoWhite:[373:20] Found expr:[371:8->376:3] @@ -1457,7 +1484,17 @@ looking for: Cpath Value[x] posCursor:[373:21] posNoWhite:[373:20] Found expr:[372:2->376:3] posCursor:[373:21] posNoWhite:[373:20] Found pattern:[373:7->375:5] posCursor:[373:21] posNoWhite:[373:20] Found pattern:[373:7->373:21] -[] +Ppat_construct AndThatOther.T:[373:7->373:21] +Completable: Cpath Value[AndThatOther, T] +Raw opens: 2 Shadow.B.place holder ... Shadow.A.place holder +Resolved opens 2 Completion.res Completion.res +[{ + "label": "ThatOther", + "kind": 4, + "tags": [], + "detail": "ThatOther\n\ntype v = And | ThatOther", + "documentation": null + }] Complete src/Completion.res 378:24 posCursor:[378:24] posNoWhite:[378:23] Found expr:[378:12->378:26] diff --git a/analysis/tests/src/expected/CompletionPattern.res.txt b/analysis/tests/src/expected/CompletionPattern.res.txt index f9e0b4de3..06d67f83a 100644 --- a/analysis/tests/src/expected/CompletionPattern.res.txt +++ b/analysis/tests/src/expected/CompletionPattern.res.txt @@ -261,7 +261,9 @@ Complete src/CompletionPattern.res 87:20 looking for: Cpath Value[z] posCursor:[87:20] posNoWhite:[87:19] Found expr:[87:3->87:22] posCursor:[87:20] posNoWhite:[87:19] Found pattern:[87:16->87:21] +Ppat_construct Two:[87:16->87:19] posCursor:[87:20] posNoWhite:[87:19] Found pattern:[87:19->87:21] +Ppat_construct ():[87:19->87:21] Completable: Cpattern Value[z]->variantPayload::Two($0) [{ "label": "true", @@ -281,6 +283,7 @@ Complete src/CompletionPattern.res 90:21 looking for: Cpath Value[z] posCursor:[90:21] posNoWhite:[90:20] Found expr:[90:3->90:23] posCursor:[90:21] posNoWhite:[90:20] Found pattern:[90:16->90:22] +Ppat_construct Two:[90:16->90:19] posCursor:[90:21] posNoWhite:[90:20] Found pattern:[90:20->90:21] Completable: Cpattern Value[z]=t->variantPayload::Two($0) [{ @@ -295,6 +298,7 @@ Complete src/CompletionPattern.res 93:23 looking for: Cpath Value[z] posCursor:[93:23] posNoWhite:[93:22] Found expr:[93:3->93:26] posCursor:[93:23] posNoWhite:[93:22] Found pattern:[93:16->93:25] +Ppat_construct Three:[93:16->93:21] posCursor:[93:23] posNoWhite:[93:22] Found pattern:[93:22->93:24] Completable: Cpattern Value[z]->variantPayload::Three($0), recordBody [{ @@ -327,6 +331,7 @@ Complete src/CompletionPattern.res 96:27 looking for: Cpath Value[z] posCursor:[96:27] posNoWhite:[96:26] Found expr:[96:3->96:29] posCursor:[96:27] posNoWhite:[96:26] Found pattern:[96:16->96:28] +Ppat_construct Three:[96:16->96:21] posCursor:[96:27] posNoWhite:[96:26] Found pattern:[96:21->96:29] posCursor:[96:27] posNoWhite:[96:26] Found pattern:[96:26->96:27] Completable: Cpattern Value[z]=t->variantPayload::Three($1) @@ -343,6 +348,7 @@ looking for: Cpath Value[b] posCursor:[103:21] posNoWhite:[103:20] Found expr:[103:3->103:23] posCursor:[103:21] posNoWhite:[103:20] Found pattern:[103:16->103:22] posCursor:[103:21] posNoWhite:[103:20] Found pattern:[103:20->103:21] +Ppat_construct ():[103:20->103:21] Completable: Cpattern Value[b]->polyvariantPayload::two($0) [{ "label": "true", @@ -456,7 +462,9 @@ Complete src/CompletionPattern.res 127:21 looking for: Cpath Value[o] posCursor:[127:21] posNoWhite:[127:20] Found expr:[127:3->127:24] posCursor:[127:21] posNoWhite:[127:20] Found pattern:[127:16->127:22] +Ppat_construct Some:[127:16->127:20] posCursor:[127:21] posNoWhite:[127:20] Found pattern:[127:20->127:22] +Ppat_construct ():[127:20->127:22] Completable: Cpattern Value[o]->variantPayload::Some($0) [{ "label": "true", @@ -476,6 +484,7 @@ Complete src/CompletionPattern.res 134:23 looking for: Cpath Value[p] posCursor:[134:23] posNoWhite:[134:22] Found expr:[134:3->134:26] posCursor:[134:23] posNoWhite:[134:22] Found pattern:[134:16->134:25] +Ppat_construct Test:[134:16->134:20] Completable: Cpattern Value[p]->variantPayload::Test($1) [{ "label": "true", @@ -495,6 +504,7 @@ Complete src/CompletionPattern.res 137:29 looking for: Cpath Value[p] posCursor:[137:29] posNoWhite:[137:28] Found expr:[137:3->137:32] posCursor:[137:29] posNoWhite:[137:28] Found pattern:[137:16->137:31] +Ppat_construct Test:[137:16->137:20] posCursor:[137:29] posNoWhite:[137:28] Found pattern:[137:20->137:32] Completable: Cpattern Value[p]->variantPayload::Test($2) [{ @@ -517,6 +527,7 @@ Complete src/CompletionPattern.res 140:23 looking for: Cpath Value[p] posCursor:[140:23] posNoWhite:[140:22] Found expr:[140:3->140:32] posCursor:[140:23] posNoWhite:[140:22] Found pattern:[140:16->140:31] +Ppat_construct Test:[140:16->140:20] posCursor:[140:23] posNoWhite:[140:22] Found pattern:[140:20->140:32] Completable: Cpattern Value[p]->variantPayload::Test($1) [{ @@ -537,6 +548,7 @@ Complete src/CompletionPattern.res 143:35 looking for: Cpath Value[p] posCursor:[143:35] posNoWhite:[143:34] Found expr:[143:3->143:38] posCursor:[143:35] posNoWhite:[143:34] Found pattern:[143:16->143:37] +Ppat_construct Test:[143:16->143:20] posCursor:[143:35] posNoWhite:[143:34] Found pattern:[143:20->143:38] Completable: Cpattern Value[p]->variantPayload::Test($3) [{ @@ -632,6 +644,7 @@ Complete src/CompletionPattern.res 164:17 looking for: Cpath Value[s] posCursor:[164:17] posNoWhite:[164:16] Found expr:[164:3->164:20] posCursor:[164:17] posNoWhite:[164:16] Found pattern:[164:16->164:18] +Ppat_construct ():[164:16->164:18] Completable: Cpattern Value[s]->tuple($0) [{ "label": "true", @@ -757,6 +770,7 @@ looking for: Cpath Value[z] posCursor:[182:32] posNoWhite:[182:31] Found expr:[182:3->182:37] posCursor:[182:32] posNoWhite:[182:31] Found pattern:[182:16->182:34] posCursor:[182:32] posNoWhite:[182:31] Found pattern:[182:22->182:34] +Ppat_construct Two:[182:22->182:25] Completable: Cpattern Value[z]->variantPayload::Two($0) [{ "label": "true", @@ -777,6 +791,7 @@ looking for: Cpath Value[z] posCursor:[185:48] posNoWhite:[185:47] Found expr:[185:3->185:53] posCursor:[185:48] posNoWhite:[185:47] Found pattern:[185:16->185:50] posCursor:[185:48] posNoWhite:[185:47] Found pattern:[185:22->185:50] +Ppat_construct Three:[185:22->185:27] posCursor:[185:48] posNoWhite:[185:47] Found pattern:[185:27->185:53] Completable: Cpattern Value[z]->variantPayload::Three($1) [{ From bba5805d438b9e86ed88045af668935d3cab5e41 Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Thu, 5 Jan 2023 19:00:25 +0100 Subject: [PATCH 30/31] refactor nested pattern completion handling in backend --- analysis/src/CompletionBackEnd.ml | 38 +++++++++---------------------- 1 file changed, 11 insertions(+), 27 deletions(-) diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index e0934e11e..0333cf350 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -2171,27 +2171,14 @@ Note: The `@react.component` decorator requires the react-jsx config to be set i Utils.startsWith name prefix && (forHover || not (List.mem name identsSeen))) |> List.map mkLabel - | Cpattern {typ; prefix; nested = []; fallback} -> ( - let envWhereCompletionStarted = env in - match - typ - |> getCompletionsForContextPath ~full ~opens ~rawOpens ~allFiles ~pos ~env - ~exact:true ~scope - |> completionsGetTypeEnv - with - | Some (typ, env) -> ( - let items = - typ - |> completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix - ~expandOption:false ~includeLocalValues:false - ~completionContext:None - in - match (items, fallback) with - | [], Some fallback -> - fallback |> processCompletable ~debug ~full ~scope ~env ~pos ~forHover - | items, _ -> items) - | None -> []) | Cpattern {typ; prefix; nested; fallback} -> ( + let fallbackOrEmpty ?items () = + match (fallback, items) with + | Some fallback, (None | Some []) -> + fallback |> processCompletable ~debug ~full ~scope ~env ~pos ~forHover + | _, Some items -> items + | None, None -> [] + in let envWhereCompletionStarted = env in match typ @@ -2201,15 +2188,12 @@ Note: The `@react.component` decorator requires the react-jsx config to be set i with | Some (typ, env) -> ( match typ |> resolveNestedPattern ~env ~package:full.package ~nested with - | None -> [] - | Some (typ, env, completionContext) -> ( + | None -> fallbackOrEmpty () + | Some (typ, env, completionContext) -> let items = typ |> completeTypedValue ~env ~envWhereCompletionStarted ~full ~prefix ~expandOption:false ~includeLocalValues:false ~completionContext in - match (items, fallback) with - | [], Some fallback -> - fallback |> processCompletable ~debug ~full ~scope ~env ~pos ~forHover - | items, _ -> items)) - | None -> []) + fallbackOrEmpty ~items ()) + | None -> fallbackOrEmpty ()) From 391d8d4d8b690b7813ad34364c91d546b85563fc Mon Sep 17 00:00:00 2001 From: Gabriel Nordeborn Date: Thu, 5 Jan 2023 19:01:35 +0100 Subject: [PATCH 31/31] remove unecessary log --- analysis/src/CompletionFrontEnd.ml | 7 +--- .../src/expected/BrokenParserCases.res.txt | 1 - .../tests/src/expected/Completion.res.txt | 2 - .../src/expected/CompletionPattern.res.txt | 38 ------------------- 4 files changed, 2 insertions(+), 46 deletions(-) diff --git a/analysis/src/CompletionFrontEnd.ml b/analysis/src/CompletionFrontEnd.ml index 6be8b8b45..007625c53 100644 --- a/analysis/src/CompletionFrontEnd.ml +++ b/analysis/src/CompletionFrontEnd.ml @@ -664,11 +664,8 @@ let completionWithParser1 ~currentFile ~debug ~offset ~path ~posCursor ~text = scope := !scope |> Scope.addModule ~name:md.pmd_name.txt ~loc:md.pmd_name.loc in - let setLookingForPat ctxPath = - lookingForPat := Some ctxPath; - if debug then - Printf.printf "looking for: %s \n" (Completable.toString (Cpath ctxPath)) - in + let setLookingForPat ctxPath = lookingForPat := Some ctxPath in + let unsetLookingForPat () = lookingForPat := None in (* Identifies expressions where we can do typed pattern or expr completion. *) let typedCompletionExpr (exp : Parsetree.expression) = diff --git a/analysis/tests/src/expected/BrokenParserCases.res.txt b/analysis/tests/src/expected/BrokenParserCases.res.txt index ffa42e128..d64b3ea80 100644 --- a/analysis/tests/src/expected/BrokenParserCases.res.txt +++ b/analysis/tests/src/expected/BrokenParserCases.res.txt @@ -5,7 +5,6 @@ Completable: Cargument Value[someFn]($0) [] Complete src/BrokenParserCases.res 6:18 -looking for: Cpath Value[s] posCursor:[6:18] posNoWhite:[6:17] Found expr:[6:3->6:21] posCursor:[6:18] posNoWhite:[6:17] Found pattern:[6:16->6:19] Completable: Cpattern Value[s]=t diff --git a/analysis/tests/src/expected/Completion.res.txt b/analysis/tests/src/expected/Completion.res.txt index 6601f01ac..8a4286eab 100644 --- a/analysis/tests/src/expected/Completion.res.txt +++ b/analysis/tests/src/expected/Completion.res.txt @@ -1442,7 +1442,6 @@ posCursor:[355:23] posNoWhite:[355:22] Found expr:[355:12->355:23] Complete src/Completion.res 362:8 posCursor:[362:8] posNoWhite:[362:7] Found expr:[360:8->365:3] -looking for: Cpath Value[x] posCursor:[362:8] posNoWhite:[362:7] Found expr:[361:2->365:3] posCursor:[362:8] posNoWhite:[362:7] Found pattern:[362:7->364:5] posCursor:[362:8] posNoWhite:[362:7] Found pattern:[362:7->362:8] @@ -1480,7 +1479,6 @@ Resolved opens 2 Completion.res Completion.res Complete src/Completion.res 373:21 posCursor:[373:21] posNoWhite:[373:20] Found expr:[371:8->376:3] -looking for: Cpath Value[x] posCursor:[373:21] posNoWhite:[373:20] Found expr:[372:2->376:3] posCursor:[373:21] posNoWhite:[373:20] Found pattern:[373:7->375:5] posCursor:[373:21] posNoWhite:[373:20] Found pattern:[373:7->373:21] diff --git a/analysis/tests/src/expected/CompletionPattern.res.txt b/analysis/tests/src/expected/CompletionPattern.res.txt index 06d67f83a..5b97cb151 100644 --- a/analysis/tests/src/expected/CompletionPattern.res.txt +++ b/analysis/tests/src/expected/CompletionPattern.res.txt @@ -16,7 +16,6 @@ Completable: Cpattern Value[v] }] Complete src/CompletionPattern.res 13:18 -looking for: Cpath Value[v] posCursor:[13:18] posNoWhite:[13:17] Found expr:[13:3->13:24] posCursor:[13:18] posNoWhite:[13:17] Found pattern:[13:16->13:22] posCursor:[13:18] posNoWhite:[13:17] Found pattern:[13:17->13:18] @@ -30,7 +29,6 @@ Completable: Cpattern Value[v]=t->tuple($0) }] Complete src/CompletionPattern.res 16:25 -looking for: Cpath Value[v] posCursor:[16:25] posNoWhite:[16:24] Found expr:[16:3->16:32] posCursor:[16:25] posNoWhite:[16:24] Found pattern:[16:16->16:30] posCursor:[16:25] posNoWhite:[16:24] Found pattern:[16:23->16:29] @@ -62,7 +60,6 @@ Completable: Cpattern Value[x] }] Complete src/CompletionPattern.res 24:17 -looking for: Cpath Value[x] posCursor:[24:17] posNoWhite:[24:16] Found expr:[24:3->24:17] posCursor:[24:17] posNoWhite:[24:16] Found pattern:[24:16->24:17] Completable: Cpattern Value[x]=t @@ -89,7 +86,6 @@ Completable: Cpattern Value[f] }] Complete src/CompletionPattern.res 49:17 -looking for: Cpath Value[f] posCursor:[49:17] posNoWhite:[49:16] Found expr:[49:3->49:19] posCursor:[49:17] posNoWhite:[49:16] Found pattern:[49:16->49:18] Completable: Cpattern Value[f]->recordBody @@ -120,7 +116,6 @@ Completable: Cpattern Value[f]->recordBody }] Complete src/CompletionPattern.res 52:24 -looking for: Cpath Value[f] posCursor:[52:24] posNoWhite:[52:22] Found expr:[52:3->52:36] posCursor:[52:24] posNoWhite:[52:22] Found pattern:[52:16->52:35] Completable: Cpattern Value[f]->recordBody @@ -139,7 +134,6 @@ Completable: Cpattern Value[f]->recordBody }] Complete src/CompletionPattern.res 55:19 -looking for: Cpath Value[f] posCursor:[55:19] posNoWhite:[55:18] Found expr:[55:3->55:21] posCursor:[55:19] posNoWhite:[55:18] Found pattern:[55:16->55:20] posCursor:[55:19] posNoWhite:[55:18] Found pattern:[55:17->55:19] @@ -153,7 +147,6 @@ Completable: Cpattern Value[f]=fi->recordBody }] Complete src/CompletionPattern.res 58:19 -looking for: Cpath Value[z] posCursor:[58:19] posNoWhite:[58:18] Found expr:[58:3->58:25] posCursor:[58:19] posNoWhite:[58:18] Found pattern:[58:16->58:24] posCursor:[58:19] posNoWhite:[58:18] Found pattern:[58:17->58:20] @@ -168,7 +161,6 @@ Completable: Cpattern Value[z]=o->tuple($0), recordBody }] Complete src/CompletionPattern.res 61:22 -looking for: Cpath Value[f] posCursor:[61:22] posNoWhite:[61:21] Found expr:[61:3->74:1] posCursor:[61:22] posNoWhite:[61:21] Found pattern:[61:16->61:25] Completable: Cpattern Value[f]->recordField(nest) @@ -184,7 +176,6 @@ Completable: Cpattern Value[f]->recordField(nest) }] Complete src/CompletionPattern.res 64:24 -looking for: Cpath Value[f] posCursor:[64:24] posNoWhite:[64:23] Found expr:[64:3->64:27] posCursor:[64:24] posNoWhite:[64:23] Found pattern:[64:16->64:26] posCursor:[64:24] posNoWhite:[64:23] Found pattern:[64:23->64:25] @@ -201,7 +192,6 @@ Complete src/CompletionPattern.res 70:22 posCursor:[70:22] posNoWhite:[70:21] Found expr:[67:8->74:1] posCursor:[70:22] posNoWhite:[70:21] Found expr:[69:2->72:13] posCursor:[70:22] posNoWhite:[70:21] Found expr:[70:5->72:13] -looking for: Cpath Value[nest] posCursor:[70:22] posNoWhite:[70:21] Found expr:[70:5->70:24] posCursor:[70:22] posNoWhite:[70:21] Found pattern:[70:21->70:23] Completable: Cpattern Value[nest]->recordBody @@ -214,7 +204,6 @@ Completable: Cpattern Value[nest]->recordBody }] Complete src/CompletionPattern.res 76:8 -looking for: Cpath Value[f] posCursor:[76:8] posNoWhite:[76:7] Found pattern:[76:7->76:9] Completable: Cpattern Value[f]->recordBody [{ @@ -244,7 +233,6 @@ Completable: Cpattern Value[f]->recordBody }] Complete src/CompletionPattern.res 79:16 -looking for: Cpath Value[f] posCursor:[79:16] posNoWhite:[79:15] Found pattern:[79:7->79:18] posCursor:[79:16] posNoWhite:[79:15] Found pattern:[79:14->79:17] posCursor:[79:16] posNoWhite:[79:15] Found pattern:[79:15->79:16] @@ -258,7 +246,6 @@ Completable: Cpattern Value[f]=n->recordField(nest), recordBody }] Complete src/CompletionPattern.res 87:20 -looking for: Cpath Value[z] posCursor:[87:20] posNoWhite:[87:19] Found expr:[87:3->87:22] posCursor:[87:20] posNoWhite:[87:19] Found pattern:[87:16->87:21] Ppat_construct Two:[87:16->87:19] @@ -280,7 +267,6 @@ Completable: Cpattern Value[z]->variantPayload::Two($0) }] Complete src/CompletionPattern.res 90:21 -looking for: Cpath Value[z] posCursor:[90:21] posNoWhite:[90:20] Found expr:[90:3->90:23] posCursor:[90:21] posNoWhite:[90:20] Found pattern:[90:16->90:22] Ppat_construct Two:[90:16->90:19] @@ -295,7 +281,6 @@ Completable: Cpattern Value[z]=t->variantPayload::Two($0) }] Complete src/CompletionPattern.res 93:23 -looking for: Cpath Value[z] posCursor:[93:23] posNoWhite:[93:22] Found expr:[93:3->93:26] posCursor:[93:23] posNoWhite:[93:22] Found pattern:[93:16->93:25] Ppat_construct Three:[93:16->93:21] @@ -328,7 +313,6 @@ Completable: Cpattern Value[z]->variantPayload::Three($0), recordBody }] Complete src/CompletionPattern.res 96:27 -looking for: Cpath Value[z] posCursor:[96:27] posNoWhite:[96:26] Found expr:[96:3->96:29] posCursor:[96:27] posNoWhite:[96:26] Found pattern:[96:16->96:28] Ppat_construct Three:[96:16->96:21] @@ -344,7 +328,6 @@ Completable: Cpattern Value[z]=t->variantPayload::Three($1) }] Complete src/CompletionPattern.res 103:21 -looking for: Cpath Value[b] posCursor:[103:21] posNoWhite:[103:20] Found expr:[103:3->103:23] posCursor:[103:21] posNoWhite:[103:20] Found pattern:[103:16->103:22] posCursor:[103:21] posNoWhite:[103:20] Found pattern:[103:20->103:21] @@ -365,7 +348,6 @@ Completable: Cpattern Value[b]->polyvariantPayload::two($0) }] Complete src/CompletionPattern.res 106:22 -looking for: Cpath Value[b] posCursor:[106:22] posNoWhite:[106:21] Found expr:[106:3->106:24] posCursor:[106:22] posNoWhite:[106:21] Found pattern:[106:16->106:23] posCursor:[106:22] posNoWhite:[106:21] Found pattern:[106:21->106:22] @@ -379,7 +361,6 @@ Completable: Cpattern Value[b]=t->polyvariantPayload::two($0) }] Complete src/CompletionPattern.res 109:24 -looking for: Cpath Value[b] posCursor:[109:24] posNoWhite:[109:23] Found expr:[109:3->109:27] posCursor:[109:24] posNoWhite:[109:23] Found pattern:[109:16->109:26] posCursor:[109:24] posNoWhite:[109:23] Found pattern:[109:23->109:25] @@ -411,7 +392,6 @@ Completable: Cpattern Value[b]->polyvariantPayload::three($0), recordBody }] Complete src/CompletionPattern.res 112:28 -looking for: Cpath Value[b] posCursor:[112:28] posNoWhite:[112:27] Found expr:[112:3->112:30] posCursor:[112:28] posNoWhite:[112:27] Found pattern:[112:16->112:29] posCursor:[112:28] posNoWhite:[112:27] Found pattern:[112:22->112:29] @@ -440,7 +420,6 @@ Completable: Cpattern Value[c] }] Complete src/CompletionPattern.res 121:17 -looking for: Cpath Value[c] posCursor:[121:17] posNoWhite:[121:16] Found expr:[121:3->121:20] posCursor:[121:17] posNoWhite:[121:16] Found pattern:[121:16->121:18] Completable: Cpattern Value[c]->array @@ -459,7 +438,6 @@ Completable: Cpattern Value[c]->array }] Complete src/CompletionPattern.res 127:21 -looking for: Cpath Value[o] posCursor:[127:21] posNoWhite:[127:20] Found expr:[127:3->127:24] posCursor:[127:21] posNoWhite:[127:20] Found pattern:[127:16->127:22] Ppat_construct Some:[127:16->127:20] @@ -481,7 +459,6 @@ Completable: Cpattern Value[o]->variantPayload::Some($0) }] Complete src/CompletionPattern.res 134:23 -looking for: Cpath Value[p] posCursor:[134:23] posNoWhite:[134:22] Found expr:[134:3->134:26] posCursor:[134:23] posNoWhite:[134:22] Found pattern:[134:16->134:25] Ppat_construct Test:[134:16->134:20] @@ -501,7 +478,6 @@ Completable: Cpattern Value[p]->variantPayload::Test($1) }] Complete src/CompletionPattern.res 137:29 -looking for: Cpath Value[p] posCursor:[137:29] posNoWhite:[137:28] Found expr:[137:3->137:32] posCursor:[137:29] posNoWhite:[137:28] Found pattern:[137:16->137:31] Ppat_construct Test:[137:16->137:20] @@ -524,7 +500,6 @@ Completable: Cpattern Value[p]->variantPayload::Test($2) }] Complete src/CompletionPattern.res 140:23 -looking for: Cpath Value[p] posCursor:[140:23] posNoWhite:[140:22] Found expr:[140:3->140:32] posCursor:[140:23] posNoWhite:[140:22] Found pattern:[140:16->140:31] Ppat_construct Test:[140:16->140:20] @@ -545,7 +520,6 @@ Completable: Cpattern Value[p]->variantPayload::Test($1) }] Complete src/CompletionPattern.res 143:35 -looking for: Cpath Value[p] posCursor:[143:35] posNoWhite:[143:34] Found expr:[143:3->143:38] posCursor:[143:35] posNoWhite:[143:34] Found pattern:[143:16->143:37] Ppat_construct Test:[143:16->143:20] @@ -563,7 +537,6 @@ Completable: Cpattern Value[p]->variantPayload::Test($3) }] Complete src/CompletionPattern.res 150:24 -looking for: Cpath Value[v] posCursor:[150:24] posNoWhite:[150:23] Found expr:[150:3->150:27] posCursor:[150:24] posNoWhite:[150:23] Found pattern:[150:16->150:26] Completable: Cpattern Value[v]->polyvariantPayload::test($1) @@ -582,7 +555,6 @@ Completable: Cpattern Value[v]->polyvariantPayload::test($1) }] Complete src/CompletionPattern.res 153:30 -looking for: Cpath Value[v] posCursor:[153:30] posNoWhite:[153:29] Found expr:[153:3->153:33] posCursor:[153:30] posNoWhite:[153:29] Found pattern:[153:16->153:32] posCursor:[153:30] posNoWhite:[153:29] Found pattern:[153:21->153:32] @@ -604,7 +576,6 @@ Completable: Cpattern Value[v]->polyvariantPayload::test($2) }] Complete src/CompletionPattern.res 156:24 -looking for: Cpath Value[v] posCursor:[156:24] posNoWhite:[156:23] Found expr:[156:3->156:33] posCursor:[156:24] posNoWhite:[156:23] Found pattern:[156:16->156:32] posCursor:[156:24] posNoWhite:[156:23] Found pattern:[156:21->156:32] @@ -624,7 +595,6 @@ Completable: Cpattern Value[v]->polyvariantPayload::test($1) }] Complete src/CompletionPattern.res 159:36 -looking for: Cpath Value[v] posCursor:[159:36] posNoWhite:[159:35] Found expr:[159:3->159:39] posCursor:[159:36] posNoWhite:[159:35] Found pattern:[159:16->159:38] posCursor:[159:36] posNoWhite:[159:35] Found pattern:[159:21->159:38] @@ -641,7 +611,6 @@ Completable: Cpattern Value[v]->polyvariantPayload::test($3) }] Complete src/CompletionPattern.res 164:17 -looking for: Cpath Value[s] posCursor:[164:17] posNoWhite:[164:16] Found expr:[164:3->164:20] posCursor:[164:17] posNoWhite:[164:16] Found pattern:[164:16->164:18] Ppat_construct ():[164:16->164:18] @@ -661,7 +630,6 @@ Completable: Cpattern Value[s]->tuple($0) }] Complete src/CompletionPattern.res 167:23 -looking for: Cpath Value[s] posCursor:[167:23] posNoWhite:[167:21] Found expr:[167:3->167:26] posCursor:[167:23] posNoWhite:[167:21] Found pattern:[167:16->167:24] Completable: Cpattern Value[s]->tuple($1) @@ -682,7 +650,6 @@ Completable: Cpattern Value[s]->tuple($1) }] Complete src/CompletionPattern.res 170:22 -looking for: Cpath Value[s] posCursor:[170:22] posNoWhite:[170:21] Found expr:[170:3->170:30] posCursor:[170:22] posNoWhite:[170:21] Found pattern:[170:16->170:28] Completable: Cpattern Value[s]->tuple($1) @@ -716,7 +683,6 @@ Completable: Cpattern Value[s] }] Complete src/CompletionPattern.res 176:41 -looking for: Cpath Value[s] posCursor:[176:41] posNoWhite:[176:40] Found expr:[176:3->176:50] posCursor:[176:41] posNoWhite:[176:40] Found pattern:[176:35->176:47] Completable: Cpattern Value[s]->tuple($1) @@ -766,7 +732,6 @@ Completable: Cpattern Value[z] }] Complete src/CompletionPattern.res 182:32 -looking for: Cpath Value[z] posCursor:[182:32] posNoWhite:[182:31] Found expr:[182:3->182:37] posCursor:[182:32] posNoWhite:[182:31] Found pattern:[182:16->182:34] posCursor:[182:32] posNoWhite:[182:31] Found pattern:[182:22->182:34] @@ -787,7 +752,6 @@ Completable: Cpattern Value[z]->variantPayload::Two($0) }] Complete src/CompletionPattern.res 185:48 -looking for: Cpath Value[z] posCursor:[185:48] posNoWhite:[185:47] Found expr:[185:3->185:53] posCursor:[185:48] posNoWhite:[185:47] Found pattern:[185:16->185:50] posCursor:[185:48] posNoWhite:[185:47] Found pattern:[185:22->185:50] @@ -809,7 +773,6 @@ Completable: Cpattern Value[z]->variantPayload::Three($1) }] Complete src/CompletionPattern.res 188:34 -looking for: Cpath Value[b] posCursor:[188:34] posNoWhite:[188:33] Found expr:[188:3->188:39] posCursor:[188:34] posNoWhite:[188:33] Found pattern:[188:16->188:36] posCursor:[188:34] posNoWhite:[188:33] Found pattern:[188:23->188:36] @@ -829,7 +792,6 @@ Completable: Cpattern Value[b]->polyvariantPayload::two($0) }] Complete src/CompletionPattern.res 191:50 -looking for: Cpath Value[b] posCursor:[191:50] posNoWhite:[191:49] Found expr:[191:3->191:55] posCursor:[191:50] posNoWhite:[191:49] Found pattern:[191:16->191:52] posCursor:[191:50] posNoWhite:[191:49] Found pattern:[191:23->191:52]