@@ -361,27 +361,87 @@ let extract_function_type2 ?type_arg_context ~state ~env ~package typ =
361361 in
362362 loop ?type_arg_context ~env [] typ
363363
364- (* The file being edited and the module path at the cursor, while a
365- completion request runs. Extension constructors are named relative to
366- it: unlike ordinary constructors they can't be found from the expected
367- type, so they have to be written so that they are in scope there. *)
368- let completion_scope : (File.t * string list * Pos.t) option ref = ref None
369-
370- let with_completion_scope ~file ~module_path ~pos f =
364+ (* The file being edited, the module path at the cursor and the modules
365+ opened there, most recent first, while a completion request runs.
366+ Extension constructors are named relative to it: unlike ordinary
367+ constructors they can't be found from the expected type, so they have to
368+ be written so that they are in scope there. *)
369+ let completion_scope :
370+ (File. t * string list * Pos. t * string list list ) option ref =
371+ ref None
372+
373+ let with_completion_scope ~file ~module_path ~pos ~opens f =
371374 let previous = ! completion_scope in
372- completion_scope := Some (file, module_path, pos);
375+ completion_scope := Some (file, module_path, pos, opens );
373376 Fun. protect ~finally: (fun () -> completion_scope := previous) f
374377
375- (* Whether [c] is the constructor written [name] in a payload position, where
376- only the last part of a path such as [Sub.Flag] is kept. Extension
377- constructors are named by their module path, ordinary ones are not. *)
378- let constructor_has_name (c : Constructor.t ) name =
379- c.cname.txt = name
380- ||
381- match String. rindex_opt c.cname.txt '.' with
382- | Some i ->
383- String. sub c.cname.txt (i + 1 ) (String. length c.cname.txt - i - 1 ) = name
384- | None -> false
378+ (* The constructor written [path] in a payload position, with the
379+ environment its payload is typed in. Extension constructors are named by
380+ their module path, so the one sharing the longest suffix with [path] is
381+ meant. Of several equally good ones, the one that came into scope last
382+ wins: through the most recent open naming its unwritten qualifier, else
383+ with nothing left unwritten, the last declared shadowing the others. *)
384+ let find_payload_constructor ~env ~constructors ~extension_envs path =
385+ let opens =
386+ match ! completion_scope with
387+ | Some (_ , _ , _ , opens ) -> opens
388+ | None -> []
389+ in
390+ let rec common_suffix a b =
391+ match (a, b) with
392+ | x :: a , y :: b when x = y -> 1 + common_suffix a b
393+ | _ -> 0
394+ in
395+ let rec ends_with l suffix =
396+ l = suffix
397+ ||
398+ match l with
399+ | _ :: l -> ends_with l suffix
400+ | [] -> false
401+ in
402+ let written = List. rev path in
403+ let scored =
404+ constructors
405+ |> List. map (fun (c : Constructor.t ) ->
406+ let parts = List. rev (String. split_on_char '.' c.cname.txt) in
407+ let score = common_suffix parts written in
408+ let qualifier = List. rev (List. filteri (fun i _ -> i > = score) parts) in
409+ (c, score, qualifier))
410+ in
411+ let best = List. fold_left (fun m (_ , score , _ ) -> max m score) 0 scored in
412+ let candidates =
413+ scored |> List. filter (fun (_ , score , _ ) -> best > 0 && score = best)
414+ in
415+ (* How recently the constructor came into scope: the position in [opens]
416+ of the module its unwritten qualifier names, or after all opens when
417+ nothing is left unwritten. *)
418+ let scope_rank qualifier =
419+ let rec find i = function
420+ | [] -> None
421+ | o :: opens ->
422+ if ends_with qualifier o || ends_with o qualifier then Some i
423+ else find (i + 1 ) opens
424+ in
425+ if qualifier = [] then Some (List. length opens) else find 0 opens
426+ in
427+ let in_scope =
428+ candidates
429+ |> List. fold_left
430+ (fun found (c , _ , qualifier ) ->
431+ match (found, scope_rank qualifier) with
432+ | Some (best , _ ), Some rank when rank < = best -> Some (rank, c)
433+ | None , Some rank -> Some (rank, c)
434+ | found , _ -> found)
435+ None
436+ in
437+ let found =
438+ match (in_scope, List. rev candidates) with
439+ | Some (_ , c ), _ | None , (c , _ , _ ) :: _ -> Some c
440+ | None , [] -> None
441+ in
442+ found
443+ |> Option. map (fun c ->
444+ (c, List. assq_opt c extension_envs |> Option. value ~default: env))
385445
386446(* The stamp under which [typ] is declared in [file]. *)
387447let type_stamp (file : File.t ) (typ : Type.t ) =
@@ -396,12 +456,13 @@ let type_stamp (file : File.t) (typ : Type.t) =
396456 type it extends by its path where it is declared: an identifier of the
397457 type's own file, or a path resolved from the extension's file. The
398458 constructors are named by their module path, since an extension
399- constructor can't be found from the expected type alone. *)
459+ constructor can't be found from the expected type alone, and come with
460+ the environment of their file, where their payloads are typed. *)
400461let extension_constructors ~state ~package ~(current_file : File.t )
401462 ~(type_file : File.t ) ~type_stamp ~type_name ~decl =
402463 let current_file, scope_path, cursor =
403464 match ! completion_scope with
404- | Some (file , module_path , pos ) -> (file, module_path, Some pos)
465+ | Some (file , module_path , pos , _ ) -> (file, module_path, Some pos)
405466 | None -> (current_file, [] , None )
406467 in
407468 (* An extension of the edited file is in scope from its declaration on. *)
@@ -470,11 +531,12 @@ let extension_constructors ~state ~package ~(current_file : File.t)
470531 if file.uri = current_file.uri then relative path scope_path
471532 else Utils. flatten_any_namespace_in_path (file.module_name :: path)
472533 in
473- {
474- declared.item with
475- cname = {declared.item.cname with txt = String. concat " ." path};
476- type_decl = (type_name, decl);
477- })
534+ ( {
535+ declared.item with
536+ cname = {declared.item.cname with txt = String. concat " ." path};
537+ type_decl = (type_name, decl);
538+ },
539+ Query_env. from_file file ))
478540
479541let rec extract_type ?(print_opening_debug = true )
480542 ?(type_arg_context : type_arg_context option )
@@ -556,6 +618,7 @@ let rec extract_type ?(print_opening_debug = true)
556618 {
557619 env = env_from_item;
558620 constructors;
621+ extension_envs = [] ;
559622 variant_name = name.txt;
560623 variant_decl = decl;
561624 },
@@ -573,12 +636,13 @@ let rec extract_type ?(print_opening_debug = true)
573636 | [] when name.txt = " t" ->
574637 Some (TtypeT {env = env_from_item; path}, type_arg_context)
575638 | [] -> None
576- | constructors ->
639+ | extension_envs ->
577640 Some
578641 ( Tvariant
579642 {
580643 env = env_from_item;
581- constructors;
644+ constructors = List. map fst extension_envs;
645+ extension_envs;
582646 variant_name = name.txt;
583647 variant_decl = decl;
584648 },
@@ -738,19 +802,30 @@ let extract_type_from_resolved_type (typ : Type.t) ~env ~full ~state =
738802 | Variant constructors ->
739803 Some
740804 (Tvariant
741- {env; constructors; variant_name = typ.name; variant_decl = typ.decl})
805+ {
806+ env;
807+ constructors;
808+ extension_envs = [] ;
809+ variant_name = typ.name;
810+ variant_decl = typ.decl;
811+ })
742812 | Open when typ.decl.type_manifest = None -> (
743813 match
744814 extension_constructors ~state ~package: full.package
745815 ~current_file: full.file ~type_file: env.file
746816 ~type_stamp: (type_stamp env.file typ) ~type_name: typ.name ~decl: typ.decl
747817 with
748818 | [] -> None
749- | constructors ->
819+ | extension_envs ->
750820 Some
751821 (Tvariant
752- {env; constructors; variant_name = typ.name; variant_decl = typ.decl})
753- )
822+ {
823+ env;
824+ constructors = List. map fst extension_envs;
825+ extension_envs;
826+ variant_name = typ.name;
827+ variant_decl = typ.decl;
828+ }))
754829 | Abstract _ | Open -> (
755830 match typ.decl.type_manifest with
756831 | None -> None
@@ -910,19 +985,19 @@ let rec resolve_nested ?type_arg_context ~env ~full ~state ~nested ?ctx
910985 |> extract_type ~env ~state ~package: full.package
911986 |> Utils.Option. flat_map (fun (t , type_arg_context ) ->
912987 t |> resolve_nested ?type_arg_context ~env ~state ~full ~nested )
913- | ( NVariantPayload {constructor_name; item_num; source_arity},
914- Tvariant {env; constructors} ) -> (
988+ | ( NVariantPayload
989+ {constructor_name; constructor_path; item_num; source_arity},
990+ Tvariant {env; constructors; extension_envs} ) -> (
915991 if Debug. verbose () then
916992 Printf. printf
917993 " [nested]--> trying to move into variant payload $%i of constructor \
918994 '%s'\n "
919995 item_num constructor_name;
920996 match
921- constructors
922- |> List. find_opt (fun (c : Constructor.t ) ->
923- constructor_has_name c constructor_name)
997+ find_payload_constructor ~env ~constructors ~extension_envs
998+ constructor_path
924999 with
925- | Some {args = Args args } -> (
1000+ | Some ( {args = Args args } , env ) -> (
9261001 if Debug. verbose () then
9271002 print_endline " [nested]--> found constructor (Args type)" ;
9281003 let item_num, nested =
@@ -949,7 +1024,7 @@ let rec resolve_nested ?type_arg_context ~env ~full ~state ~nested ?ctx
9491024 (List. length nested);
9501025 typ |> resolve_nested ?type_arg_context ~env ~state ~full ~nested )
9511026 )
952- | Some {args = InlineRecord fields } when item_num = 0 ->
1027+ | Some ( {args = InlineRecord fields } , env ) when item_num = 0 ->
9531028 if Debug. verbose () then
9541029 print_endline " [nested]--> found constructor (inline record)" ;
9551030 TinlineRecord {env; fields}
@@ -1051,14 +1126,13 @@ let rec resolve_nested_pattern_path (typ : inner_type) ~env ~full ~state ~nested
10511126 | None -> None
10521127 | Some typ ->
10531128 TypeExpr typ |> resolve_nested_pattern_path ~env ~state ~full ~nested )
1054- | ( NVariantPayload {constructor_name ; item_num; source_arity},
1055- Tvariant {env; constructors} ) -> (
1129+ | ( NVariantPayload {constructor_path ; item_num; source_arity},
1130+ Tvariant {env; constructors; extension_envs } ) -> (
10561131 match
1057- constructors
1058- |> List. find_opt (fun (constructor : Constructor.t ) ->
1059- constructor_has_name constructor constructor_name)
1132+ find_payload_constructor ~env ~constructors ~extension_envs
1133+ constructor_path
10601134 with
1061- | Some {args = Args args } -> (
1135+ | Some ( {args = Args args } , env ) -> (
10621136 let item_num, nested =
10631137 normalize_constructor_payload_path
10641138 ~argument_count: (List. length args) ~source_arity ~item_num ~nested
@@ -1068,10 +1142,10 @@ let rec resolve_nested_pattern_path (typ : inner_type) ~env ~full ~state ~nested
10681142 TypeExpr typ
10691143 |> resolve_nested_pattern_path ~env ~state ~full ~nested
10701144 | None -> None )
1071- | Some {args = InlineRecord fields } when item_num = 0 ->
1145+ | Some ( {args = InlineRecord fields } , env ) when item_num = 0 ->
10721146 ExtractedType (TinlineRecord {env; fields})
10731147 |> resolve_nested_pattern_path ~env ~state ~full ~nested
1074- | Some {args = InlineRecord _ } | None -> None )
1148+ | Some ( {args = InlineRecord _ } , _ ) | None -> None )
10751149 | ( NPolyvariantPayload {constructor_name; item_num; source_arity},
10761150 Tpolyvariant {env; constructors} ) -> (
10771151 match
0 commit comments