Skip to content

Commit dcb9728

Browse files
committed
refactor to share pipe completion code logic
1 parent 96aa78f commit dcb9728

File tree

2 files changed

+105
-170
lines changed

2 files changed

+105
-170
lines changed

analysis/src/CompletionBackEnd.ml

Lines changed: 101 additions & 170 deletions
Original file line numberDiff line numberDiff line change
@@ -664,6 +664,97 @@ let completionsForPipeFromCompletionPath ~envCompletionIsMadeFrom ~opens ~pos
664664
in
665665
completions
666666

667+
let getPipeCompletions ~env ~full ~identifierLoc ~debug ~envCompletionIsMadeFrom
668+
~opens ~pos ~scope ~prefix ~rawOpens ~inJsx
669+
?(mainCompletionsAreSynthetic = false) ?(formatCompletionsWithPipes = false)
670+
typ =
671+
let env, typ =
672+
typ
673+
|> TypeUtils.resolveTypeForPipeCompletion2 ~env ~package:full.package ~full
674+
~lhsLoc:identifierLoc
675+
in
676+
let mainTypeId = TypeUtils.findRootTypeId ~full ~env typ in
677+
let typePath = TypeUtils.pathFromTypeExpr typ in
678+
match mainTypeId with
679+
| None ->
680+
if Debug.verbose () then
681+
Printf.printf
682+
"[pipe_completion] Could not find mainTypeId. Aborting pipe completions.\n";
683+
[]
684+
| Some mainTypeId ->
685+
if Debug.verbose () then
686+
Printf.printf "[pipe_completion] mainTypeId: %s\n" mainTypeId;
687+
let pipeCompletions =
688+
(* We now need a completion path from where to look up the module for our dot completion type.
689+
This is from where we pull all of the functions we want to complete for the pipe.
690+
691+
A completion path here could be one of two things:
692+
1. A module path to the main module for the type we've found
693+
2. A module path to a builtin module, like `Int` for `int`, or `Array` for `array`
694+
695+
The below code will deliberately _not_ dig into type aliases for the main type when we're looking
696+
for what _module_ to complete from. This is because you should be able to control where completions
697+
come from even if your type is an alias.
698+
*)
699+
let completeAsBuiltin =
700+
match typePath with
701+
| Some t ->
702+
TypeUtils.completionPathFromMaybeBuiltin t ~package:full.package
703+
| None -> None
704+
in
705+
(* TODO(in-compiler) No need to have this configurable anymore, just use the new integrated modules from Core..*)
706+
let completionPath =
707+
match (completeAsBuiltin, typePath) with
708+
| Some completionPathForBuiltin, _ -> Some completionPathForBuiltin
709+
| _, Some p -> (
710+
(* If this isn't a builtin, but we have a path, we try to resolve the
711+
module path relative to the env we're completing from. This ensures that
712+
what we get here is a module path we can find completions for regardless of
713+
of the current scope for the position we're at.*)
714+
match
715+
TypeUtils.getModulePathRelativeToEnv ~debug
716+
~env:envCompletionIsMadeFrom ~envFromItem:env (Utils.expandPath p)
717+
with
718+
| None -> Some [env.file.moduleName]
719+
| Some p -> Some p)
720+
| _ -> None
721+
in
722+
match completionPath with
723+
| None -> []
724+
| Some completionPath ->
725+
completionsForPipeFromCompletionPath ~envCompletionIsMadeFrom ~opens
726+
~pos ~scope ~debug ~prefix ~env ~rawOpens ~full completionPath
727+
|> TypeUtils.filterPipeableFunctions ~env ~full
728+
~synthetic:mainCompletionsAreSynthetic ~targetTypeId:mainTypeId
729+
in
730+
(* Extra completions can be drawn from the @editor.completeFrom attribute. Here we
731+
find and add those completions as well. *)
732+
let extraCompletions =
733+
TypeUtils.getExtraModuleTosCompleteFromForType ~env ~full typ
734+
|> List.map (fun completionPath ->
735+
completionsForPipeFromCompletionPath ~envCompletionIsMadeFrom
736+
~opens ~pos ~scope ~debug ~prefix ~env ~rawOpens ~full
737+
completionPath)
738+
|> List.flatten
739+
|> TypeUtils.filterPipeableFunctions ~synthetic:true ~env ~full
740+
~targetTypeId:mainTypeId
741+
in
742+
(* Add JSX completion items if we're in a JSX context. *)
743+
let jsxCompletions =
744+
if inJsx then
745+
PipeCompletionUtils.addJsxCompletionItems ~env ~mainTypeId ~prefix ~full
746+
~rawOpens typ
747+
else []
748+
in
749+
let allCompletions = jsxCompletions @ pipeCompletions @ extraCompletions in
750+
if formatCompletionsWithPipes then
751+
allCompletions
752+
|> List.filter_map (fun (c : Completion.t) ->
753+
c
754+
|> TypeUtils.transformCompletionToPipeCompletion ~env
755+
~replaceRange:identifierLoc ~synthetic:c.synthetic)
756+
else allCompletions
757+
667758
let rec digToRecordFieldsForCompletion ~debug ~package ~opens ~full ~pos ~env
668759
~scope path =
669760
match
@@ -1035,99 +1126,16 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact
10351126
"[dot_completion] Could not extract main type completion env.\n";
10361127
[]
10371128
| Some (typ, env) ->
1038-
if Debug.verbose () then
1039-
Printf.printf "[dot_completion] env module path: %s, type: %s\n"
1040-
(TypeUtils.modulePathFromEnv env |> String.concat ".")
1041-
(Shared.typeToString typ);
1042-
(* Let's first find the actual field completions. *)
10431129
let fieldCompletions =
10441130
DotCompletionUtils.fieldCompletionsForDotCompletion typ ~env ~package
10451131
~prefix:fieldName ~fieldNameLoc ~exact
10461132
in
1047-
(* Now, let's get the type id for the type of parent of the dot completion.
1048-
The type id is a string that uniquely identifies a type, and that we can use
1049-
to filter completions for pipe completion etc.*)
1050-
let mainTypeId = TypeUtils.findRootTypeId ~full ~env typ in
1051-
let allExtraCompletions =
1052-
match mainTypeId with
1053-
| None ->
1054-
if Debug.verbose () then
1055-
Printf.printf
1056-
"[dot_completion] Could not find mainTypeId. Aborting extra pipe \
1057-
completions.\n";
1058-
[]
1059-
| Some mainTypeId ->
1060-
if Debug.verbose () then
1061-
Printf.printf "[dot_completion] mainTypeId: %s\n" mainTypeId;
1062-
1063-
let pipeCompletions =
1064-
(* We now need a completion path from where to look up the module for our dot completion type.
1065-
This is from where we pull all of the functions we want to complete for the pipe. *)
1066-
1067-
(* TODO(in-compiler) No need to have this configurable anymore, just use the new integrated modules from Core..*)
1068-
let completionPath =
1069-
(* First try completing it as a builtin *)
1070-
match mainTypeId with
1071-
| "array" -> Some package.builtInCompletionModules.arrayModulePath
1072-
| "option" ->
1073-
Some package.builtInCompletionModules.optionModulePath
1074-
| "string" ->
1075-
Some package.builtInCompletionModules.stringModulePath
1076-
| "int" -> Some package.builtInCompletionModules.intModulePath
1077-
| "float" -> Some package.builtInCompletionModules.floatModulePath
1078-
| "promise" ->
1079-
Some package.builtInCompletionModules.promiseModulePath
1080-
| "list" -> Some package.builtInCompletionModules.listModulePath
1081-
| "result" ->
1082-
Some package.builtInCompletionModules.resultModulePath
1083-
| "Js_re.t" ->
1084-
Some package.builtInCompletionModules.regexpModulePath
1085-
| "char" -> Some ["Char"]
1086-
| _ ->
1087-
(* Currently, when completing regular types, we stop at the first module we find that owns the type.
1088-
This means that completions will be made not from the root type necessarily, but from the module with
1089-
a type alias if it's a type alias. *)
1090-
let completionPathForType =
1091-
match TypeUtils.pathFromTypeExpr typ with
1092-
| None -> None
1093-
| Some tPath -> (
1094-
match
1095-
TypeUtils.getModulePathRelativeToEnv ~debug
1096-
~env:envCompletionIsMadeFrom ~envFromItem:env
1097-
(Utils.expandPath tPath)
1098-
with
1099-
| None -> Some [env.file.moduleName]
1100-
| Some p -> Some p)
1101-
in
1102-
if Debug.verbose () then
1103-
Printf.printf
1104-
"[dot_completion] Looked up completion path: %s\n"
1105-
(match completionPathForType with
1106-
| None -> "-"
1107-
| Some p -> p |> String.concat ".");
1108-
completionPathForType
1109-
in
1110-
match completionPath with
1111-
| None -> []
1112-
| Some completionPath ->
1113-
completionsForPipeFromCompletionPath ~envCompletionIsMadeFrom
1114-
~opens ~pos ~scope ~debug ~prefix:fieldName ~env ~rawOpens ~full
1115-
completionPath
1116-
in
1117-
(* TODO: Explain *)
1118-
let extraCompletions =
1119-
TypeUtils.getExtraModuleTosCompleteFromForType ~env ~full typ
1120-
|> List.map (fun completionPath ->
1121-
completionsForPipeFromCompletionPath ~envCompletionIsMadeFrom
1122-
~opens ~pos ~scope ~debug ~prefix:fieldName ~env ~rawOpens
1123-
~full completionPath)
1124-
|> List.flatten
1125-
in
1126-
pipeCompletions @ extraCompletions
1127-
|> TypeUtils.filterPipeableFunctions ~synthetic:true ~env ~full
1128-
~replaceRange:fieldNameLoc ~targetTypeId:mainTypeId
1133+
let pipeCompletions =
1134+
getPipeCompletions ~env ~full ~identifierLoc:fieldNameLoc
1135+
~envCompletionIsMadeFrom ~debug ~opens ~rawOpens ~scope ~pos
1136+
~inJsx:false ~prefix:fieldName ~formatCompletionsWithPipes:true typ
11291137
in
1130-
fieldCompletions @ allExtraCompletions)
1138+
fieldCompletions @ pipeCompletions)
11311139
| CPObj (cp, label) -> (
11321140
(* TODO: Also needs to support ExtractedType *)
11331141
if Debug.verbose () then print_endline "[ctx_path]--> CPObj";
@@ -1148,7 +1156,7 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact
11481156
else None)
11491157
| None -> [])
11501158
| None -> [])
1151-
| CPPipe {contextPath = cp; id = funNamePrefix; lhsLoc; inJsx} -> (
1159+
| CPPipe {contextPath = cp; id = prefix; lhsLoc; inJsx} -> (
11521160
if Debug.verbose () then print_endline "[ctx_path]--> CPPipe";
11531161
match
11541162
cp
@@ -1160,87 +1168,10 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact
11601168
if Debug.verbose () then
11611169
print_endline "[CPPipe]--> Could not resolve type env";
11621170
[]
1163-
| Some (typ, env) -> (
1164-
let env, typ =
1165-
typ
1166-
|> TypeUtils.resolveTypeForPipeCompletion2 ~env ~package ~full ~lhsLoc
1167-
in
1168-
let mainTypeId = TypeUtils.findRootTypeId ~full ~env typ in
1169-
let typePath = TypeUtils.pathFromTypeExpr typ in
1170-
match mainTypeId with
1171-
| None ->
1172-
if Debug.verbose () then
1173-
Printf.printf
1174-
"[pipe_completion] Could not find mainTypeId. Aborting pipe \
1175-
completions.\n";
1176-
[]
1177-
| Some mainTypeId ->
1178-
if Debug.verbose () then
1179-
Printf.printf "[pipe_completion] mainTypeId: %s\n" mainTypeId;
1180-
let pipeCompletions =
1181-
(* We now need a completion path from where to look up the module for our dot completion type.
1182-
This is from where we pull all of the functions we want to complete for the pipe.
1183-
1184-
A completion path here could be one of two things:
1185-
1. A module path to the main module for the type we've found
1186-
2. A module path to a builtin module, like `Int` for `int`, or `Array` for `array`
1187-
1188-
The below code will deliberately _not_ dig into type aliases for the main type when we're looking
1189-
for what _module_ to complete from. This is because you should be able to control where completions
1190-
come from even if your type is an alias.
1191-
*)
1192-
let completeAsBuiltin =
1193-
match typePath with
1194-
| Some t -> TypeUtils.completionPathFromMaybeBuiltin t ~package
1195-
| None -> None
1196-
in
1197-
(* TODO(in-compiler) No need to have this configurable anymore, just use the new integrated modules from Core..*)
1198-
let completionPath =
1199-
match (completeAsBuiltin, typePath) with
1200-
| Some completionPathForBuiltin, _ -> Some completionPathForBuiltin
1201-
| _, Some p -> (
1202-
(* If this isn't a builtin, but we have a path, we try to resolve the
1203-
module path relative to the env we're completing from. This ensures that
1204-
what we get here is a module path we can find completions for regardless of
1205-
of the current scope for the position we're at.*)
1206-
match
1207-
TypeUtils.getModulePathRelativeToEnv ~debug
1208-
~env:envCompletionIsMadeFrom ~envFromItem:env
1209-
(Utils.expandPath p)
1210-
with
1211-
| None -> Some [env.file.moduleName]
1212-
| Some p -> Some p)
1213-
| _ -> None
1214-
in
1215-
match completionPath with
1216-
| None -> []
1217-
| Some completionPath ->
1218-
completionsForPipeFromCompletionPath ~envCompletionIsMadeFrom ~opens
1219-
~pos ~scope ~debug ~prefix:funNamePrefix ~env ~rawOpens ~full
1220-
completionPath
1221-
|> TypeUtils.filterPipeableFunctions ~env ~full ~synthetic:false
1222-
~targetTypeId:mainTypeId
1223-
in
1224-
(* Extra completions can be drawn from the @editor.completeFrom attribute. Here we
1225-
find and add those completions as well. *)
1226-
let extraCompletions =
1227-
TypeUtils.getExtraModuleTosCompleteFromForType ~env ~full typ
1228-
|> List.map (fun completionPath ->
1229-
completionsForPipeFromCompletionPath ~envCompletionIsMadeFrom
1230-
~opens ~pos ~scope ~debug ~prefix:funNamePrefix ~env
1231-
~rawOpens ~full completionPath)
1232-
|> List.flatten
1233-
|> TypeUtils.filterPipeableFunctions ~synthetic:true ~env ~full
1234-
~targetTypeId:mainTypeId
1235-
in
1236-
(* Add JSX completion items if we're in a JSX context. *)
1237-
let jsxCompletions =
1238-
if inJsx then
1239-
PipeCompletionUtils.addJsxCompletionItems ~env ~mainTypeId
1240-
~prefix:funNamePrefix ~full ~rawOpens typ
1241-
else []
1242-
in
1243-
jsxCompletions @ pipeCompletions @ extraCompletions))
1171+
| Some (typ, env) ->
1172+
getPipeCompletions ~env ~full ~identifierLoc:lhsLoc
1173+
~envCompletionIsMadeFrom ~debug ~opens ~rawOpens ~scope ~pos ~inJsx
1174+
~prefix typ)
12441175
| CTuple ctxPaths ->
12451176
if Debug.verbose () then print_endline "[ctx_path]--> CTuple";
12461177
(* Turn a list of context paths into a list of type expressions. *)

analysis/tests/src/expected/Completion.res.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1088,6 +1088,8 @@ ContextPath Value[FAO, forAutoObject]["forAutoLabel"].""
10881088
ContextPath Value[FAO, forAutoObject]["forAutoLabel"]
10891089
ContextPath Value[FAO, forAutoObject]
10901090
Path FAO.forAutoObject
1091+
CPPipe pathFromEnv:FAR found:true
1092+
Path FAR.
10911093
[{
10921094
"label": "forAuto",
10931095
"kind": 5,
@@ -1113,6 +1115,8 @@ ContextPath Value[FAO, forAutoObject]["forAutoLabel"].forAuto
11131115
ContextPath Value[FAO, forAutoObject]["forAutoLabel"]
11141116
ContextPath Value[FAO, forAutoObject]
11151117
Path FAO.forAutoObject
1118+
CPPipe pathFromEnv:FAR found:true
1119+
Path FAR.forAuto
11161120
CPPipe pathFromEnv:ForAuto found:false
11171121
Path ForAuto.
11181122
[{

0 commit comments

Comments
 (0)