Skip to content

Commit 44c80c3

Browse files
committed
complete creator/maker functions for type t that cannot be resolved further
1 parent 2d1742a commit 44c80c3

File tree

6 files changed

+243
-60
lines changed

6 files changed

+243
-60
lines changed

analysis/src/CompletionBackEnd.ml

Lines changed: 97 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -510,7 +510,7 @@ let getComplementaryCompletionsForTypedValue ~opens ~allFiles ~scope ~env prefix
510510
in
511511
localCompletionsWithOpens @ fileModules
512512

513-
let getCompletionsForPath ~debug ~package ~opens ~full ~pos ~exact ~scope
513+
let getCompletionsForPath ~debug ~opens ~full ~pos ~exact ~scope
514514
~completionContext ~env path =
515515
if debug then Printf.printf "Path %s\n" (path |> String.concat ".");
516516
let allFiles = allFilesInPackage full.package in
@@ -537,13 +537,33 @@ let getCompletionsForPath ~debug ~package ~opens ~full ~pos ~exact ~scope
537537
localCompletionsWithOpens @ fileModules
538538
| moduleName :: path -> (
539539
Log.log ("Path " ^ pathToString path);
540-
match getEnvWithOpens ~scope ~env ~package ~opens ~moduleName path with
540+
match
541+
getEnvWithOpens ~scope ~env ~package:full.package ~opens ~moduleName path
542+
with
541543
| Some (env, prefix) ->
542544
Log.log "Got the env";
543545
let namesUsed = Hashtbl.create 10 in
544546
findAllCompletions ~env ~prefix ~exact ~namesUsed ~completionContext
545547
| None -> [])
546548

549+
let rec digToRecordFieldsForCompletion ~debug ~package ~opens ~full ~pos ~env
550+
~scope path =
551+
match
552+
path
553+
|> getCompletionsForPath ~debug ~completionContext:Type ~exact:true ~opens
554+
~full ~pos ~env ~scope
555+
with
556+
| {kind = Type {kind = Abstract (Some (p, _))}} :: _ ->
557+
(* This case happens when what we're looking for is a type alias.
558+
This is the case in newer rescript-react versions where
559+
ReactDOM.domProps is an alias for JsxEvent.t. *)
560+
let pathRev = p |> Utils.expandPath in
561+
pathRev |> List.rev
562+
|> digToRecordFieldsForCompletion ~debug ~package ~opens ~full ~pos ~env
563+
~scope
564+
| {kind = Type {kind = Record fields}} :: _ -> Some fields
565+
| _ -> None
566+
547567
let mkItem ~name ~kind ~detail ~deprecated ~docstring =
548568
let docContent =
549569
(match deprecated with
@@ -567,7 +587,7 @@ let mkItem ~name ~kind ~detail ~deprecated ~docstring =
567587
detail;
568588
documentation =
569589
(if docContent = "" then None
570-
else Some {kind = "markdown"; value = docContent});
590+
else Some {kind = "markdown"; value = docContent});
571591
sortText = None;
572592
insertText = None;
573593
insertTextFormat = None;
@@ -747,8 +767,8 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact
747767
| _ -> [])
748768
| CPId (path, completionContext) ->
749769
path
750-
|> getCompletionsForPath ~debug ~package ~opens ~full ~pos ~exact
751-
~completionContext ~env ~scope
770+
|> getCompletionsForPath ~debug ~opens ~full ~pos ~exact ~completionContext
771+
~env ~scope
752772
| CPApply (cp, labels) -> (
753773
match
754774
cp
@@ -793,7 +813,7 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact
793813
| CPField (CPId (path, Module), fieldName) ->
794814
(* M.field *)
795815
path @ [fieldName]
796-
|> getCompletionsForPath ~debug ~package ~opens ~full ~pos ~exact
816+
|> getCompletionsForPath ~debug ~opens ~full ~pos ~exact
797817
~completionContext:Field ~env ~scope
798818
| CPField (cp, fieldName) -> (
799819
let completionsForCtxPath =
@@ -911,52 +931,18 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact
911931
| Tconstr (path, _typeArgs, _)
912932
| Tlink {desc = Tconstr (path, _typeArgs, _)}
913933
| Tsubst {desc = Tconstr (path, _typeArgs, _)}
914-
| Tpoly ({desc = Tconstr (path, _typeArgs, _)}, []) -> (
934+
| Tpoly ({desc = Tconstr (path, _typeArgs, _)}, []) ->
915935
if debug then Printf.printf "CPPipe type path:%s\n" (Path.name path);
916-
match Utils.expandPath path with
917-
| _ :: pathRev ->
918-
(* type path is relative to the completion environment
919-
express it from the root of the file *)
920-
let found, pathFromEnv =
921-
QueryEnv.pathFromEnv envFromCompletionItem (List.rev pathRev)
922-
in
923-
if debug then
924-
Printf.printf "CPPipe pathFromEnv:%s found:%b\n"
925-
(pathFromEnv |> String.concat ".")
926-
found;
927-
if pathFromEnv = [] then None
928-
else if
929-
env.file.moduleName <> envFromCompletionItem.file.moduleName
930-
&& found
931-
(* If the module names are different, then one needs to qualify the path.
932-
But only if the path belongs to the env from completion *)
933-
then Some (envFromCompletionItem.file.moduleName :: pathFromEnv)
934-
else Some pathFromEnv
935-
| _ -> None)
936+
TypeUtils.getPathRelativeToEnv ~debug ~env
937+
~envFromItem:envFromCompletionItem (Utils.expandPath path)
936938
| _ -> None)
937939
in
938940
match completionPath with
939941
| Some completionPath -> (
940-
let rec removeRawOpen rawOpen modulePath =
941-
match (rawOpen, modulePath) with
942-
| [_], _ -> Some modulePath
943-
| s :: inner, first :: restPath when s = first ->
944-
removeRawOpen inner restPath
945-
| _ -> None
946-
in
947-
let rec removeRawOpens rawOpens modulePath =
948-
match rawOpens with
949-
| rawOpen :: restOpens -> (
950-
let newModulePath = removeRawOpens restOpens modulePath in
951-
match removeRawOpen rawOpen newModulePath with
952-
| None -> newModulePath
953-
| Some mp -> mp)
954-
| [] -> modulePath
955-
in
956942
let completionPathMinusOpens =
957-
completionPath |> Utils.flattenAnyNamespaceInPath
958-
|> removeRawOpens package.opens
959-
|> removeRawOpens rawOpens |> String.concat "."
943+
TypeUtils.removeOpensFromCompletionPath ~rawOpens ~package
944+
completionPath
945+
|> String.concat "."
960946
in
961947
let completionName name =
962948
if completionPathMinusOpens = "" then name
@@ -965,7 +951,7 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact
965951
let completions =
966952
completionPath @ [funNamePrefix]
967953
|> getCompletionsForPath ~debug ~completionContext:Value ~exact:false
968-
~package ~opens ~full ~pos ~env ~scope
954+
~opens ~full ~pos ~env ~scope
969955
in
970956
let completions =
971957
completions
@@ -1029,7 +1015,7 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact
10291015
let findTypeOfValue path =
10301016
path
10311017
|> getCompletionsForPath ~debug ~completionContext:Value ~exact:true
1032-
~package ~opens ~full ~pos ~env ~scope
1018+
~opens ~full ~pos ~env ~scope
10331019
|> completionsGetTypeEnv2 ~debug ~full ~opens ~rawOpens ~pos
10341020
in
10351021
let lowercaseComponent =
@@ -1043,7 +1029,7 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact
10431029
match
10441030
path
10451031
|> getCompletionsForPath ~debug ~completionContext:Type ~exact:true
1046-
~package ~opens ~full ~pos ~env ~scope
1032+
~opens ~full ~pos ~env ~scope
10471033
with
10481034
| {kind = Type {kind = Abstract (Some (p, _))}} :: _ ->
10491035
(* This case happens when what we're looking for is a type alias.
@@ -1189,9 +1175,62 @@ let printConstructorArgs argsLen ~asSnippet =
11891175

11901176
type completionMode = Pattern of Completable.patternMode | Expression
11911177

1192-
let rec completeTypedValue ~full ~prefix ~completionContext ~mode
1178+
let rec completeTypedValue ~rawOpens ~full ~prefix ~completionContext ~mode
11931179
(t : SharedTypes.completionType) =
11941180
match t with
1181+
| TtypeT {env; path} ->
1182+
(* Find all functions in the module that returns type t *)
1183+
let rec fnReturnsTypeT t =
1184+
match t.Types.desc with
1185+
| Tlink t1
1186+
| Tsubst t1
1187+
| Tpoly (t1, [])
1188+
| Tconstr (Pident {name = "function$"}, [t1; _], _) ->
1189+
fnReturnsTypeT t1
1190+
| Tarrow _ -> (
1191+
match TypeUtils.extractFunctionType ~env ~package:full.package t with
1192+
| ( (Nolabel, {desc = Tconstr (Path.Pident {name = "t"}, _, _)}) :: _,
1193+
{desc = Tconstr (Path.Pident {name = "t"}, _, _)} ) ->
1194+
(* Filter out functions that take type t first. These are often
1195+
@send style functions that we don't want to have here because
1196+
they usually aren't meant to create a type t from scratch. *)
1197+
false
1198+
| _args, {desc = Tconstr (Path.Pident {name = "t"}, _, _)} -> true
1199+
| _ -> false)
1200+
| _ -> false
1201+
in
1202+
let functionsReturningTypeT =
1203+
Hashtbl.create (Hashtbl.length env.exported.values_)
1204+
in
1205+
env.exported.values_
1206+
|> Hashtbl.iter (fun name stamp ->
1207+
match Stamps.findValue env.file.stamps stamp with
1208+
| None -> ()
1209+
| Some {item} -> (
1210+
if fnReturnsTypeT item then
1211+
let fnNname =
1212+
TypeUtils.getPathRelativeToEnv ~debug:false
1213+
~env:(QueryEnv.fromFile full.file)
1214+
~envFromItem:env (Utils.expandPath path)
1215+
in
1216+
1217+
match fnNname with
1218+
| None -> ()
1219+
| Some base ->
1220+
let base =
1221+
TypeUtils.removeOpensFromCompletionPath ~rawOpens
1222+
~package:full.package base
1223+
in
1224+
Hashtbl.add functionsReturningTypeT
1225+
((base |> String.concat ".") ^ "." ^ name)
1226+
item));
1227+
Hashtbl.fold
1228+
(fun fnName typeExpr all ->
1229+
Completion.createWithSnippet
1230+
~name:(Printf.sprintf "%s()" fnName)
1231+
~insertText:(fnName ^ "($0)") ~kind:(Value typeExpr) ~env ()
1232+
:: all)
1233+
functionsReturningTypeT []
11951234
| Tbool env ->
11961235
[
11971236
Completion.create "true" ~kind:(Label "bool") ~env;
@@ -1250,7 +1289,7 @@ let rec completeTypedValue ~full ~prefix ~completionContext ~mode
12501289
| None -> []
12511290
| Some innerType ->
12521291
innerType
1253-
|> completeTypedValue ~full ~prefix ~completionContext ~mode
1292+
|> completeTypedValue ~rawOpens ~full ~prefix ~completionContext ~mode
12541293
|> List.map (fun (c : Completion.t) ->
12551294
{
12561295
c with
@@ -1294,7 +1333,7 @@ let rec completeTypedValue ~full ~prefix ~completionContext ~mode
12941333
| None -> []
12951334
| Some innerType ->
12961335
innerType
1297-
|> completeTypedValue ~full ~prefix ~completionContext ~mode
1336+
|> completeTypedValue ~rawOpens ~full ~prefix ~completionContext ~mode
12981337
|> List.map (fun (c : Completion.t) ->
12991338
{
13001339
c with
@@ -1311,7 +1350,7 @@ let rec completeTypedValue ~full ~prefix ~completionContext ~mode
13111350
| None -> []
13121351
| Some innerType ->
13131352
innerType
1314-
|> completeTypedValue ~full ~prefix ~completionContext ~mode
1353+
|> completeTypedValue ~rawOpens ~full ~prefix ~completionContext ~mode
13151354
|> List.map (fun (c : Completion.t) ->
13161355
{
13171356
c with
@@ -1527,8 +1566,8 @@ let rec processCompletable ~debug ~full ~scope ~env ~pos ~forHover completable =
15271566
let allFiles = allFilesInPackage package in
15281567
let findTypeOfValue path =
15291568
path
1530-
|> getCompletionsForPath ~debug ~completionContext:Value ~exact:true
1531-
~package ~opens ~full ~pos ~env ~scope
1569+
|> getCompletionsForPath ~debug ~completionContext:Value ~exact:true ~opens
1570+
~full ~pos ~env ~scope
15321571
|> completionsGetTypeEnv2 ~debug ~full ~opens ~rawOpens ~pos
15331572
in
15341573
match completable with
@@ -1730,8 +1769,8 @@ let rec processCompletable ~debug ~full ~scope ~env ~pos ~forHover completable =
17301769
| Some (typ, _env, completionContext) ->
17311770
let items =
17321771
typ
1733-
|> completeTypedValue ~mode:(Pattern patternMode) ~full ~prefix
1734-
~completionContext
1772+
|> completeTypedValue ~rawOpens ~mode:(Pattern patternMode) ~full
1773+
~prefix ~completionContext
17351774
in
17361775
fallbackOrEmpty ~items ())
17371776
| None -> fallbackOrEmpty ())
@@ -1768,7 +1807,7 @@ let rec processCompletable ~debug ~full ~scope ~env ~pos ~forHover completable =
17681807
in
17691808
let items =
17701809
typ
1771-
|> completeTypedValue ~mode:Expression ~full ~prefix
1810+
|> completeTypedValue ~rawOpens ~mode:Expression ~full ~prefix
17721811
~completionContext
17731812
|> List.map (fun (c : Completion.t) ->
17741813
if wrapInsertTextInBraces then

analysis/src/SharedTypes.ml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,7 @@ and completionType =
331331
| Tbool of QueryEnv.t
332332
| Tarray of QueryEnv.t * innerType
333333
| Tstring of QueryEnv.t
334+
| TtypeT of {env: QueryEnv.t; path: Path.t}
334335
| Tvariant of {
335336
env: QueryEnv.t;
336337
constructors: Constructor.t list;

analysis/src/TypeUtils.ml

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ let rec extractType ~env ~package (t : Types.type_expr) =
149149
})
150150
| Some (env, {item = {kind = Record fields}}) ->
151151
Some (Trecord {env; fields; definition = `TypeExpr t})
152+
| Some (env, {item = {name = "t"}}) -> Some (TtypeT {env; path})
152153
| _ -> None)
153154
| Ttuple expressions -> Some (Tuple (env, expressions, t))
154155
| Tvariant {row_fields} ->
@@ -631,6 +632,7 @@ let rec extractedTypeToString ?(inner = false) = function
631632
else Shared.typeToString typ
632633
| Tbool _ -> "bool"
633634
| Tstring _ -> "string"
635+
| TtypeT _ -> "type t"
634636
| Tarray (_, TypeExpr innerTyp) ->
635637
"array<" ^ Shared.typeToString innerTyp ^ ">"
636638
| Tarray (_, ExtractedType innerTyp) ->
@@ -757,3 +759,48 @@ module Codegen = struct
757759
|> List.map (fun (pat : Parsetree.pattern) ->
758760
Ast_helper.Exp.case pat (mkFailWithExp ())))
759761
end
762+
763+
let getPathRelativeToEnv ~debug ~(env : QueryEnv.t) ~envFromItem path =
764+
match path with
765+
| _ :: pathRev ->
766+
(* type path is relative to the completion environment
767+
express it from the root of the file *)
768+
let found, pathFromEnv =
769+
QueryEnv.pathFromEnv envFromItem (List.rev pathRev)
770+
in
771+
if debug then
772+
Printf.printf "CPPipe pathFromEnv:%s found:%b\n"
773+
(pathFromEnv |> String.concat ".")
774+
found;
775+
if pathFromEnv = [] then None
776+
else if
777+
env.file.moduleName <> envFromItem.file.moduleName && found
778+
(* If the module names are different, then one needs to qualify the path.
779+
But only if the path belongs to the env from completion *)
780+
then Some (envFromItem.file.moduleName :: pathFromEnv)
781+
else Some pathFromEnv
782+
| _ -> None
783+
784+
let removeOpensFromCompletionPath ~rawOpens ~package completionPath =
785+
let rec removeRawOpen rawOpen modulePath =
786+
match (rawOpen, modulePath) with
787+
| [_], _ -> Some modulePath
788+
| s :: inner, first :: restPath when s = first ->
789+
removeRawOpen inner restPath
790+
| _ -> None
791+
in
792+
let rec removeRawOpens rawOpens modulePath =
793+
match rawOpens with
794+
| rawOpen :: restOpens -> (
795+
let newModulePath = removeRawOpens restOpens modulePath in
796+
match removeRawOpen rawOpen newModulePath with
797+
| None -> newModulePath
798+
| Some mp -> mp)
799+
| [] -> modulePath
800+
in
801+
let completionPathMinusOpens =
802+
completionPath |> Utils.flattenAnyNamespaceInPath
803+
|> removeRawOpens package.opens
804+
|> removeRawOpens rawOpens
805+
in
806+
completionPathMinusOpens

analysis/tests/src/CompletionExpressions.res

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -287,3 +287,29 @@ let fnTakingPolyVariant = (a: somePolyVariant) => {
287287

288288
// fnTakingPolyVariant(o)
289289
// ^com
290+
291+
module SuperInt: {
292+
type t
293+
let increment: (t, int) => t
294+
let decrement: (t, int => int) => t
295+
let make: int => t
296+
let toInt: t => int
297+
} = {
298+
type t = int
299+
let increment = (t, num) => t + num
300+
let decrement = (t, decrementer) => decrementer(t)
301+
let make = t => t
302+
let toInt = t => t
303+
}
304+
305+
type withIntLocal = {superInt: SuperInt.t}
306+
307+
// let withInt: withIntLocal = {superInt: }
308+
// ^com
309+
310+
// CompletionSupport.makeTestHidden()
311+
// ^com
312+
313+
open CompletionSupport
314+
// CompletionSupport.makeTestHidden()
315+
// ^com

0 commit comments

Comments
 (0)