diff --git a/.vscode/settings.json b/.vscode/settings.json index 11909bf1d..7cf99ab8c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,13 +1,13 @@ { - "editor.insertSpaces": false, - "tslint.enable": true, - "typescript.tsc.autoDetect": "off", - "typescript.preferences.quoteStyle": "single", - "editor.codeActionsOnSave": { - "source.fixAll.eslint": true - }, - "ocaml.sandbox": { - "kind": "opam", - "switch": "${workspaceFolder:rescript-vscode}/analysis" - } -} \ No newline at end of file + "editor.insertSpaces": false, + "tslint.enable": true, + "typescript.tsc.autoDetect": "off", + "typescript.preferences.quoteStyle": "single", + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "explicit" + }, + "ocaml.sandbox": { + "kind": "opam", + "switch": "4.14.0" + } +} diff --git a/CHANGELOG.md b/CHANGELOG.md index dbbe7e6c1..f543ec136 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,10 @@ - Complete for maker-style functions (functions returning type `t` of a module) when encountering a `type t` in relevant scenarios. https://github.com/rescript-lang/rescript-vscode/pull/884 - Expand type aliases in hovers. https://github.com/rescript-lang/rescript-vscode/pull/881 +#### :bug: Bug Fix + +- Many bugfixes around nested pattern and expression completion. https://github.com/rescript-lang/rescript-vscode/pull/892 + #### :nail_care: Polish - Better error recovery when analysis fails. https://github.com/rescript-lang/rescript-vscode/pull/880 diff --git a/analysis/.ocamlformat b/analysis/.ocamlformat index 3bef5a7b4..87496a125 100644 --- a/analysis/.ocamlformat +++ b/analysis/.ocamlformat @@ -1,5 +1,5 @@ profile = default -version = 0.22.4 +version = 0.26.1 field-space = tight-decl break-cases = toplevel diff --git a/analysis/dune-project b/analysis/dune-project index 47a122217..6d54204c8 100644 --- a/analysis/dune-project +++ b/analysis/dune-project @@ -17,7 +17,7 @@ (ocaml (>= 4.10)) (ocamlformat - (= 0.22.4)) + (= 0.26.1)) (cppo (= 1.6.9)) (reanalyze diff --git a/analysis/rescript-vscode.opam b/analysis/rescript-vscode.opam index 1645dea53..72c8e4dd3 100644 --- a/analysis/rescript-vscode.opam +++ b/analysis/rescript-vscode.opam @@ -7,7 +7,7 @@ homepage: "https://github.com/rescript-lang/rescript-vscode" bug-reports: "https://github.com/rescript-lang/rescript-vscode/issues" depends: [ "ocaml" {>= "4.10"} - "ocamlformat" {= "0.22.4"} + "ocamlformat" {= "0.26.1"} "cppo" {= "1.6.9"} "reanalyze" {= "2.23.0"} "dune" diff --git a/analysis/src/Cli.ml b/analysis/src/Cli.ml index dcb86bdd6..d2ed112cd 100644 --- a/analysis/src/Cli.ml +++ b/analysis/src/Cli.ml @@ -144,10 +144,11 @@ let main () = ~maxLength ~debug | [_; "codeLens"; path] -> Commands.codeLens ~path ~debug | [_; "extractDocs"; path] -> - - let () = match Sys.getenv_opt "FROM_COMPILER" with - | Some("true") -> Cfg.isDocGenFromCompiler := true - | _ -> () in + let () = + match Sys.getenv_opt "FROM_COMPILER" with + | Some "true" -> Cfg.isDocGenFromCompiler := true + | _ -> () + in DocExtraction.extractDocs ~path ~debug | [_; "codeAction"; path; startLine; startCol; endLine; endCol; currentFile] diff --git a/analysis/src/Commands.ml b/analysis/src/Commands.ml index 486f46743..3a91151cc 100644 --- a/analysis/src/Commands.ml +++ b/analysis/src/Commands.ml @@ -163,7 +163,7 @@ let references ~path ~pos ~debug = in print_endline (if allLocs = [] then Protocol.null - else "[\n" ^ (allLocs |> String.concat ",\n") ^ "\n]") + else "[\n" ^ (allLocs |> String.concat ",\n") ^ "\n]") let rename ~path ~pos ~newName ~debug = let result = @@ -304,6 +304,8 @@ let test ~path = (match String.sub rest 0 3 with | "db+" -> Log.verbose := true | "db-" -> Log.verbose := false + | "dv+" -> Debug.debugLevel := Verbose + | "dv-" -> Debug.debugLevel := Off | "def" -> print_endline ("Definition " ^ path ^ " " ^ string_of_int line ^ ":" diff --git a/analysis/src/CompletionBackEnd.ml b/analysis/src/CompletionBackEnd.ml index 9ffc51fcc..975fef31a 100644 --- a/analysis/src/CompletionBackEnd.ml +++ b/analysis/src/CompletionBackEnd.ml @@ -638,7 +638,7 @@ let completionsGetCompletionType ~full = function | {Completion.kind = Field ({typ}, _); env} :: _ -> typ |> TypeUtils.extractType ~env ~package:full.package - |> Option.map (fun typ -> (typ, env)) + |> Option.map (fun (typ, _) -> (typ, env)) | {Completion.kind = Type typ; env} :: _ -> ( match TypeUtils.extractTypeFromResolvedType typ ~env ~full with | None -> None @@ -1061,6 +1061,12 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact ~kind:(Completion.Value (Utils.unwrapIfOption typ)); ]) | CArgument {functionContextPath; argumentLabel} -> ( + if Debug.verbose () then + Printf.printf "--> function argument: %s\n" + (match argumentLabel with + | Labelled n | Optional n -> n + | Unlabelled {argumentPosition} -> "$" ^ string_of_int argumentPosition); + let labels, env = match functionContextPath @@ -1069,8 +1075,12 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact |> completionsGetCompletionType2 ~debug ~full ~opens ~rawOpens ~pos with | Some ((TypeExpr typ | ExtractedType (Tfunction {typ})), env) -> + if Debug.verbose () then print_endline "--> found function type"; (typ |> TypeUtils.getArgs ~full ~env, env) - | _ -> ([], env) + | _ -> + if Debug.verbose () then + print_endline "--> could not find function type"; + ([], env) in let targetLabel = labels @@ -1090,8 +1100,12 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact | Some (Optional _, _) -> true in match targetLabel with - | None -> [] + | None -> + if Debug.verbose () then + print_endline "--> could not look up function argument"; + [] | Some (_, typ) -> + if Debug.verbose () then print_endline "--> found function argument!"; [ Completion.create "dummy" ~env ~kind: @@ -1099,6 +1113,7 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact (if expandOption then Utils.unwrapIfOption typ else typ)); ]) | CPatternPath {rootCtxPath; nested} -> ( + (* TODO(env-stuff) Get rid of innerType etc *) match rootCtxPath |> getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env @@ -1190,12 +1205,15 @@ let printConstructorArgs ~mode ~asSnippet argsLen = if List.length !args > 0 then "(" ^ (!args |> String.concat ", ") ^ ")" else "" -let rec completeTypedValue ~rawOpens ~full ~prefix ~completionContext ~mode - (t : SharedTypes.completionType) = +let rec completeTypedValue ?(typeArgContext : typeArgContext option) ~rawOpens + ~full ~prefix ~completionContext ~mode (t : SharedTypes.completionType) = let emptyCase = emptyCase ~mode in let printConstructorArgs = printConstructorArgs ~mode in + let createWithSnippet = Completion.createWithSnippet ?typeArgContext in + let create = Completion.create ?typeArgContext in match t with | TtypeT {env; path} -> + if Debug.verbose () then print_endline "[complete_typed_value]--> TtypeT"; (* Find all functions in the module that returns type t *) let rec fnReturnsTypeT t = match t.Types.desc with @@ -1243,18 +1261,20 @@ let rec completeTypedValue ~rawOpens ~full ~prefix ~completionContext ~mode item)); Hashtbl.fold (fun fnName typeExpr all -> - Completion.createWithSnippet + createWithSnippet ~name:(Printf.sprintf "%s()" fnName) ~insertText:(fnName ^ "($0)") ~kind:(Value typeExpr) ~env () :: all) functionsReturningTypeT [] | Tbool env -> + if Debug.verbose () then print_endline "[complete_typed_value]--> Tbool"; [ - Completion.create "true" ~kind:(Label "bool") ~env; - Completion.create "false" ~kind:(Label "bool") ~env; + create "true" ~kind:(Label "bool") ~env; + create "false" ~kind:(Label "bool") ~env; ] |> filterItems ~prefix | Tvariant {env; constructors; variantDecl; variantName} -> + if Debug.verbose () then print_endline "[complete_typed_value]--> Tvariant"; constructors |> List.map (fun (constructor : Constructor.t) -> let numArgs = @@ -1262,7 +1282,7 @@ let rec completeTypedValue ~rawOpens ~full ~prefix ~completionContext ~mode | InlineRecord _ -> 1 | Args args -> List.length args in - Completion.createWithSnippet ?deprecated:constructor.deprecated + createWithSnippet ?deprecated:constructor.deprecated ~name: (constructor.cname.txt ^ printConstructorArgs numArgs ~asSnippet:false) @@ -1275,9 +1295,11 @@ let rec completeTypedValue ~rawOpens ~full ~prefix ~completionContext ~mode ~env ()) |> filterItems ~prefix | Tpolyvariant {env; constructors; typeExpr} -> + if Debug.verbose () then + print_endline "[complete_typed_value]--> Tpolyvariant"; constructors |> List.map (fun (constructor : polyVariantConstructor) -> - Completion.createWithSnippet + createWithSnippet ~name: ("#" ^ constructor.displayName ^ printConstructorArgs @@ -1296,15 +1318,16 @@ let rec completeTypedValue ~rawOpens ~full ~prefix ~completionContext ~mode |> filterItems ~prefix:(if Utils.startsWith prefix "#" then prefix else "#" ^ prefix) | Toption (env, t) -> + if Debug.verbose () then print_endline "[complete_typed_value]--> Toption"; let innerType = match t with - | ExtractedType t -> Some t + | ExtractedType t -> Some (t, None) | TypeExpr t -> t |> TypeUtils.extractType ~env ~package:full.package in let expandedCompletions = match innerType with | None -> [] - | Some innerType -> + | Some (innerType, _typeArgsContext) -> innerType |> completeTypedValue ~rawOpens ~full ~prefix ~completionContext ~mode |> List.map (fun (c : Completion.t) -> @@ -1320,8 +1343,7 @@ let rec completeTypedValue ~rawOpens ~full ~prefix ~completionContext ~mode in let noneCase = Completion.create "None" ~kind:(kindFromInnerType t) ~env in let someAnyCase = - Completion.createWithSnippet ~name:"Some(_)" ~kind:(kindFromInnerType t) - ~env + createWithSnippet ~name:"Some(_)" ~kind:(kindFromInnerType t) ~env ~insertText:(Printf.sprintf "Some(%s)" (emptyCase 1)) () in @@ -1329,7 +1351,7 @@ let rec completeTypedValue ~rawOpens ~full ~prefix ~completionContext ~mode match completionContext with | Some (Completable.CameFromRecordField fieldName) -> [ - Completion.createWithSnippet + createWithSnippet ~name:("Some(" ^ fieldName ^ ")") ~kind:(kindFromInnerType t) ~env ~insertText:("Some(" ^ fieldName ^ ")$0") @@ -1341,6 +1363,7 @@ let rec completeTypedValue ~rawOpens ~full ~prefix ~completionContext ~mode in completions @ expandedCompletions |> filterItems ~prefix | Tresult {env; okType; errorType} -> + if Debug.verbose () then print_endline "[complete_typed_value]--> Tresult"; let okInnerType = okType |> TypeUtils.extractType ~env ~package:full.package in @@ -1350,7 +1373,7 @@ let rec completeTypedValue ~rawOpens ~full ~prefix ~completionContext ~mode let expandedOkCompletions = match okInnerType with | None -> [] - | Some innerType -> + | Some (innerType, _) -> innerType |> completeTypedValue ~rawOpens ~full ~prefix ~completionContext ~mode |> List.map (fun (c : Completion.t) -> @@ -1367,7 +1390,7 @@ let rec completeTypedValue ~rawOpens ~full ~prefix ~completionContext ~mode let expandedErrorCompletions = match errorInnerType with | None -> [] - | Some innerType -> + | Some (innerType, _) -> innerType |> completeTypedValue ~rawOpens ~full ~prefix ~completionContext ~mode |> List.map (fun (c : Completion.t) -> @@ -1382,12 +1405,12 @@ let rec completeTypedValue ~rawOpens ~full ~prefix ~completionContext ~mode }) in let okAnyCase = - Completion.createWithSnippet ~name:"Ok(_)" ~kind:(Value okType) ~env + createWithSnippet ~name:"Ok(_)" ~kind:(Value okType) ~env ~insertText:(Printf.sprintf "Ok(%s)" (emptyCase 1)) () in let errorAnyCase = - Completion.createWithSnippet ~name:"Error(_)" ~kind:(Value errorType) ~env + createWithSnippet ~name:"Error(_)" ~kind:(Value errorType) ~env ~insertText:(Printf.sprintf "Error(%s)" (emptyCase 1)) () in @@ -1395,7 +1418,7 @@ let rec completeTypedValue ~rawOpens ~full ~prefix ~completionContext ~mode match completionContext with | Some (Completable.CameFromRecordField fieldName) -> [ - Completion.createWithSnippet + createWithSnippet ~name:("Ok(" ^ fieldName ^ ")") ~kind:(Value okType) ~env ~insertText:("Ok(" ^ fieldName ^ ")$0") @@ -1408,14 +1431,16 @@ let rec completeTypedValue ~rawOpens ~full ~prefix ~completionContext ~mode completions @ expandedOkCompletions @ expandedErrorCompletions |> filterItems ~prefix | Tuple (env, exprs, typ) -> + if Debug.verbose () then print_endline "[complete_typed_value]--> Tuple"; let numExprs = List.length exprs in [ - Completion.createWithSnippet + createWithSnippet ~name:(printConstructorArgs numExprs ~asSnippet:false) ~insertText:(printConstructorArgs numExprs ~asSnippet:true) ~kind:(Value typ) ~env (); ] | Trecord {env; fields} as extractedType -> ( + if Debug.verbose () then print_endline "[complete_typed_value]--> Trecord"; (* 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. *) @@ -1427,8 +1452,7 @@ let rec completeTypedValue ~rawOpens ~full ~prefix ~completionContext ~mode |> List.map (fun (field : field) -> match (field.optional, mode) with | true, Pattern Destructuring -> - Completion.create ("?" ^ field.fname.txt) - ?deprecated:field.deprecated + create ("?" ^ field.fname.txt) ?deprecated:field.deprecated ~docstring: [ field.fname.txt @@ -1439,7 +1463,7 @@ let rec completeTypedValue ~rawOpens ~full ~prefix ~completionContext ~mode (Field (field, TypeUtils.extractedTypeToString extractedType)) ~env | _ -> - Completion.create field.fname.txt ?deprecated:field.deprecated + create field.fname.txt ?deprecated:field.deprecated ~kind: (Field (field, TypeUtils.extractedTypeToString extractedType)) ~env) @@ -1447,7 +1471,7 @@ let rec completeTypedValue ~rawOpens ~full ~prefix ~completionContext ~mode | _ -> if prefix = "" then [ - Completion.createWithSnippet ~name:"{}" + createWithSnippet ~name:"{}" ~insertText:(if !Cfg.supportsSnippets then "{$0}" else "{}") ~sortText:"A" ~kind: @@ -1460,27 +1484,30 @@ let rec completeTypedValue ~rawOpens ~full ~prefix ~completionContext ~mode ] else []) | TinlineRecord {env; fields} -> ( + if Debug.verbose () then + print_endline "[complete_typed_value]--> TinlineRecord"; match completionContext with | Some (Completable.RecordField {seenFields}) -> fields |> List.filter (fun (field : field) -> List.mem field.fname.txt seenFields = false) |> List.map (fun (field : field) -> - Completion.create field.fname.txt ~kind:(Label "Inline record") + create field.fname.txt ~kind:(Label "Inline record") ?deprecated:field.deprecated ~env) |> filterItems ~prefix | _ -> if prefix = "" then [ - Completion.createWithSnippet ~name:"{}" + createWithSnippet ~name:"{}" ~insertText:(if !Cfg.supportsSnippets then "{$0}" else "{}") ~sortText:"A" ~kind:(Label "Inline record") ~env (); ] else []) | Tarray (env, typ) -> + if Debug.verbose () then print_endline "[complete_typed_value]--> Tarray"; if prefix = "" then [ - Completion.createWithSnippet ~name:"[]" + createWithSnippet ~name:"[]" ~insertText:(if !Cfg.supportsSnippets then "[$0]" else "[]") ~sortText:"A" ~kind: @@ -1496,9 +1523,10 @@ let rec completeTypedValue ~rawOpens ~full ~prefix ~completionContext ~mode ] else [] | Tstring env -> + if Debug.verbose () then print_endline "[complete_typed_value]--> Tstring"; if prefix = "" then [ - Completion.createWithSnippet ~name:"\"\"" + createWithSnippet ~name:"\"\"" ~insertText:(if !Cfg.supportsSnippets then "\"$0\"" else "\"\"") ~sortText:"A" ~kind: @@ -1508,6 +1536,8 @@ let rec completeTypedValue ~rawOpens ~full ~prefix ~completionContext ~mode else [] | Tfunction {env; typ; args; uncurried; returnType} when prefix = "" && mode = Expression -> + if Debug.verbose () then + print_endline "[complete_typed_value]--> Tfunction #1"; let shouldPrintAsUncurried = uncurried && !Config.uncurried <> Uncurried in let mkFnArgs ~asSnippet = match args with @@ -1545,12 +1575,12 @@ let rec completeTypedValue ~rawOpens ~full ~prefix ~completionContext ~mode in let isAsync = match TypeUtils.extractType ~env ~package:full.package returnType with - | Some (Tpromise _) -> true + | Some (Tpromise _, _) -> true | _ -> false in let asyncPrefix = if isAsync then "async " else "" in [ - Completion.createWithSnippet + createWithSnippet ~name:(asyncPrefix ^ mkFnArgs ~asSnippet:false ^ " => {}") ~insertText: (asyncPrefix @@ -1559,10 +1589,14 @@ let rec completeTypedValue ~rawOpens ~full ~prefix ~completionContext ~mode ^ if !Cfg.supportsSnippets then "{$0}" else "{}") ~sortText:"A" ~kind:(Value typ) ~env (); ] - | Tfunction _ -> [] + | Tfunction _ -> + if Debug.verbose () then + print_endline "[complete_typed_value]--> Tfunction #other"; + [] | Texn env -> + if Debug.verbose () then print_endline "[complete_typed_value]--> Texn"; [ - Completion.create + create (full.package.builtInCompletionModules.exnModulePath @ ["Error(error)"] |> ident) ~kind:(Label "Catches errors from JavaScript errors.") @@ -1574,7 +1608,9 @@ let rec completeTypedValue ~rawOpens ~full ~prefix ~completionContext ~mode ] ~env; ] - | Tpromise _ -> [] + | Tpromise _ -> + if Debug.verbose () then print_endline "[complete_typed_value]--> Tpromise"; + [] module StringSet = Set.Make (String) @@ -1812,15 +1848,15 @@ let rec processCompletable ~debug ~full ~scope ~env ~pos ~forHover completable = match typ |> TypeUtils.extractType ~env ~package:full.package - |> Utils.Option.flatMap (fun typ -> - typ |> TypeUtils.resolveNested ~env ~full ~nested) + |> Utils.Option.flatMap (fun (typ, typeArgContext) -> + typ |> TypeUtils.resolveNested ?typeArgContext ~env ~full ~nested) with | None -> fallbackOrEmpty () - | Some (typ, _env, completionContext) -> + | Some (typ, _env, completionContext, typeArgContext) -> let items = typ - |> completeTypedValue ~rawOpens ~mode:(Pattern patternMode) ~full - ~prefix ~completionContext + |> completeTypedValue ?typeArgContext ~rawOpens + ~mode:(Pattern patternMode) ~full ~prefix ~completionContext in fallbackOrEmpty ~items ()) | None -> fallbackOrEmpty ()) @@ -1841,11 +1877,19 @@ let rec processCompletable ~debug ~full ~scope ~env ~pos ~forHover completable = ~exact:true ~scope |> completionsGetCompletionType ~full with - | None -> regularCompletions + | None -> + if Debug.verbose () then + print_endline "--> could not get completions for context path"; + regularCompletions | Some (typ, env) -> ( match typ |> TypeUtils.resolveNested ~env ~full ~nested with - | None -> regularCompletions - | Some (typ, _env, completionContext) -> ( + | None -> + if Debug.verbose () then + print_endline "--> could not resolve nested expression path"; + regularCompletions + | Some (typ, _env, completionContext, typeArgContext) -> ( + if Debug.verbose () then + print_endline "--> found type in nested expression completion"; (* Wrap the insert text in braces when we're completing the root of a JSX prop value. *) let wrapInsertTextInBraces = @@ -1857,8 +1901,8 @@ let rec processCompletable ~debug ~full ~scope ~env ~pos ~forHover completable = in let items = typ - |> completeTypedValue ~rawOpens ~mode:Expression ~full ~prefix - ~completionContext + |> completeTypedValue ?typeArgContext ~rawOpens ~mode:Expression ~full + ~prefix ~completionContext |> List.map (fun (c : Completion.t) -> if wrapInsertTextInBraces then { @@ -1925,7 +1969,7 @@ let rec processCompletable ~debug ~full ~scope ~env ~pos ~forHover completable = match c.kind with | Value typExpr -> ( match typExpr |> TypeUtils.extractType ~env:c.env ~package with - | Some (Tvariant v) -> + | Some (Tvariant v, _) -> withExhaustiveItem c ~cases: (v.constructors @@ -1935,7 +1979,7 @@ let rec processCompletable ~debug ~full ~scope ~env ~pos ~forHover completable = match constructor.args with | Args [] -> "" | _ -> "(_)")) - | Some (Tpolyvariant v) -> + | Some (Tpolyvariant v, _) -> withExhaustiveItem c ~cases: (v.constructors @@ -1945,11 +1989,12 @@ let rec processCompletable ~debug ~full ~scope ~env ~pos ~forHover completable = match constructor.args with | [] -> "" | _ -> "(_)")) - | Some (Toption (_env, _typ)) -> + | Some (Toption (_env, _typ), _) -> withExhaustiveItem c ~cases:["Some($1)"; "None"] ~startIndex:1 - | Some (Tresult _) -> + | Some (Tresult _, _) -> withExhaustiveItem c ~cases:["Ok($1)"; "Error($1)"] ~startIndex:1 - | Some (Tbool _) -> withExhaustiveItem c ~cases:["true"; "false"] + | Some (Tbool _, _) -> + withExhaustiveItem c ~cases:["true"; "false"] | _ -> [c]) | _ -> [c]) |> List.flatten diff --git a/analysis/src/Debug.ml b/analysis/src/Debug.ml index 383ba1f67..d19d7dfa5 100644 --- a/analysis/src/Debug.ml +++ b/analysis/src/Debug.ml @@ -7,4 +7,7 @@ let log s = | Regular | Verbose -> print_endline s | Off -> () -let logVerbose s = if !debugLevel = Verbose then print_endline s +let debugPrintEnv (env : SharedTypes.QueryEnv.t) = + env.pathRev @ [env.file.moduleName] |> List.rev |> String.concat "." + +let verbose () = !debugLevel = Verbose diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml index 2528b6021..cc25c5031 100644 --- a/analysis/src/SharedTypes.ml +++ b/analysis/src/SharedTypes.ml @@ -311,12 +311,19 @@ end = struct {env with exported = structure.exported; pathRev; parent = Some env} end +type typeArgContext = { + env: QueryEnv.t; + typeArgs: Types.type_expr list; + typeParams: Types.type_expr list; +} + type polyVariantConstructor = { name: string; displayName: string; args: Types.type_expr list; } +(* TODO(env-stuff) All envs for bool string etc can be removed. *) type innerType = TypeExpr of Types.type_expr | ExtractedType of completionType and completionType = | Tuple of QueryEnv.t * Types.type_expr list * Types.type_expr @@ -337,8 +344,6 @@ and completionType = constructors: Constructor.t list; variantDecl: Types.type_declaration; variantName: string; - typeArgs: Types.type_expr list; - typeParams: Types.type_expr list; } | Tpolyvariant of { env: QueryEnv.t; @@ -780,10 +785,11 @@ module Completion = struct docstring: string list; kind: kind; detail: string option; + typeArgContext: typeArgContext option; } - let create ~kind ~env ?(docstring = []) ?filterText ?detail ?deprecated - ?insertText name = + let create ~kind ~env ?typeArgContext ?(docstring = []) ?filterText ?detail + ?deprecated ?insertText name = { name; env; @@ -795,10 +801,11 @@ module Completion = struct insertTextFormat = None; filterText; detail; + typeArgContext; } - let createWithSnippet ~name ?insertText ~kind ~env ?sortText ?deprecated - ?filterText ?detail ?(docstring = []) () = + let createWithSnippet ~name ?typeArgContext ?insertText ~kind ~env ?sortText + ?deprecated ?filterText ?detail ?(docstring = []) () = { name; env; @@ -810,6 +817,7 @@ module Completion = struct insertTextFormat = Some Protocol.Snippet; filterText; detail; + typeArgContext; } (* https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion *) diff --git a/analysis/src/TypeUtils.ml b/analysis/src/TypeUtils.ml index 5979cef92..534ec7391 100644 --- a/analysis/src/TypeUtils.ml +++ b/analysis/src/TypeUtils.ml @@ -1,5 +1,69 @@ open SharedTypes +let debugLogTypeArgContext {env; typeArgs; typeParams} = + Printf.sprintf "Type arg context. env: %s, typeArgs: %s, typeParams: %s\n" + (Debug.debugPrintEnv env) + (typeArgs |> List.map Shared.typeToString |> String.concat ", ") + (typeParams |> List.map Shared.typeToString |> String.concat ", ") + +let rec pathFromTypeExpr (t : Types.type_expr) = + match t.desc with + | Tconstr (Pident {name = "function$"}, [t; _], _) -> pathFromTypeExpr t + | Tconstr (path, _typeArgs, _) + | Tlink {desc = Tconstr (path, _typeArgs, _)} + | Tsubst {desc = Tconstr (path, _typeArgs, _)} + | Tpoly ({desc = Tconstr (path, _typeArgs, _)}, []) -> + Some path + | _ -> None + +let printRecordFromFields ?name (fields : field list) = + (match name with + | None -> "" + | Some name -> "type " ^ name ^ " = ") + ^ "{" + ^ (fields + |> List.map (fun f -> f.fname.txt ^ ": " ^ Shared.typeToString f.typ) + |> String.concat ", ") + ^ "}" + +let rec extractedTypeToString ?(inner = false) = function + | Tuple (_, _, typ) + | Tpolyvariant {typeExpr = typ} + | Tfunction {typ} + | Trecord {definition = `TypeExpr typ} -> + if inner then + match pathFromTypeExpr typ with + | None -> "record" (* Won't happen *) + | Some p -> p |> SharedTypes.pathIdentToString + else Shared.typeToString typ + | Tbool _ -> "bool" + | Tstring _ -> "string" + | TtypeT _ -> "type t" + | Tarray (_, TypeExpr innerTyp) -> + "array<" ^ Shared.typeToString innerTyp ^ ">" + | Tarray (_, ExtractedType innerTyp) -> + "array<" ^ extractedTypeToString ~inner:true innerTyp ^ ">" + | Toption (_, TypeExpr innerTyp) -> + "option<" ^ Shared.typeToString innerTyp ^ ">" + | Tresult {okType; errorType} -> + "result<" ^ Shared.typeToString okType ^ ", " + ^ Shared.typeToString errorType + ^ ">" + | Toption (_, ExtractedType innerTyp) -> + "option<" ^ extractedTypeToString ~inner:true innerTyp ^ ">" + | Tpromise (_, innerTyp) -> "promise<" ^ Shared.typeToString innerTyp ^ ">" + | Tvariant {variantDecl; variantName} -> + if inner then variantName else Shared.declToString variantName variantDecl + | Trecord {definition = `NameOnly name; fields} -> + if inner then name else printRecordFromFields ~name fields + | TinlineRecord {fields} -> printRecordFromFields fields + | Texn _ -> "exn" + +let getExtractedType maybeRes = + match maybeRes with + | None -> None + | Some (extractedType, _) -> Some extractedType + let instantiateType ~typeParams ~typeArgs (t : Types.type_expr) = if typeParams = [] || typeArgs = [] then t else @@ -48,6 +112,59 @@ let instantiateType ~typeParams ~typeArgs (t : Types.type_expr) = in loop t +let instantiateType2 ?(typeArgContext : typeArgContext option) + (t : Types.type_expr) = + match typeArgContext with + | None | Some {typeArgs = []} | Some {typeParams = []} -> t + | Some {typeArgs; typeParams} -> + let rec applySub tp ta name = + match (tp, ta) with + | {Types.desc = Tvar (Some varName)} :: tRest1, t2 :: tRest2 -> + if varName = name then t2 else applySub tRest1 tRest2 name + | _ :: tRest1, _ :: tRest2 -> applySub tRest1 tRest2 name + | [], _ | _, [] -> t + in + + let rec loop (t : Types.type_expr) = + match t.desc with + | Tlink t -> loop t + | Tvar (Some name) -> applySub typeParams typeArgs name + | Tvar _ -> t + | Tunivar _ -> t + | Tconstr (path, args, memo) -> + {t with desc = Tconstr (path, args |> List.map loop, memo)} + | Tsubst t -> loop t + | Tvariant rd -> {t with desc = Tvariant (rowDesc rd)} + | Tnil -> t + | Tarrow (lbl, t1, t2, c) -> + {t with desc = Tarrow (lbl, loop t1, loop t2, c)} + | Ttuple tl -> {t with desc = Ttuple (tl |> List.map loop)} + | Tobject (t, r) -> {t with desc = Tobject (loop t, r)} + | Tfield (n, k, t1, t2) -> {t with desc = Tfield (n, k, loop t1, loop t2)} + | Tpoly (t, []) -> loop t + | Tpoly (t, tl) -> {t with desc = Tpoly (loop t, tl |> List.map loop)} + | Tpackage (p, l, tl) -> + {t with desc = Tpackage (p, l, tl |> List.map loop)} + and rowDesc (rd : Types.row_desc) = + let row_fields = + rd.row_fields |> List.map (fun (l, rf) -> (l, rowField rf)) + in + let row_more = loop rd.row_more in + let row_name = + match rd.row_name with + | None -> None + | Some (p, tl) -> Some (p, tl |> List.map loop) + in + {rd with row_fields; row_more; row_name} + and rowField (rf : Types.row_field) = + match rf with + | Rpresent None -> rf + | Rpresent (Some t) -> Rpresent (Some (loop t)) + | Reither (b1, tl, b2, r) -> Reither (b1, tl |> List.map loop, b2, r) + | Rabsent -> Rabsent + in + loop t + let rec extractRecordType ~env ~package (t : Types.type_expr) = match t.desc with | Tlink t1 | Tsubst t1 | Tpoly (t1, []) -> extractRecordType ~env ~package t1 @@ -109,49 +226,153 @@ let rec extractFunctionType ~env ~package typ = in loop ~env [] typ -(** Pulls out a type we can complete from a type expr. *) -let rec extractType ~env ~package (t : Types.type_expr) = +let maybeSetTypeArgCtx ?typeArgContextFromTypeManifest ~typeParams ~typeArgs env + = + match typeArgContextFromTypeManifest with + | Some typeArgContextFromTypeManifest -> Some typeArgContextFromTypeManifest + | None -> + let typeArgContext = + if List.length typeParams > 0 then Some {env; typeParams; typeArgs} + else None + in + (match typeArgContext with + | None -> () + | Some typeArgContext -> + if Debug.verbose () then + Printf.printf "[#type_arg_ctx]--> setting new type arg ctx: %s" + (debugLogTypeArgContext typeArgContext)); + typeArgContext + +(* TODO(env-stuff) Maybe this could be removed entirely if we can guarantee that we don't have to look up functions from in here. *) +let rec extractFunctionType2 ?typeArgContext ~env ~package typ = + let rec loop ?typeArgContext ~env acc (t : Types.type_expr) = + match t.desc with + | Tlink t1 | Tsubst t1 | Tpoly (t1, []) -> loop ?typeArgContext ~env acc t1 + | Tarrow (label, tArg, tRet, _) -> + loop ?typeArgContext ~env ((label, tArg) :: acc) tRet + | Tconstr (Pident {name = "function$"}, [t; _], _) -> + extractFunctionType2 ?typeArgContext ~env ~package t + | Tconstr (path, typeArgs, _) -> ( + match References.digConstructor ~env ~package path with + | Some + ( env, + { + item = {decl = {type_manifest = Some t1; type_params = typeParams}}; + } ) -> + let typeArgContext = maybeSetTypeArgCtx ~typeParams ~typeArgs env in + loop ?typeArgContext ~env acc t1 + | _ -> (List.rev acc, t, typeArgContext)) + | _ -> (List.rev acc, t, typeArgContext) + in + loop ?typeArgContext ~env [] typ + +let rec extractType ?(printOpeningDebug = true) + ?(typeArgContext : typeArgContext option) + ?(typeArgContextFromTypeManifest : typeArgContext option) ~env ~package + (t : Types.type_expr) = + let maybeSetTypeArgCtx = maybeSetTypeArgCtx ?typeArgContextFromTypeManifest in + if Debug.verbose () && printOpeningDebug then + Printf.printf + "[extract_type]--> starting extraction of type: %s, in env: %s. Has type \ + arg ctx: %b\n" + (Shared.typeToString t) (Debug.debugPrintEnv env) + (Option.is_some typeArgContext); + (match typeArgContext with + | None -> () + | Some typeArgContext -> + if Debug.verbose () && printOpeningDebug then + Printf.printf "[extract_type]--> %s" + (debugLogTypeArgContext typeArgContext)); + let instantiateType = instantiateType2 in match t.desc with - | Tlink t1 | Tsubst t1 | Tpoly (t1, []) -> extractType ~env ~package t1 + | Tlink t1 | Tsubst t1 | Tpoly (t1, []) -> + extractType ?typeArgContext ~printOpeningDebug:false ~env ~package t1 | Tconstr (Path.Pident {name = "option"}, [payloadTypeExpr], _) -> - Some (Toption (env, TypeExpr payloadTypeExpr)) + Some (Toption (env, TypeExpr payloadTypeExpr), typeArgContext) | Tconstr (Path.Pident {name = "promise"}, [payloadTypeExpr], _) -> - Some (Tpromise (env, payloadTypeExpr)) + Some (Tpromise (env, payloadTypeExpr), typeArgContext) | Tconstr (Path.Pident {name = "array"}, [payloadTypeExpr], _) -> - Some (Tarray (env, TypeExpr payloadTypeExpr)) + Some (Tarray (env, TypeExpr payloadTypeExpr), typeArgContext) | Tconstr (Path.Pident {name = "result"}, [okType; errorType], _) -> - Some (Tresult {env; okType; errorType}) - | Tconstr (Path.Pident {name = "bool"}, [], _) -> Some (Tbool env) - | Tconstr (Path.Pident {name = "string"}, [], _) -> Some (Tstring env) - | Tconstr (Path.Pident {name = "exn"}, [], _) -> Some (Texn env) + Some (Tresult {env; okType; errorType}, typeArgContext) + | Tconstr (Path.Pident {name = "bool"}, [], _) -> + Some (Tbool env, typeArgContext) + | Tconstr (Path.Pident {name = "string"}, [], _) -> + Some (Tstring env, typeArgContext) + | Tconstr (Path.Pident {name = "exn"}, [], _) -> + Some (Texn env, typeArgContext) | Tconstr (Pident {name = "function$"}, [t; _], _) -> ( - (* Uncurried functions. *) - match extractFunctionType t ~env ~package with - | args, tRet when args <> [] -> - Some (Tfunction {env; args; typ = t; uncurried = true; returnType = tRet}) - | _args, _tRet -> None) + match extractFunctionType2 ?typeArgContext t ~env ~package with + | args, tRet, typeArgContext when args <> [] -> + Some + ( Tfunction {env; args; typ = t; uncurried = true; returnType = tRet}, + typeArgContext ) + | _args, _tRet, _typeArgContext -> None) + | Tarrow _ -> ( + match extractFunctionType2 ?typeArgContext t ~env ~package with + | args, tRet, typeArgContext when args <> [] -> + Some + ( Tfunction {env; args; typ = t; uncurried = false; returnType = tRet}, + typeArgContext ) + | _args, _tRet, _typeArgContext -> None) | Tconstr (path, typeArgs, _) -> ( + if Debug.verbose () then + Printf.printf "[extract_type]--> digging for type %s in %s\n" + (Path.name path) (Debug.debugPrintEnv env); match References.digConstructor ~env ~package path with - | Some (_env, {item = {decl = {type_manifest = Some t1; type_params}}}) -> + | Some + ( envFromDeclaration, + {item = {decl = {type_manifest = Some t1; type_params}}} ) -> + if Debug.verbose () then + print_endline "[extract_type]--> found type manifest"; + + (* Type manifests inherit the last type args ctx that wasn't for a type manifest. + This is because the manifest itself doesn't have type args and an env that can + be used to instantiate. *) + let typeArgContext = + maybeSetTypeArgCtx ~typeParams:type_params ~typeArgs env + in t1 - |> instantiateType ~typeParams:type_params ~typeArgs - |> extractType ~env ~package - | Some (_env, {name; item = {decl; kind = Type.Variant constructors}}) -> + |> extractType ?typeArgContextFromTypeManifest:typeArgContext + ~env:envFromDeclaration ~package + | Some (envFromItem, {name; item = {decl; kind = Type.Variant constructors}}) + -> + if Debug.verbose () then print_endline "[extract_type]--> found variant"; + let typeArgContext = + maybeSetTypeArgCtx ~typeParams:decl.type_params ~typeArgs env + in Some - (Tvariant - { - env; - constructors; - variantName = name.txt; - variantDecl = decl; - typeArgs; - typeParams = decl.type_params; - }) - | Some (env, {item = {kind = Record fields}}) -> - Some (Trecord {env; fields; definition = `TypeExpr t}) - | Some (env, {item = {name = "t"}}) -> Some (TtypeT {env; path}) - | _ -> None) - | Ttuple expressions -> Some (Tuple (env, expressions, t)) + ( Tvariant + { + env = envFromItem; + constructors; + variantName = name.txt; + variantDecl = decl; + }, + typeArgContext ) + | Some (envFromDeclaration, {item = {kind = Record fields; decl}}) -> + if Debug.verbose () then print_endline "[extract_type]--> found record"; + (* Need to create a new type arg context here because we're sending along a type expr that might have type vars. *) + let typeArgContext = + maybeSetTypeArgCtx ~typeParams:decl.type_params ~typeArgs env + in + Some + ( Trecord {env = envFromDeclaration; fields; definition = `TypeExpr t}, + typeArgContext ) + | Some (envFromDeclaration, {item = {name = "t"; decl = {type_params}}}) -> + let typeArgContext = + maybeSetTypeArgCtx ~typeParams:type_params ~typeArgs env + in + Some (TtypeT {env = envFromDeclaration; path}, typeArgContext) + | None -> + if Debug.verbose () then + print_endline "[extract_type]--> found nothing when digging"; + None + | _ -> + if Debug.verbose () then + print_endline "[extract_type]--> found something else when digging"; + None) + | Ttuple expressions -> Some (Tuple (env, expressions, t), typeArgContext) | Tvariant {row_fields} -> let constructors = row_fields @@ -169,14 +390,45 @@ let rec extractType ~env ~package (t : Types.type_expr) = | _ -> []); }) in - Some (Tpolyvariant {env; constructors; typeExpr = t}) - | Tarrow _ -> ( - match extractFunctionType t ~env ~package with - | args, tRet when args <> [] -> - Some - (Tfunction {env; args; typ = t; uncurried = false; returnType = tRet}) - | _args, _tRet -> None) - | _ -> None + Some (Tpolyvariant {env; constructors; typeExpr = t}, typeArgContext) + | Tvar (Some varName) -> ( + if Debug.verbose () then + Printf.printf + "[extract_type]--> found type variable: '%s. Trying to instantiate %s" + varName + (match typeArgContext with + | None -> "with no type args ctx\n" + | Some typeArgContext -> + Printf.sprintf "with %s" (debugLogTypeArgContext typeArgContext)); + + let instantiated = t |> instantiateType ?typeArgContext in + let rec extractInstantiated t = + match t.Types.desc with + | Tlink t1 | Tsubst t1 | Tpoly (t1, []) -> extractInstantiated t1 + | _ -> t + in + match extractInstantiated instantiated with + | {desc = Tvar _} -> + if Debug.verbose () then + Printf.printf "[extract_type]--> could not instantiate '%s. Skipping.\n" + varName; + None + | _ -> + if Debug.verbose () then + Printf.printf + "[extract_type]--> SUCCEEDED instantiation, new type is: %s\n" + (Shared.typeToString instantiated); + + (* Use the env from instantiation if we managed to instantiate the type param *) + let nextEnv = + match typeArgContext with + | Some {env} -> env + | None -> env + in + instantiated |> extractType ?typeArgContext ~env:nextEnv ~package) + | _ -> + if Debug.verbose () then print_endline "[extract_type]--> miss"; + None let findReturnTypeOfFunctionAtLoc loc ~(env : QueryEnv.t) ~full ~debug = match References.getLocItem ~full ~pos:(loc |> Loc.end_) ~debug with @@ -219,16 +471,6 @@ let getBuiltinFromTypePath path = Some Result | _ -> None -let rec pathFromTypeExpr (t : Types.type_expr) = - match t.desc with - | Tconstr (Pident {name = "function$"}, [t; _], _) -> pathFromTypeExpr t - | Tconstr (path, _typeArgs, _) - | Tlink {desc = Tconstr (path, _typeArgs, _)} - | Tsubst {desc = Tconstr (path, _typeArgs, _)} - | Tpoly ({desc = Tconstr (path, _typeArgs, _)}, []) -> - Some path - | _ -> None - let rec digToRelevantTemplateNameType ~env ~package ?(suffix = "") (t : Types.type_expr) = match t.desc with @@ -294,104 +536,167 @@ let extractTypeFromResolvedType (typ : Type.t) ~env ~full = | Variant constructors -> Some (Tvariant - { - env; - constructors; - variantName = typ.name; - variantDecl = typ.decl; - typeParams = typ.decl.type_params; - typeArgs = []; - }) + {env; constructors; variantName = typ.name; variantDecl = typ.decl}) | Abstract _ | Open -> ( match typ.decl.type_manifest with | None -> None - | Some t -> t |> extractType ~env ~package:full.package) + | Some t -> t |> extractType ~env ~package:full.package |> getExtractedType) (** The context we just came from as we resolve the nested structure. *) type ctx = Rfield of string (** A record field of name *) -(** This moves through a nested path via a set of instructions, trying to resolve the type at the end of the path. *) -let rec resolveNested ~env ~full ~nested ?ctx (typ : completionType) = +let rec resolveNested ?typeArgContext ~env ~full ~nested ?ctx + (typ : completionType) = + let extractType = extractType ?typeArgContext in + if Debug.verbose () then + Printf.printf + "[nested]--> running nested in env: %s. Has type arg ctx: %b\n" + (Debug.debugPrintEnv env) + (Option.is_some typeArgContext); + (match typeArgContext with + | None -> () + | Some typeArgContext -> + if Debug.verbose () then + Printf.printf "[nested]--> %s" (debugLogTypeArgContext typeArgContext)); match nested with | [] -> + if Debug.verbose () then + print_endline "[nested]--> reached end of pattern, returning type"; Some ( typ, env, - match ctx with + (match ctx with | None -> None | Some (Rfield fieldName) -> - Some (Completable.CameFromRecordField fieldName) ) + Some (Completable.CameFromRecordField fieldName)), + typeArgContext ) | patternPath :: nested -> ( match (patternPath, typ) with | Completable.NTupleItem {itemNum}, Tuple (env, tupleItems, _) -> ( + if Debug.verbose () then + print_endline "[nested]--> trying to move into tuple"; match List.nth_opt tupleItems itemNum with - | None -> None + | None -> + if Debug.verbose () then + print_endline "[nested]--> tuple element not found"; + None | Some typ -> typ |> extractType ~env ~package:full.package - |> Utils.Option.flatMap (fun typ -> - typ |> resolveNested ~env ~full ~nested)) + |> Utils.Option.flatMap (fun (typ, typeArgContext) -> + typ |> resolveNested ?typeArgContext ~env ~full ~nested)) | ( NFollowRecordField {fieldName}, (TinlineRecord {env; fields} | Trecord {env; fields}) ) -> ( + if Debug.verbose () then + print_endline "[nested]--> trying to move into record field"; match fields |> List.find_opt (fun (field : field) -> field.fname.txt = fieldName) with - | None -> None + | None -> + if Debug.verbose () then + print_endline "[nested]--> did not find record field"; + None | Some {typ; optional} -> + if Debug.verbose () then + print_endline "[nested]--> found record field type"; let typ = if optional then Utils.unwrapIfOption typ else typ in + + if Debug.verbose () then + Printf.printf "[nested]--> extracting from type %s in env %s\n" + (Shared.typeToString typ) (Debug.debugPrintEnv env); typ |> extractType ~env ~package:full.package - |> Utils.Option.flatMap (fun typ -> - typ |> resolveNested ~ctx:(Rfield fieldName) ~env ~full ~nested)) + |> Utils.Option.flatMap (fun (typ, typeArgContext) -> + typ + |> resolveNested ?typeArgContext ~ctx:(Rfield fieldName) ~env + ~full ~nested)) | NRecordBody {seenFields}, Trecord {env; definition = `TypeExpr typeExpr} -> typeExpr |> extractType ~env ~package:full.package - |> Option.map (fun typ -> - (typ, env, Some (Completable.RecordField {seenFields}))) + |> Option.map (fun (typ, typeArgContext) -> + ( typ, + env, + Some (Completable.RecordField {seenFields}), + typeArgContext )) | ( NRecordBody {seenFields}, (Trecord {env; definition = `NameOnly _} as extractedType) ) -> - Some (extractedType, env, Some (Completable.RecordField {seenFields})) + Some + ( extractedType, + env, + Some (Completable.RecordField {seenFields}), + typeArgContext ) | NRecordBody {seenFields}, TinlineRecord {env; fields} -> Some ( TinlineRecord {fields; env}, env, - Some (Completable.RecordField {seenFields}) ) + Some (Completable.RecordField {seenFields}), + typeArgContext ) | ( NVariantPayload {constructorName = "Some"; itemNum = 0}, Toption (env, ExtractedType typ) ) -> + if Debug.verbose () then + print_endline "[nested]--> moving into option Some"; typ |> resolveNested ~env ~full ~nested | ( NVariantPayload {constructorName = "Some"; itemNum = 0}, Toption (env, TypeExpr typ) ) -> + if Debug.verbose () then + print_endline "[nested]--> moving into option Some"; typ |> extractType ~env ~package:full.package - |> Utils.Option.flatMap (fun t -> t |> resolveNested ~env ~full ~nested) + |> Utils.Option.flatMap (fun (t, typeArgContext) -> + t |> resolveNested ?typeArgContext ~env ~full ~nested) | NVariantPayload {constructorName = "Ok"; itemNum = 0}, Tresult {okType} -> + if Debug.verbose () then print_endline "[nested]--> moving into result Ok"; okType |> extractType ~env ~package:full.package - |> Utils.Option.flatMap (fun t -> t |> resolveNested ~env ~full ~nested) + |> Utils.Option.flatMap (fun (t, typeArgContext) -> + t |> resolveNested ?typeArgContext ~env ~full ~nested) | ( NVariantPayload {constructorName = "Error"; itemNum = 0}, Tresult {errorType} ) -> + if Debug.verbose () then + print_endline "[nested]--> moving into result Error"; errorType |> extractType ~env ~package:full.package - |> Utils.Option.flatMap (fun t -> t |> resolveNested ~env ~full ~nested) - | ( NVariantPayload {constructorName; itemNum}, - Tvariant {env; constructors; typeParams; typeArgs} ) -> ( + |> Utils.Option.flatMap (fun (t, typeArgContext) -> + t |> resolveNested ?typeArgContext ~env ~full ~nested) + | NVariantPayload {constructorName; itemNum}, Tvariant {env; constructors} + -> ( + if Debug.verbose () then + Printf.printf + "[nested]--> trying to move into variant payload $%i of constructor \ + '%s'\n" + itemNum constructorName; match constructors |> List.find_opt (fun (c : Constructor.t) -> c.cname.txt = constructorName) with | Some {args = Args args} -> ( + if Debug.verbose () then + print_endline "[nested]--> found constructor (Args type)"; match List.nth_opt args itemNum with - | None -> None + | None -> + if Debug.verbose () then + print_endline "[nested]--> did not find relevant args num"; + None | Some (typ, _) -> + if Debug.verbose () then + Printf.printf "[nested]--> found arg of type: %s\n" + (Shared.typeToString typ); + typ - |> instantiateType ~typeParams ~typeArgs |> extractType ~env ~package:full.package - |> Utils.Option.flatMap (fun typ -> - typ |> resolveNested ~env ~full ~nested)) + |> Utils.Option.flatMap (fun (typ, typeArgContext) -> + if Debug.verbose () then + Printf.printf + "[nested]--> extracted %s, continuing descent of %i items\n" + (extractedTypeToString typ) + (List.length nested); + typ |> resolveNested ?typeArgContext ~env ~full ~nested)) | Some {args = InlineRecord fields} when itemNum = 0 -> + if Debug.verbose () then + print_endline "[nested]--> found constructor (inline record)"; TinlineRecord {env; fields} |> resolveNested ~env ~full ~nested | _ -> None) | ( NPolyvariantPayload {constructorName; itemNum}, @@ -408,15 +713,15 @@ let rec resolveNested ~env ~full ~nested ?ctx (typ : completionType) = | Some typ -> typ |> extractType ~env ~package:full.package - |> Utils.Option.flatMap (fun typ -> - typ |> resolveNested ~env ~full ~nested))) + |> Utils.Option.flatMap (fun (typ, typeArgContext) -> + typ |> resolveNested ?typeArgContext ~env ~full ~nested))) | NArray, Tarray (env, ExtractedType typ) -> typ |> resolveNested ~env ~full ~nested | NArray, Tarray (env, TypeExpr typ) -> typ |> extractType ~env ~package:full.package - |> Utils.Option.flatMap (fun typ -> - typ |> resolveNested ~env ~full ~nested) + |> Utils.Option.flatMap (fun (typ, typeArgContext) -> + typ |> resolveNested ?typeArgContext ~env ~full ~nested) | _ -> None) let findTypeOfRecordField fields ~fieldName = @@ -454,9 +759,11 @@ let findTypeOfPolyvariantArg constructors ~constructorName ~payloadNum = | None -> None let rec resolveNestedPatternPath (typ : innerType) ~env ~full ~nested = + if Debug.verbose () then print_endline "[nested_pattern_path]"; let t = match typ with - | TypeExpr t -> t |> extractType ~env ~package:full.package + | TypeExpr t -> + t |> extractType ~env ~package:full.package |> getExtractedType | ExtractedType t -> Some t in match nested with @@ -514,6 +821,7 @@ let rec resolveNestedPatternPath (typ : innerType) ~env ~full ~nested = | Some typ -> typ |> extractType ~env ~package:full.package + |> getExtractedType |> Utils.Option.flatMap (fun typ -> ExtractedType typ |> resolveNestedPatternPath ~env ~full ~nested)) @@ -523,6 +831,7 @@ let rec resolveNestedPatternPath (typ : innerType) ~env ~full ~nested = | Some typ -> typ |> extractType ~env ~package:full.package + |> getExtractedType |> Utils.Option.flatMap (fun typ -> ExtractedType typ |> resolveNestedPatternPath ~env ~full ~nested)) @@ -610,49 +919,6 @@ let rec contextPathFromCoreType (coreType : Parsetree.core_type) = Some (CPId (lid.txt |> Utils.flattenLongIdent, Type)) | _ -> None -let printRecordFromFields ?name (fields : field list) = - (match name with - | None -> "" - | Some name -> "type " ^ name ^ " = ") - ^ "{" - ^ (fields - |> List.map (fun f -> f.fname.txt ^ ": " ^ Shared.typeToString f.typ) - |> String.concat ", ") - ^ "}" - -let rec extractedTypeToString ?(inner = false) = function - | Tuple (_, _, typ) - | Tpolyvariant {typeExpr = typ} - | Tfunction {typ} - | Trecord {definition = `TypeExpr typ} -> - if inner then - match pathFromTypeExpr typ with - | None -> "record" (* Won't happen *) - | Some p -> p |> SharedTypes.pathIdentToString - else Shared.typeToString typ - | Tbool _ -> "bool" - | Tstring _ -> "string" - | TtypeT _ -> "type t" - | Tarray (_, TypeExpr innerTyp) -> - "array<" ^ Shared.typeToString innerTyp ^ ">" - | Tarray (_, ExtractedType innerTyp) -> - "array<" ^ extractedTypeToString ~inner:true innerTyp ^ ">" - | Toption (_, TypeExpr innerTyp) -> - "option<" ^ Shared.typeToString innerTyp ^ ">" - | Tresult {okType; errorType} -> - "result<" ^ Shared.typeToString okType ^ ", " - ^ Shared.typeToString errorType - ^ ">" - | Toption (_, ExtractedType innerTyp) -> - "option<" ^ extractedTypeToString ~inner:true innerTyp ^ ">" - | Tpromise (_, innerTyp) -> "promise<" ^ Shared.typeToString innerTyp ^ ">" - | Tvariant {variantDecl; variantName} -> - if inner then variantName else Shared.declToString variantName variantDecl - | Trecord {definition = `NameOnly name; fields} -> - if inner then name else printRecordFromFields ~name fields - | TinlineRecord {fields} -> printRecordFromFields fields - | Texn _ -> "exn" - let unwrapCompletionTypeIfOption (t : SharedTypes.completionType) = match t with | Toption (_, ExtractedType unwrapped) -> unwrapped @@ -699,7 +965,8 @@ module Codegen = struct let extractedType = match innerType with | ExtractedType t -> Some t - | TypeExpr t -> extractType t ~env ~package:full.package + | TypeExpr t -> + extractType t ~env ~package:full.package |> getExtractedType in let expandedBranches = match extractedType with @@ -718,9 +985,11 @@ module Codegen = struct |> List.map (fun (pat : Parsetree.pattern) -> mkConstructPat ~payload:pat "Some"))) | Tresult {okType; errorType} -> - let extractedOkType = okType |> extractType ~env ~package:full.package in + let extractedOkType = + okType |> extractType ~env ~package:full.package |> getExtractedType + in let extractedErrorType = - errorType |> extractType ~env ~package:full.package + errorType |> extractType ~env ~package:full.package |> getExtractedType in let expandedOkBranches = match extractedOkType with diff --git a/analysis/src/Xform.ml b/analysis/src/Xform.ml index 4eb3e667c..3a27a60b5 100644 --- a/analysis/src/Xform.ml +++ b/analysis/src/Xform.ml @@ -297,7 +297,9 @@ module ExhaustiveSwitch = struct let extractedType = match typ with | ExtractedType t -> Some t - | TypeExpr t -> TypeUtils.extractType t ~env ~package:full.package + | TypeExpr t -> + TypeUtils.extractType t ~env ~package:full.package + |> TypeUtils.getExtractedType in extractedType | None -> None) @@ -326,13 +328,13 @@ module ExhaustiveSwitch = struct let expEndPos = Pos.ofLexing exp.pexp_loc.loc_end in (if expStartPos = startPos then - match !foundSelection with - | None, endExpr -> foundSelection := (Some exp, endExpr) - | _ -> ()); + match !foundSelection with + | None, endExpr -> foundSelection := (Some exp, endExpr) + | _ -> ()); (if expEndPos = endPos then - match !foundSelection with - | startExp, _ -> foundSelection := (startExp, Some exp)); + match !foundSelection with + | startExp, _ -> foundSelection := (startExp, Some exp)); Ast_iterator.default_iterator.expr iterator exp in @@ -668,7 +670,7 @@ let extractCodeActions ~path ~startPos ~endPos ~currentFile ~debug = ExhaustiveSwitch.xform ~printExpr ~path ~pos: (if startPos = endPos then Single startPos - else Range (startPos, endPos)) + else Range (startPos, endPos)) ~full ~structure ~codeActions ~debug ~currentFile | None -> () in diff --git a/analysis/tests/src/EnvCompletion.res b/analysis/tests/src/EnvCompletion.res new file mode 100644 index 000000000..1bddb38e1 --- /dev/null +++ b/analysis/tests/src/EnvCompletion.res @@ -0,0 +1,63 @@ +type things = One | Two +type things2 = Four | Five + +let res: EnvCompletionOtherFile.someResult = Okay(One) + +let use = (): EnvCompletionOtherFile.response => { + stuff: First, + res: Failure(""), +} + +// switch res { | } +// ^com + +// switch res { | Okay() } +// ^com + +// switch res { | Failure() } +// ^com + +// switch use() { | } +// ^com + +// switch use() { | {} } +// ^com + +// switch use() { | {stuff: } } +// ^com + +// switch use() { | {stuff: Second() } } +// ^com + +// switch use() { | {stuff: Second({}) } } +// ^com + +// switch use() { | {res: } } +// ^com + +// switch use() { | {res: Okay() } } +// ^com + +// switch use() { | {res: Okay(Second()) } } +// ^com + +// switch use() { | {res: Okay(Second({})) } } +// ^com + +let res2: EnvCompletionOtherFile.someRecord = { + name: "string", + theThing: Four, + theVariant: First, +} + +// switch res2 { | } +// ^com + +// switch res2 { | {} } +// ^com + +// switch res2 { | {theThing: } } +// ^com + +// switch res2 { | {theVariant: } } +// ^com diff --git a/analysis/tests/src/EnvCompletionOtherFile.res b/analysis/tests/src/EnvCompletionOtherFile.res new file mode 100644 index 000000000..1218b0010 --- /dev/null +++ b/analysis/tests/src/EnvCompletionOtherFile.res @@ -0,0 +1,13 @@ +type someResult<'a, 'b> = Okay('a) | Failure('b) + +type r1 = {age: int} + +type theVariant = First | Second(r1) + +type someRecord<'thing> = { + name: string, + theThing: 'thing, + theVariant: theVariant, +} + +type response = {stuff: theVariant, res: someResult} diff --git a/analysis/tests/src/QueryFile.res b/analysis/tests/src/QueryFile.res new file mode 100644 index 000000000..0ba6f3d1d --- /dev/null +++ b/analysis/tests/src/QueryFile.res @@ -0,0 +1,6 @@ +module Types = { + type byAddress = SchemaAssets.input_ByAddress + type location = SchemaAssets.input_Location + + type variables = {location: location} +} diff --git a/analysis/tests/src/Reprod.res b/analysis/tests/src/Reprod.res new file mode 100644 index 000000000..e617686fc --- /dev/null +++ b/analysis/tests/src/Reprod.res @@ -0,0 +1,56 @@ +module Query = { + let use = (~variables: QueryFile.Types.variables) => { + ignore(variables) + "" + } +} + +// let x = Query.use(~variables={location: ByAddress()}) +// ^com + +type nestedRecord = {nested: bool} + +type rec someRecord = { + first: int, + second: (bool, option), + optThird: option<[#first | #second(someRecord)]>, + nest: nestedRecord, +} + +type somePolyVariant = [#one | #two(bool) | #three(someRecord, bool)] + +type someVariant = One | Two(bool) | Three(someRecord, bool) + +type paramRecord<'a, 'b> = { + first: 'a, + second: 'b, +} + +let record: paramRecord = { + first: One, + second: {city: "city"}, +} + +// switch record { | {first: }} +// ^com + +// switch record { | {second: }} +// ^com + +// TODO: Functions, aliases/definitions, records, variants, polyvariants, tuples + +let res: result = Ok(One) + +// switch res { | Ok() } +// ^com + +// switch res { | Error() } +// ^com + +let resOpt: result, unit> = Ok(None) + +// switch resOpt { | Ok() } +// ^com + +// switch resOpt { | Ok(Some()) } +// ^com diff --git a/analysis/tests/src/SchemaAssets.res b/analysis/tests/src/SchemaAssets.res new file mode 100644 index 000000000..b83ff5c0d --- /dev/null +++ b/analysis/tests/src/SchemaAssets.res @@ -0,0 +1,6 @@ +@live +type rec input_ByAddress = {city: string} +@tag("__$inputUnion") +and input_Location = + | @as("byAddress") ByAddress(input_ByAddress) + | @as("byId") ById(string) diff --git a/analysis/tests/src/expected/EnvCompletion.res.txt b/analysis/tests/src/expected/EnvCompletion.res.txt new file mode 100644 index 000000000..f512374c2 --- /dev/null +++ b/analysis/tests/src/expected/EnvCompletion.res.txt @@ -0,0 +1,380 @@ +Complete src/EnvCompletion.res 10:17 +XXX Not found! +Completable: Cpattern Value[res] +Package opens Pervasives.JsxModules.place holder +Resolved opens 1 pervasives +ContextPath Value[res] +Path res +[{ + "label": "Okay(_)", + "kind": 4, + "tags": [], + "detail": "Okay('a)\n\ntype someResult<'a, 'b> = Okay('a) | Failure('b)", + "documentation": null, + "insertText": "Okay(${1:_})", + "insertTextFormat": 2 + }, { + "label": "Failure(_)", + "kind": 4, + "tags": [], + "detail": "Failure('b)\n\ntype someResult<'a, 'b> = Okay('a) | Failure('b)", + "documentation": null, + "insertText": "Failure(${1:_})", + "insertTextFormat": 2 + }] + +Complete src/EnvCompletion.res 13:23 +posCursor:[13:23] posNoWhite:[13:22] Found pattern:[13:18->13:24] +Ppat_construct Okay:[13:18->13:22] +posCursor:[13:23] posNoWhite:[13:22] Found pattern:[13:22->13:24] +Ppat_construct ():[13:22->13:24] +Completable: Cpattern Value[res]->variantPayload::Okay($0) +Package opens Pervasives.JsxModules.place holder +Resolved opens 1 pervasives +ContextPath Value[res] +Path res +[{ + "label": "One", + "kind": 4, + "tags": [], + "detail": "One\n\ntype things = One | Two", + "documentation": null, + "insertText": "One", + "insertTextFormat": 2 + }, { + "label": "Two", + "kind": 4, + "tags": [], + "detail": "Two\n\ntype things = One | Two", + "documentation": null, + "insertText": "Two", + "insertTextFormat": 2 + }] + +Complete src/EnvCompletion.res 16:26 +posCursor:[16:26] posNoWhite:[16:25] Found pattern:[16:18->16:27] +Ppat_construct Failure:[16:18->16:25] +posCursor:[16:26] posNoWhite:[16:25] Found pattern:[16:25->16:27] +Ppat_construct ():[16:25->16:27] +Completable: Cpattern Value[res]->variantPayload::Failure($0) +Package opens Pervasives.JsxModules.place holder +Resolved opens 1 pervasives +ContextPath Value[res] +Path res +[{ + "label": "\"\"", + "kind": 12, + "tags": [], + "detail": "string", + "documentation": null, + "sortText": "A", + "insertText": "\"$0\"", + "insertTextFormat": 2 + }] + +Complete src/EnvCompletion.res 19:19 +XXX Not found! +Completable: Cpattern Value[use](Nolabel) +Package opens Pervasives.JsxModules.place holder +Resolved opens 1 pervasives +ContextPath Value[use](Nolabel) +ContextPath Value[use] +Path use +[{ + "label": "{}", + "kind": 22, + "tags": [], + "detail": "EnvCompletionOtherFile.response", + "documentation": null, + "sortText": "A", + "insertText": "{$0}", + "insertTextFormat": 2 + }] + +Complete src/EnvCompletion.res 22:21 +posCursor:[22:21] posNoWhite:[22:20] Found pattern:[22:20->22:22] +Completable: Cpattern Value[use](Nolabel)->recordBody +Package opens Pervasives.JsxModules.place holder +Resolved opens 1 pervasives +ContextPath Value[use](Nolabel) +ContextPath Value[use] +Path use +[{ + "label": "stuff", + "kind": 5, + "tags": [], + "detail": "stuff: theVariant\n\nEnvCompletionOtherFile.response", + "documentation": null + }, { + "label": "res", + "kind": 5, + "tags": [], + "detail": "res: someResult\n\nEnvCompletionOtherFile.response", + "documentation": null + }] + +Complete src/EnvCompletion.res 25:27 +posCursor:[25:27] posNoWhite:[25:26] Found pattern:[25:20->25:31] +Completable: Cpattern Value[use](Nolabel)->recordField(stuff) +Package opens Pervasives.JsxModules.place holder +Resolved opens 1 pervasives +ContextPath Value[use](Nolabel) +ContextPath Value[use] +Path use +[{ + "label": "First", + "kind": 4, + "tags": [], + "detail": "First\n\ntype theVariant = First | Second(r1)", + "documentation": null, + "insertText": "First", + "insertTextFormat": 2 + }, { + "label": "Second(_)", + "kind": 4, + "tags": [], + "detail": "Second(r1)\n\ntype theVariant = First | Second(r1)", + "documentation": null, + "insertText": "Second(${1:_})", + "insertTextFormat": 2 + }] + +Complete src/EnvCompletion.res 28:35 +posCursor:[28:35] posNoWhite:[28:34] Found pattern:[28:20->28:38] +posCursor:[28:35] posNoWhite:[28:34] Found pattern:[28:28->28:36] +Ppat_construct Second:[28:28->28:34] +posCursor:[28:35] posNoWhite:[28:34] Found pattern:[28:34->28:36] +Ppat_construct ():[28:34->28:36] +Completable: Cpattern Value[use](Nolabel)->recordField(stuff), variantPayload::Second($0) +Package opens Pervasives.JsxModules.place holder +Resolved opens 1 pervasives +ContextPath Value[use](Nolabel) +ContextPath Value[use] +Path use +[{ + "label": "{}", + "kind": 22, + "tags": [], + "detail": "r1", + "documentation": null, + "sortText": "A", + "insertText": "{$0}", + "insertTextFormat": 2 + }] + +Complete src/EnvCompletion.res 31:36 +posCursor:[31:36] posNoWhite:[31:35] Found pattern:[31:20->31:40] +posCursor:[31:36] posNoWhite:[31:35] Found pattern:[31:28->31:38] +Ppat_construct Second:[31:28->31:34] +posCursor:[31:36] posNoWhite:[31:35] Found pattern:[31:35->31:37] +Completable: Cpattern Value[use](Nolabel)->recordField(stuff), variantPayload::Second($0), recordBody +Package opens Pervasives.JsxModules.place holder +Resolved opens 1 pervasives +ContextPath Value[use](Nolabel) +ContextPath Value[use] +Path use +[{ + "label": "age", + "kind": 5, + "tags": [], + "detail": "age: int\n\nr1", + "documentation": null + }] + +Complete src/EnvCompletion.res 34:25 +posCursor:[34:25] posNoWhite:[34:24] Found pattern:[34:20->34:29] +Completable: Cpattern Value[use](Nolabel)->recordField(res) +Package opens Pervasives.JsxModules.place holder +Resolved opens 1 pervasives +ContextPath Value[use](Nolabel) +ContextPath Value[use] +Path use +[{ + "label": "Okay(_)", + "kind": 4, + "tags": [], + "detail": "Okay('a)\n\ntype someResult<'a, 'b> = Okay('a) | Failure('b)", + "documentation": null, + "insertText": "Okay(${1:_})", + "insertTextFormat": 2 + }, { + "label": "Failure(_)", + "kind": 4, + "tags": [], + "detail": "Failure('b)\n\ntype someResult<'a, 'b> = Okay('a) | Failure('b)", + "documentation": null, + "insertText": "Failure(${1:_})", + "insertTextFormat": 2 + }] + +Complete src/EnvCompletion.res 37:31 +posCursor:[37:31] posNoWhite:[37:30] Found pattern:[37:20->37:34] +posCursor:[37:31] posNoWhite:[37:30] Found pattern:[37:26->37:32] +Ppat_construct Okay:[37:26->37:30] +posCursor:[37:31] posNoWhite:[37:30] Found pattern:[37:30->37:32] +Ppat_construct ():[37:30->37:32] +Completable: Cpattern Value[use](Nolabel)->recordField(res), variantPayload::Okay($0) +Package opens Pervasives.JsxModules.place holder +Resolved opens 1 pervasives +ContextPath Value[use](Nolabel) +ContextPath Value[use] +Path use +[{ + "label": "First", + "kind": 4, + "tags": [], + "detail": "First\n\ntype theVariant = First | Second(r1)", + "documentation": null, + "insertText": "First", + "insertTextFormat": 2 + }, { + "label": "Second(_)", + "kind": 4, + "tags": [], + "detail": "Second(r1)\n\ntype theVariant = First | Second(r1)", + "documentation": null, + "insertText": "Second(${1:_})", + "insertTextFormat": 2 + }] + +Complete src/EnvCompletion.res 40:38 +posCursor:[40:38] posNoWhite:[40:37] Found pattern:[40:20->40:42] +posCursor:[40:38] posNoWhite:[40:37] Found pattern:[40:26->40:40] +Ppat_construct Okay:[40:26->40:30] +posCursor:[40:38] posNoWhite:[40:37] Found pattern:[40:31->40:39] +Ppat_construct Second:[40:31->40:37] +posCursor:[40:38] posNoWhite:[40:37] Found pattern:[40:37->40:39] +Ppat_construct ():[40:37->40:39] +Completable: Cpattern Value[use](Nolabel)->recordField(res), variantPayload::Okay($0), variantPayload::Second($0) +Package opens Pervasives.JsxModules.place holder +Resolved opens 1 pervasives +ContextPath Value[use](Nolabel) +ContextPath Value[use] +Path use +[{ + "label": "{}", + "kind": 22, + "tags": [], + "detail": "r1", + "documentation": null, + "sortText": "A", + "insertText": "{$0}", + "insertTextFormat": 2 + }] + +Complete src/EnvCompletion.res 43:39 +posCursor:[43:39] posNoWhite:[43:38] Found pattern:[43:20->43:44] +posCursor:[43:39] posNoWhite:[43:38] Found pattern:[43:26->43:42] +Ppat_construct Okay:[43:26->43:30] +posCursor:[43:39] posNoWhite:[43:38] Found pattern:[43:31->43:41] +Ppat_construct Second:[43:31->43:37] +posCursor:[43:39] posNoWhite:[43:38] Found pattern:[43:38->43:40] +Completable: Cpattern Value[use](Nolabel)->recordField(res), variantPayload::Okay($0), variantPayload::Second($0), recordBody +Package opens Pervasives.JsxModules.place holder +Resolved opens 1 pervasives +ContextPath Value[use](Nolabel) +ContextPath Value[use] +Path use +[{ + "label": "age", + "kind": 5, + "tags": [], + "detail": "age: int\n\nr1", + "documentation": null + }] + +Complete src/EnvCompletion.res 52:18 +XXX Not found! +Completable: Cpattern Value[res2] +Package opens Pervasives.JsxModules.place holder +Resolved opens 1 pervasives +ContextPath Value[res2] +Path res2 +[{ + "label": "{}", + "kind": 22, + "tags": [], + "detail": "EnvCompletionOtherFile.someRecord", + "documentation": null, + "sortText": "A", + "insertText": "{$0}", + "insertTextFormat": 2 + }] + +Complete src/EnvCompletion.res 55:20 +posCursor:[55:20] posNoWhite:[55:19] Found pattern:[55:19->55:21] +Completable: Cpattern Value[res2]->recordBody +Package opens Pervasives.JsxModules.place holder +Resolved opens 1 pervasives +ContextPath Value[res2] +Path res2 +[{ + "label": "name", + "kind": 5, + "tags": [], + "detail": "name: string\n\nEnvCompletionOtherFile.someRecord", + "documentation": null + }, { + "label": "theThing", + "kind": 5, + "tags": [], + "detail": "theThing: 'thing\n\nEnvCompletionOtherFile.someRecord", + "documentation": null + }, { + "label": "theVariant", + "kind": 5, + "tags": [], + "detail": "theVariant: theVariant\n\nEnvCompletionOtherFile.someRecord", + "documentation": null + }] + +Complete src/EnvCompletion.res 58:29 +posCursor:[58:29] posNoWhite:[58:28] Found pattern:[58:19->58:33] +Completable: Cpattern Value[res2]->recordField(theThing) +Package opens Pervasives.JsxModules.place holder +Resolved opens 1 pervasives +ContextPath Value[res2] +Path res2 +[{ + "label": "Four", + "kind": 4, + "tags": [], + "detail": "Four\n\ntype things2 = Four | Five", + "documentation": null, + "insertText": "Four", + "insertTextFormat": 2 + }, { + "label": "Five", + "kind": 4, + "tags": [], + "detail": "Five\n\ntype things2 = Four | Five", + "documentation": null, + "insertText": "Five", + "insertTextFormat": 2 + }] + +Complete src/EnvCompletion.res 61:31 +posCursor:[61:31] posNoWhite:[61:30] Found pattern:[61:19->61:35] +Completable: Cpattern Value[res2]->recordField(theVariant) +Package opens Pervasives.JsxModules.place holder +Resolved opens 1 pervasives +ContextPath Value[res2] +Path res2 +[{ + "label": "First", + "kind": 4, + "tags": [], + "detail": "First\n\ntype theVariant = First | Second(r1)", + "documentation": null, + "insertText": "First", + "insertTextFormat": 2 + }, { + "label": "Second(_)", + "kind": 4, + "tags": [], + "detail": "Second(r1)\n\ntype theVariant = First | Second(r1)", + "documentation": null, + "insertText": "Second(${1:_})", + "insertTextFormat": 2 + }] + diff --git a/analysis/tests/src/expected/EnvCompletionOtherFile.res.txt b/analysis/tests/src/expected/EnvCompletionOtherFile.res.txt new file mode 100644 index 000000000..e69de29bb diff --git a/analysis/tests/src/expected/QueryFile.res.txt b/analysis/tests/src/expected/QueryFile.res.txt new file mode 100644 index 000000000..e69de29bb diff --git a/analysis/tests/src/expected/Reprod.res.txt b/analysis/tests/src/expected/Reprod.res.txt new file mode 100644 index 000000000..fff7807f8 --- /dev/null +++ b/analysis/tests/src/expected/Reprod.res.txt @@ -0,0 +1,231 @@ +Complete src/Reprod.res 7:53 +posCursor:[7:53] posNoWhite:[7:52] Found expr:[7:11->7:56] +Pexp_apply ...[7:11->7:20] (~variables7:22->7:31=...[7:32->7:55]) +Completable: Cexpression CArgument Value[Query, use](~variables)->recordField(location), variantPayload::ByAddress($0) +Package opens Pervasives.JsxModules.place holder +Resolved opens 1 pervasives +ContextPath CArgument Value[Query, use](~variables) +ContextPath Value[Query, use] +Path Query.use +[{ + "label": "{}", + "kind": 12, + "tags": [], + "detail": "input_ByAddress", + "documentation": null, + "sortText": "A", + "insertText": "{$0}", + "insertTextFormat": 2 + }] + +Complete src/Reprod.res 33:28 +posCursor:[33:28] posNoWhite:[33:27] Found pattern:[33:21->33:31] +Completable: Cpattern Value[record]->recordField(first) +Package opens Pervasives.JsxModules.place holder +Resolved opens 1 pervasives +ContextPath Value[record] +Path record +[{ + "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 + }] + +Complete src/Reprod.res 36:29 +posCursor:[36:29] posNoWhite:[36:28] Found pattern:[36:21->36:32] +Completable: Cpattern Value[record]->recordField(second) +Package opens Pervasives.JsxModules.place holder +Resolved opens 1 pervasives +ContextPath Value[record] +Path record +[{ + "label": "{}", + "kind": 22, + "tags": [], + "detail": "SchemaAssets.input_ByAddress", + "documentation": null, + "sortText": "A", + "insertText": "{$0}", + "insertTextFormat": 2 + }] + +Complete src/Reprod.res 43:21 +posCursor:[43:21] posNoWhite:[43:20] Found pattern:[43:18->43:22] +Ppat_construct Ok:[43:18->43:20] +posCursor:[43:21] posNoWhite:[43:20] Found pattern:[43:20->43:22] +Ppat_construct ():[43:20->43:22] +Completable: Cpattern Value[res]->variantPayload::Ok($0) +Package opens Pervasives.JsxModules.place holder +Resolved opens 1 pervasives +ContextPath Value[res] +Path res +[{ + "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 + }] + +Complete src/Reprod.res 46:24 +posCursor:[46:24] posNoWhite:[46:23] Found pattern:[46:18->46:25] +Ppat_construct Error:[46:18->46:23] +posCursor:[46:24] posNoWhite:[46:23] Found pattern:[46:23->46:25] +Ppat_construct ():[46:23->46:25] +Completable: Cpattern Value[res]->variantPayload::Error($0) +Package opens Pervasives.JsxModules.place holder +Resolved opens 1 pervasives +ContextPath Value[res] +Path res +[{ + "label": "#one", + "kind": 4, + "tags": [], + "detail": "#one\n\n[#one | #three(someRecord, bool) | #two(bool)]", + "documentation": null, + "insertText": "#one", + "insertTextFormat": 2 + }, { + "label": "#three(_, _)", + "kind": 4, + "tags": [], + "detail": "#three(someRecord, bool)\n\n[#one | #three(someRecord, bool) | #two(bool)]", + "documentation": null, + "insertText": "#three(${1:_}, ${2:_})", + "insertTextFormat": 2 + }, { + "label": "#two(_)", + "kind": 4, + "tags": [], + "detail": "#two(bool)\n\n[#one | #three(someRecord, bool) | #two(bool)]", + "documentation": null, + "insertText": "#two(${1:_})", + "insertTextFormat": 2 + }] + +Complete src/Reprod.res 51:24 +posCursor:[51:24] posNoWhite:[51:23] Found pattern:[51:21->51:25] +Ppat_construct Ok:[51:21->51:23] +posCursor:[51:24] posNoWhite:[51:23] Found pattern:[51:23->51:25] +Ppat_construct ():[51:23->51:25] +Completable: Cpattern Value[resOpt]->variantPayload::Ok($0) +Package opens Pervasives.JsxModules.place holder +Resolved opens 1 pervasives +ContextPath Value[resOpt] +Path resOpt +[{ + "label": "None", + "kind": 12, + "tags": [], + "detail": "someVariant", + "documentation": null + }, { + "label": "Some(_)", + "kind": 12, + "tags": [], + "detail": "someVariant", + "documentation": null, + "insertText": "Some(${1:_})", + "insertTextFormat": 2 + }, { + "label": "Some(One)", + "kind": 4, + "tags": [], + "detail": "One\n\ntype someVariant = One | Two(bool) | Three(someRecord, bool)", + "documentation": null, + "insertText": "Some(One)", + "insertTextFormat": 2 + }, { + "label": "Some(Two(_))", + "kind": 4, + "tags": [], + "detail": "Two(bool)\n\ntype someVariant = One | Two(bool) | Three(someRecord, bool)", + "documentation": null, + "insertText": "Some(Two(${1:_}))", + "insertTextFormat": 2 + }, { + "label": "Some(Three(_, _))", + "kind": 4, + "tags": [], + "detail": "Three(someRecord, bool)\n\ntype someVariant = One | Two(bool) | Three(someRecord, bool)", + "documentation": null, + "insertText": "Some(Three(${1:_}, ${2:_}))", + "insertTextFormat": 2 + }] + +Complete src/Reprod.res 54:29 +posCursor:[54:29] posNoWhite:[54:28] Found pattern:[54:21->54:31] +Ppat_construct Ok:[54:21->54:23] +posCursor:[54:29] posNoWhite:[54:28] Found pattern:[54:24->54:30] +Ppat_construct Some:[54:24->54:28] +posCursor:[54:29] posNoWhite:[54:28] Found pattern:[54:28->54:30] +Ppat_construct ():[54:28->54:30] +Completable: Cpattern Value[resOpt]->variantPayload::Ok($0), variantPayload::Some($0) +Package opens Pervasives.JsxModules.place holder +Resolved opens 1 pervasives +ContextPath Value[resOpt] +Path resOpt +[{ + "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 + }] + diff --git a/analysis/tests/src/expected/SchemaAssets.res.txt b/analysis/tests/src/expected/SchemaAssets.res.txt new file mode 100644 index 000000000..e69de29bb