Skip to content

Commit ebc5d9e

Browse files
committed
refactor dot completion
1 parent 943b01f commit ebc5d9e

14 files changed

+226
-187
lines changed

analysis/src/CompletionBackEnd.ml

Lines changed: 114 additions & 139 deletions
Original file line numberDiff line numberDiff line change
@@ -770,46 +770,58 @@ let completionsGetTypeEnv = function
770770

771771
type getCompletionsForContextPathMode = Regular | Pipe
772772

773-
let completionsGetCompletionType ~full = function
774-
| {Completion.kind = Value typ; env} :: _
775-
| {Completion.kind = ObjLabel typ; env} :: _
776-
| {Completion.kind = Field ({typ}, _); env} :: _ ->
773+
let completionsGetCompletionType ~full completions =
774+
let firstNonSyntheticCompletion =
775+
List.find_opt (fun c -> not c.Completion.synthetic) completions
776+
in
777+
match firstNonSyntheticCompletion with
778+
| Some {Completion.kind = Value typ; env}
779+
| Some {Completion.kind = ObjLabel typ; env}
780+
| Some {Completion.kind = Field ({typ}, _); env} ->
777781
typ
778782
|> TypeUtils.extractType ~env ~package:full.package
779783
|> Option.map (fun (typ, _) -> (typ, env))
780-
| {Completion.kind = Type typ; env} :: _ -> (
784+
| Some {Completion.kind = Type typ; env} -> (
781785
match TypeUtils.extractTypeFromResolvedType typ ~env ~full with
782786
| None -> None
783787
| Some extractedType -> Some (extractedType, env))
784-
| {Completion.kind = ExtractedType (typ, _); env} :: _ -> Some (typ, env)
788+
| Some {Completion.kind = ExtractedType (typ, _); env} -> Some (typ, env)
785789
| _ -> None
786790

787-
let rec completionsGetCompletionType2 ~debug ~full ~opens ~rawOpens ~pos =
788-
function
789-
| {Completion.kind = Value typ; env} :: _
790-
| {Completion.kind = ObjLabel typ; env} :: _
791-
| {Completion.kind = Field ({typ}, _); env} :: _ ->
791+
let rec completionsGetCompletionType2 ~debug ~full ~opens ~rawOpens ~pos
792+
completions =
793+
let firstNonSyntheticCompletion =
794+
List.find_opt (fun c -> not c.Completion.synthetic) completions
795+
in
796+
match firstNonSyntheticCompletion with
797+
| Some
798+
( {Completion.kind = Value typ; env}
799+
| {Completion.kind = ObjLabel typ; env}
800+
| {Completion.kind = Field ({typ}, _); env} ) ->
792801
Some (TypeExpr typ, env)
793-
| {Completion.kind = FollowContextPath (ctxPath, scope); env} :: _ ->
802+
| Some {Completion.kind = FollowContextPath (ctxPath, scope); env} ->
794803
ctxPath
795804
|> getCompletionsForContextPath ~debug ~full ~env ~exact:true ~opens
796805
~rawOpens ~pos ~scope
797806
|> completionsGetCompletionType2 ~debug ~full ~opens ~rawOpens ~pos
798-
| {Completion.kind = Type typ; env} :: _ -> (
807+
| Some {Completion.kind = Type typ; env} -> (
799808
match TypeUtils.extractTypeFromResolvedType typ ~env ~full with
800809
| None -> None
801810
| Some extractedType -> Some (ExtractedType extractedType, env))
802-
| {Completion.kind = ExtractedType (typ, _); env} :: _ ->
811+
| Some {Completion.kind = ExtractedType (typ, _); env} ->
803812
Some (ExtractedType typ, env)
804813
| _ -> None
805814

806815
and completionsGetTypeEnv2 ~debug (completions : Completion.t list) ~full ~opens
807816
~rawOpens ~pos =
808-
match completions with
809-
| {Completion.kind = Value typ; env} :: _ -> Some (typ, env)
810-
| {Completion.kind = ObjLabel typ; env} :: _ -> Some (typ, env)
811-
| {Completion.kind = Field ({typ}, _); env} :: _ -> Some (typ, env)
812-
| {Completion.kind = FollowContextPath (ctxPath, scope); env} :: _ ->
817+
let firstNonSyntheticCompletion =
818+
List.find_opt (fun c -> not c.Completion.synthetic) completions
819+
in
820+
match firstNonSyntheticCompletion with
821+
| Some {Completion.kind = Value typ; env} -> Some (typ, env)
822+
| Some {Completion.kind = ObjLabel typ; env} -> Some (typ, env)
823+
| Some {Completion.kind = Field ({typ}, _); env} -> Some (typ, env)
824+
| Some {Completion.kind = FollowContextPath (ctxPath, scope); env} ->
813825
ctxPath
814826
|> getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env
815827
~exact:true ~scope
@@ -993,19 +1005,21 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact
9931005
path @ [fieldName]
9941006
|> getCompletionsForPath ~debug ~opens ~full ~pos ~exact
9951007
~completionContext:Field ~env ~scope
996-
| CPField {contextPath = cp; fieldName; fieldNameLoc} when Debug.verbose () ->
997-
(* TODO: this should only happen when the dot completion is at the end of the path *)
1008+
| CPField {contextPath = cp; fieldName; fieldNameLoc} ->
9981009
if Debug.verbose () then print_endline "[ctx_path]--> dot completion!";
999-
let completionsForCtxPath =
1010+
let completions =
10001011
cp
10011012
|> getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env
10021013
~exact:true ~scope
1003-
|> completionsGetTypeEnv2 ~debug ~full ~opens ~rawOpens ~pos
1014+
in
1015+
let completionsForCtxPath =
1016+
completions
1017+
|> completionsGetCompletionType2 ~debug ~full ~opens ~rawOpens ~pos
10041018
in
10051019
(* These are the main completions for the dot. *)
10061020
let mainCompletions =
10071021
match completionsForCtxPath with
1008-
| Some (typ, env)
1022+
| Some (TypeExpr typ, env)
10091023
when typ |> TypeUtils.extractObjectType ~env ~package |> Option.is_some
10101024
->
10111025
(* Handle obj completion via dot *)
@@ -1019,46 +1033,70 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact
10191033
if Utils.checkName field ~prefix:fieldName ~exact then
10201034
let fullObjFieldName = Printf.sprintf "[\"%s\"]" field in
10211035
Some
1022-
(Completion.create fullObjFieldName ~range:fieldNameLoc
1023-
~insertText:fullObjFieldName ~env:objEnv
1024-
~kind:(Completion.ObjLabel typ))
1036+
(Completion.create fullObjFieldName ~synthetic:true
1037+
~range:fieldNameLoc ~insertText:fullObjFieldName
1038+
~env:objEnv ~kind:(Completion.ObjLabel typ))
10251039
else None)
1026-
| Some (typ, env)
1027-
when typ |> TypeUtils.extractRecordType ~env ~package |> Option.is_some
1028-
->
1029-
let env, fields, decl, _path, _attributes =
1030-
typ |> TypeUtils.extractRecordType ~env ~package |> Option.get
1031-
in
1032-
if Debug.verbose () then
1033-
Printf.printf "[dot_completion]--> Record type found\n";
1034-
let recordAsString =
1035-
decl.item.decl |> Shared.declToString decl.name.txt
1040+
| Some (t, env) -> (
1041+
let extracted =
1042+
match t with
1043+
| TypeExpr typ -> (
1044+
if Debug.verbose () then
1045+
Printf.printf
1046+
"[dot_completion]--> Found type expr for main completions\n";
1047+
match typ |> TypeUtils.extractRecordType ~env ~package with
1048+
| Some (env, fields, typDecl, _path, _attributes) ->
1049+
Some
1050+
( env,
1051+
fields,
1052+
typDecl.item.decl |> Shared.declToString typDecl.name.txt )
1053+
| None -> None)
1054+
| ExtractedType typ -> (
1055+
if Debug.verbose () then
1056+
Printf.printf
1057+
"[dot_completion]--> Found extracted type for main completions\n";
1058+
match typ with
1059+
| Trecord {fields} ->
1060+
Some (env, fields, typ |> TypeUtils.extractedTypeToString)
1061+
| _ -> None)
10361062
in
1037-
fields
1038-
|> Utils.filterMap (fun field ->
1039-
if Utils.checkName field.fname.txt ~prefix:fieldName ~exact then
1040-
Some
1041-
(Completion.create field.fname.txt ~env
1042-
?deprecated:field.deprecated ~docstring:field.docstring
1043-
~kind:(Completion.Field (field, recordAsString)))
1044-
else None)
1045-
| Some (_typ, _env) ->
1046-
(* No more primary completions, for now. *)
1047-
[]
1063+
match extracted with
1064+
| None -> []
1065+
| Some (envFromExtracted, fields, recordAsString) ->
1066+
fields
1067+
|> Utils.filterMap (fun field ->
1068+
if Utils.checkName field.fname.txt ~prefix:fieldName ~exact
1069+
then
1070+
Some
1071+
(Completion.create field.fname.txt ~env:envFromExtracted
1072+
?deprecated:field.deprecated ~docstring:field.docstring
1073+
~kind:(Completion.Field (field, recordAsString)))
1074+
else None))
10481075
| None -> []
10491076
in
10501077
let pipeCompletions =
10511078
match completionsForCtxPath with
1052-
| None -> []
1053-
| Some (typ, envFromCompletionItem) -> (
1054-
let tPath = TypeUtils.pathFromTypeExpr typ in
1079+
| Some (TypeExpr typ, envFromCompletionItem) -> (
1080+
if Debug.verbose () then
1081+
Printf.printf
1082+
"[dot_completion]--> Found Type expr when doing pipe completions\n";
1083+
let tPath =
1084+
match TypeUtils.pathFromTypeExpr typ with
1085+
| None -> None
1086+
| Some tPath -> (
1087+
match
1088+
TypeUtils.getPathRelativeToEnv ~debug ~env:envCompletionIsMadeFrom
1089+
~envFromItem:envFromCompletionItem (Utils.expandPath tPath)
1090+
with
1091+
| None -> None
1092+
| Some completionPath -> Some (completionPath, tPath))
1093+
in
10551094
match tPath with
10561095
| None -> []
1057-
| Some tPath ->
1058-
let completionPath =
1059-
(tPath |> Utils.expandPath |> List.tl |> List.rev)
1060-
@ (envFromCompletionItem.pathRev |> List.rev)
1061-
in
1096+
| Some (completionPath, tPath) ->
1097+
if Debug.verbose () then
1098+
Printf.printf "[dot_completion]--> Got completion path %s\n"
1099+
(completionPath |> String.concat ".");
10621100
if List.length completionPath = 0 then []
10631101
else
10641102
let completions =
@@ -1067,14 +1105,20 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact
10671105
completionPath
10681106
in
10691107
completions
1070-
|> TypeUtils.filterPipeableFunctions ~env ~full
1108+
|> TypeUtils.filterPipeableFunctions ~synthetic:true ~env ~full
10711109
~lastPath:(Path.last tPath) ~replaceRange:fieldNameLoc)
1110+
| Some (ExtractedType _, _) ->
1111+
if Debug.verbose () then
1112+
Printf.printf
1113+
"[dot_completion]--> PROBLEM: Found extracted type when trying to \
1114+
do pipe completions\n";
1115+
[]
1116+
| _ -> []
10721117
in
10731118
(* Extra completions from configure extra module(s) *)
10741119
let extraCompletions =
10751120
match completionsForCtxPath with
1076-
| None -> []
1077-
| Some (typ, envFromCompletionItem) -> (
1121+
| Some (TypeExpr typ, envFromCompletionItem) -> (
10781122
match
10791123
TypeUtils.getExtraModuleToCompleteFromForType typ
10801124
~env:envFromCompletionItem ~full
@@ -1084,90 +1128,21 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact
10841128
completionsForPipeFromCompletionPath ~envCompletionIsMadeFrom ~opens
10851129
~pos ~scope ~debug ~prefix:fieldName ~env ~rawOpens ~full
10861130
completionPath
1087-
|> TypeUtils.filterPipeableFunctions ~env ~full
1131+
|> TypeUtils.filterPipeableFunctions ~synthetic:true ~env ~full
10881132
~replaceRange:fieldNameLoc
10891133
?lastPath:
10901134
(match TypeUtils.pathFromTypeExpr typ with
10911135
| None -> None
10921136
| Some tPath -> Some (Path.last tPath)))
1137+
| Some (ExtractedType _, _) ->
1138+
if Debug.verbose () then
1139+
Printf.printf
1140+
"[dot_completion]--> PROBLEM: Found extracted type when trying to \
1141+
do extra completions\n";
1142+
[]
1143+
| _ -> []
10931144
in
1094-
mainCompletions @ pipeCompletions @ extraCompletions
1095-
| CPField {contextPath = cp; fieldName; fieldNameLoc} -> (
1096-
if Debug.verbose () then print_endline "[ctx_path]--> CPField";
1097-
let completionsForCtxPath =
1098-
cp
1099-
|> getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env
1100-
~exact:true ~scope
1101-
in
1102-
let extracted =
1103-
match
1104-
completionsForCtxPath
1105-
|> completionsGetCompletionType2 ~debug ~full ~opens ~rawOpens ~pos
1106-
with
1107-
| Some (TypeExpr typ, env) -> (
1108-
match typ |> TypeUtils.extractRecordType ~env ~package with
1109-
| Some (env, fields, typDecl, path, attributes) ->
1110-
Some
1111-
( env,
1112-
fields,
1113-
typDecl.item.decl |> Shared.declToString typDecl.name.txt,
1114-
Some path,
1115-
attributes )
1116-
| None -> None)
1117-
| Some (ExtractedType typ, env) -> (
1118-
match typ with
1119-
| Trecord {fields; path; attributes} ->
1120-
Some
1121-
( env,
1122-
fields,
1123-
typ |> TypeUtils.extractedTypeToString,
1124-
path,
1125-
attributes )
1126-
| _ -> None)
1127-
| None -> None
1128-
in
1129-
match extracted with
1130-
| None -> []
1131-
| Some (envFromExtracted, fields, recordAsString, path, attributes) ->
1132-
let pipeCompletion =
1133-
match
1134-
(path, ProcessAttributes.findEditorCompleteFromAttribute attributes)
1135-
with
1136-
| Some path, _ when Path.last path = "t" ->
1137-
if Debug.verbose () then Printf.printf "CPField--> type is type t\n";
1138-
Some
1139-
( path,
1140-
path |> SharedTypes.pathIdentToString |> String.split_on_char '.'
1141-
|> List.rev |> List.tl )
1142-
| Some path, Some modulePath ->
1143-
if Debug.verbose () then
1144-
Printf.printf
1145-
"CPField--> type has completeFrom config for module %s, hd: %s, \
1146-
env moduleName: %s\n"
1147-
(modulePath |> SharedTypes.pathToString)
1148-
(List.hd modulePath) env.file.moduleName;
1149-
Some (path, modulePath)
1150-
| _ -> None
1151-
in
1152-
let pipeCompletionsForModule =
1153-
match pipeCompletion with
1154-
| Some (path, completionPath) ->
1155-
completionsForPipeFromCompletionPath ~opens ~pos ~scope ~debug
1156-
~prefix:fieldName ~envCompletionIsMadeFrom:env ~env:envFromExtracted
1157-
~rawOpens ~full completionPath
1158-
|> TypeUtils.filterPipeableFunctions ~env:envFromExtracted ~full
1159-
~lastPath:(Path.last path) ~replaceRange:fieldNameLoc
1160-
| None -> []
1161-
in
1162-
pipeCompletionsForModule
1163-
@ (fields
1164-
|> Utils.filterMap (fun field ->
1165-
if Utils.checkName field.fname.txt ~prefix:fieldName ~exact then
1166-
Some
1167-
(Completion.create field.fname.txt ~env:envFromExtracted
1168-
?deprecated:field.deprecated ~docstring:field.docstring
1169-
~kind:(Completion.Field (field, recordAsString)))
1170-
else None)))
1145+
pipeCompletions @ extraCompletions @ mainCompletions
11711146
| CPObj (cp, label) -> (
11721147
(* TODO: Also needs to support ExtractedType *)
11731148
if Debug.verbose () then print_endline "[ctx_path]--> CPObj";
@@ -1310,8 +1285,8 @@ and getCompletionsForContextPath ~debug ~full ~opens ~rawOpens ~pos ~env ~exact
13101285
|> String.concat "."
13111286
in
13121287
[
1313-
Completion.create name ~includesSnippets:true ~kind:(Value typ) ~env
1314-
~sortText:"A"
1288+
Completion.create name ~synthetic:true ~includesSnippets:true
1289+
~kind:(Value typ) ~env ~sortText:"A"
13151290
~docstring:
13161291
[
13171292
"Turns `" ^ builtinNameToComplete

analysis/src/SharedTypes.ml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -815,11 +815,13 @@ module Completion = struct
815815
typeArgContext: typeArgContext option;
816816
data: (string * string) list option;
817817
range: Location.t option;
818+
synthetic: bool;
819+
(** Whether this item is an made up, synthetic item or not. *)
818820
}
819821

820-
let create ?range ?data ?typeArgContext ?(includesSnippets = false)
821-
?insertText ~kind ~env ?sortText ?deprecated ?filterText ?detail
822-
?(docstring = []) name =
822+
let create ?(synthetic = false) ?range ?data ?typeArgContext
823+
?(includesSnippets = false) ?insertText ~kind ~env ?sortText ?deprecated
824+
?filterText ?detail ?(docstring = []) name =
823825
{
824826
name;
825827
env;
@@ -835,6 +837,7 @@ module Completion = struct
835837
typeArgContext;
836838
data;
837839
range;
840+
synthetic;
838841
}
839842

840843
(* https://microsoft.github.io/language-server-protocol/specifications/specification-current/#textDocument_completion *)

0 commit comments

Comments
 (0)