From a9c13e11888afd6c8b17e849d6f399e9e894e9da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ulysse=20G=C3=A9rard?= Date: Fri, 15 Nov 2024 16:37:56 +0100 Subject: [PATCH 1/5] Aggregate related uids in an union find structure Co-authored-by: art-w Refactors suggested-by: xvw --- src/index-format/index_format.ml | 27 ++++++++++++- src/index-format/index_format.mli | 4 +- src/index-format/union_find.ml | 40 +++++++++++++++++++ src/ocaml-index/lib/index.ml | 32 +++++++++++++-- .../tests/tests-dirs/index-project.t | 3 ++ src/ocaml-index/tests/tests-dirs/interfaces.t | 1 + .../tests-dirs/local-shape-and-include.t | 2 + .../tests/tests-dirs/transitive-deps.t | 3 ++ .../occurrences/project-wide/mli-vs-ml.t | 1 + .../occurrences/project-wide/prefix.t/run.t | 1 + .../occurrences/project-wide/pwo-basic.t | 1 + .../occurrences/project-wide/pwo-ml-gen.t | 2 + .../server-tests/pwo-uid-stability.t | 3 ++ 13 files changed, 114 insertions(+), 6 deletions(-) create mode 100644 src/index-format/union_find.ml diff --git a/src/index-format/index_format.ml b/src/index-format/index_format.ml index 541455cc6f..848b0c08d0 100644 --- a/src/index-format/index_format.ml +++ b/src/index-format/index_format.ml @@ -18,6 +18,7 @@ end module Lid_set = Set.Make (Lid) module Uid_map = Shape.Uid.Map module Stats = Map.Make (String) +module Uid_set = Shape.Uid.Set let add map uid locs = Uid_map.update uid @@ -33,7 +34,8 @@ type index = approximated : Lid_set.t Uid_map.t; cu_shape : (string, Shape.t) Hashtbl.t; stats : stat Stats.t; - root_directory : string option + root_directory : string option; + related_uids : Uid_set.t Union_find.element Uid_map.t } let pp_partials (fmt : Format.formatter) (partials : Lid_set.t Uid_map.t) = @@ -52,6 +54,26 @@ let pp_partials (fmt : Format.formatter) (partials : Lid_set.t Uid_map.t) = partials; Format.fprintf fmt "@]}" +let pp_related_uids (fmt : Format.formatter) + (related_uids : Uid_set.t Union_find.element Uid_map.t) = + let rec gather acc map = + match Uid_map.choose_opt map with + | Some (_key, union) -> + let group = Union_find.get union |> Uid_set.to_list in + List.fold_left (fun acc key -> Uid_map.remove key acc) map group + |> gather (group :: acc) + | None -> acc + in + Format.pp_print_list + ~pp_sep:(fun fmt () -> Format.fprintf fmt ";@;") + (fun fmt group -> + Format.fprintf fmt "(%a)" + (Format.pp_print_list + ~pp_sep:(fun fmt () -> Format.fprintf fmt "@ ") + Shape.Uid.print) + group) + fmt (gather [] related_uids) + let pp (fmt : Format.formatter) pl = Format.fprintf fmt "%i uids:@ {@[" (Uid_map.cardinal pl.defs); Uid_map.iter @@ -71,7 +93,8 @@ let pp (fmt : Format.formatter) pl = (Uid_map.cardinal pl.approximated) pp_partials pl.approximated; Format.fprintf fmt "and shapes for CUS %s.@ " - (String.concat ";@," (Hashtbl.to_seq_keys pl.cu_shape |> List.of_seq)) + (String.concat ";@," (Hashtbl.to_seq_keys pl.cu_shape |> List.of_seq)); + Format.fprintf fmt "and related uids:@[{%a}@]" pp_related_uids pl.related_uids let ext = "ocaml-index" diff --git a/src/index-format/index_format.mli b/src/index-format/index_format.mli index a69da5bc18..beded4158e 100644 --- a/src/index-format/index_format.mli +++ b/src/index-format/index_format.mli @@ -7,6 +7,7 @@ module Lid : Set.OrderedType with type t = Longident.t Location.loc module Lid_set : Set.S with type elt = Lid.t module Stats : Map.S with type key = String.t module Uid_map = Shape.Uid.Map +module Uid_set = Shape.Uid.Set type stat = { mtime : float; size : int; source_digest : string option } @@ -15,7 +16,8 @@ type index = approximated : Lid_set.t Uid_map.t; cu_shape : (string, Shape.t) Hashtbl.t; stats : stat Stats.t; - root_directory : string option + root_directory : string option; + related_uids : Uid_set.t Union_find.element Uid_map.t } val pp : Format.formatter -> index -> unit diff --git a/src/index-format/union_find.ml b/src/index-format/union_find.ml new file mode 100644 index 0000000000..e2d2bb47df --- /dev/null +++ b/src/index-format/union_find.ml @@ -0,0 +1,40 @@ +type 'a content = + | Root of { mutable value : 'a; mutable rank : int } + | Link of { mutable parent : 'a element } +and 'a element = 'a content ref + +let make value = ref (Root { value; rank = 0 }) + +let rec find x = + match !x with + | Root _ -> x + | Link ({ parent; _ } as link) -> + let root = find parent in + if root != parent then link.parent <- root; + root + +let union ~f x y = + let x = find x in + let y = find y in + if x == y then x + else begin + match (!x, !y) with + | ( Root ({ rank = rank_x; value = value_x } as root_x), + Root ({ rank = rank_y; value = value_y } as root_y) ) -> + let new_value = f value_x value_y in + if rank_x < rank_y then ( + x := Link { parent = y }; + root_y.value <- new_value; + y) + else ( + y := Link { parent = x }; + root_x.value <- new_value; + if rank_x = rank_y then root_x.rank <- root_x.rank + 1; + x) + | _ -> assert false + end + +let get elt = + match !(find elt) with + | Root { value; _ } -> value + | Link _ -> assert false diff --git a/src/ocaml-index/lib/index.ml b/src/ocaml-index/lib/index.ml index c3618994b1..e605584763 100644 --- a/src/ocaml-index/lib/index.ml +++ b/src/ocaml-index/lib/index.ml @@ -89,6 +89,7 @@ let index_of_cmt ~into ~root ~rewrite_root ~build_path ~do_not_use_cmt_loadpath cmt_initial_env; cmt_sourcefile; cmt_source_digest; + cmt_declaration_dependencies; _ } = cmt_infos @@ -135,15 +136,39 @@ let index_of_cmt ~into ~root ~rewrite_root ~build_path ~do_not_use_cmt_loadpath into.stats with Unix.Unix_error _ -> into.stats) in - { defs; approximated; cu_shape; stats; root_directory = into.root_directory } + let related_uids = + List.fold_left + (fun acc (_, uid1, uid2) -> + let union = Union_find.make (Uid_set.of_list [ uid1; uid2 ]) in + let map_update uid = + Uid_map.update uid (function + | None -> Some union + | Some union' -> + Some (Union_find.union ~f:Uid_set.union union' union)) + in + acc |> map_update uid1 |> map_update uid2) + into.related_uids cmt_declaration_dependencies + in + { defs; + approximated; + cu_shape; + stats; + related_uids; + root_directory = into.root_directory + } let merge_index ~store_shapes ~into index = let defs = merge index.defs into.defs in let approximated = merge index.approximated into.approximated in let stats = Stats.union (fun _ f1 _f2 -> Some f1) into.stats index.stats in + let related_uids = + Uid_map.union + (fun _ a b -> Some (Union_find.union ~f:Uid_set.union a b)) + index.related_uids into.related_uids + in if store_shapes then Hashtbl.add_seq index.cu_shape (Hashtbl.to_seq into.cu_shape); - { into with defs; approximated; stats } + { into with defs; approximated; stats; related_uids } let from_files ~store_shapes ~output_file ~root ~rewrite_root ~build_path ~do_not_use_cmt_loadpath files = @@ -153,7 +178,8 @@ let from_files ~store_shapes ~output_file ~root ~rewrite_root ~build_path approximated = Shape.Uid.Map.empty; cu_shape = Hashtbl.create 64; stats = Stats.empty; - root_directory = root + root_directory = root; + related_uids = Uid_map.empty } in let final_index = diff --git a/src/ocaml-index/tests/tests-dirs/index-project.t b/src/ocaml-index/tests/tests-dirs/index-project.t index d521d6918a..44fa2ff07f 100644 --- a/src/ocaml-index/tests/tests-dirs/index-project.t +++ b/src/ocaml-index/tests/tests-dirs/index-project.t @@ -58,6 +58,7 @@ "+": File "main.ml", line 2, characters 14-15; "+": File "main.ml", line 4, characters 26-27 }, 0 approx shapes: {}, and shapes for CUS . + and related uids:{} $ ocaml-index dump foo.uideps 5 uids: @@ -71,6 +72,7 @@ "+": File "foo.ml", line 3, characters 11-12; "+": File "foo.ml", line 3, characters 19-20 }, 0 approx shapes: {}, and shapes for CUS . + and related uids:{} @@ -116,6 +118,7 @@ "+": File "main.ml", line 2, characters 14-15; "+": File "main.ml", line 4, characters 26-27 }, 0 approx shapes: {}, and shapes for CUS . + and related uids:{} $ ocaml-index stats foo.uideps test.uideps Index "test.uideps" contains: diff --git a/src/ocaml-index/tests/tests-dirs/interfaces.t b/src/ocaml-index/tests/tests-dirs/interfaces.t index 7514c55cf2..ecbd20face 100644 --- a/src/ocaml-index/tests/tests-dirs/interfaces.t +++ b/src/ocaml-index/tests/tests-dirs/interfaces.t @@ -22,3 +22,4 @@ uid: Stdlib__Float.81; locs: "Float.t": File "main.mli", line 1, characters 9-16 }, 0 approx shapes: {}, and shapes for CUS . + and related uids:{} diff --git a/src/ocaml-index/tests/tests-dirs/local-shape-and-include.t b/src/ocaml-index/tests/tests-dirs/local-shape-and-include.t index 1111fef1db..548196af8b 100644 --- a/src/ocaml-index/tests/tests-dirs/local-shape-and-include.t +++ b/src/ocaml-index/tests/tests-dirs/local-shape-and-include.t @@ -36,6 +36,7 @@ uid: Stdlib__String.174; locs: "String.equal": File "main.ml", line 1, characters 8-20 }, 0 approx shapes: {}, and shapes for CUS . + and related uids:{(Main.3 Main.4)} $ ocaml-index dump test.uideps @@ -53,4 +54,5 @@ uid: Stdlib__String.174; locs: "String.equal": File "main.ml", line 1, characters 8-20 }, 0 approx shapes: {}, and shapes for CUS . + and related uids:{(Main.3 Main.4)} diff --git a/src/ocaml-index/tests/tests-dirs/transitive-deps.t b/src/ocaml-index/tests/tests-dirs/transitive-deps.t index df75b37c32..8e9fec07ba 100644 --- a/src/ocaml-index/tests/tests-dirs/transitive-deps.t +++ b/src/ocaml-index/tests/tests-dirs/transitive-deps.t @@ -33,11 +33,13 @@ uid: Stdlib__List.45; locs: "List.init": File "main.ml", line 1, characters 8-17 }, 0 approx shapes: {}, and shapes for CUS . + and related uids:{} $ ocaml-index dump lib1/foo.uideps 1 uids: {uid: Bar; locs: "Bar": File "lib1/foo.ml", line 1, characters 8-11 }, 0 approx shapes: {}, and shapes for CUS . + and related uids:{} $ ocaml-index dump test.uideps 5 uids: @@ -50,4 +52,5 @@ uid: Stdlib__List.45; locs: "List.init": File "main.ml", line 1, characters 8-17 }, 0 approx shapes: {}, and shapes for CUS . + and related uids:{} diff --git a/tests/test-dirs/occurrences/project-wide/mli-vs-ml.t b/tests/test-dirs/occurrences/project-wide/mli-vs-ml.t index 23b2a6a91f..1119a240a2 100644 --- a/tests/test-dirs/occurrences/project-wide/mli-vs-ml.t +++ b/tests/test-dirs/occurrences/project-wide/mli-vs-ml.t @@ -25,6 +25,7 @@ The indexer should not mixup uids from mli and ml files: "t": File "main.ml", line 2, characters 5-6; "t": File "main.ml", line 3, characters 8-9 }, 0 approx shapes: {}, and shapes for CUS . + and related uids:{(Main.0 [intf]Main.1); ([intf]Main.0 Main.1)} Merlin should not mixup uids from mli and ml files, and return results in both the interface and the implementation. diff --git a/tests/test-dirs/occurrences/project-wide/prefix.t/run.t b/tests/test-dirs/occurrences/project-wide/prefix.t/run.t index e8a2327d51..d6f9bac79d 100644 --- a/tests/test-dirs/occurrences/project-wide/prefix.t/run.t +++ b/tests/test-dirs/occurrences/project-wide/prefix.t/run.t @@ -35,6 +35,7 @@ Create an index "+": File "a.ml", line 2, characters 14-15; "+": File "b.ml", line 2, characters 10-11 }, 0 approx shapes: {}, and shapes for CUS . + and related uids:{} Merlin fails to find occurrences outside of file because of the module prefixes diff --git a/tests/test-dirs/occurrences/project-wide/pwo-basic.t b/tests/test-dirs/occurrences/project-wide/pwo-basic.t index 5fcd73f6e1..f794a600e8 100644 --- a/tests/test-dirs/occurrences/project-wide/pwo-basic.t +++ b/tests/test-dirs/occurrences/project-wide/pwo-basic.t @@ -20,6 +20,7 @@ "print_string": File "lib.ml", line 2, characters 9-21; "print_string": File "main.ml", line 1, characters 9-21 }, 0 approx shapes: {}, and shapes for CUS . + and related uids:{} $ $MERLIN single occurrences -scope project -identifier-at 1:28 \ > -index-file project.ocaml-index \ diff --git a/tests/test-dirs/occurrences/project-wide/pwo-ml-gen.t b/tests/test-dirs/occurrences/project-wide/pwo-ml-gen.t index b3c0a34e46..b1c7a824e4 100644 --- a/tests/test-dirs/occurrences/project-wide/pwo-ml-gen.t +++ b/tests/test-dirs/occurrences/project-wide/pwo-ml-gen.t @@ -43,6 +43,7 @@ We should not index generated modules (lib.ml-gen) "foo": File "lib/aux.ml", line 1, characters 4-7; "foo": File "lib/aux.ml", line 2, characters 8-11 }, 0 approx shapes: {}, and shapes for CUS . + and related uids:{} $ ocaml-index dump _build/default/.main.eobjs/cctx.ocaml-index 4 uids: @@ -52,6 +53,7 @@ We should not index generated modules (lib.ml-gen) uid: Stdlib.312; locs: "print_string": File "main.ml", line 3, characters 9-21 }, 0 approx shapes: {}, and shapes for CUS . + and related uids:{} $ $MERLIN single occurrences -scope project -identifier-at 3:23 \ > -filename main.ml Date: Mon, 18 Nov 2024 14:49:20 +0100 Subject: [PATCH 2/5] Return usages of all linked decls when in the new "renaming" scope Refactors suggested-by: xvw --- src/analysis/occurrences.ml | 66 ++++++++++---- src/analysis/occurrences.mli | 2 +- src/commands/new_commands.ml | 1 + src/commands/query_json.ml | 5 +- src/frontend/query_protocol.ml | 2 +- .../for-renaming/r-modules-and-types.t | 91 +++++++++++++++++++ .../occurrences/project-wide/pwo-relative.t | 1 + 7 files changed, 145 insertions(+), 23 deletions(-) create mode 100644 tests/test-dirs/occurrences/project-wide/for-renaming/r-modules-and-types.t diff --git a/src/analysis/occurrences.ml b/src/analysis/occurrences.ml index cd9acd810a..2a89efc54a 100644 --- a/src/analysis/occurrences.ml +++ b/src/analysis/occurrences.ml @@ -155,26 +155,54 @@ let get_external_locs ~(config : Mconfig.t) ~current_buffer_path uid = locs, Stat_check.get_outdated_files stats ))) -let find_linked_uids ~config ~name uid = +let lookup_related_uids_in_indexes ~(config : Mconfig.t) uid = + let title = "lookup_related_uids_in_indexes" in + let open Index_format in + let related_uids = + List.fold_left ~init:Uid_map.empty config.merlin.index_files + ~f:(fun acc index_file -> + try + let index = Index_cache.read index_file in + Uid_map.union + (fun _ a b -> Some (Union_find.union ~f:Uid_set.union a b)) + index.related_uids acc + with Index_format.Not_an_index _ | Sys_error _ -> + log ~title "Could not load index %s" index_file; + acc) + in + Uid_map.find_opt uid related_uids + |> Option.value_map ~default:[] ~f:(fun x -> + x |> Union_find.get |> Uid_set.to_list) + +let find_linked_uids ~config ~scope ~name uid = let title = "find_linked_uids" in match uid with - | Shape.Uid.Item { from = _; comp_unit; _ } -> ( - let config = + | Shape.Uid.Item { from = _; comp_unit; _ } -> + let locate_config = { Locate.mconfig = config; ml_or_mli = `ML; traverse_aliases = false } in - match Locate.get_linked_uids ~config ~comp_unit uid with - | [ uid' ] -> - log ~title "Found linked uid: %a" Logger.fmt (fun fmt -> - Shape.Uid.print fmt uid'); - let name_check = - Locate.lookup_uid_decl ~config:config.mconfig uid' - |> Option.bind ~f:(Typedtree_utils.location_of_declaration ~uid:uid') - |> Option.value_map - ~f:(fun { Location.txt; _ } -> String.equal name txt) - ~default:false - in - if name_check then [ uid' ] else [] - | _ -> []) + let check_name uid = + Locate.lookup_uid_decl ~config uid + |> Option.bind ~f:(Typedtree_utils.location_of_declaration ~uid) + |> Option.value_map + ~f:(fun { Location.txt; _ } -> + let result = String.equal name txt in + if not result then + log ~title "Found clashing idents %S <> %S. Ignoring UID %a." + name txt Logger.fmt + (Fun.flip Shape.Uid.print uid); + result) + ~default:false + in + let related_uids = + match scope with + | `Buffer -> [] + | `Project -> Locate.get_linked_uids ~config:locate_config ~comp_unit uid + | `Renaming -> lookup_related_uids_in_indexes ~config uid + in + log ~title "Found related uids: [%a]" Logger.fmt (fun fmt -> + List.iter ~f:(fprintf fmt "%a;" Shape.Uid.print) related_uids); + List.filter ~f:check_name related_uids | _ -> [] let locs_of ~config ~env ~typer_result ~pos ~scope path = @@ -230,7 +258,7 @@ let locs_of ~config ~env ~typer_result ~pos ~scope path = let name = String.split_on_char ~sep:'.' path |> List.last |> Option.get in - let additional_uids = find_linked_uids ~config ~name def_uid in + let additional_uids = find_linked_uids ~config ~scope ~name def_uid in List.concat_map (def_uid :: additional_uids) ~f:(get_external_locs ~config ~current_buffer_path) @@ -284,9 +312,9 @@ let locs_of ~config ~env ~typer_result ~pos ~scope path = in let status = match (scope, String.Set.to_list out_of_sync_files) with - | `Project, [] -> `Included - | `Project, l -> `Out_of_sync l | `Buffer, _ -> `Not_requested + | _, [] -> `Included + | _, l -> `Out_of_sync l in if not def_uid_is_in_current_unit then { locs; status } else diff --git a/src/analysis/occurrences.mli b/src/analysis/occurrences.mli index d41d4d4070..ba6d8dcc94 100644 --- a/src/analysis/occurrences.mli +++ b/src/analysis/occurrences.mli @@ -6,6 +6,6 @@ val locs_of : env:Env.t -> typer_result:Mtyper.result -> pos:Lexing.position -> - scope:[ `Project | `Buffer ] -> + scope:[ `Project | `Buffer | `Renaming ] -> string -> t diff --git a/src/commands/new_commands.ml b/src/commands/new_commands.ml index 67a11911e1..ebf1aee4ae 100644 --- a/src/commands/new_commands.ml +++ b/src/commands/new_commands.ml @@ -509,6 +509,7 @@ let all_commands = match scope with | "buffer" -> (pos, `Buffer) | "project" -> (pos, `Project) + | "renaming" -> (pos, `Renaming) | _ -> failwith "-scope should be one of buffer or project")) ] ~doc: diff --git a/src/commands/query_json.ml b/src/commands/query_json.ml index b2b0fb5e3b..f9f2f75d8c 100644 --- a/src/commands/query_json.ml +++ b/src/commands/query_json.ml @@ -192,7 +192,8 @@ let dump (type a) : a t -> json = ( "scope", match scope with | `Buffer -> `String "local" - | `Project -> `String "project" ) + | `Project -> `String "project" + | `Renaming -> `String "renaming" ) ] | Refactor_open (action, pos) -> mk "refactor-open" @@ -488,7 +489,7 @@ let json_of_response (type a) (query : a t) (response : a) : json = | Extension_list _, strs -> `List (List.map ~f:Json.string strs) | Path_list _, strs -> `List (List.map ~f:Json.string strs) | Occurrences (_, scope), (locations, _project) -> - let with_file = scope = `Project in + let with_file = scope = `Project || scope = `Renaming in `List (List.map locations ~f:(fun loc -> with_location ~with_file loc [])) | Signature_help _, s -> json_of_signature_help s | Version, version -> `String version diff --git a/src/frontend/query_protocol.ml b/src/frontend/query_protocol.ml index 4ac5d92095..43b8c65779 100644 --- a/src/frontend/query_protocol.ml +++ b/src/frontend/query_protocol.ml @@ -212,7 +212,7 @@ type _ t = | Extension_list : [ `All | `Enabled | `Disabled ] -> string list t | Path_list : [ `Build | `Source ] -> string list t | Occurrences (* *) : - [ `Ident_at of Msource.position ] * [ `Project | `Buffer ] + [ `Ident_at of Msource.position ] * [ `Project | `Buffer | `Renaming ] -> (Location.t list * occurrences_status) t | Signature_help : signature_help -> signature_help_result option t (** In current version, Merlin only uses the parameter [position] to answer diff --git a/tests/test-dirs/occurrences/project-wide/for-renaming/r-modules-and-types.t b/tests/test-dirs/occurrences/project-wide/for-renaming/r-modules-and-types.t new file mode 100644 index 0000000000..376e4c5910 --- /dev/null +++ b/tests/test-dirs/occurrences/project-wide/for-renaming/r-modules-and-types.t @@ -0,0 +1,91 @@ + $ cat >lib.mli <<'EOF' + > module type S = sig + > val x : unit + > end + > EOF + + $ cat >lib.ml <<'EOF' + > module type S = sig + > val x : unit + > end + > EOF + + $ cat >main.ml <<'EOF' + > module M : Lib.S = struct + > let x = () + > end + > let () = M.x + > EOF + + $ ocamlc -bin-annot -bin-annot-occurrences -c lib.mli lib.ml main.ml + $ ocaml-index aggregate *.cmti *.cmt + + $ ocaml-index dump project.ocaml-index + 6 uids: + {uid: [intf]Lib.0; locs: "x": File "lib.mli", line 2, characters 6-7 + uid: Lib.0; locs: "x": File "lib.ml", line 2, characters 6-7 + uid: [intf]Lib.1; locs: "S": File "lib.mli", line 1, characters 12-13 + uid: Lib.1; locs: + "S": File "lib.ml", line 1, characters 12-13; + "Lib.S": File "main.ml", line 1, characters 11-16 + uid: Main.0; locs: + "x": File "main.ml", line 2, characters 6-7; + "M.x": File "main.ml", line 4, characters 9-12 + uid: Main.1; locs: "M": File "main.ml", line 1, characters 7-8 }, + 0 approx shapes: {}, and shapes for CUS . + and related uids:{([intf]Lib.1 Lib.1); ([intf]Lib.0 Lib.0 Main.0)} + + $ $MERLIN single occurrences -scope renaming -identifier-at 4:11 \ + > -index-file project.ocaml-index \ + > -filename main.ml main/.merlin << EOF From 266c435bc493a3bd1f983b3a0f93cfe59fa87115 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ulysse=20G=C3=A9rard?= Date: Fri, 20 Dec 2024 17:33:06 +0100 Subject: [PATCH 3/5] Add a changelog entry for #1877 --- CHANGES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index a8062c3ed8..38911ca382 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,6 +6,8 @@ unreleased - Use new 5.3 features to improve locate behavior in some cases. Merlin no longer confuses uids from interfaces and implementations. (#1857) - Perform less merges in the indexer (#1881) + - Add initial support for project-wide renaming: occurrences can now return + all usages of all related definitions. (#1877) + vim plugin - Added support for search-by-type (#1846) This is exposed through the existing `:MerlinSearch` command, that From dcb1e417d251cbb67830070367174a45b47f795e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ulysse=20G=C3=A9rard?= Date: Mon, 23 Dec 2024 13:51:28 +0100 Subject: [PATCH 4/5] test: renaming a functor value included from its parameter --- .../for-renaming/r-with-functors.t/dune | 2 ++ .../r-with-functors.t/dune-project | 1 + .../for-renaming/r-with-functors.t/func.ml | 5 +++ .../for-renaming/r-with-functors.t/func.mli | 5 +++ .../for-renaming/r-with-functors.t/main.ml | 4 +++ .../for-renaming/r-with-functors.t/run.t | 34 +++++++++++++++++++ 6 files changed, 51 insertions(+) create mode 100644 tests/test-dirs/occurrences/project-wide/for-renaming/r-with-functors.t/dune create mode 100644 tests/test-dirs/occurrences/project-wide/for-renaming/r-with-functors.t/dune-project create mode 100644 tests/test-dirs/occurrences/project-wide/for-renaming/r-with-functors.t/func.ml create mode 100644 tests/test-dirs/occurrences/project-wide/for-renaming/r-with-functors.t/func.mli create mode 100644 tests/test-dirs/occurrences/project-wide/for-renaming/r-with-functors.t/main.ml create mode 100644 tests/test-dirs/occurrences/project-wide/for-renaming/r-with-functors.t/run.t diff --git a/tests/test-dirs/occurrences/project-wide/for-renaming/r-with-functors.t/dune b/tests/test-dirs/occurrences/project-wide/for-renaming/r-with-functors.t/dune new file mode 100644 index 0000000000..69dd4adeac --- /dev/null +++ b/tests/test-dirs/occurrences/project-wide/for-renaming/r-with-functors.t/dune @@ -0,0 +1,2 @@ +(executable + (name main)) diff --git a/tests/test-dirs/occurrences/project-wide/for-renaming/r-with-functors.t/dune-project b/tests/test-dirs/occurrences/project-wide/for-renaming/r-with-functors.t/dune-project new file mode 100644 index 0000000000..3e2a6150dd --- /dev/null +++ b/tests/test-dirs/occurrences/project-wide/for-renaming/r-with-functors.t/dune-project @@ -0,0 +1 @@ +(lang dune 3.17) diff --git a/tests/test-dirs/occurrences/project-wide/for-renaming/r-with-functors.t/func.ml b/tests/test-dirs/occurrences/project-wide/for-renaming/r-with-functors.t/func.ml new file mode 100644 index 0000000000..216fb381b9 --- /dev/null +++ b/tests/test-dirs/occurrences/project-wide/for-renaming/r-with-functors.t/func.ml @@ -0,0 +1,5 @@ +module type P = sig val txt : string end + +module Make (Params : sig val txt : string end) = struct + include Params +end diff --git a/tests/test-dirs/occurrences/project-wide/for-renaming/r-with-functors.t/func.mli b/tests/test-dirs/occurrences/project-wide/for-renaming/r-with-functors.t/func.mli new file mode 100644 index 0000000000..a8b2a2671d --- /dev/null +++ b/tests/test-dirs/occurrences/project-wide/for-renaming/r-with-functors.t/func.mli @@ -0,0 +1,5 @@ +module type P = sig val txt : string end + +module Make (_ : P) : sig + include P +end diff --git a/tests/test-dirs/occurrences/project-wide/for-renaming/r-with-functors.t/main.ml b/tests/test-dirs/occurrences/project-wide/for-renaming/r-with-functors.t/main.ml new file mode 100644 index 0000000000..c9bd6e8fa2 --- /dev/null +++ b/tests/test-dirs/occurrences/project-wide/for-renaming/r-with-functors.t/main.ml @@ -0,0 +1,4 @@ +module P = struct let txt = "Hello world!" end;; +module M = Func.Make(P);; + +print_endline M.txt diff --git a/tests/test-dirs/occurrences/project-wide/for-renaming/r-with-functors.t/run.t b/tests/test-dirs/occurrences/project-wide/for-renaming/r-with-functors.t/run.t new file mode 100644 index 0000000000..d28346bf3b --- /dev/null +++ b/tests/test-dirs/occurrences/project-wide/for-renaming/r-with-functors.t/run.t @@ -0,0 +1,34 @@ + + $ $DUNE build @ocaml-index + + $ $DUNE exec ./main.exe + Hello world! + +We expect 2 occurrences in func.ml, 1 in func.mli and 2 in main.ml + $ $MERLIN single occurrences -scope renaming -identifier-at 4:18 \ + > -filename main.ml Date: Fri, 10 Jan 2025 12:11:02 +0100 Subject: [PATCH 5/5] Update protocol documentation --- doc/dev/PROTOCOL.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/doc/dev/PROTOCOL.md b/doc/dev/PROTOCOL.md index 7e84bcd7e1..a6b86a0efb 100644 --- a/doc/dev/PROTOCOL.md +++ b/doc/dev/PROTOCOL.md @@ -355,9 +355,13 @@ Returns either: Returns a list of locations `{'start': position, 'end': position}` of all occurrences in current buffer of the entity at the specified position. If scope -is set to `project` the returned locations will also contain a field `file`: +is set to `project` or `renaming`‡ the returned locations will also contain a field `file`: `{'file': string, 'start': position, 'end': position}`. +When the scope is set to `renaming`, all usages of all the related definitions +corresponding to an identifier will be returned. When scope is `project` only +the usages of the current definition will be returned. + ### `outline`