From 4a9486e71eecb6f9066a3c3d88fc9dd7370bed71 Mon Sep 17 00:00:00 2001 From: PizieDust Date: Mon, 18 Mar 2024 07:21:51 +0100 Subject: [PATCH 01/36] expand-node boilerplate --- src/frontend/ocamlmerlin/new/new_commands.ml | 15 +++++++++++++++ src/frontend/ocamlmerlin/query_json.ml | 7 +++++++ src/frontend/query_protocol.ml | 6 ++++++ 3 files changed, 28 insertions(+) diff --git a/src/frontend/ocamlmerlin/new/new_commands.ml b/src/frontend/ocamlmerlin/new/new_commands.ml index a1b753dd7..f6f2db88c 100644 --- a/src/frontend/ocamlmerlin/new/new_commands.ml +++ b/src/frontend/ocamlmerlin/new/new_commands.ml @@ -214,6 +214,21 @@ Otherwise, Merlin looks for the documentation for the entity under the cursor (a end ; + command "expand-node" + ~doc: "Returns the generated code of a PPX." + ~spec: [ + arg "-position" " Position to complete" + (marg_position (fun pos _pos -> pos)); + ] + ~default: `None + begin fun buffer pos -> + match pos with + | `None -> failwith "-position is mandatory" + | #Msource.position as pos -> + run buffer (Query_protocol.Expand_node pos) + end + ; + command "enclosing" ~spec: [ arg "-position" " Position to complete" diff --git a/src/frontend/ocamlmerlin/query_json.ml b/src/frontend/ocamlmerlin/query_json.ml index 69515bc1f..3cbd68a1a 100644 --- a/src/frontend/ocamlmerlin/query_json.ml +++ b/src/frontend/ocamlmerlin/query_json.ml @@ -114,6 +114,8 @@ let dump (type a) : a t -> json = ] | Syntax_document pos -> mk "syntax-document" [ ("position", mk_position pos) ] + | Expand_node pos -> + mk "ppx-expand" [ ("position", mk_position pos) ] | Locate (prefix, look_for, pos) -> mk "locate" [ "prefix", (match prefix with @@ -392,6 +394,11 @@ let json_of_response (type a) (query : a t) (response : a) : json = ("url", `String info.documentation); ] | `No_documentation -> `String "No documentation found") + | Expand_node _, resp -> + (match resp with + | `Found info -> `String info + | `No_deriver -> `String "No deriver on this node" + | `No_code -> `String "No code generated") | Locate_type _, resp -> json_of_locate resp | Locate _, resp -> json_of_locate resp | Jump _, resp -> diff --git a/src/frontend/query_protocol.ml b/src/frontend/query_protocol.ml index cd8871e47..0964f7160 100644 --- a/src/frontend/query_protocol.ml +++ b/src/frontend/query_protocol.ml @@ -145,6 +145,12 @@ type _ t = -> [ `Found of syntax_doc_result | `No_documentation ] t + | Expand_node + : Msource.position + -> [ `Found of string + | `No_deriver + | `No_code + ] t | Locate_type : Msource.position -> [ `Found of string option * Lexing.position From c298306d2e4f48188142b63348a9ed90fd3ca8a6 Mon Sep 17 00:00:00 2001 From: PizieDust Date: Mon, 18 Mar 2024 07:35:30 +0100 Subject: [PATCH 02/36] Add parsetree processing functions --- src/kernel/mtyper.ml | 40 ++++++++++++++++++++++++++++++++++++++++ src/kernel/mtyper.mli | 17 +++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/src/kernel/mtyper.ml b/src/kernel/mtyper.ml index 034cb10c7..0f5f2ae04 100644 --- a/src/kernel/mtyper.ml +++ b/src/kernel/mtyper.ml @@ -19,6 +19,11 @@ type typedtree = [ | `Implementation of Typedtree.structure ] +type parsetree = [ + | `Interface of Parsetree.signature + | `Implementation of Parsetree.structure +] + type typer_cache_stats = Miss | Hit of { reused : int; typed : int } let cache = s_ref None @@ -206,6 +211,13 @@ let get_typedtree t = let sig_items, sig_type = split_items l in `Interface {Typedtree. sig_items; sig_type; sig_final_env = get_env t} +let get_parsetree t = + match t.typedtree with + | `Interface signature_items -> + `Interface (List.map ~f:(fun { parsetree_item = tree; _} -> tree) signature_items) + | `Implementation structure_items -> + `Implementation (List.map ~f:(fun { parsetree_item = tree; _} -> tree) structure_items) + let node_at ?(skip_recovered=false) t pos_cursor = let node = Mbrowse.of_typedtree (get_typedtree t) in log ~title:"node_at" "Node: %s" (Mbrowse.print () node); @@ -223,3 +235,31 @@ let node_at ?(skip_recovered=false) t pos_cursor = log ~title:"node_at" "Deepest before %s" (Mbrowse.print () path); path + +(* Get the node under the cursor in the Parsetree*) +let node_at_p ?(skip_recovered=false) t pos_cursor = + let node = Mbrowse_p.of_parsetree (get_parsetree t) in + let rec select = function + | _ :: (node' :: _ as ancestors) + when Mbrowse_p.is_recovered node' -> select ancestors + | l -> l + in + match Mbrowse_p.deepest_before pos_cursor [node] with + | [] -> [Browse_raw_p.Dummy] + | path when skip_recovered -> select path + | path -> path + + +(* Get the node under the cursor in the Ppxed-Parsetree*) +let node_at_pp ?(skip_recovered=false) p pos_cursor = + let node = Mbrowse_p.of_parsetree p in + let rec select = function + | _ :: (node' :: _ as ancestors) + when Mbrowse_p.is_recovered node' -> select ancestors + | l -> l + in + match Mbrowse_p.deepest_before pos_cursor [node] with + | [] -> [Browse_raw_p.Dummy] + | path when skip_recovered -> select path + | path -> path + diff --git a/src/kernel/mtyper.mli b/src/kernel/mtyper.mli index fd6a7a6b7..f7b4671b1 100644 --- a/src/kernel/mtyper.mli +++ b/src/kernel/mtyper.mli @@ -14,6 +14,11 @@ type typedtree = [ | `Implementation of Typedtree.structure ] +type parsetree = [ + | `Interface of Parsetree.signature + | `Implementation of Parsetree.structure +] + type typer_cache_stats = Miss | Hit of { reused : int; typed : int } val run : Mconfig.t -> Mreader.parsetree -> result @@ -22,6 +27,8 @@ val get_env : ?pos:Msource.position -> result -> Env.t val get_typedtree : result -> typedtree +val get_parsetree : result -> parsetree + val get_errors : result -> exn list val initial_env : result -> Env.t @@ -44,3 +51,13 @@ val get_cache_stat : result -> typer_cache_stats *) val node_at : ?skip_recovered:bool -> result -> Lexing.position -> Mbrowse.t + +(* Get the node under the cursor in the Parsetree*) +val node_at_p : + ?skip_recovered:bool -> result -> Lexing.position -> Mbrowse_p.t + +(* Get the node under the cursor in the Ppxed-Parsetree*) +val node_at_pp : + ?skip_recovered:bool -> Mreader.parsetree -> Lexing.position -> Mbrowse_p.t + + From bff7ee703d16b9c34ec3ddb5184a3d7786ba5195 Mon Sep 17 00:00:00 2001 From: PizieDust Date: Mon, 18 Mar 2024 07:36:19 +0100 Subject: [PATCH 03/36] Mbrowse for parsetrees --- src/kernel/mbrowse_p.ml | 282 +++++++++++++++++++++++++++++++++++++++ src/kernel/mbrowse_p.mli | 85 ++++++++++++ 2 files changed, 367 insertions(+) create mode 100644 src/kernel/mbrowse_p.ml create mode 100644 src/kernel/mbrowse_p.mli diff --git a/src/kernel/mbrowse_p.ml b/src/kernel/mbrowse_p.ml new file mode 100644 index 000000000..8a334bcdf --- /dev/null +++ b/src/kernel/mbrowse_p.ml @@ -0,0 +1,282 @@ +(* {{{ COPYING *( + Same as Mbrowse module but for the Parstree. + This file is part of Merlin, an helper for ocaml editors + + Copyright (C) 2013 - Pizie Dust + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation the + rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + The Software is provided "as is", without warranty of any kind, express or + implied, including but not limited to the warranties of merchantability, + fitness for a particular purpose and noninfringement. In no event shall + the authors or copyright holders be liable for any claim, damages or other + liability, whether in an action of contract, tort or otherwise, arising + from, out of or in connection with the software or the use or other dealings + in the Software. + +)* }}} *) + +open Std +open Parsetree +open Browse_raw_p + +type node = Browse_raw_p.node +type t = node list + +let print_node () node = + Browse_raw_p.string_of_node node + +let print () t = + List.print (fun () node -> print_node () node) () t + +let fold_node f t acc = + let acc = + match + Msupport.get_saved_types_from_attributes (Browse_raw_p.node_attributes t) + with + | [] -> acc + | parts -> + let rec aux acc = function + | [] -> acc + | parts -> + aux (f t acc) parts + in + aux acc parts + in + Browse_raw_p.fold_node f t acc + +let approximate_loc get_loc node = + let loc = get_loc Location.none node in + if loc == Location.none then + let rec aux node acc = + let loc = get_loc Location.none node in + if loc != Location.none then + Location_aux.union loc acc + else fold_node aux node acc + in + aux node Location.none + else + loc + +let node_loc node = approximate_loc Browse_raw_p.node_real_loc node + +(* Fuzzy locations, more likely to locate the appropriate node *) +let node_merlin_loc node = approximate_loc Browse_raw_p.node_merlin_loc node + +let leaf_node lst:node = List.hd lst +let leaf_loc t = node_loc ((leaf_node t)) + +let drop_leaf t = + match t with + | [] | [ _ ] -> None + | _leaf :: parents -> Some parents + +let is_hidden node = + Browse_raw_p.has_attr ~name:"merlin.hide" node + +let is_focus node = + Browse_raw_p.has_attr ~name:"merlin.focus" node + +let select_leafs pos root = + let branches = ref [] in + let rec select_child branch node has_selected = + let loc = node_merlin_loc node in + if Location_aux.compare_pos pos loc = 0 && + not (is_hidden node) + then + (traverse (node :: branch); true) + else + has_selected + and traverse branch = + let node = leaf_node branch in + if (is_focus node) then ( + branches := []; + let has_leaves = fold_node (select_child branch) node false in + if not has_leaves then + branches := [branch]; + raise Exit + ) + else if not (is_hidden node) then ( + let has_leaves = fold_node (select_child branch) node false in + if not has_leaves then + branches := branch :: !branches + ) + in + (try traverse root with Exit -> ()); + !branches + +let compare_locations pos l1 l2 = + let t2_first = +1 in + let t1_first = -1 in + match + Location_aux.compare_pos pos l1, + Location_aux.compare_pos pos l2 + with + | 0, 0 -> + (* Cursor inside both locations: favor non-ghost closer to the end *) + begin match l1.Location.loc_ghost, l2.Location.loc_ghost with + | true, false -> 1 + | false, true -> -1 + | _ -> + Lexing.compare_pos l1.Location.loc_end l2.Location.loc_end + end + (* Cursor inside one location: it has priority *) + | 0, _ -> t1_first + | _, 0 -> t2_first + (* Cursor outside locations: favor before *) + | n, m when n > 0 && m < 0 -> t1_first + | n, m when m > 0 && n < 0 -> t2_first + (* Cursor is after both, select the closest one *) + | _, _ -> + Lexing.compare_pos l2.Location.loc_end l1.Location.loc_end + +let best_node pos = function + | [] -> [] + | init :: xs -> + let f acc x = + if compare_locations pos (leaf_loc acc) (leaf_loc x) <= 0 + then acc + else x + in + List.fold_left ~f ~init xs + +let enclosing pos roots = + match best_node pos roots with + | [] -> [] + | root -> best_node pos (select_leafs pos root) + +let deepest_before pos roots = + match enclosing pos roots with + | [] -> [] + | root -> + let rec aux path = + let node0 = leaf_node path in + let loc0 = node_merlin_loc node0 in + let select_candidate node acc = + let loc = node_merlin_loc node in + if path == root || + Location_aux.compare_pos pos loc = 0 || + Lexing.compare_pos loc.Location.loc_end loc0.Location.loc_end = 0 + then match acc with + | Some (loc',_) when compare_locations pos loc' loc <= 0 -> acc + | Some _ | None -> Some (loc,node) + else acc + in + match fold_node select_candidate node0 None with + | None -> path + | Some (_,node) -> + aux (node :: path) + in + (aux root) + +(* Select open nodes *) + +let rec select_open_node = + function[@warning "-9"] + | (Structure_item {pstr_desc = + Pstr_open { popen_expr = + { pmod_desc = Pmod_ident ({txt = longident}) }}}) + :: ancestors -> + Some (longident, ancestors) + | (Signature_item {psig_desc = Psig_open op}) :: ancestors -> + let ({ Asttypes.txt = longident; }) = op.popen_expr in + Some (longident, ancestors) + | (Expression { pexp_desc = + Pexp_open ({ popen_expr = + { pmod_desc = Pmod_ident ({txt = longident})}}, _); _}) + :: _ as ancestors -> + Some (longident, ancestors) + | [] -> None + | _ :: ancestors -> select_open_node ancestors + +let of_structure str = [Browse_raw_p.Structure str] + +let rec of_structure_items lst = + match lst with + | [] -> [] + | head :: tl -> + Browse_raw_p.Structure_item head :: (of_structure_items tl) + +let rec of_signature_items lst = + match lst with + | [] -> [] + | head :: tl -> + Browse_raw_p.Signature_item head :: (of_signature_items tl) + +let of_signature sg = [Browse_raw_p.Signature sg] + +let of_parsetree = function + | `Implementation str -> of_structure str + | `Interface sg -> of_signature sg + +let to_parsetree node = + let n = (List.hd (List.rev node)) in + match n with + | Browse_raw_p.Structure str -> + `Implementation str + | Browse_raw_p.Signature sg -> + `Interface sg + | _ -> raise (Invalid_argument "Invalid input") + +let optional_label_sugar = function + | Parsetree.Pexp_construct (id, e) + when id.Location.loc.Location.loc_ghost + && id.Location.txt = Longident.Lident "Some" -> + e + | _ -> None + +let rec is_recovered_expression e = + match e.Parsetree.pexp_desc with + | (* Recovery on arbitrary expressions *) + Pexp_tuple [_] -> + true + | (* Recovery on desugared optional label application *) + Pexp_construct _ as cstr + when is_recovered_Pexp_construct cstr -> + true + | _ -> false + +and is_recovered_Pexp_construct cstr = + match optional_label_sugar cstr with + | Some e -> is_recovered_expression e + | _ -> false + +let is_recovered = function + | Expression e -> is_recovered_expression e + | _ -> false + +let check_node pos node = + let loc = node_merlin_loc node in + if Location_aux.compare pos loc = 0 then true else false + +let get_children pos root = + let children = + match root with + | Structure str -> + of_structure_items (List.filter ~f:(fun x -> check_node pos (Structure_item(x))) str) + | Signature str -> + of_signature_items (List.filter ~f:(fun x -> check_node pos (Signature_item(x))) str) + | _ -> raise (Invalid_argument "Not a valid root node") + in children + +let pprint_deriver_node () node = + let ppf, to_string = Format.to_string () in + begin match node with + | Browse_raw_p.Structure_item n -> Pprintast.structure ppf [n] + | Browse_raw_p.Signature_item n -> Pprintast.signature ppf [n] + | _ -> raise (Invalid_argument "Wrong nodes") + end; + Format.pp_print_newline ppf (); + to_string () + +let pprint_deriver_nodes () nodes = + List.print (fun () node -> pprint_deriver_node () node) () nodes + \ No newline at end of file diff --git a/src/kernel/mbrowse_p.mli b/src/kernel/mbrowse_p.mli new file mode 100644 index 000000000..039470faf --- /dev/null +++ b/src/kernel/mbrowse_p.mli @@ -0,0 +1,85 @@ +(* {{{ COPYING *( + Same as Mbrowse module but for the Parstree. + This file is part of Merlin, an helper for ocaml editors + + Copyright (C) 2013 - Pizie Dust + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation the + rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + The Software is provided "as is", without warranty of any kind, express or + implied, including but not limited to the warranties of merchantability, + fitness for a particular purpose and noninfringement. In no event shall + the authors or copyright holders be liable for any claim, damages or other + liability, whether in an action of contract, tort or otherwise, arising + from, out of or in connection with the software or the use or other dealings + in the Software. + +)* }}} *) + +open Std + +type node = Browse_raw_p.node +type t = node list + +val fold_node : (Browse_raw_p.node -> 'a -> 'a) -> + Browse_raw_p.node -> 'a -> 'a +val node_loc : Browse_raw_p.node -> Location.t +val leaf_node : t -> node +val drop_leaf : t -> t option + +(* Navigate through tree *) + +(** The deepest context inside or before the node, for instance, navigating + * through: + * foo bar (baz :: tail) + * asking for node from cursor position will return context of "tail". + * Returns the matching node and all its ancestors or the empty list. *) +val deepest_before : Lexing.position -> t list -> t + + +val select_open_node : t -> (Longident.t * t) option + +val enclosing : Lexing.position -> t list -> t + +val of_structure : Parsetree.structure -> t +val of_structure_items : Parsetree.structure_item list -> node list +val of_signature_items : Parsetree.signature_item list -> node list +val of_signature : Parsetree.signature -> t + +val of_parsetree : + [ `Implementation of Parsetree.structure + | `Interface of Parsetree.signature ] -> t + +val to_parsetree : t -> + [ `Implementation of Parsetree.structure + | `Interface of Parsetree.signature ] + +(** Identify nodes introduced by recovery *) +val is_recovered_expression : Parsetree.expression -> bool +val is_recovered : Browse_raw_p.node -> bool + +(** When an optional argument is applied with labelled syntax + sugar (~a:v instead of ?a:(Some v)), the frontend will have + wrapped it in [Some _]. + [optional_label_sugar exp] returns [Some exp'] with the sugar + removed in that case. *) +val optional_label_sugar : + Parsetree.expression_desc -> Parsetree.expression option + +(** {1 Dump} *) + +val print_node : unit -> node -> string +val print : unit -> t -> string +val pprint_deriver_node : unit -> node -> string +val pprint_deriver_nodes : unit -> node list -> string + +val get_children : Warnings.loc -> node -> node list + From f732486ba34ed4e588c1e8d040cfa314ea421bab Mon Sep 17 00:00:00 2001 From: PizieDust Date: Mon, 18 Mar 2024 07:36:32 +0100 Subject: [PATCH 04/36] Browse_raw for parsetrees --- src/ocaml/merlin_specific/browse_raw_p.ml | 725 +++++++++++++++++++++ src/ocaml/merlin_specific/browse_raw_p.mli | 111 ++++ 2 files changed, 836 insertions(+) create mode 100644 src/ocaml/merlin_specific/browse_raw_p.ml create mode 100644 src/ocaml/merlin_specific/browse_raw_p.mli diff --git a/src/ocaml/merlin_specific/browse_raw_p.ml b/src/ocaml/merlin_specific/browse_raw_p.ml new file mode 100644 index 000000000..864037dbb --- /dev/null +++ b/src/ocaml/merlin_specific/browse_raw_p.ml @@ -0,0 +1,725 @@ +(* {{{ COPYING *( + Same as Browse_raw module but for the Parstree. + This file is part of Merlin, an helper for ocaml editors + + Copyright (C) 2013 - Pizie Dust + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation the + rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + The Software is provided "as is", without warranty of any kind, express or + implied, including but not limited to the warranties of merchantability, + fitness for a particular purpose and noninfringement. In no event shall + the authors or copyright holders be liable for any claim, damages or other + liability, whether in an action of contract, tort or otherwise, arising + from, out of or in connection with the software or the use or other dealings + in the Software. + +)* }}} *) + +[@@@ocaml.warning "-9"] + +open Std + +type constructor_declaration = Parsetree.constructor_declaration + +open Parsetree + +type node = + | Dummy + | Pattern of pattern + | Expression of expression + | Case of case + | Class_expr of class_expr + | Class_structure of class_structure + | Class_field of class_field + | Class_field_kind of class_field_kind + | Module_expr of module_expr + | Structure of structure + | Signature of signature + | Structure_item of structure_item + | Signature_item of signature_item + | Module_binding of module_binding + | Value_binding of value_binding + | Module_type of module_type + | Module_declaration of module_declaration + | Module_type_declaration of module_type_declaration + | With_constraint of with_constraint + | Core_type of core_type + | Package_type of package_type + | Row_field of row_field + | Value_description of value_description + | Type_declaration of type_declaration + | Type_kind of type_kind + | Type_extension of type_extension + | Extension_constructor of extension_constructor + | Label_declaration of label_declaration + | Constructor_declaration of constructor_declaration + | Class_type of class_type + | Class_signature of class_signature + | Class_type_field of class_type_field + | Class_declaration of class_declaration + | Class_description of class_description + | Class_type_declaration of class_type_declaration + | Binding_op of binding_op + + | Include_description of include_description + | Include_declaration of include_declaration + | Open_description of open_description + | Open_declaration of open_declaration + (* | Method_call of expression * meth * Location.t + | Record_field of [`Expression of expression | `Pattern of pattern] + * Types.label_description + * Longident.t Location.loc *) + | Module_binding_name of module_binding + | Module_declaration_name of module_declaration + | Module_type_declaration_name of module_type_declaration + +let node_update_env env0 = function + | _ -> env0 + +let node_real_loc loc0 = function + | Expression {pexp_loc = loc} + | Pattern {ppat_loc = loc} + (* | Method_call (_, _, loc) *) + (* | Record_field (_, _, {loc}) *) + | Class_expr {pcl_loc = loc} + | Module_expr {pmod_loc = loc} + | Structure_item {pstr_loc = loc} + | Signature_item {psig_loc = loc} + | Module_type {pmty_loc = loc} + | Core_type {ptyp_loc = loc} + | Class_type {pcty_loc = loc} + | Class_field {pcf_loc = loc} + | Module_binding {pmb_loc = loc} + | Module_declaration {pmd_loc = loc} + | Module_type_declaration {pmtd_loc = loc} + | Value_description {pval_loc = loc} + | Value_binding {pvb_loc = loc} + | Type_declaration {ptype_loc = loc} + | Label_declaration {pld_loc = loc} + | Constructor_declaration {pcd_loc = loc} + | Class_type_field {pctf_loc = loc} + | Class_declaration {pci_loc = loc} + | Class_description {pci_loc = loc} + | Class_type_declaration {pci_loc = loc} + | Extension_constructor {pext_loc = loc} + | Include_description {pincl_loc = loc} + | Include_declaration {pincl_loc = loc} + | Open_description {popen_loc = loc} + | Open_declaration {popen_loc = loc} + | Binding_op {pbop_loc = loc} + -> loc + | Module_type_declaration_name {pmtd_name = loc} + -> loc.Location.loc + | Module_declaration_name {pmd_name = loc} + | Module_binding_name {pmb_name = loc} + -> loc.Location.loc + | Structure _ | Signature _ | Case _ | Class_structure _ | Type_extension _ + | Class_field_kind _ | With_constraint _ + | Row_field _ | Type_kind _ | Class_signature _ | Package_type _ + | Dummy + -> loc0 + +let node_attributes = function + | Expression exp -> exp.pexp_attributes + | Pattern pat -> pat.ppat_attributes + | Class_expr cl -> cl.pcl_attributes + | Class_field cf -> cf.pcf_attributes + | Module_expr me -> me.pmod_attributes + | Structure_item ({pstr_desc = Pstr_eval (_,attr)}) -> attr + | Structure_item ({pstr_desc = Pstr_attribute a}) -> [a] + | Structure_item ({pstr_desc = Pstr_extension (_,a)}) -> a + | Signature_item ({psig_desc = Psig_attribute a}) -> [a] + | Module_binding mb -> mb.pmb_attributes + | Value_binding vb -> vb.pvb_attributes + | Module_type mt -> mt.pmty_attributes + | Module_declaration md -> md.pmd_attributes + | Module_type_declaration mtd -> mtd.pmtd_attributes + | Open_description o -> o.popen_attributes + | Include_declaration i -> i.pincl_attributes + | Include_description i -> i.pincl_attributes + | Core_type ct -> ct.ptyp_attributes + | Row_field rf -> rf.prf_attributes + | Value_description vd -> vd.pval_attributes + | Type_declaration td -> td.ptype_attributes + | Label_declaration ld -> ld.pld_attributes + | Constructor_declaration cd -> cd.pcd_attributes + | Type_extension te -> te.ptyext_attributes + | Extension_constructor ec -> ec.pext_attributes + | Class_type ct -> ct.pcty_attributes + | Class_type_field ctf -> ctf.pctf_attributes + | Class_declaration ci -> ci.pci_attributes + | Class_description ci -> ci.pci_attributes + | Class_type_declaration ci -> ci.pci_attributes + (* | Method_call (obj,_,_) -> obj.pexp_attributes *) + (* | Record_field (`Expression obj,_,_) -> obj.pexp_attributes + | Record_field (`Pattern obj,_,_) -> obj.ppat_attributes *) + | _ -> [] + +let has_attr ~name node = + let attrs = node_attributes node in + List.exists ~f:(fun a -> + let (str,_) = Ast_helper.Attr.as_tuple a in + str.Location.txt = name + ) attrs + +let node_merlin_loc loc0 node = + let attributes = node_attributes node in + let loc = + let open Parsetree in + let pred { attr_name = loc; _ } = Location_aux.is_relaxed_location loc in + match List.find attributes ~f:pred with + | { attr_name; _ } -> attr_name.Location.loc + | exception Not_found -> node_real_loc loc0 node + in loc + +let app node f acc = + f node acc + +type 'a f0 = node -> 'a -> 'a +type ('b,'a) f1 = 'b -> 'a f0 -> 'a -> 'a + +let id_fold (_f : _ f0) acc = acc + +let ( ** ) f1 f2 (f : _ f0) acc = + f2 f (f1 f acc) + +let rec list_fold (f' : _ f1) xs f acc = match xs with + | x :: xs -> list_fold f' xs f (f' x f acc) + | [] -> acc + +let _array_fold (f' : _ f1) arr env f acc = + let acc = ref acc in + for i = 0 to Array.length arr - 1 do + acc := f' arr.(i) env f !acc + done; + !acc + +let rec _list_fold_with_next (f' : _ -> _ f1) xs f acc = match xs with + | x :: (y :: _ as xs) -> _list_fold_with_next f' xs f (f' (Some y) x f acc) + | [x] -> f' None x f acc + | [] -> acc + +let option_fold f' o (f : _ f0) acc = match o with + | None -> acc + | Some x -> f' x f acc + +let of_core_type ct = app (Core_type ct) + +let of_expression e = app (Expression e) + +let of_case c = app (Case c) +let of_label_declaration ct = app (Label_declaration ct) +let of_value_binding vb = app (Value_binding vb) +let of_module_type mt = app (Module_type mt) +let of_module_expr me = app (Module_expr me) +let of_typ_param (ct,_) = of_core_type ct +let of_constructor_arguments = function + | Pcstr_tuple cts -> list_fold of_core_type cts + | Pcstr_record lbls -> list_fold of_label_declaration lbls + +let of_bop ({ pbop_exp; _ } as bop) = + app (Binding_op bop) ** of_expression pbop_exp + +let of_pattern p = + app (Pattern p) + +(* let of_record_field obj loc lbl = + fun env (f : _ f0) acc -> + app (Record_field (obj,lbl,loc)) env f acc *) +(* +let of_exp_record_field obj lid_loc lbl = + of_record_field (`Expression obj) lid_loc lbl + +let of_pat_record_field obj loc lbl = + of_record_field (`Pattern obj) loc lbl *) + +let of_pattern_desc desc = + match desc with + | Ppat_any | Ppat_var _ | Ppat_constant _ + | Ppat_interval _ | Ppat_type _ | Ppat_unpack _ + -> id_fold + | Ppat_alias (p,_) | Ppat_lazy p + | Ppat_exception p -> of_pattern p + | Ppat_variant (_,p) -> + (match p with + | Some p -> of_pattern p + | None -> id_fold) + | Ppat_tuple ps | Ppat_array ps -> + list_fold of_pattern ps + | Ppat_construct (_,ps) -> + (match ps with + | Some (_, ps) -> of_pattern ps + | None -> id_fold) + | Ppat_record (ps,_) -> + list_fold (fun (_, p) -> + of_pattern p) ps + | Ppat_or (p1,p2) -> + of_pattern p1 ** of_pattern p2 + | Ppat_extension _ -> + id_fold + | Ppat_constraint (p,ct) -> + of_pattern p ** + of_core_type ct + | Ppat_open (_,p) -> + of_pattern p + +(* let of_method_call obj meth loc = + fun env (f : _ f0) acc -> + let loc_start = obj.exp_loc.Location.loc_end in + let loc_end = loc.Location.loc_end in + let loc = {loc with Location. loc_start; loc_end} in + app (Method_call (obj,meth,loc)) env f acc *) + +let of_expression_desc = function + | Pexp_ident _ | Pexp_constant _ + | Pexp_variant (_,None) | Pexp_new _ -> id_fold + | Pexp_let (_,vbs,e) -> + of_expression e ** list_fold of_value_binding vbs + | Pexp_function cases -> + list_fold of_case cases + | Pexp_apply (e,ls) -> + of_expression e ** + list_fold (fun (_,e) ->of_expression e) ls + | Pexp_match (e,cs) -> + of_expression e ** + list_fold of_case cs + | Pexp_try (e,cs) -> + of_expression e ** + list_fold of_case cs + | Pexp_tuple es | Pexp_array es -> + list_fold of_expression es + | Pexp_construct (_,es) -> + (match es with + | None -> id_fold + | Some es -> of_expression es) + | Pexp_variant (_,Some e) + | Pexp_assert e | Pexp_lazy e | Pexp_setinstvar (_,e) -> + of_expression e + | Pexp_record (ess, es) -> + option_fold of_expression es ** + list_fold (fun (_, e) -> of_expression e) ess + | Pexp_field (e1, _) -> + of_expression e1 + | Pexp_setfield (e1,_,e2) -> + of_expression e1 ** of_expression e2 + | Pexp_ifthenelse (e1,e2,None) + | Pexp_sequence (e1,e2) | Pexp_while (e1,e2) -> + of_expression e1 ** of_expression e2 + | Pexp_ifthenelse (e1,e2,Some e3) | Pexp_for (_,e1,e2,_,e3) -> + of_expression e1 ** of_expression e2 ** of_expression e3 + | Pexp_send (e,_) -> + of_expression e + | Pexp_override ls -> + list_fold (fun (_,e) -> of_expression e) ls + | Pexp_letmodule (_,me, e) -> + app (Module_expr me) ** of_expression e + | Pexp_letexception (ec,e) -> + app (Extension_constructor ec) ** of_expression e + | Pexp_object cs -> + app (Class_structure cs) + | Pexp_pack me -> + of_module_expr me + | Pexp_unreachable | Pexp_extension _ -> + id_fold + | Pexp_letop { let_; ands; body;} -> + let bindops = let_ :: ands in + list_fold of_bop bindops ** + of_expression body + | Pexp_open (od, e) -> + app (Module_expr od.popen_expr) ** of_expression e + | Pexp_fun (_, e1, p, e2) -> + (match e1 with + | None -> id_fold + | Some e -> of_expression e) + ** of_pattern p + ** of_expression e2 + | Pexp_constraint (e, ct) -> + of_expression e ** of_core_type ct + | Pexp_newtype (_, e) -> + of_expression e + | Pexp_coerce (e, cto, ct) -> + of_expression e ** + (match cto with + | None -> id_fold + | Some c -> of_core_type c) ** + of_core_type ct + | Pexp_poly (e, cto) -> + of_expression e ** + match cto with + | None -> id_fold + | Some ct -> of_core_type ct + +and of_class_expr_desc = function + | Pcl_structure cs -> + app (Class_structure cs) + | Pcl_fun (_,es,p,ce) -> + (match es with + | None -> id_fold + | Some es -> of_expression es) ** + of_pattern p ** + app (Class_expr ce) + | Pcl_apply (ce,es) -> + list_fold (fun (_,e) -> of_expression e) + es ** + app (Class_expr ce) + | Pcl_let (_,vbs,ce) -> + list_fold of_value_binding vbs ** + app (Class_expr ce) + | Pcl_constraint (ce,ct) -> + app (Class_type ct) ** + app (Class_expr ce) + | Pcl_open (_,ce) -> + app (Class_expr ce) + | Pcl_constr (_, ct) -> + list_fold of_core_type ct + | Pcl_extension _ -> + id_fold + +and of_class_field_desc = function + | Pcf_inherit (_,ce,_) -> + app (Class_expr ce) + | Pcf_val (_,_,cfk) | Pcf_method (_,_,cfk) -> + app (Class_field_kind cfk) + | Pcf_constraint (ct1,ct2) -> + of_core_type ct1 ** of_core_type ct2 + | Pcf_initializer e -> + of_expression e + | Pcf_attribute _ -> + id_fold + | Pcf_extension _ -> + id_fold + +and of_module_expr_desc = function + | Pmod_ident _ -> id_fold + | Pmod_structure str -> + app (Structure str) + | Pmod_functor (Unit,me) -> of_module_expr me + | Pmod_functor (Named (_, mt),me) -> + of_module_type mt ** of_module_expr me + | Pmod_apply (me1,me2) -> + of_module_expr me1 ** + of_module_expr me2 + | Pmod_constraint (me,mt) -> + of_module_expr me ** + app (Module_type mt) + | Pmod_unpack e -> + of_expression e + | Pmod_extension _ -> + id_fold + + +and of_structure_item_desc = function + | Pstr_eval (e,_) -> + of_expression e + | Pstr_value (_,vbs) -> + list_fold of_value_binding vbs + | Pstr_primitive vd -> + app (Value_description vd) + | Pstr_type (_,tds) -> + list_fold (fun td -> app (Type_declaration td)) tds + | Pstr_typext text -> + app (Type_extension text) + | Pstr_exception texn -> + app (Extension_constructor texn.ptyexn_constructor) + | Pstr_module mb -> + app (Module_binding mb) + | Pstr_recmodule mbs -> + list_fold (fun x -> app (Module_binding x)) mbs + | Pstr_modtype mtd -> + app (Module_type_declaration mtd) + | Pstr_class cds -> + list_fold (fun (cd) -> app (Class_declaration cd)) cds + | Pstr_class_type ctds -> + list_fold (fun (ctd) -> app (Class_type_declaration ctd)) ctds + | Pstr_include i -> + app (Include_declaration i) + | Pstr_open d -> + app (Open_declaration d) + | Pstr_attribute _ -> + id_fold + | Pstr_extension _ -> + id_fold + +and of_module_type_desc = function + | Pmty_ident _ | Pmty_alias _ -> id_fold + | Pmty_signature sg -> + app (Signature sg) + | Pmty_functor (Named (_,mt1),mt2) -> + of_module_type mt1 ** of_module_type mt2 + | Pmty_functor (Unit,mt) -> of_module_type mt + | Pmty_with (mt,wcs) -> + list_fold (fun (wc) -> app (With_constraint wc)) wcs ** + of_module_type mt + | Pmty_typeof me -> + of_module_expr me + | Pmty_extension _ -> + id_fold + +and of_signature_item_desc = function + | Psig_attribute _ -> + id_fold + | Psig_open d -> + app (Open_description d) + | Psig_value vd -> + app (Value_description vd) + | Psig_type (_,tds) -> + list_fold (fun td -> app (Type_declaration td)) tds + | Psig_typext text -> + app (Type_extension text) + | Psig_exception texn -> + app (Extension_constructor texn.ptyexn_constructor) + | Psig_module md -> + app (Module_declaration md) + | Psig_recmodule mds -> + list_fold (fun md -> app (Module_declaration md)) mds + | Psig_modtype mtd -> + app (Module_type_declaration mtd) + | Psig_include i -> + app (Include_description i) + | Psig_class cds -> + list_fold (fun cd -> app (Class_description cd)) cds + | Psig_class_type ctds -> + list_fold (fun ctd -> app (Class_type_declaration ctd)) ctds + | Psig_typesubst tds -> + list_fold (fun td -> app (Type_declaration td)) tds + | Psig_modsubst _ms -> + id_fold + | Psig_modtypesubst _mts -> + id_fold + | Psig_extension _ -> + id_fold + +and of_core_type_desc = function + | Ptyp_any | Ptyp_var _ -> id_fold + | Ptyp_arrow (_,ct1,ct2) -> + of_core_type ct1 ** of_core_type ct2 + | Ptyp_tuple cts | Ptyp_constr (_,cts) | Ptyp_class (_,cts) -> + list_fold of_core_type cts + | Ptyp_object (cts,_) -> + list_fold (fun of_ -> + match of_.pof_desc with + | Otag (_,ct) + | Oinherit ct -> of_core_type ct + ) cts + | Ptyp_poly (_,ct) | Ptyp_alias (ct,_) -> + of_core_type ct + | Ptyp_variant (rfs,_,_) -> + list_fold (fun rf -> app (Row_field rf)) rfs + | Ptyp_package pt -> + app (Package_type pt) + | Ptyp_extension _ -> + id_fold + +and of_class_type_desc = function + | Pcty_constr (_,cts) -> + list_fold of_core_type cts + | Pcty_signature cs -> + app (Class_signature cs) + | Pcty_arrow (_,ct,clt) -> + of_core_type ct ** app (Class_type clt) + | Pcty_open (_,ct) -> + app (Class_type ct) + | Pcty_extension _ -> id_fold + +and of_class_type_field_desc = function + | Pctf_inherit ct -> + app (Class_type ct) + | Pctf_val (_,_,_,ct) | Pctf_method (_,_,_,ct) -> + of_core_type ct + | Pctf_constraint (ct1,ct2) -> + of_core_type ct1 ** of_core_type ct2 + | Pctf_attribute _ -> + id_fold + | Pctf_extension _ -> + id_fold + +let of_node = function + | Dummy -> id_fold + | Pattern { ppat_desc } -> + of_pattern_desc ppat_desc + | Expression { pexp_desc; } -> + of_expression_desc pexp_desc + | Case { pc_lhs; pc_guard; pc_rhs } -> + of_pattern pc_lhs ** of_expression pc_rhs ** + option_fold of_expression pc_guard + | Class_expr { pcl_desc } -> + of_class_expr_desc pcl_desc + | Class_structure { pcstr_self; pcstr_fields } -> + of_pattern pcstr_self ** + list_fold (fun f -> app (Class_field f)) pcstr_fields + | Class_field { pcf_desc } -> + of_class_field_desc pcf_desc + | Class_field_kind (Cfk_virtual ct) -> + of_core_type ct + | Class_field_kind (Cfk_concrete (_,e)) -> + of_expression e + | Module_expr { pmod_desc } -> + of_module_expr_desc pmod_desc + | Structure str -> + list_fold (fun st -> app (Structure_item(st))) str + | Structure_item ({ pstr_desc }) -> + of_structure_item_desc pstr_desc + | Module_binding pmb -> + app (Module_expr pmb.pmb_expr) ** + app (Module_binding_name pmb) + | Value_binding { pvb_pat; pvb_expr } -> + of_pattern pvb_pat ** + of_expression pvb_expr + | Module_type { pmty_desc } -> + of_module_type_desc pmty_desc + | Signature sgs -> + list_fold (fun sg -> app(Signature_item(sg))) sgs + | Signature_item ({ psig_desc }) -> + of_signature_item_desc psig_desc + | Module_declaration pmd -> + of_module_type pmd.pmd_type ** + app (Module_declaration_name pmd) + | Module_type_declaration pmtd -> + option_fold of_module_type pmtd.pmtd_type ** + app (Module_type_declaration_name pmtd) + | With_constraint (Pwith_type (_,td) | Pwith_typesubst (_,td)) -> + app (Type_declaration td) + | With_constraint (Pwith_module _ | Pwith_modsubst _) -> + id_fold + | With_constraint (Pwith_modtype (_, mt) | Pwith_modtypesubst (_,mt)) -> + of_module_type mt + | Core_type { ptyp_desc } -> + of_core_type_desc ptyp_desc + | Package_type (_, pack_fields) -> + list_fold (fun (_,ct) -> of_core_type ct) pack_fields + | Row_field prf -> begin + match prf.prf_desc with + | Rtag (_,_,cts) -> list_fold of_core_type cts + | Rinherit ct -> of_core_type ct + end + | Value_description { pval_type } -> + of_core_type pval_type + | Type_declaration { ptype_params; ptype_cstrs; ptype_kind; ptype_manifest } -> + let of_typ_cstrs (ct1,ct2,_) = of_core_type ct1 ** of_core_type ct2 in + option_fold of_core_type ptype_manifest ** + list_fold of_typ_param ptype_params ** + app (Type_kind ptype_kind) ** + list_fold of_typ_cstrs ptype_cstrs + | Type_kind (Ptype_abstract | Ptype_open) -> + id_fold + | Type_kind (Ptype_variant cds) -> + list_fold (fun cd -> app (Constructor_declaration cd)) cds + | Type_kind (Ptype_record lds) -> + list_fold (fun ld -> app (Label_declaration ld)) lds + | Type_extension { ptyext_params; ptyext_constructors } -> + list_fold of_typ_param ptyext_params ** + list_fold (fun ec -> app (Extension_constructor ec)) ptyext_constructors + | Extension_constructor { pext_kind = Pext_decl (_, carg,cto) } -> + option_fold of_core_type cto ** + of_constructor_arguments carg + | Extension_constructor { pext_kind = Pext_rebind _ } -> + id_fold + | Label_declaration { pld_type } -> + of_core_type pld_type + | Constructor_declaration { pcd_args; pcd_res } -> + option_fold of_core_type pcd_res ** + of_constructor_arguments pcd_args + | Class_type { pcty_desc } -> + of_class_type_desc pcty_desc + | Class_signature { pcsig_self; pcsig_fields } -> + of_core_type pcsig_self ** + list_fold (fun x -> app (Class_type_field x)) pcsig_fields + | Class_type_field { pctf_desc } -> + of_class_type_field_desc pctf_desc + | Class_declaration { pci_params; pci_expr } -> + app (Class_expr pci_expr) ** + list_fold of_typ_param pci_params + | Class_description { pci_params; pci_expr } -> + app (Class_type pci_expr) ** + list_fold of_typ_param pci_params + | Class_type_declaration { pci_params; pci_expr } -> + app (Class_type pci_expr) ** + list_fold of_typ_param pci_params + (* | Method_call _ -> id_fold + | Record_field _ -> id_fold *) + | Module_binding_name _ -> id_fold + | Module_declaration_name _ -> id_fold + | Module_type_declaration_name _ -> id_fold + | Open_description _ -> id_fold + | Open_declaration pod -> + app (Module_expr pod.popen_expr) + | Include_declaration pi -> + of_module_expr pi.pincl_mod + | Include_description pi -> + of_module_type pi.pincl_mod + | Binding_op { pbop_pat; pbop_exp; } -> + app (Pattern pbop_pat) ** + app (Expression pbop_exp) + +let fold_node f node acc = + of_node node f acc + +(** Accessors for information specific to a node *) + +let string_of_node = function + | Dummy -> "dummy" + | Pattern _ -> "pattern" + | Expression _ -> "expression" + | Case _ -> "case" + | Class_expr _ -> "class_expr" + | Class_structure _ -> "class_structure" + | Class_field _ -> "class_field" + | Class_field_kind _ -> "class_field_kind" + | Module_expr _ -> "module_expr" + (* | Module_type_constraint _ -> "module_type_constraint" *) + | Structure _ -> "structure" + | Structure_item _ -> "structure_item" + | Module_binding _ -> "module_binding" + | Value_binding _ -> "value_binding" + | Module_type _ -> "module_type" + | Signature _ -> "signature" + | Signature_item _ -> "signature_item" + | Module_declaration _ -> "module_declaration" + | Module_type_declaration _ -> "module_type_declaration" + | With_constraint _ -> "with_constraint" + | Core_type _ -> "core_type" + | Package_type _ -> "package_type" + | Row_field _ -> "row_field" + | Value_description _ -> "value_description" + | Type_declaration _ -> "type_declaration" + | Type_kind _ -> "type_kind" + | Type_extension _ -> "type_extension" + | Extension_constructor _ -> "extension_constructor" + | Label_declaration _ -> "label_declaration" + | Constructor_declaration _ -> "constructor_declaration" + | Class_type _ -> "class_type" + | Class_signature _ -> "class_signature" + | Class_type_field _ -> "class_type_field" + | Class_declaration _ -> "class_declaration" + | Class_description _ -> "class_description" + | Class_type_declaration _ -> "class_type_declaration" + | Binding_op _ -> "binding_op" + (* | Method_call _ -> "method_call" + | Record_field _ -> "record_field" *) + | Module_binding_name _ -> "module_binding_name" + | Module_declaration_name _ -> "module_declaration_name" + | Module_type_declaration_name _ -> "module_type_declaration_name" + | Open_description _ -> "open_description" + | Open_declaration _ -> "open_declaration" + | Include_description _ -> "include_description" + | Include_declaration _ -> "include_declaration" + +let node_is_constructor = function + | Constructor_declaration decl -> + Some {decl.pcd_name with Location.txt = `Declaration decl} + | Expression {pexp_desc = Pexp_construct (loc, desc)} -> + Some {loc with Location.txt = `Description desc} + | Extension_constructor ext_cons -> + Some { Location.loc = ext_cons.pext_loc; + txt = `Extension_constructor ext_cons} + | _ -> None + diff --git a/src/ocaml/merlin_specific/browse_raw_p.mli b/src/ocaml/merlin_specific/browse_raw_p.mli new file mode 100644 index 000000000..8c91c5777 --- /dev/null +++ b/src/ocaml/merlin_specific/browse_raw_p.mli @@ -0,0 +1,111 @@ +(* {{{ COPYING *( + Same as Browse_raw module but for the Parstree. + This file is part of Merlin, an helper for ocaml editors + + Copyright (C) 2013 - Pizie Dust + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation the + rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + sell copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + The Software is provided "as is", without warranty of any kind, express or + implied, including but not limited to the warranties of merchantability, + fitness for a particular purpose and noninfringement. In no event shall + the authors or copyright holders be liable for any claim, damages or other + liability, whether in an action of contract, tort or otherwise, arising + from, out of or in connection with the software or the use or other dealings + in the Software. + +)* }}} *) + +type constructor_declaration = Parsetree.constructor_declaration + +open Parsetree + +type node = + | Dummy + | Pattern of pattern + | Expression of expression + | Case of case + | Class_expr of class_expr + | Class_structure of class_structure + | Class_field of class_field + | Class_field_kind of class_field_kind + | Module_expr of module_expr + | Structure of structure + | Signature of signature + | Structure_item of structure_item + | Signature_item of signature_item + | Module_binding of module_binding + | Value_binding of value_binding + | Module_type of module_type + | Module_declaration of module_declaration + | Module_type_declaration of module_type_declaration + | With_constraint of with_constraint + | Core_type of core_type + | Package_type of package_type + | Row_field of row_field + | Value_description of value_description + | Type_declaration of type_declaration + | Type_kind of type_kind + | Type_extension of type_extension + | Extension_constructor of extension_constructor + | Label_declaration of label_declaration + | Constructor_declaration of constructor_declaration + | Class_type of class_type + | Class_signature of class_signature + | Class_type_field of class_type_field + | Class_declaration of class_declaration + | Class_description of class_description + | Class_type_declaration of class_type_declaration + | Binding_op of binding_op + + | Include_description of include_description + | Include_declaration of include_declaration + | Open_description of open_description + | Open_declaration of open_declaration + + (* | Method_call of expression * meth * Location.t + | Record_field of [ `Expression of expression + | `Pattern of pattern ] + * Types.label_description + * Longident.t Location.loc *) + | Module_binding_name of module_binding + | Module_declaration_name of module_declaration + | Module_type_declaration_name of module_type_declaration + +val fold_node : (node -> 'a -> 'a) -> node -> 'a -> 'a + +(** Accessors for information specific to a node *) + +val node_update_env : Env.t -> node -> Env.t +val node_real_loc : Location.t -> node -> Location.t +val node_merlin_loc : Location.t -> node -> Location.t +val node_attributes : node -> attribute list +val has_attr : name:string -> node -> bool + +val string_of_node : node -> string + +(* val node_paths : node -> Path.t Location.loc list +val node_paths_and_longident : node -> (Path.t Location.loc * Longident.t) list *) + +val node_is_constructor : node -> + [ `Description of Parsetree.expression option + | `Declaration of Parsetree.constructor_declaration + | `Extension_constructor of Parsetree.extension_constructor ] + Location.loc option + +(* val node_of_binary_part : Env.t -> Cmt_format.binary_part -> node *) + +(* val all_holes : + Env.t * node -> + (Location.t * + Env.t * + [`Exp of Types.type_expr | `Mod of Types.module_type]) list *) + \ No newline at end of file From 6c92b164feaeb0f555e70ce24d8ff1d3432dad67 Mon Sep 17 00:00:00 2001 From: PizieDust Date: Mon, 18 Mar 2024 07:36:57 +0100 Subject: [PATCH 05/36] expand node logic --- src/frontend/query_commands.ml | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/frontend/query_commands.ml b/src/frontend/query_commands.ml index ce64da921..f7e552861 100644 --- a/src/frontend/query_commands.ml +++ b/src/frontend/query_commands.ml @@ -512,6 +512,31 @@ let dispatch pipeline (type a) : a Query_protocol.t -> a = | Some res -> `Found res | None -> `No_documentation) + | Expand_node pos -> + let typer = Mpipeline.typer_result pipeline in + let pos = Mpipeline.get_lexing_pos pipeline pos in + let p_p = Mpipeline.ppx_parsetree pipeline in + let node = Mtyper.node_at_pp p_p pos in + let t_node = Mtyper.node_at typer pos in + Format.eprintf "Parsetree nodes: %s\n" (Mbrowse_p.print () node); + Format.eprintf "Typedtree nodes: %s\n" (Mbrowse.print () t_node); + let check_deriver (node: Mbrowse_p.node) : bool = + Browse_raw_p.has_attr ~name:"deriving" node && + let attrs = Browse_raw_p.node_attributes node in + List.exists ~f:(fun a -> + let (str,_) = Ast_helper.Attr.as_tuple a in + str.loc.loc_start.pos_cnum - 2 < pos.pos_cnum && str.loc.loc_end.pos_cnum + 1 > pos.pos_cnum + ) attrs + in + let has_deriver = List.exists ~f:check_deriver node in + if has_deriver then + let root = (List.hd (List.rev node)) in + let deriver_node_loc = Mbrowse_p.node_loc (List.nth (List.rev node) 2) in + let derived_nodes = Mbrowse_p.get_children deriver_node_loc root in + `Found (Mbrowse_p.pprint_deriver_nodes () derived_nodes) + else + `No_deriver + | Locate (patho, ml_or_mli, pos) -> let typer = Mpipeline.typer_result pipeline in let local_defs = Mtyper.get_typedtree typer in From bfd199d40c3c68dec63e42f94bd84d8088cd6621 Mon Sep 17 00:00:00 2001 From: PizieDust Date: Mon, 18 Mar 2024 07:37:22 +0100 Subject: [PATCH 06/36] add tests --- tests/test-dirs/expand_node/derivers.t | 228 +++++++++++++++++ tests/test-dirs/expand_node/diff-derivers.t | 92 +++++++ tests/test-dirs/expand_node/record-types.t | 257 ++++++++++++++++++++ 3 files changed, 577 insertions(+) create mode 100644 tests/test-dirs/expand_node/derivers.t create mode 100644 tests/test-dirs/expand_node/diff-derivers.t create mode 100644 tests/test-dirs/expand_node/record-types.t diff --git a/tests/test-dirs/expand_node/derivers.t b/tests/test-dirs/expand_node/derivers.t new file mode 100644 index 000000000..186befb2e --- /dev/null +++ b/tests/test-dirs/expand_node/derivers.t @@ -0,0 +1,228 @@ + $ cat >dune-project < (lang dune 2.9) + > EOF + + $ cat >dune < (executable + > (name apx) + > (libraries yojson) + > (preprocess (pps ppx_yojson_conv))) + > EOF + + $ cat > apx.ml << EOF + > open Ppx_yojson_conv_lib.Yojson_conv.Primitives + > type tttttt = int [@@deriving yojson] + > type point = {x:float; y:float} [@@deriving yojson] + > EOF + + $ dune build +# expand-node will trigger only if the cursor is within the range of the attribute name, in this case "deriving", not on the payload. +# This is a current design choice. +# TODO: Think through if this choice is good. + +cursor is on type +type tttttt = int [@@deriving yojson] +expand-node returns `type_kind` as its leaf, when the position points to a `type` keyword in a type declaration + $ $MERLIN single expand-node -position 2:2 -filename ./apx.ml < ./apx.ml + Parsetree nodes: [ type_kind; type_declaration; structure_item; structure ] + Typedtree nodes: [ type_kind; type_declaration; structure_item; structure ] + { + "class": "return", + "value": "No deriver on this node", + "notifications": [] + } + +cursor is on tttttt +expand-node returns `type_kind` as its leaf, when the position points to a `type` keyword in a type declaration + $ $MERLIN single expand-node -position 2:7 -filename ./apx.ml < ./apx.ml + Parsetree nodes: [ type_kind; type_declaration; structure_item; structure ] + Typedtree nodes: [ type_kind; type_declaration; structure_item; structure ] + { + "class": "return", + "value": "No deriver on this node", + "notifications": [] + } + +cursor is on int +expand-node returns `core_type` as its leaf, when the position points to the core type in a type declaration + $ $MERLIN single expand-node -position 2:17 -filename ./apx.ml < ./apx.ml + Parsetree nodes: [ core_type; type_declaration; structure_item; structure ] + Typedtree nodes: [ core_type; type_declaration; structure_item; structure ] + { + "class": "return", + "value": "No deriver on this node", + "notifications": [] + } + +cursor is on "deriving" +expand-node returns `core_type` as its leaf, when the position points to the the name of an attribute (e.g. "deriving") + $ $MERLIN single expand-node -position 2:25 -filename ./apx.ml < ./apx.ml + Parsetree nodes: [ core_type; type_declaration; structure_item; structure ] + Typedtree nodes: [ core_type; type_declaration; structure_item; structure ] + { + "class": "return", + "value": "[ type tttttt = int[@@deriving yojson] + ; include + struct + let _ = fun (_ : tttttt) -> () + let tttttt_of_yojson = + (int_of_yojson : Ppx_yojson_conv_lib.Yojson.Safe.t -> tttttt) + let _ = tttttt_of_yojson + let yojson_of_tttttt = + (yojson_of_int : tttttt -> Ppx_yojson_conv_lib.Yojson.Safe.t) + let _ = yojson_of_tttttt + end[@@ocaml.doc \"@inline\"][@@merlin.hide ] + ]", + "notifications": [] + } + + +cursor is on "yojson" +expand-node returns `core_type` as its leaf, when the position points to the the payload of an attribute (e.g. "yojson") + $ $MERLIN single expand-node -position 2:34 -filename ./apx.ml < ./apx.ml + Parsetree nodes: [ core_type; type_declaration; structure_item; structure ] + Typedtree nodes: [ core_type; type_declaration; structure_item; structure ] + { + "class": "return", + "value": "No deriver on this node", + "notifications": [] + } + +cursor is on "deriving" +expand-node returns `core_type` as its leaf, when the position points to the name of the attribute (e.g. "deriving") + $ $MERLIN single expand-node -position 3:39 -filename ./apx.ml < ./apx.ml + Parsetree nodes: [ core_type; label_declaration; type_kind; type_declaration; structure_item; structure ] + Typedtree nodes: [ core_type; core_type; label_declaration; type_kind; type_declaration; structure_item; structure ] + { + "class": "return", + "value": "[ type point = { + x: float ; + y: float }[@@deriving yojson] + ; include + struct + let _ = fun (_ : point) -> () + let point_of_yojson = + (let _tp_loc = \"apx.ml.point\" in + function + | `Assoc field_yojsons as yojson -> + let x_field = ref Ppx_yojson_conv_lib.Option.None + and y_field = ref Ppx_yojson_conv_lib.Option.None + and duplicates = ref [] + and extra = ref [] in + let rec iter = + function + | (field_name, _field_yojson)::tail -> + ((match field_name with + | \"x\" -> + (match Ppx_yojson_conv_lib.(!) x_field with + | Ppx_yojson_conv_lib.Option.None -> + let fvalue = float_of_yojson _field_yojson in + x_field := + (Ppx_yojson_conv_lib.Option.Some fvalue) + | Ppx_yojson_conv_lib.Option.Some _ -> + duplicates := (field_name :: + (Ppx_yojson_conv_lib.(!) duplicates))) + | \"y\" -> + (match Ppx_yojson_conv_lib.(!) y_field with + | Ppx_yojson_conv_lib.Option.None -> + let fvalue = float_of_yojson _field_yojson in + y_field := + (Ppx_yojson_conv_lib.Option.Some fvalue) + | Ppx_yojson_conv_lib.Option.Some _ -> + duplicates := (field_name :: + (Ppx_yojson_conv_lib.(!) duplicates))) + | _ -> + if + Ppx_yojson_conv_lib.(!) + Ppx_yojson_conv_lib.Yojson_conv.record_check_extra_fields + then + extra := (field_name :: + (Ppx_yojson_conv_lib.(!) extra)) + else ()); + iter tail) + | [] -> () in + (iter field_yojsons; + (match Ppx_yojson_conv_lib.(!) duplicates with + | _::_ -> + Ppx_yojson_conv_lib.Yojson_conv_error.record_duplicate_fields + _tp_loc (Ppx_yojson_conv_lib.(!) duplicates) yojson + | [] -> + (match Ppx_yojson_conv_lib.(!) extra with + | _::_ -> + Ppx_yojson_conv_lib.Yojson_conv_error.record_extra_fields + _tp_loc (Ppx_yojson_conv_lib.(!) extra) yojson + | [] -> + (match ((Ppx_yojson_conv_lib.(!) x_field), + (Ppx_yojson_conv_lib.(!) y_field)) + with + | (Ppx_yojson_conv_lib.Option.Some x_value, + Ppx_yojson_conv_lib.Option.Some y_value) -> + { x = x_value; y = y_value } + | _ -> + Ppx_yojson_conv_lib.Yojson_conv_error.record_undefined_elements + _tp_loc yojson + [((Ppx_yojson_conv_lib.poly_equal + (Ppx_yojson_conv_lib.(!) x_field) + Ppx_yojson_conv_lib.Option.None), \"x\"); + ((Ppx_yojson_conv_lib.poly_equal + (Ppx_yojson_conv_lib.(!) y_field) + Ppx_yojson_conv_lib.Option.None), \"y\")])))) + | _ as yojson -> + Ppx_yojson_conv_lib.Yojson_conv_error.record_list_instead_atom + _tp_loc yojson : Ppx_yojson_conv_lib.Yojson.Safe.t -> point) + let _ = point_of_yojson + let yojson_of_point = + (function + | { x = v_x; y = v_y } -> + let bnds : (string * Ppx_yojson_conv_lib.Yojson.Safe.t) list = [] in + let bnds = let arg = yojson_of_float v_y in (\"y\", arg) :: bnds in + let bnds = let arg = yojson_of_float v_x in (\"x\", arg) :: bnds in + `Assoc bnds : point -> Ppx_yojson_conv_lib.Yojson.Safe.t) + let _ = yojson_of_point + end[@@ocaml.doc \"@inline\"][@@merlin.hide ] + ]", + "notifications": [] + } + + $ cat >dune-project < (lang dune 2.9) + > EOF + + $ cat >dune < (executable + > (name apc) + > (libraries ppx_compare) + > (preprocess (pps ppx_compare))) + > EOF + + $ cat > apc.ml << EOF + > open Base + > type t = {v:int ; w:int} [@@deriving compare] + > EOF + + $ dune build + + $ $MERLIN single expand-node -position 2:31 -filename ./apc.ml < ./apc.ml + Parsetree nodes: [ core_type; label_declaration; type_kind; type_declaration; structure_item; structure ] + Typedtree nodes: [ core_type; core_type; label_declaration; type_kind; type_declaration; structure_item; structure ] + { + "class": "return", + "value": "[ type t = { + v: int ; + w: int }[@@deriving compare] + ; include + struct + let _ = fun (_ : t) -> () + let compare = + (fun a__001_ b__002_ -> + if Stdlib.(==) a__001_ b__002_ + then 0 + else + (match compare_int a__001_.v b__002_.v with + | 0 -> compare_int a__001_.w b__002_.w + | n -> n) : t -> ((t)[@merlin.hide ]) -> int) + let _ = compare + end[@@ocaml.doc \"@inline\"][@@merlin.hide ] + ]", + "notifications": [] + } diff --git a/tests/test-dirs/expand_node/diff-derivers.t b/tests/test-dirs/expand_node/diff-derivers.t new file mode 100644 index 000000000..8c5c4bbe5 --- /dev/null +++ b/tests/test-dirs/expand_node/diff-derivers.t @@ -0,0 +1,92 @@ +Test with ppx_deriving_yojson + $ cat >dune-project < (lang dune 2.9) + > EOF + + $ cat >dune < (executable + > (name apx) + > (libraries yojson) + > (preprocess (pps ppx_deriving_yojson))) + > EOF + + $ cat > apx.ml << EOF + > type t = int [@@deriving yojson] + > EOF + + $ dune build + +cursor is on "deriving" +expand-node returns expression as a leaf node when cursor is on deriving + $ $MERLIN single expand-node -position 1:17 -filename ./apx.ml < ./apx.ml + Parsetree nodes: [ expression; value_binding; structure_item; structure ] + Typedtree nodes: [ expression; value_binding; structure_item; structure ] + { + "class": "return", + "value": "No deriver on this node", + "notifications": [] + } + +cursor is on "type" +expand-node returns expression as a leaf node when cursor is on the keyword type + $ $MERLIN single expand-node -position 1:3 -filename ./apx.ml < ./apx.ml + Parsetree nodes: [ expression; value_binding; structure_item; structure ] + Typedtree nodes: [ expression; value_binding; structure_item; structure ] + { + "class": "return", + "value": "No deriver on this node", + "notifications": [] + } + + +Test with ppx_yojson_conv + $ cat >dune-project < (lang dune 2.9) + > EOF + + $ cat >dune < (executable + > (name apt) + > (libraries yojson) + > (preprocess (pps ppx_yojson_conv))) + > EOF + + $ cat > apt.ml << EOF + > open Ppx_yojson_conv_lib.Yojson_conv.Primitives + > type t = int [@@deriving yojson] + > EOF + + $ dune build + +cursor is on "deriving" +expand-node returns core_type as a leaf node when cursor is on deriving + $ $MERLIN single expand-node -position 2:17 -filename ./apt.ml < ./apt.ml + Parsetree nodes: [ core_type; type_declaration; structure_item; structure ] + Typedtree nodes: [ core_type; type_declaration; structure_item; structure ] + { + "class": "return", + "value": "[ type t = int[@@deriving yojson] + ; include + struct + let _ = fun (_ : t) -> () + let t_of_yojson = + (int_of_yojson : Ppx_yojson_conv_lib.Yojson.Safe.t -> t) + let _ = t_of_yojson + let yojson_of_t = + (yojson_of_int : t -> Ppx_yojson_conv_lib.Yojson.Safe.t) + let _ = yojson_of_t + end[@@ocaml.doc \"@inline\"][@@merlin.hide ] + ]", + "notifications": [] + } + +cursor is on type +expand-node returns type_kind as a leaf node when cursor is on the keyword type + $ $MERLIN single expand-node -position 2:3 -filename ./apt.ml < ./apt.ml + Parsetree nodes: [ type_kind; type_declaration; structure_item; structure ] + Typedtree nodes: [ type_kind; type_declaration; structure_item; structure ] + { + "class": "return", + "value": "No deriver on this node", + "notifications": [] + } diff --git a/tests/test-dirs/expand_node/record-types.t b/tests/test-dirs/expand_node/record-types.t new file mode 100644 index 000000000..0c5c308aa --- /dev/null +++ b/tests/test-dirs/expand_node/record-types.t @@ -0,0 +1,257 @@ +Tests with ppx_yojson_conv + $ cat >dune-project < (lang dune 2.9) + > EOF + + $ cat >dune < (executable + > (name apx) + > (libraries yojson) + > (preprocess (pps ppx_yojson_conv))) + > EOF + + $ cat > apx.ml << EOF + > open Ppx_yojson_conv_lib.Yojson_conv.Primitives + > type t = {x:float; y:float} [@@deriving yojson] + > type z = {a:float; b:float;} [@@deriving yojson] + > EOF + + $ dune build + +cursor is on "deriving" +type t = {x:float; y:float} without a semicolon after the y field +expand-node has core-type as a leaf node when a semicolon is not added + $ $MERLIN single expand-node -position 2:34 -filename ./apx.ml < ./apx.ml + Parsetree nodes: [ core_type; label_declaration; type_kind; type_declaration; structure_item; structure ] + Typedtree nodes: [ core_type; core_type; label_declaration; type_kind; type_declaration; structure_item; structure ] + { + "class": "return", + "value": "[ type t = { + x: float ; + y: float }[@@deriving yojson] + ; include + struct + let _ = fun (_ : t) -> () + let t_of_yojson = + (let _tp_loc = \"apx.ml.t\" in + function + | `Assoc field_yojsons as yojson -> + let x_field = ref Ppx_yojson_conv_lib.Option.None + and y_field = ref Ppx_yojson_conv_lib.Option.None + and duplicates = ref [] + and extra = ref [] in + let rec iter = + function + | (field_name, _field_yojson)::tail -> + ((match field_name with + | \"x\" -> + (match Ppx_yojson_conv_lib.(!) x_field with + | Ppx_yojson_conv_lib.Option.None -> + let fvalue = float_of_yojson _field_yojson in + x_field := + (Ppx_yojson_conv_lib.Option.Some fvalue) + | Ppx_yojson_conv_lib.Option.Some _ -> + duplicates := (field_name :: + (Ppx_yojson_conv_lib.(!) duplicates))) + | \"y\" -> + (match Ppx_yojson_conv_lib.(!) y_field with + | Ppx_yojson_conv_lib.Option.None -> + let fvalue = float_of_yojson _field_yojson in + y_field := + (Ppx_yojson_conv_lib.Option.Some fvalue) + | Ppx_yojson_conv_lib.Option.Some _ -> + duplicates := (field_name :: + (Ppx_yojson_conv_lib.(!) duplicates))) + | _ -> + if + Ppx_yojson_conv_lib.(!) + Ppx_yojson_conv_lib.Yojson_conv.record_check_extra_fields + then + extra := (field_name :: + (Ppx_yojson_conv_lib.(!) extra)) + else ()); + iter tail) + | [] -> () in + (iter field_yojsons; + (match Ppx_yojson_conv_lib.(!) duplicates with + | _::_ -> + Ppx_yojson_conv_lib.Yojson_conv_error.record_duplicate_fields + _tp_loc (Ppx_yojson_conv_lib.(!) duplicates) yojson + | [] -> + (match Ppx_yojson_conv_lib.(!) extra with + | _::_ -> + Ppx_yojson_conv_lib.Yojson_conv_error.record_extra_fields + _tp_loc (Ppx_yojson_conv_lib.(!) extra) yojson + | [] -> + (match ((Ppx_yojson_conv_lib.(!) x_field), + (Ppx_yojson_conv_lib.(!) y_field)) + with + | (Ppx_yojson_conv_lib.Option.Some x_value, + Ppx_yojson_conv_lib.Option.Some y_value) -> + { x = x_value; y = y_value } + | _ -> + Ppx_yojson_conv_lib.Yojson_conv_error.record_undefined_elements + _tp_loc yojson + [((Ppx_yojson_conv_lib.poly_equal + (Ppx_yojson_conv_lib.(!) x_field) + Ppx_yojson_conv_lib.Option.None), \"x\"); + ((Ppx_yojson_conv_lib.poly_equal + (Ppx_yojson_conv_lib.(!) y_field) + Ppx_yojson_conv_lib.Option.None), \"y\")])))) + | _ as yojson -> + Ppx_yojson_conv_lib.Yojson_conv_error.record_list_instead_atom + _tp_loc yojson : Ppx_yojson_conv_lib.Yojson.Safe.t -> t) + let _ = t_of_yojson + let yojson_of_t = + (function + | { x = v_x; y = v_y } -> + let bnds : (string * Ppx_yojson_conv_lib.Yojson.Safe.t) list = [] in + let bnds = let arg = yojson_of_float v_y in (\"y\", arg) :: bnds in + let bnds = let arg = yojson_of_float v_x in (\"x\", arg) :: bnds in + `Assoc bnds : t -> Ppx_yojson_conv_lib.Yojson.Safe.t) + let _ = yojson_of_t + end[@@ocaml.doc \"@inline\"][@@merlin.hide ] + ]", + "notifications": [] + } + +cursor is on "deriving" +type t = {x:float; y:float;} with a semicolon after the y field +expand-node has label-declaration as a leaf node when the semicolon is added. + $ $MERLIN single expand-node -position 3:34 -filename ./apx.ml < ./apx.ml + Parsetree nodes: [ label_declaration; type_kind; type_declaration; structure_item; structure ] + Typedtree nodes: [ label_declaration; type_kind; type_declaration; structure_item; structure ] + { + "class": "return", + "value": "[ type z = { + a: float ; + b: float }[@@deriving yojson] + ; include + struct + let _ = fun (_ : z) -> () + let z_of_yojson = + (let _tp_loc = \"apx.ml.z\" in + function + | `Assoc field_yojsons as yojson -> + let a_field = ref Ppx_yojson_conv_lib.Option.None + and b_field = ref Ppx_yojson_conv_lib.Option.None + and duplicates = ref [] + and extra = ref [] in + let rec iter = + function + | (field_name, _field_yojson)::tail -> + ((match field_name with + | \"a\" -> + (match Ppx_yojson_conv_lib.(!) a_field with + | Ppx_yojson_conv_lib.Option.None -> + let fvalue = float_of_yojson _field_yojson in + a_field := + (Ppx_yojson_conv_lib.Option.Some fvalue) + | Ppx_yojson_conv_lib.Option.Some _ -> + duplicates := (field_name :: + (Ppx_yojson_conv_lib.(!) duplicates))) + | \"b\" -> + (match Ppx_yojson_conv_lib.(!) b_field with + | Ppx_yojson_conv_lib.Option.None -> + let fvalue = float_of_yojson _field_yojson in + b_field := + (Ppx_yojson_conv_lib.Option.Some fvalue) + | Ppx_yojson_conv_lib.Option.Some _ -> + duplicates := (field_name :: + (Ppx_yojson_conv_lib.(!) duplicates))) + | _ -> + if + Ppx_yojson_conv_lib.(!) + Ppx_yojson_conv_lib.Yojson_conv.record_check_extra_fields + then + extra := (field_name :: + (Ppx_yojson_conv_lib.(!) extra)) + else ()); + iter tail) + | [] -> () in + (iter field_yojsons; + (match Ppx_yojson_conv_lib.(!) duplicates with + | _::_ -> + Ppx_yojson_conv_lib.Yojson_conv_error.record_duplicate_fields + _tp_loc (Ppx_yojson_conv_lib.(!) duplicates) yojson + | [] -> + (match Ppx_yojson_conv_lib.(!) extra with + | _::_ -> + Ppx_yojson_conv_lib.Yojson_conv_error.record_extra_fields + _tp_loc (Ppx_yojson_conv_lib.(!) extra) yojson + | [] -> + (match ((Ppx_yojson_conv_lib.(!) a_field), + (Ppx_yojson_conv_lib.(!) b_field)) + with + | (Ppx_yojson_conv_lib.Option.Some a_value, + Ppx_yojson_conv_lib.Option.Some b_value) -> + { a = a_value; b = b_value } + | _ -> + Ppx_yojson_conv_lib.Yojson_conv_error.record_undefined_elements + _tp_loc yojson + [((Ppx_yojson_conv_lib.poly_equal + (Ppx_yojson_conv_lib.(!) a_field) + Ppx_yojson_conv_lib.Option.None), \"a\"); + ((Ppx_yojson_conv_lib.poly_equal + (Ppx_yojson_conv_lib.(!) b_field) + Ppx_yojson_conv_lib.Option.None), \"b\")])))) + | _ as yojson -> + Ppx_yojson_conv_lib.Yojson_conv_error.record_list_instead_atom + _tp_loc yojson : Ppx_yojson_conv_lib.Yojson.Safe.t -> z) + let _ = z_of_yojson + let yojson_of_z = + (function + | { a = v_a; b = v_b } -> + let bnds : (string * Ppx_yojson_conv_lib.Yojson.Safe.t) list = [] in + let bnds = let arg = yojson_of_float v_b in (\"b\", arg) :: bnds in + let bnds = let arg = yojson_of_float v_a in (\"a\", arg) :: bnds in + `Assoc bnds : z -> Ppx_yojson_conv_lib.Yojson.Safe.t) + let _ = yojson_of_z + end[@@ocaml.doc \"@inline\"][@@merlin.hide ] + ]", + "notifications": [] + } + + +Tests with ppx_deriving_yojson + $ cat >dune-project < (lang dune 2.9) + > EOF + + $ cat >dune < (executable + > (name apt) + > (libraries yojson) + > (preprocess (pps ppx_deriving_yojson))) + > EOF + + $ cat > apt.ml << EOF + > type t = {x:float; y:float} [@@deriving yojson] + > type z = {x:float; y:float;} [@@deriving yojson] + > EOF + + $ dune build + +cursor is on "deriving" +type t = {x:float; y:float} without a semicolon after the y field +expand-node has expression as a leaf node when a semicolon is not added + $ $MERLIN single expand-node -position 1:34 -filename ./apt.ml < ./apt.ml + Parsetree nodes: [ expression; value_binding; structure_item; structure ] + Typedtree nodes: [ expression; value_binding; structure_item; structure ] + { + "class": "return", + "value": "No deriver on this node", + "notifications": [] + } + +cursor is on "deriving" +type t = {x:float; y:float} with a semicolon after the y field +expand-node has expression as a leaf node when the semicolon is added. + $ $MERLIN single expand-node -position 2:35 -filename ./apt.ml < ./apt.ml + Parsetree nodes: [ expression; value_binding; structure_item; structure ] + Typedtree nodes: [ expression; value_binding; structure_item; structure ] + { + "class": "return", + "value": "No deriver on this node", + "notifications": [] + } From 156d31e1d3f940c3e2a365a900c81dbe1d3555ae Mon Sep 17 00:00:00 2001 From: PizieDust Date: Tue, 2 Apr 2024 16:50:09 +0100 Subject: [PATCH 07/36] refactor logic --- src/frontend/query_commands.ml | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/src/frontend/query_commands.ml b/src/frontend/query_commands.ml index f7e552861..08c647993 100644 --- a/src/frontend/query_commands.ml +++ b/src/frontend/query_commands.ml @@ -513,26 +513,22 @@ let dispatch pipeline (type a) : a Query_protocol.t -> a = | None -> `No_documentation) | Expand_node pos -> - let typer = Mpipeline.typer_result pipeline in let pos = Mpipeline.get_lexing_pos pipeline pos in let p_p = Mpipeline.ppx_parsetree pipeline in let node = Mtyper.node_at_pp p_p pos in - let t_node = Mtyper.node_at typer pos in - Format.eprintf "Parsetree nodes: %s\n" (Mbrowse_p.print () node); - Format.eprintf "Typedtree nodes: %s\n" (Mbrowse.print () t_node); - let check_deriver (node: Mbrowse_p.node) : bool = - Browse_raw_p.has_attr ~name:"deriving" node && + let check_ppx (node: Mbrowse_p.node) : bool = + let has_a_deriver = + Browse_raw_p.has_attr ~name:"deriving" node in let attrs = Browse_raw_p.node_attributes node in - List.exists ~f:(fun a -> - let (str,_) = Ast_helper.Attr.as_tuple a in - str.loc.loc_start.pos_cnum - 2 < pos.pos_cnum && str.loc.loc_end.pos_cnum + 1 > pos.pos_cnum - ) attrs + List.exists ~f:(fun (a:Parsetree.attribute) -> + let loc = a.attr_loc in + loc.loc_start.pos_cnum <= pos.pos_cnum && loc.loc_end.pos_cnum >= pos.pos_cnum + ) attrs && has_a_deriver in - let has_deriver = List.exists ~f:check_deriver node in + let has_deriver = List.exists ~f:check_ppx node in if has_deriver then let root = (List.hd (List.rev node)) in - let deriver_node_loc = Mbrowse_p.node_loc (List.nth (List.rev node) 2) in - let derived_nodes = Mbrowse_p.get_children deriver_node_loc root in + let derived_nodes = Mbrowse_p.get_children pos root in `Found (Mbrowse_p.pprint_deriver_nodes () derived_nodes) else `No_deriver From 1e69de031b0561557f379e3b03e3ce4d5d947681 Mon Sep 17 00:00:00 2001 From: PizieDust Date: Tue, 2 Apr 2024 16:50:29 +0100 Subject: [PATCH 08/36] better json response --- src/frontend/ocamlmerlin/query_json.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/frontend/ocamlmerlin/query_json.ml b/src/frontend/ocamlmerlin/query_json.ml index 3cbd68a1a..92df8c98d 100644 --- a/src/frontend/ocamlmerlin/query_json.ml +++ b/src/frontend/ocamlmerlin/query_json.ml @@ -397,7 +397,7 @@ let json_of_response (type a) (query : a t) (response : a) : json = | Expand_node _, resp -> (match resp with | `Found info -> `String info - | `No_deriver -> `String "No deriver on this node" + | `No_deriver -> `String "No PPX deriver/extension node found on this position" | `No_code -> `String "No code generated") | Locate_type _, resp -> json_of_locate resp | Locate _, resp -> json_of_locate resp From 69c5a59f5eb4d63a39c4a0e4264fc011d83bd15f Mon Sep 17 00:00:00 2001 From: PizieDust Date: Tue, 2 Apr 2024 16:51:06 +0100 Subject: [PATCH 09/36] use lexing locations --- src/kernel/mbrowse_p.ml | 10 +++++++--- src/kernel/mbrowse_p.mli | 3 +-- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/kernel/mbrowse_p.ml b/src/kernel/mbrowse_p.ml index 8a334bcdf..69c64c73c 100644 --- a/src/kernel/mbrowse_p.ml +++ b/src/kernel/mbrowse_p.ml @@ -255,15 +255,19 @@ let is_recovered = function let check_node pos node = let loc = node_merlin_loc node in - if Location_aux.compare pos loc = 0 then true else false + if Location_aux.compare_pos pos loc = 0 then true else false let get_children pos root = let children = match root with | Structure str -> - of_structure_items (List.filter ~f:(fun x -> check_node pos (Structure_item(x))) str) + of_structure_items (List.filter ~f:(fun x -> + check_node pos (Structure_item(x)) + ) str) | Signature str -> - of_signature_items (List.filter ~f:(fun x -> check_node pos (Signature_item(x))) str) + of_signature_items (List.filter ~f:(fun x -> + check_node pos (Signature_item(x)) + ) str) | _ -> raise (Invalid_argument "Not a valid root node") in children diff --git a/src/kernel/mbrowse_p.mli b/src/kernel/mbrowse_p.mli index 039470faf..2dabf0d83 100644 --- a/src/kernel/mbrowse_p.mli +++ b/src/kernel/mbrowse_p.mli @@ -80,6 +80,5 @@ val print_node : unit -> node -> string val print : unit -> t -> string val pprint_deriver_node : unit -> node -> string val pprint_deriver_nodes : unit -> node list -> string - -val get_children : Warnings.loc -> node -> node list +val get_children : Lexing.position -> node -> node list From 00d5981aa871d2ed9b6aa24bcf1f0f5522f28224 Mon Sep 17 00:00:00 2001 From: PizieDust Date: Tue, 2 Apr 2024 16:51:25 +0100 Subject: [PATCH 10/36] refactor type exceptions --- src/ocaml/merlin_specific/browse_raw_p.ml | 29 ++++++++++++++-------- src/ocaml/merlin_specific/browse_raw_p.mli | 1 + 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/ocaml/merlin_specific/browse_raw_p.ml b/src/ocaml/merlin_specific/browse_raw_p.ml index 864037dbb..126f04192 100644 --- a/src/ocaml/merlin_specific/browse_raw_p.ml +++ b/src/ocaml/merlin_specific/browse_raw_p.ml @@ -59,6 +59,7 @@ type node = | Type_declaration of type_declaration | Type_kind of type_kind | Type_extension of type_extension + | Type_exception of type_exception | Extension_constructor of extension_constructor | Label_declaration of label_declaration | Constructor_declaration of constructor_declaration @@ -116,13 +117,15 @@ let node_real_loc loc0 = function | Open_description {popen_loc = loc} | Open_declaration {popen_loc = loc} | Binding_op {pbop_loc = loc} + | Type_exception {ptyexn_loc = loc} + | Type_extension {ptyext_loc = loc} -> loc | Module_type_declaration_name {pmtd_name = loc} -> loc.Location.loc | Module_declaration_name {pmd_name = loc} | Module_binding_name {pmb_name = loc} -> loc.Location.loc - | Structure _ | Signature _ | Case _ | Class_structure _ | Type_extension _ + | Structure _ | Signature _ | Case _ | Class_structure _ | Class_field_kind _ | With_constraint _ | Row_field _ | Type_kind _ | Class_signature _ | Package_type _ | Dummy @@ -153,6 +156,7 @@ let node_attributes = function | Label_declaration ld -> ld.pld_attributes | Constructor_declaration cd -> cd.pcd_attributes | Type_extension te -> te.ptyext_attributes + | Type_exception texn -> texn.ptyexn_attributes | Extension_constructor ec -> ec.pext_attributes | Class_type ct -> ct.pcty_attributes | Class_type_field ctf -> ctf.pctf_attributes @@ -164,12 +168,7 @@ let node_attributes = function | Record_field (`Pattern obj,_,_) -> obj.ppat_attributes *) | _ -> [] -let has_attr ~name node = - let attrs = node_attributes node in - List.exists ~f:(fun a -> - let (str,_) = Ast_helper.Attr.as_tuple a in - str.Location.txt = name - ) attrs + let node_merlin_loc loc0 node = let attributes = node_attributes node in @@ -426,10 +425,10 @@ and of_structure_item_desc = function app (Value_description vd) | Pstr_type (_,tds) -> list_fold (fun td -> app (Type_declaration td)) tds - | Pstr_typext text -> + | Pstr_typext text -> app (Type_extension text) | Pstr_exception texn -> - app (Extension_constructor texn.ptyexn_constructor) + app (Type_exception texn) | Pstr_module mb -> app (Module_binding mb) | Pstr_recmodule mbs -> @@ -476,7 +475,7 @@ and of_signature_item_desc = function | Psig_typext text -> app (Type_extension text) | Psig_exception texn -> - app (Extension_constructor texn.ptyexn_constructor) + app (Type_exception texn) | Psig_module md -> app (Module_declaration md) | Psig_recmodule mds -> @@ -618,6 +617,8 @@ let of_node = function | Type_extension { ptyext_params; ptyext_constructors } -> list_fold of_typ_param ptyext_params ** list_fold (fun ec -> app (Extension_constructor ec)) ptyext_constructors + | Type_exception {ptyexn_constructor} -> + app (Extension_constructor ptyexn_constructor) | Extension_constructor { pext_kind = Pext_decl (_, carg,cto) } -> option_fold of_core_type cto ** of_constructor_arguments carg @@ -693,6 +694,7 @@ let string_of_node = function | Type_declaration _ -> "type_declaration" | Type_kind _ -> "type_kind" | Type_extension _ -> "type_extension" + | Type_exception _ -> "type_exception" | Extension_constructor _ -> "extension_constructor" | Label_declaration _ -> "label_declaration" | Constructor_declaration _ -> "constructor_declaration" @@ -723,3 +725,10 @@ let node_is_constructor = function txt = `Extension_constructor ext_cons} | _ -> None +let has_attr ~name node = + let attrs = node_attributes node in + List.exists ~f:(fun a -> + let (str,_) = Ast_helper.Attr.as_tuple a in + str.Location.txt = name + ) attrs + \ No newline at end of file diff --git a/src/ocaml/merlin_specific/browse_raw_p.mli b/src/ocaml/merlin_specific/browse_raw_p.mli index 8c91c5777..f7ad76cd9 100644 --- a/src/ocaml/merlin_specific/browse_raw_p.mli +++ b/src/ocaml/merlin_specific/browse_raw_p.mli @@ -55,6 +55,7 @@ type node = | Type_declaration of type_declaration | Type_kind of type_kind | Type_extension of type_extension + | Type_exception of type_exception | Extension_constructor of extension_constructor | Label_declaration of label_declaration | Constructor_declaration of constructor_declaration From 95dc061fac3e6843a109e02cced37f7428e27853 Mon Sep 17 00:00:00 2001 From: PizieDust Date: Tue, 2 Apr 2024 16:51:54 +0100 Subject: [PATCH 11/36] discard tests of external dependencies --- tests/test-dirs/expand_node/derivers.t | 228 ----------------- tests/test-dirs/expand_node/diff-derivers.t | 92 ------- tests/test-dirs/expand_node/record-types.t | 257 -------------------- 3 files changed, 577 deletions(-) delete mode 100644 tests/test-dirs/expand_node/derivers.t delete mode 100644 tests/test-dirs/expand_node/diff-derivers.t delete mode 100644 tests/test-dirs/expand_node/record-types.t diff --git a/tests/test-dirs/expand_node/derivers.t b/tests/test-dirs/expand_node/derivers.t deleted file mode 100644 index 186befb2e..000000000 --- a/tests/test-dirs/expand_node/derivers.t +++ /dev/null @@ -1,228 +0,0 @@ - $ cat >dune-project < (lang dune 2.9) - > EOF - - $ cat >dune < (executable - > (name apx) - > (libraries yojson) - > (preprocess (pps ppx_yojson_conv))) - > EOF - - $ cat > apx.ml << EOF - > open Ppx_yojson_conv_lib.Yojson_conv.Primitives - > type tttttt = int [@@deriving yojson] - > type point = {x:float; y:float} [@@deriving yojson] - > EOF - - $ dune build -# expand-node will trigger only if the cursor is within the range of the attribute name, in this case "deriving", not on the payload. -# This is a current design choice. -# TODO: Think through if this choice is good. - -cursor is on type -type tttttt = int [@@deriving yojson] -expand-node returns `type_kind` as its leaf, when the position points to a `type` keyword in a type declaration - $ $MERLIN single expand-node -position 2:2 -filename ./apx.ml < ./apx.ml - Parsetree nodes: [ type_kind; type_declaration; structure_item; structure ] - Typedtree nodes: [ type_kind; type_declaration; structure_item; structure ] - { - "class": "return", - "value": "No deriver on this node", - "notifications": [] - } - -cursor is on tttttt -expand-node returns `type_kind` as its leaf, when the position points to a `type` keyword in a type declaration - $ $MERLIN single expand-node -position 2:7 -filename ./apx.ml < ./apx.ml - Parsetree nodes: [ type_kind; type_declaration; structure_item; structure ] - Typedtree nodes: [ type_kind; type_declaration; structure_item; structure ] - { - "class": "return", - "value": "No deriver on this node", - "notifications": [] - } - -cursor is on int -expand-node returns `core_type` as its leaf, when the position points to the core type in a type declaration - $ $MERLIN single expand-node -position 2:17 -filename ./apx.ml < ./apx.ml - Parsetree nodes: [ core_type; type_declaration; structure_item; structure ] - Typedtree nodes: [ core_type; type_declaration; structure_item; structure ] - { - "class": "return", - "value": "No deriver on this node", - "notifications": [] - } - -cursor is on "deriving" -expand-node returns `core_type` as its leaf, when the position points to the the name of an attribute (e.g. "deriving") - $ $MERLIN single expand-node -position 2:25 -filename ./apx.ml < ./apx.ml - Parsetree nodes: [ core_type; type_declaration; structure_item; structure ] - Typedtree nodes: [ core_type; type_declaration; structure_item; structure ] - { - "class": "return", - "value": "[ type tttttt = int[@@deriving yojson] - ; include - struct - let _ = fun (_ : tttttt) -> () - let tttttt_of_yojson = - (int_of_yojson : Ppx_yojson_conv_lib.Yojson.Safe.t -> tttttt) - let _ = tttttt_of_yojson - let yojson_of_tttttt = - (yojson_of_int : tttttt -> Ppx_yojson_conv_lib.Yojson.Safe.t) - let _ = yojson_of_tttttt - end[@@ocaml.doc \"@inline\"][@@merlin.hide ] - ]", - "notifications": [] - } - - -cursor is on "yojson" -expand-node returns `core_type` as its leaf, when the position points to the the payload of an attribute (e.g. "yojson") - $ $MERLIN single expand-node -position 2:34 -filename ./apx.ml < ./apx.ml - Parsetree nodes: [ core_type; type_declaration; structure_item; structure ] - Typedtree nodes: [ core_type; type_declaration; structure_item; structure ] - { - "class": "return", - "value": "No deriver on this node", - "notifications": [] - } - -cursor is on "deriving" -expand-node returns `core_type` as its leaf, when the position points to the name of the attribute (e.g. "deriving") - $ $MERLIN single expand-node -position 3:39 -filename ./apx.ml < ./apx.ml - Parsetree nodes: [ core_type; label_declaration; type_kind; type_declaration; structure_item; structure ] - Typedtree nodes: [ core_type; core_type; label_declaration; type_kind; type_declaration; structure_item; structure ] - { - "class": "return", - "value": "[ type point = { - x: float ; - y: float }[@@deriving yojson] - ; include - struct - let _ = fun (_ : point) -> () - let point_of_yojson = - (let _tp_loc = \"apx.ml.point\" in - function - | `Assoc field_yojsons as yojson -> - let x_field = ref Ppx_yojson_conv_lib.Option.None - and y_field = ref Ppx_yojson_conv_lib.Option.None - and duplicates = ref [] - and extra = ref [] in - let rec iter = - function - | (field_name, _field_yojson)::tail -> - ((match field_name with - | \"x\" -> - (match Ppx_yojson_conv_lib.(!) x_field with - | Ppx_yojson_conv_lib.Option.None -> - let fvalue = float_of_yojson _field_yojson in - x_field := - (Ppx_yojson_conv_lib.Option.Some fvalue) - | Ppx_yojson_conv_lib.Option.Some _ -> - duplicates := (field_name :: - (Ppx_yojson_conv_lib.(!) duplicates))) - | \"y\" -> - (match Ppx_yojson_conv_lib.(!) y_field with - | Ppx_yojson_conv_lib.Option.None -> - let fvalue = float_of_yojson _field_yojson in - y_field := - (Ppx_yojson_conv_lib.Option.Some fvalue) - | Ppx_yojson_conv_lib.Option.Some _ -> - duplicates := (field_name :: - (Ppx_yojson_conv_lib.(!) duplicates))) - | _ -> - if - Ppx_yojson_conv_lib.(!) - Ppx_yojson_conv_lib.Yojson_conv.record_check_extra_fields - then - extra := (field_name :: - (Ppx_yojson_conv_lib.(!) extra)) - else ()); - iter tail) - | [] -> () in - (iter field_yojsons; - (match Ppx_yojson_conv_lib.(!) duplicates with - | _::_ -> - Ppx_yojson_conv_lib.Yojson_conv_error.record_duplicate_fields - _tp_loc (Ppx_yojson_conv_lib.(!) duplicates) yojson - | [] -> - (match Ppx_yojson_conv_lib.(!) extra with - | _::_ -> - Ppx_yojson_conv_lib.Yojson_conv_error.record_extra_fields - _tp_loc (Ppx_yojson_conv_lib.(!) extra) yojson - | [] -> - (match ((Ppx_yojson_conv_lib.(!) x_field), - (Ppx_yojson_conv_lib.(!) y_field)) - with - | (Ppx_yojson_conv_lib.Option.Some x_value, - Ppx_yojson_conv_lib.Option.Some y_value) -> - { x = x_value; y = y_value } - | _ -> - Ppx_yojson_conv_lib.Yojson_conv_error.record_undefined_elements - _tp_loc yojson - [((Ppx_yojson_conv_lib.poly_equal - (Ppx_yojson_conv_lib.(!) x_field) - Ppx_yojson_conv_lib.Option.None), \"x\"); - ((Ppx_yojson_conv_lib.poly_equal - (Ppx_yojson_conv_lib.(!) y_field) - Ppx_yojson_conv_lib.Option.None), \"y\")])))) - | _ as yojson -> - Ppx_yojson_conv_lib.Yojson_conv_error.record_list_instead_atom - _tp_loc yojson : Ppx_yojson_conv_lib.Yojson.Safe.t -> point) - let _ = point_of_yojson - let yojson_of_point = - (function - | { x = v_x; y = v_y } -> - let bnds : (string * Ppx_yojson_conv_lib.Yojson.Safe.t) list = [] in - let bnds = let arg = yojson_of_float v_y in (\"y\", arg) :: bnds in - let bnds = let arg = yojson_of_float v_x in (\"x\", arg) :: bnds in - `Assoc bnds : point -> Ppx_yojson_conv_lib.Yojson.Safe.t) - let _ = yojson_of_point - end[@@ocaml.doc \"@inline\"][@@merlin.hide ] - ]", - "notifications": [] - } - - $ cat >dune-project < (lang dune 2.9) - > EOF - - $ cat >dune < (executable - > (name apc) - > (libraries ppx_compare) - > (preprocess (pps ppx_compare))) - > EOF - - $ cat > apc.ml << EOF - > open Base - > type t = {v:int ; w:int} [@@deriving compare] - > EOF - - $ dune build - - $ $MERLIN single expand-node -position 2:31 -filename ./apc.ml < ./apc.ml - Parsetree nodes: [ core_type; label_declaration; type_kind; type_declaration; structure_item; structure ] - Typedtree nodes: [ core_type; core_type; label_declaration; type_kind; type_declaration; structure_item; structure ] - { - "class": "return", - "value": "[ type t = { - v: int ; - w: int }[@@deriving compare] - ; include - struct - let _ = fun (_ : t) -> () - let compare = - (fun a__001_ b__002_ -> - if Stdlib.(==) a__001_ b__002_ - then 0 - else - (match compare_int a__001_.v b__002_.v with - | 0 -> compare_int a__001_.w b__002_.w - | n -> n) : t -> ((t)[@merlin.hide ]) -> int) - let _ = compare - end[@@ocaml.doc \"@inline\"][@@merlin.hide ] - ]", - "notifications": [] - } diff --git a/tests/test-dirs/expand_node/diff-derivers.t b/tests/test-dirs/expand_node/diff-derivers.t deleted file mode 100644 index 8c5c4bbe5..000000000 --- a/tests/test-dirs/expand_node/diff-derivers.t +++ /dev/null @@ -1,92 +0,0 @@ -Test with ppx_deriving_yojson - $ cat >dune-project < (lang dune 2.9) - > EOF - - $ cat >dune < (executable - > (name apx) - > (libraries yojson) - > (preprocess (pps ppx_deriving_yojson))) - > EOF - - $ cat > apx.ml << EOF - > type t = int [@@deriving yojson] - > EOF - - $ dune build - -cursor is on "deriving" -expand-node returns expression as a leaf node when cursor is on deriving - $ $MERLIN single expand-node -position 1:17 -filename ./apx.ml < ./apx.ml - Parsetree nodes: [ expression; value_binding; structure_item; structure ] - Typedtree nodes: [ expression; value_binding; structure_item; structure ] - { - "class": "return", - "value": "No deriver on this node", - "notifications": [] - } - -cursor is on "type" -expand-node returns expression as a leaf node when cursor is on the keyword type - $ $MERLIN single expand-node -position 1:3 -filename ./apx.ml < ./apx.ml - Parsetree nodes: [ expression; value_binding; structure_item; structure ] - Typedtree nodes: [ expression; value_binding; structure_item; structure ] - { - "class": "return", - "value": "No deriver on this node", - "notifications": [] - } - - -Test with ppx_yojson_conv - $ cat >dune-project < (lang dune 2.9) - > EOF - - $ cat >dune < (executable - > (name apt) - > (libraries yojson) - > (preprocess (pps ppx_yojson_conv))) - > EOF - - $ cat > apt.ml << EOF - > open Ppx_yojson_conv_lib.Yojson_conv.Primitives - > type t = int [@@deriving yojson] - > EOF - - $ dune build - -cursor is on "deriving" -expand-node returns core_type as a leaf node when cursor is on deriving - $ $MERLIN single expand-node -position 2:17 -filename ./apt.ml < ./apt.ml - Parsetree nodes: [ core_type; type_declaration; structure_item; structure ] - Typedtree nodes: [ core_type; type_declaration; structure_item; structure ] - { - "class": "return", - "value": "[ type t = int[@@deriving yojson] - ; include - struct - let _ = fun (_ : t) -> () - let t_of_yojson = - (int_of_yojson : Ppx_yojson_conv_lib.Yojson.Safe.t -> t) - let _ = t_of_yojson - let yojson_of_t = - (yojson_of_int : t -> Ppx_yojson_conv_lib.Yojson.Safe.t) - let _ = yojson_of_t - end[@@ocaml.doc \"@inline\"][@@merlin.hide ] - ]", - "notifications": [] - } - -cursor is on type -expand-node returns type_kind as a leaf node when cursor is on the keyword type - $ $MERLIN single expand-node -position 2:3 -filename ./apt.ml < ./apt.ml - Parsetree nodes: [ type_kind; type_declaration; structure_item; structure ] - Typedtree nodes: [ type_kind; type_declaration; structure_item; structure ] - { - "class": "return", - "value": "No deriver on this node", - "notifications": [] - } diff --git a/tests/test-dirs/expand_node/record-types.t b/tests/test-dirs/expand_node/record-types.t deleted file mode 100644 index 0c5c308aa..000000000 --- a/tests/test-dirs/expand_node/record-types.t +++ /dev/null @@ -1,257 +0,0 @@ -Tests with ppx_yojson_conv - $ cat >dune-project < (lang dune 2.9) - > EOF - - $ cat >dune < (executable - > (name apx) - > (libraries yojson) - > (preprocess (pps ppx_yojson_conv))) - > EOF - - $ cat > apx.ml << EOF - > open Ppx_yojson_conv_lib.Yojson_conv.Primitives - > type t = {x:float; y:float} [@@deriving yojson] - > type z = {a:float; b:float;} [@@deriving yojson] - > EOF - - $ dune build - -cursor is on "deriving" -type t = {x:float; y:float} without a semicolon after the y field -expand-node has core-type as a leaf node when a semicolon is not added - $ $MERLIN single expand-node -position 2:34 -filename ./apx.ml < ./apx.ml - Parsetree nodes: [ core_type; label_declaration; type_kind; type_declaration; structure_item; structure ] - Typedtree nodes: [ core_type; core_type; label_declaration; type_kind; type_declaration; structure_item; structure ] - { - "class": "return", - "value": "[ type t = { - x: float ; - y: float }[@@deriving yojson] - ; include - struct - let _ = fun (_ : t) -> () - let t_of_yojson = - (let _tp_loc = \"apx.ml.t\" in - function - | `Assoc field_yojsons as yojson -> - let x_field = ref Ppx_yojson_conv_lib.Option.None - and y_field = ref Ppx_yojson_conv_lib.Option.None - and duplicates = ref [] - and extra = ref [] in - let rec iter = - function - | (field_name, _field_yojson)::tail -> - ((match field_name with - | \"x\" -> - (match Ppx_yojson_conv_lib.(!) x_field with - | Ppx_yojson_conv_lib.Option.None -> - let fvalue = float_of_yojson _field_yojson in - x_field := - (Ppx_yojson_conv_lib.Option.Some fvalue) - | Ppx_yojson_conv_lib.Option.Some _ -> - duplicates := (field_name :: - (Ppx_yojson_conv_lib.(!) duplicates))) - | \"y\" -> - (match Ppx_yojson_conv_lib.(!) y_field with - | Ppx_yojson_conv_lib.Option.None -> - let fvalue = float_of_yojson _field_yojson in - y_field := - (Ppx_yojson_conv_lib.Option.Some fvalue) - | Ppx_yojson_conv_lib.Option.Some _ -> - duplicates := (field_name :: - (Ppx_yojson_conv_lib.(!) duplicates))) - | _ -> - if - Ppx_yojson_conv_lib.(!) - Ppx_yojson_conv_lib.Yojson_conv.record_check_extra_fields - then - extra := (field_name :: - (Ppx_yojson_conv_lib.(!) extra)) - else ()); - iter tail) - | [] -> () in - (iter field_yojsons; - (match Ppx_yojson_conv_lib.(!) duplicates with - | _::_ -> - Ppx_yojson_conv_lib.Yojson_conv_error.record_duplicate_fields - _tp_loc (Ppx_yojson_conv_lib.(!) duplicates) yojson - | [] -> - (match Ppx_yojson_conv_lib.(!) extra with - | _::_ -> - Ppx_yojson_conv_lib.Yojson_conv_error.record_extra_fields - _tp_loc (Ppx_yojson_conv_lib.(!) extra) yojson - | [] -> - (match ((Ppx_yojson_conv_lib.(!) x_field), - (Ppx_yojson_conv_lib.(!) y_field)) - with - | (Ppx_yojson_conv_lib.Option.Some x_value, - Ppx_yojson_conv_lib.Option.Some y_value) -> - { x = x_value; y = y_value } - | _ -> - Ppx_yojson_conv_lib.Yojson_conv_error.record_undefined_elements - _tp_loc yojson - [((Ppx_yojson_conv_lib.poly_equal - (Ppx_yojson_conv_lib.(!) x_field) - Ppx_yojson_conv_lib.Option.None), \"x\"); - ((Ppx_yojson_conv_lib.poly_equal - (Ppx_yojson_conv_lib.(!) y_field) - Ppx_yojson_conv_lib.Option.None), \"y\")])))) - | _ as yojson -> - Ppx_yojson_conv_lib.Yojson_conv_error.record_list_instead_atom - _tp_loc yojson : Ppx_yojson_conv_lib.Yojson.Safe.t -> t) - let _ = t_of_yojson - let yojson_of_t = - (function - | { x = v_x; y = v_y } -> - let bnds : (string * Ppx_yojson_conv_lib.Yojson.Safe.t) list = [] in - let bnds = let arg = yojson_of_float v_y in (\"y\", arg) :: bnds in - let bnds = let arg = yojson_of_float v_x in (\"x\", arg) :: bnds in - `Assoc bnds : t -> Ppx_yojson_conv_lib.Yojson.Safe.t) - let _ = yojson_of_t - end[@@ocaml.doc \"@inline\"][@@merlin.hide ] - ]", - "notifications": [] - } - -cursor is on "deriving" -type t = {x:float; y:float;} with a semicolon after the y field -expand-node has label-declaration as a leaf node when the semicolon is added. - $ $MERLIN single expand-node -position 3:34 -filename ./apx.ml < ./apx.ml - Parsetree nodes: [ label_declaration; type_kind; type_declaration; structure_item; structure ] - Typedtree nodes: [ label_declaration; type_kind; type_declaration; structure_item; structure ] - { - "class": "return", - "value": "[ type z = { - a: float ; - b: float }[@@deriving yojson] - ; include - struct - let _ = fun (_ : z) -> () - let z_of_yojson = - (let _tp_loc = \"apx.ml.z\" in - function - | `Assoc field_yojsons as yojson -> - let a_field = ref Ppx_yojson_conv_lib.Option.None - and b_field = ref Ppx_yojson_conv_lib.Option.None - and duplicates = ref [] - and extra = ref [] in - let rec iter = - function - | (field_name, _field_yojson)::tail -> - ((match field_name with - | \"a\" -> - (match Ppx_yojson_conv_lib.(!) a_field with - | Ppx_yojson_conv_lib.Option.None -> - let fvalue = float_of_yojson _field_yojson in - a_field := - (Ppx_yojson_conv_lib.Option.Some fvalue) - | Ppx_yojson_conv_lib.Option.Some _ -> - duplicates := (field_name :: - (Ppx_yojson_conv_lib.(!) duplicates))) - | \"b\" -> - (match Ppx_yojson_conv_lib.(!) b_field with - | Ppx_yojson_conv_lib.Option.None -> - let fvalue = float_of_yojson _field_yojson in - b_field := - (Ppx_yojson_conv_lib.Option.Some fvalue) - | Ppx_yojson_conv_lib.Option.Some _ -> - duplicates := (field_name :: - (Ppx_yojson_conv_lib.(!) duplicates))) - | _ -> - if - Ppx_yojson_conv_lib.(!) - Ppx_yojson_conv_lib.Yojson_conv.record_check_extra_fields - then - extra := (field_name :: - (Ppx_yojson_conv_lib.(!) extra)) - else ()); - iter tail) - | [] -> () in - (iter field_yojsons; - (match Ppx_yojson_conv_lib.(!) duplicates with - | _::_ -> - Ppx_yojson_conv_lib.Yojson_conv_error.record_duplicate_fields - _tp_loc (Ppx_yojson_conv_lib.(!) duplicates) yojson - | [] -> - (match Ppx_yojson_conv_lib.(!) extra with - | _::_ -> - Ppx_yojson_conv_lib.Yojson_conv_error.record_extra_fields - _tp_loc (Ppx_yojson_conv_lib.(!) extra) yojson - | [] -> - (match ((Ppx_yojson_conv_lib.(!) a_field), - (Ppx_yojson_conv_lib.(!) b_field)) - with - | (Ppx_yojson_conv_lib.Option.Some a_value, - Ppx_yojson_conv_lib.Option.Some b_value) -> - { a = a_value; b = b_value } - | _ -> - Ppx_yojson_conv_lib.Yojson_conv_error.record_undefined_elements - _tp_loc yojson - [((Ppx_yojson_conv_lib.poly_equal - (Ppx_yojson_conv_lib.(!) a_field) - Ppx_yojson_conv_lib.Option.None), \"a\"); - ((Ppx_yojson_conv_lib.poly_equal - (Ppx_yojson_conv_lib.(!) b_field) - Ppx_yojson_conv_lib.Option.None), \"b\")])))) - | _ as yojson -> - Ppx_yojson_conv_lib.Yojson_conv_error.record_list_instead_atom - _tp_loc yojson : Ppx_yojson_conv_lib.Yojson.Safe.t -> z) - let _ = z_of_yojson - let yojson_of_z = - (function - | { a = v_a; b = v_b } -> - let bnds : (string * Ppx_yojson_conv_lib.Yojson.Safe.t) list = [] in - let bnds = let arg = yojson_of_float v_b in (\"b\", arg) :: bnds in - let bnds = let arg = yojson_of_float v_a in (\"a\", arg) :: bnds in - `Assoc bnds : z -> Ppx_yojson_conv_lib.Yojson.Safe.t) - let _ = yojson_of_z - end[@@ocaml.doc \"@inline\"][@@merlin.hide ] - ]", - "notifications": [] - } - - -Tests with ppx_deriving_yojson - $ cat >dune-project < (lang dune 2.9) - > EOF - - $ cat >dune < (executable - > (name apt) - > (libraries yojson) - > (preprocess (pps ppx_deriving_yojson))) - > EOF - - $ cat > apt.ml << EOF - > type t = {x:float; y:float} [@@deriving yojson] - > type z = {x:float; y:float;} [@@deriving yojson] - > EOF - - $ dune build - -cursor is on "deriving" -type t = {x:float; y:float} without a semicolon after the y field -expand-node has expression as a leaf node when a semicolon is not added - $ $MERLIN single expand-node -position 1:34 -filename ./apt.ml < ./apt.ml - Parsetree nodes: [ expression; value_binding; structure_item; structure ] - Typedtree nodes: [ expression; value_binding; structure_item; structure ] - { - "class": "return", - "value": "No deriver on this node", - "notifications": [] - } - -cursor is on "deriving" -type t = {x:float; y:float} with a semicolon after the y field -expand-node has expression as a leaf node when the semicolon is added. - $ $MERLIN single expand-node -position 2:35 -filename ./apt.ml < ./apt.ml - Parsetree nodes: [ expression; value_binding; structure_item; structure ] - Typedtree nodes: [ expression; value_binding; structure_item; structure ] - { - "class": "return", - "value": "No deriver on this node", - "notifications": [] - } From adc61d369f1d2327096772256d178842729881f7 Mon Sep 17 00:00:00 2001 From: PizieDust Date: Tue, 2 Apr 2024 16:57:13 +0100 Subject: [PATCH 12/36] custom ppx deriver that just renames --- .../expand_node/ppx-tests.t/c_ppx/c_ppx.ml | 163 ++++++++++++++++++ .../expand_node/ppx-tests.t/c_ppx/dune | 4 + 2 files changed, 167 insertions(+) create mode 100644 tests/test-dirs/expand_node/ppx-tests.t/c_ppx/c_ppx.ml create mode 100644 tests/test-dirs/expand_node/ppx-tests.t/c_ppx/dune diff --git a/tests/test-dirs/expand_node/ppx-tests.t/c_ppx/c_ppx.ml b/tests/test-dirs/expand_node/ppx-tests.t/c_ppx/c_ppx.ml new file mode 100644 index 000000000..a910f4a6d --- /dev/null +++ b/tests/test-dirs/expand_node/ppx-tests.t/c_ppx/c_ppx.ml @@ -0,0 +1,163 @@ +open Ppxlib +open Ast_builder.Default + +(* Type declarations in structure *) +let generate_impl ~ctxt (rec_flag, type_declarations) = + let loc = Expansion_context.Deriver.derived_item_loc ctxt in + List.map (fun ty -> + pstr_type ~loc rec_flag + [{ + ptype_loc = loc; + ptype_params = ty.ptype_params; + ptype_cstrs = ty.ptype_cstrs; + ptype_kind = ty.ptype_kind; + ptype_manifest = ty.ptype_manifest; + ptype_private = ty.ptype_private; + ptype_attributes = []; + ptype_name = {txt = ty.ptype_name.txt ^ "_renamed"; loc = loc} + }] + ) type_declarations + +(* Type declarations in signature *) +let generate_intf ~ctxt (rec_flag, type_declarations) = + let loc = Expansion_context.Deriver.derived_item_loc ctxt in + List.map (fun ty -> + psig_type ~loc rec_flag + [{ + ptype_loc = loc; + ptype_params = ty.ptype_params; + ptype_cstrs = ty.ptype_cstrs; + ptype_kind = ty.ptype_kind; + ptype_manifest = ty.ptype_manifest; + ptype_private = ty.ptype_private; + ptype_attributes = []; + ptype_name = {txt = ty.ptype_name.txt ^ "_renamed"; loc = loc} + }] + ) type_declarations + +(* Type_extensions in structure *) +let generate_ext_impl ~ctxt type_extension = + let new_path = Longident.parse ((Longident.name type_extension.ptyext_path.txt) ^ "_renamed") in + let loc = Expansion_context.Deriver.derived_item_loc ctxt in + [ + pstr_typext ~loc + { + ptyext_path = {txt = new_path; loc = loc}; + ptyext_params = type_extension.ptyext_params; + ptyext_constructors = type_extension.ptyext_constructors; + ptyext_private = type_extension.ptyext_private; + ptyext_loc = type_extension.ptyext_loc; + ptyext_attributes = []; + } + ] + +(* Type_extensions in signature *) +let generate_ext_intf ~ctxt type_extension = + let new_path = Longident.parse ((Longident.name type_extension.ptyext_path.txt) ^ "_renamed") in + let loc = Expansion_context.Deriver.derived_item_loc ctxt in + [ + psig_typext ~loc + { + ptyext_path = {txt = new_path; loc = loc}; + ptyext_params = type_extension.ptyext_params; + ptyext_constructors = type_extension.ptyext_constructors; + ptyext_private = type_extension.ptyext_private; + ptyext_loc = type_extension.ptyext_loc; + ptyext_attributes = []; + } + ] + +(* Type_exceptions in structure *) +let generate_exn_impl ~ctxt type_exception = + let loc = Expansion_context.Deriver.derived_item_loc ctxt in + [ + pstr_exception ~loc + { + ptyexn_constructor = { + pext_name = {txt = type_exception.ptyexn_constructor.pext_name.txt ^ "_renamed"; loc = loc}; + pext_kind = type_exception.ptyexn_constructor.pext_kind; + pext_loc = loc; + pext_attributes = []; + }; + ptyexn_loc = loc; + ptyexn_attributes = []; + } + ] + +(* Type_exceptions in signature *) +let generate_exn_intf ~ctxt type_exception = + let loc = Expansion_context.Deriver.derived_item_loc ctxt in + [ + psig_exception ~loc + { + ptyexn_constructor = { + pext_name = {txt = type_exception.ptyexn_constructor.pext_name.txt ^ "_renamed"; loc = loc}; + pext_kind = type_exception.ptyexn_constructor.pext_kind; + pext_loc = loc; + pext_attributes = []; + }; + ptyexn_loc = loc; + ptyexn_attributes = []; + } + ] + +(* Module_type_declarations in structure *) +let generate_mt_impl ~ctxt module_type_declaration = + let loc = Expansion_context.Deriver.derived_item_loc ctxt in + [ + pstr_modtype ~loc + { + pmtd_name = {txt = module_type_declaration.pmtd_name.txt ^ "_renamed"; loc = loc;}; + pmtd_type = module_type_declaration.pmtd_type; + pmtd_attributes = []; + pmtd_loc = loc; + } + ] + +let generate_mt_intf ~ctxt module_type_declaration = + let loc = Expansion_context.Deriver.derived_item_loc ctxt in + [ + psig_modtype ~loc + { + pmtd_name = {txt = module_type_declaration.pmtd_name.txt ^ "_renamed"; loc = loc;}; + pmtd_type = module_type_declaration.pmtd_type; + pmtd_attributes = []; + pmtd_loc = loc; + } + ] + + +(* Driver for type declarations in structures*) +let ty_impl_generator = Deriving.Generator.V2.make_noarg generate_impl + +(* Driver for type declarations in signatures*) +let ty_intf_generator = Deriving.Generator.V2.make_noarg generate_intf + +(* Driver for type_extensions in structures*) +let ext_impl_generator = Deriving.Generator.V2.make_noarg generate_ext_impl + +(* Driver for type_extensions in signatures*) +let ext_intf_generator = Deriving.Generator.V2.make_noarg generate_ext_intf + +(* Driver for type_exceptions in structures*) +let exn_impl_generator = Deriving.Generator.V2.make_noarg generate_exn_impl + +(* Driver for type_exceptions in signatures*) +let exn_intf_generator = Deriving.Generator.V2.make_noarg generate_exn_intf + +(* Driver for module_type_declarations in structures*) +let mdt_impl_generator = Deriving.Generator.V2.make_noarg generate_mt_impl + +(* Driver for module_type_declarations in signatures*) +let mdt_intf_generator = Deriving.Generator.V2.make_noarg generate_mt_intf +let my_deriver = + Deriving.add "rename" + ~str_type_decl:ty_impl_generator + ~sig_type_decl:ty_intf_generator + ~str_type_ext:ext_impl_generator + ~sig_type_ext:ext_intf_generator + ~str_exception:exn_impl_generator + ~sig_exception:exn_intf_generator + ~str_module_type_decl:mdt_impl_generator + ~sig_module_type_decl:mdt_intf_generator + |> Deriving.ignore diff --git a/tests/test-dirs/expand_node/ppx-tests.t/c_ppx/dune b/tests/test-dirs/expand_node/ppx-tests.t/c_ppx/dune new file mode 100644 index 000000000..c6fb4575a --- /dev/null +++ b/tests/test-dirs/expand_node/ppx-tests.t/c_ppx/dune @@ -0,0 +1,4 @@ +(library + (name c_ppx) + (kind ppx_deriver) + (libraries ppxlib)) From c90b945b0bef7c9428d873e774cef504c4e8cdda Mon Sep 17 00:00:00 2001 From: PizieDust Date: Tue, 2 Apr 2024 16:57:27 +0100 Subject: [PATCH 13/36] tests for expand-node feature --- tests/test-dirs/expand_node/ppx-tests.t/run.t | 446 ++++++++++++++++++ 1 file changed, 446 insertions(+) create mode 100644 tests/test-dirs/expand_node/ppx-tests.t/run.t diff --git a/tests/test-dirs/expand_node/ppx-tests.t/run.t b/tests/test-dirs/expand_node/ppx-tests.t/run.t new file mode 100644 index 000000000..6ef1a8bff --- /dev/null +++ b/tests/test-dirs/expand_node/ppx-tests.t/run.t @@ -0,0 +1,446 @@ +Dune setup + $ cat > dune-project << EOF + > (lang dune 2.9) + > EOF + + $ cat > dune << EOF + > (executable + > (name apt) + > (preprocess (pps c_ppx))) + > EOF + +Type declaration in structure + $ cat > apt.ml << EOF + > module MyModule = struct + > type point = {x:int; y:int} [@@deriving rename] + > end + > EOF + + $ dune build + +on keyword module + $ $MERLIN single expand-node -position 1:4 -filename ./apt.ml < ./apt.ml + { + "class": "return", + "value": "No PPX deriver/extension node found on this position", + "notifications": [] + } + +on module name "MyModule" + $ $MERLIN single expand-node -position 1:11 -filename ./apt.ml < ./apt.ml + { + "class": "return", + "value": "No PPX deriver/extension node found on this position", + "notifications": [] + } + +on keyword struct + $ $MERLIN single expand-node -position 1:21 -filename ./apt.ml < ./apt.ml + { + "class": "return", + "value": "No PPX deriver/extension node found on this position", + "notifications": [] + } + +on keyword type + $ $MERLIN single expand-node -position 2:3 -filename ./apt.ml < ./apt.ml + { + "class": "return", + "value": "No PPX deriver/extension node found on this position", + "notifications": [] + } + +on type name "point" + $ $MERLIN single expand-node -position 2:9 -filename ./apt.ml < ./apt.ml + { + "class": "return", + "value": "No PPX deriver/extension node found on this position", + "notifications": [] + } + +on label x + $ $MERLIN single expand-node -position 2:16 -filename ./apt.ml < ./apt.ml + { + "class": "return", + "value": "No PPX deriver/extension node found on this position", + "notifications": [] + } + +on core type "int" + $ $MERLIN single expand-node -position 2:24 -filename ./apt.ml < ./apt.ml + { + "class": "return", + "value": "No PPX deriver/extension node found on this position", + "notifications": [] + } + +on attribute name "deriving" + $ $MERLIN single expand-node -position 2:36 -filename ./apt.ml < ./apt.ml + { + "class": "return", + "value": "[ module MyModule = + struct + type point = { + x: int ; + y: int }[@@deriving rename] + include + struct + let _ = fun (_ : point) -> () + type point_renamed = { + x: int ; + y: int } + end[@@ocaml.doc \"@inline\"][@@merlin.hide ] + end + ]", + "notifications": [] + } + +on attribute payload name "rename" + $ $MERLIN single expand-node -position 2:46 -filename ./apt.ml < ./apt.ml + { + "class": "return", + "value": "[ module MyModule = + struct + type point = { + x: int ; + y: int }[@@deriving rename] + include + struct + let _ = fun (_ : point) -> () + type point_renamed = { + x: int ; + y: int } + end[@@ocaml.doc \"@inline\"][@@merlin.hide ] + end + ]", + "notifications": [] + } + +Type declaration in signature + $ cat > apt.ml << EOF + > module type MyModuleSig = sig + > type tttt = Red | Green [@@deriving rename] + > end + > EOF + + $ dune build + +on keyword module + $ $MERLIN single expand-node -position 1:4 -filename ./apt.ml < ./apt.ml + { + "class": "return", + "value": "No PPX deriver/extension node found on this position", + "notifications": [] + } + +on module name "MyModule" + $ $MERLIN single expand-node -position 1:11 -filename ./apt.ml < ./apt.ml + { + "class": "return", + "value": "No PPX deriver/extension node found on this position", + "notifications": [] + } + +on keyword struct + $ $MERLIN single expand-node -position 1:21 -filename ./apt.ml < ./apt.ml + { + "class": "return", + "value": "No PPX deriver/extension node found on this position", + "notifications": [] + } + +on keyword type + $ $MERLIN single expand-node -position 2:3 -filename ./apt.ml < ./apt.ml + { + "class": "return", + "value": "No PPX deriver/extension node found on this position", + "notifications": [] + } + +on type name "point" + $ $MERLIN single expand-node -position 2:9 -filename ./apt.ml < ./apt.ml + { + "class": "return", + "value": "No PPX deriver/extension node found on this position", + "notifications": [] + } + +on label x + $ $MERLIN single expand-node -position 2:16 -filename ./apt.ml < ./apt.ml + { + "class": "return", + "value": "No PPX deriver/extension node found on this position", + "notifications": [] + } + +on core type "int" + $ $MERLIN single expand-node -position 2:24 -filename ./apt.ml < ./apt.ml + { + "class": "return", + "value": "No PPX deriver/extension node found on this position", + "notifications": [] + } + +on attribute name "deriving" + $ $MERLIN single expand-node -position 2:36 -filename ./apt.ml < ./apt.ml + { + "class": "return", + "value": "[ module type MyModuleSig = + sig + type tttt = + | Red + | Green [@@deriving rename] + include + sig [@@@ocaml.warning \"-32\"] type tttt_renamed = + | Red + | Green end[@@ocaml.doc \"@inline\"] + [@@merlin.hide ] + end + ]", + "notifications": [] + } + +on attribute payload name "rename" + $ $MERLIN single expand-node -position 2:46 -filename ./apt.ml < ./apt.ml + { + "class": "return", + "value": "[ module type MyModuleSig = + sig + type tttt = + | Red + | Green [@@deriving rename] + include + sig [@@@ocaml.warning \"-32\"] type tttt_renamed = + | Red + | Green end[@@ocaml.doc \"@inline\"] + [@@merlin.hide ] + end + ]", + "notifications": [] + } + + +Type declaration in structure + $ cat > apt.ml << EOF + > type yyyy = int [@@deriving rename] + > EOF + + $ dune build + + $ $MERLIN single expand-node -position 1:23 -filename ./apt.ml < ./apt.ml + { + "class": "return", + "value": "[ type yyyy = int[@@deriving rename] + ; include struct let _ = fun (_ : yyyy) -> () + type yyyy_renamed = int end[@@ocaml.doc \"@inline\"][@@merlin.hide + ] + ]", + "notifications": [] + } + + +Type declaration in signature + $ cat > apt.mli << EOF + > type yyyy = int [@@deriving rename] + > EOF + + $ dune build + + + $ $MERLIN single expand-node -position 1:23 -filename ./apt.mli < ./apt.mli + { + "class": "return", + "value": "[ type yyyy = int[@@deriving rename] + ; include sig [@@@ocaml.warning \"-32\"] type yyyy_renamed = int end[@@ocaml.doc + \"@inline\"] + [@@merlin.hide ] + ]", + "notifications": [] + } + + +Type extension in structure + $ cat > apy.ml << EOF + > type pppp = .. [@@deriving rename] + > type pppp += Int of int [@@deriving rename] + > EOF + + $ dune build + + $ $MERLIN single expand-node -position 1:22 -filename ./apy.ml < ./apy.ml + { + "class": "return", + "value": "[ type pppp = ..[@@deriving rename] + ; include struct let _ = fun (_ : pppp) -> () + type pppp_renamed = .. end[@@ocaml.doc \"@inline\"][@@merlin.hide + ] + ]", + "notifications": [] + } + $ $MERLIN single expand-node -position 2:30 -filename ./apy.ml < ./apy.ml + { + "class": "return", + "value": "[ type pppp += + | Int of int [@@deriving rename] + ; include struct type pppp_renamed += + | Int of int end[@@ocaml.doc \"@inline\"][@@merlin.hide ] + ]", + "notifications": [] + } + + +Type extension in signature + $ cat > apy.mli << EOF + > type pppp = .. [@@deriving rename] + > EOF + + $ dune build + + $ $MERLIN single expand-node -position 1:22 -filename ./apy.mli < ./apy.mli + { + "class": "return", + "value": "[ type pppp = ..[@@deriving rename] + ; include sig [@@@ocaml.warning \"-32\"] type pppp_renamed = .. end[@@ocaml.doc + \"@inline\"] + [@@merlin.hide ] + ]", + "notifications": [] + } + +Exception in structure + $ cat > apr.ml << EOF + > exception Foo of string [@@deriving rename] + > EOF + + $ dune build + + $ $MERLIN single expand-node -position 1:30 -filename ./apr.ml < ./apr.ml + { + "class": "return", + "value": "[ exception Foo of string [@@deriving rename] + ; include struct exception Foo_renamed of string end[@@ocaml.doc \"@inline\"] + [@@merlin.hide ] + ]", + "notifications": [] + } + +Exception in signature + $ cat > apr.mli << EOF + > exception Foo of string [@@deriving rename] + > EOF + + $ dune build + + $ $MERLIN single expand-node -position 1:30 -filename ./apr.mli < ./apr.mli + { + "class": "return", + "value": "[ exception Foo of string [@@deriving rename] + ; include sig [@@@ocaml.warning \"-32\"] exception Foo_renamed of string end + [@@ocaml.doc \"@inline\"][@@merlin.hide ] + ]", + "notifications": [] + } + + +Module type declaration in structure + $ cat > apc.ml << EOF + > module type Stack = sig + > type t + > type stack + > val empty : stack + > val is_empty : stack -> bool + > val push : t -> stack -> stack + > val pop : stack -> stack + > val peek : stack -> t + > end [@@deriving rename] + + $ dune build + + $ $MERLIN single expand-node -position 9:8 -filename ./apc.ml < ./apc.ml + { + "class": "return", + "value": "[ module type Stack = + sig + type t + type stack + val empty : stack + val is_empty : stack -> bool + val push : t -> stack -> stack + val pop : stack -> stack + val peek : stack -> t + end[@@deriving rename] + ; include + struct + module type Stack_renamed = + sig + type t + type stack + val empty : stack + val is_empty : stack -> bool + val push : t -> stack -> stack + val pop : stack -> stack + val peek : stack -> t + end + end[@@ocaml.doc \"@inline\"][@@merlin.hide ] + ]", + "notifications": [] + } + +Module type declaration in signature + $ cat > apc.mli << EOF + > module type Stack = sig + > type t + > type stack + > val empty : stack + > val is_empty : stack -> bool + > val push : t -> stack -> stack + > val pop : stack -> stack + > val peek : stack -> t + > end [@@deriving rename] + + $ dune build + + $ $MERLIN single expand-node -position 9:8 -filename ./apc.mli < ./apc.mli + { + "class": "return", + "value": "[ module type Stack = + sig + type t + type stack + val empty : stack + val is_empty : stack -> bool + val push : t -> stack -> stack + val pop : stack -> stack + val peek : stack -> t + end[@@deriving rename] + ; include + sig + [@@@ocaml.warning \"-32\"] + module type Stack_renamed = + sig + type t + type stack + val empty : stack + val is_empty : stack -> bool + val push : t -> stack -> stack + val pop : stack -> stack + val peek : stack -> t + end + end[@@ocaml.doc \"@inline\"][@@merlin.hide ] + ]", + "notifications": [] + } + +Test for an attribute that's not deriving + $ cat > apf.ml << EOF + > type t = int [@@merlin.hide] + > EOF + + $ dune build + + $ $MERLIN single expand-node -position 1:18 -filename ./apf.ml < ./apf.ml + { + "class": "return", + "value": "No PPX deriver/extension node found on this position", + "notifications": [] + } From 4df0492e971923f1122a4446cfdecf2c149187f3 Mon Sep 17 00:00:00 2001 From: PizieDust Date: Tue, 2 Apr 2024 23:13:45 +0100 Subject: [PATCH 14/36] ensure location invariants by setting loc_ghost to true --- .../expand_node/ppx-tests.t/c_ppx/c_ppx.ml | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/test-dirs/expand_node/ppx-tests.t/c_ppx/c_ppx.ml b/tests/test-dirs/expand_node/ppx-tests.t/c_ppx/c_ppx.ml index a910f4a6d..3b3a58287 100644 --- a/tests/test-dirs/expand_node/ppx-tests.t/c_ppx/c_ppx.ml +++ b/tests/test-dirs/expand_node/ppx-tests.t/c_ppx/c_ppx.ml @@ -7,7 +7,7 @@ let generate_impl ~ctxt (rec_flag, type_declarations) = List.map (fun ty -> pstr_type ~loc rec_flag [{ - ptype_loc = loc; + ptype_loc = {loc with loc_ghost = true}; ptype_params = ty.ptype_params; ptype_cstrs = ty.ptype_cstrs; ptype_kind = ty.ptype_kind; @@ -24,7 +24,7 @@ let generate_intf ~ctxt (rec_flag, type_declarations) = List.map (fun ty -> psig_type ~loc rec_flag [{ - ptype_loc = loc; + ptype_loc = {loc with loc_ghost = true}; ptype_params = ty.ptype_params; ptype_cstrs = ty.ptype_cstrs; ptype_kind = ty.ptype_kind; @@ -42,7 +42,7 @@ let generate_ext_impl ~ctxt type_extension = [ pstr_typext ~loc { - ptyext_path = {txt = new_path; loc = loc}; + ptyext_path = {txt = new_path; loc = ({loc with loc_ghost = true})}; ptyext_params = type_extension.ptyext_params; ptyext_constructors = type_extension.ptyext_constructors; ptyext_private = type_extension.ptyext_private; @@ -58,7 +58,7 @@ let generate_ext_intf ~ctxt type_extension = [ psig_typext ~loc { - ptyext_path = {txt = new_path; loc = loc}; + ptyext_path = {txt = new_path; loc = ({loc with loc_ghost = true})}; ptyext_params = type_extension.ptyext_params; ptyext_constructors = type_extension.ptyext_constructors; ptyext_private = type_extension.ptyext_private; @@ -76,10 +76,10 @@ let generate_exn_impl ~ctxt type_exception = ptyexn_constructor = { pext_name = {txt = type_exception.ptyexn_constructor.pext_name.txt ^ "_renamed"; loc = loc}; pext_kind = type_exception.ptyexn_constructor.pext_kind; - pext_loc = loc; + pext_loc = {loc with loc_ghost = true}; pext_attributes = []; }; - ptyexn_loc = loc; + ptyexn_loc = {loc with loc_ghost = true}; ptyexn_attributes = []; } ] @@ -93,10 +93,10 @@ let generate_exn_intf ~ctxt type_exception = ptyexn_constructor = { pext_name = {txt = type_exception.ptyexn_constructor.pext_name.txt ^ "_renamed"; loc = loc}; pext_kind = type_exception.ptyexn_constructor.pext_kind; - pext_loc = loc; + pext_loc = {loc with loc_ghost = true}; pext_attributes = []; }; - ptyexn_loc = loc; + ptyexn_loc = {loc with loc_ghost = true}; ptyexn_attributes = []; } ] @@ -107,10 +107,10 @@ let generate_mt_impl ~ctxt module_type_declaration = [ pstr_modtype ~loc { - pmtd_name = {txt = module_type_declaration.pmtd_name.txt ^ "_renamed"; loc = loc;}; + pmtd_name = {txt = module_type_declaration.pmtd_name.txt ^ "_renamed"; loc = {loc with loc_ghost = true};}; pmtd_type = module_type_declaration.pmtd_type; pmtd_attributes = []; - pmtd_loc = loc; + pmtd_loc = {loc with loc_ghost = true}; } ] @@ -119,10 +119,10 @@ let generate_mt_intf ~ctxt module_type_declaration = [ psig_modtype ~loc { - pmtd_name = {txt = module_type_declaration.pmtd_name.txt ^ "_renamed"; loc = loc;}; + pmtd_name = {txt = module_type_declaration.pmtd_name.txt ^ "_renamed"; loc = {loc with loc_ghost = true};}; pmtd_type = module_type_declaration.pmtd_type; pmtd_attributes = []; - pmtd_loc = loc; + pmtd_loc = {loc with loc_ghost = true}; } ] From 6058f979767be61b4e8d05d6fdc08dffb985b3c7 Mon Sep 17 00:00:00 2001 From: PizieDust Date: Wed, 3 Apr 2024 05:42:26 +0100 Subject: [PATCH 15/36] cover all ghost locations --- .../test-dirs/expand_node/ppx-tests.t/c_ppx/c_ppx.ml | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/test-dirs/expand_node/ppx-tests.t/c_ppx/c_ppx.ml b/tests/test-dirs/expand_node/ppx-tests.t/c_ppx/c_ppx.ml index 3b3a58287..8c0ec0b0f 100644 --- a/tests/test-dirs/expand_node/ppx-tests.t/c_ppx/c_ppx.ml +++ b/tests/test-dirs/expand_node/ppx-tests.t/c_ppx/c_ppx.ml @@ -14,7 +14,7 @@ let generate_impl ~ctxt (rec_flag, type_declarations) = ptype_manifest = ty.ptype_manifest; ptype_private = ty.ptype_private; ptype_attributes = []; - ptype_name = {txt = ty.ptype_name.txt ^ "_renamed"; loc = loc} + ptype_name = {txt = ty.ptype_name.txt ^ "_renamed"; loc = {loc with loc_ghost = true}} }] ) type_declarations @@ -31,7 +31,7 @@ let generate_intf ~ctxt (rec_flag, type_declarations) = ptype_manifest = ty.ptype_manifest; ptype_private = ty.ptype_private; ptype_attributes = []; - ptype_name = {txt = ty.ptype_name.txt ^ "_renamed"; loc = loc} + ptype_name = {txt = ty.ptype_name.txt ^ "_renamed"; loc = {loc with loc_ghost = true}} }] ) type_declarations @@ -46,7 +46,7 @@ let generate_ext_impl ~ctxt type_extension = ptyext_params = type_extension.ptyext_params; ptyext_constructors = type_extension.ptyext_constructors; ptyext_private = type_extension.ptyext_private; - ptyext_loc = type_extension.ptyext_loc; + ptyext_loc = {type_extension.ptyext_loc with loc_ghost = true}; ptyext_attributes = []; } ] @@ -62,7 +62,7 @@ let generate_ext_intf ~ctxt type_extension = ptyext_params = type_extension.ptyext_params; ptyext_constructors = type_extension.ptyext_constructors; ptyext_private = type_extension.ptyext_private; - ptyext_loc = type_extension.ptyext_loc; + ptyext_loc = {type_extension.ptyext_loc with loc_ghost = true}; ptyext_attributes = []; } ] @@ -74,7 +74,7 @@ let generate_exn_impl ~ctxt type_exception = pstr_exception ~loc { ptyexn_constructor = { - pext_name = {txt = type_exception.ptyexn_constructor.pext_name.txt ^ "_renamed"; loc = loc}; + pext_name = {txt = type_exception.ptyexn_constructor.pext_name.txt ^ "_renamed"; loc = {loc with loc_ghost = true}}; pext_kind = type_exception.ptyexn_constructor.pext_kind; pext_loc = {loc with loc_ghost = true}; pext_attributes = []; @@ -91,7 +91,7 @@ let generate_exn_intf ~ctxt type_exception = psig_exception ~loc { ptyexn_constructor = { - pext_name = {txt = type_exception.ptyexn_constructor.pext_name.txt ^ "_renamed"; loc = loc}; + pext_name = {txt = type_exception.ptyexn_constructor.pext_name.txt ^ "_renamed"; loc = {loc with loc_ghost = true}}; pext_kind = type_exception.ptyexn_constructor.pext_kind; pext_loc = {loc with loc_ghost = true}; pext_attributes = []; From e5b77654794bf99655928594605dbfd9be88b44b Mon Sep 17 00:00:00 2001 From: PizieDust Date: Wed, 3 Apr 2024 05:44:52 +0100 Subject: [PATCH 16/36] updated changelog --- CHANGES.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGES.md b/CHANGES.md index f522320c4..254424b8b 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,6 +5,7 @@ merlin NEXT_VERSION - destruct: Removal of residual patterns (#1737, fixes #1560) - Do not erase fields' names when destructing punned record fields (#1734, fixes #1661) + - Implement new expand-node command for expanding PPX annotations (#1745) merlin 4.14 =========== From b412a7bcce58ae99f1be1b5b2668a6f86139a6ac Mon Sep 17 00:00:00 2001 From: PizieDust Date: Wed, 3 Apr 2024 05:48:51 +0100 Subject: [PATCH 17/36] add eof to tests --- tests/test-dirs/expand_node/ppx-tests.t/run.t | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test-dirs/expand_node/ppx-tests.t/run.t b/tests/test-dirs/expand_node/ppx-tests.t/run.t index 6ef1a8bff..f9f06e659 100644 --- a/tests/test-dirs/expand_node/ppx-tests.t/run.t +++ b/tests/test-dirs/expand_node/ppx-tests.t/run.t @@ -353,6 +353,7 @@ Module type declaration in structure > val pop : stack -> stack > val peek : stack -> t > end [@@deriving rename] + > EOF $ dune build @@ -397,6 +398,7 @@ Module type declaration in signature > val pop : stack -> stack > val peek : stack -> t > end [@@deriving rename] + > EOF $ dune build From 72c624acd43786ccb8531daa2909583ec90db325 Mon Sep 17 00:00:00 2001 From: PizieDust Date: Wed, 8 May 2024 17:38:05 +0100 Subject: [PATCH 18/36] better alignment --- src/ocaml/merlin_specific/browse_raw.ml | 40 ++++++++++++------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/ocaml/merlin_specific/browse_raw.ml b/src/ocaml/merlin_specific/browse_raw.ml index 88caa4bed..c63863bd3 100644 --- a/src/ocaml/merlin_specific/browse_raw.ml +++ b/src/ocaml/merlin_specific/browse_raw.ml @@ -165,32 +165,32 @@ let node_attributes = function | Class_field cf -> cf.cf_attributes | Module_expr me -> me.mod_attributes | Structure_item ({str_desc = Tstr_eval (_,attr)},_) -> attr - | Structure_item ({str_desc = Tstr_attribute a},_) -> [a] - | Signature_item ({sig_desc = Tsig_attribute a},_) -> [a] + | Structure_item ({str_desc = Tstr_attribute a},_) -> [a] + | Signature_item ({sig_desc = Tsig_attribute a},_) -> [a] | Module_binding mb -> mb.mb_attributes | Value_binding vb -> vb.vb_attributes | Module_type mt -> mt.mty_attributes | Module_declaration md -> md.md_attributes | Module_type_declaration mtd -> mtd.mtd_attributes - | Open_description o -> o.open_attributes - | Include_declaration i -> i.incl_attributes - | Include_description i -> i.incl_attributes - | Core_type ct -> ct.ctyp_attributes - | Row_field rf -> rf.rf_attributes - | Value_description vd -> vd.val_attributes - | Type_declaration td -> td.typ_attributes - | Label_declaration ld -> ld.ld_attributes - | Constructor_declaration cd -> cd.cd_attributes - | Type_extension te -> te.tyext_attributes - | Extension_constructor ec -> ec.ext_attributes - | Class_type ct -> ct.cltyp_attributes - | Class_type_field ctf -> ctf.ctf_attributes - | Class_declaration ci -> ci.ci_attributes - | Class_description ci -> ci.ci_attributes - | Class_type_declaration ci -> ci.ci_attributes - | Method_call (obj,_,_) -> obj.exp_attributes + | Open_description o -> o.open_attributes + | Include_declaration i -> i.incl_attributes + | Include_description i -> i.incl_attributes + | Core_type ct -> ct.ctyp_attributes + | Row_field rf -> rf.rf_attributes + | Value_description vd -> vd.val_attributes + | Type_declaration td -> td.typ_attributes + | Label_declaration ld -> ld.ld_attributes + | Constructor_declaration cd -> cd.cd_attributes + | Type_extension te -> te.tyext_attributes + | Extension_constructor ec -> ec.ext_attributes + | Class_type ct -> ct.cltyp_attributes + | Class_type_field ctf -> ctf.ctf_attributes + | Class_declaration ci -> ci.ci_attributes + | Class_description ci -> ci.ci_attributes + | Class_type_declaration ci -> ci.ci_attributes + | Method_call (obj,_,_) -> obj.exp_attributes | Record_field (`Expression obj,_,_) -> obj.exp_attributes - | Record_field (`Pattern obj,_,_) -> obj.pat_attributes + | Record_field (`Pattern obj,_,_) -> obj.pat_attributes | _ -> [] let has_attr ~name node = From d2d00d4da1e25973d0bc269a22cb4250f260690c Mon Sep 17 00:00:00 2001 From: PizieDust Date: Wed, 8 May 2024 17:38:54 +0100 Subject: [PATCH 19/36] refactoring for better outputs --- src/kernel/mbrowse_p.ml | 50 ++++++++++++++++++---------------------- src/kernel/mbrowse_p.mli | 2 +- 2 files changed, 24 insertions(+), 28 deletions(-) diff --git a/src/kernel/mbrowse_p.ml b/src/kernel/mbrowse_p.ml index 69c64c73c..038c802ef 100644 --- a/src/kernel/mbrowse_p.ml +++ b/src/kernel/mbrowse_p.ml @@ -199,18 +199,12 @@ let rec select_open_node = let of_structure str = [Browse_raw_p.Structure str] -let rec of_structure_items lst = - match lst with - | [] -> [] - | head :: tl -> - Browse_raw_p.Structure_item head :: (of_structure_items tl) +let of_structure_items lst = + List.map ~f:(fun head -> Browse_raw_p.Structure_item head) lst -let rec of_signature_items lst = - match lst with - | [] -> [] - | head :: tl -> - Browse_raw_p.Signature_item head :: (of_signature_items tl) - +let of_signature_items lst = + List.map ~f:(fun head -> Browse_raw_p.Signature_item head) lst + let of_signature sg = [Browse_raw_p.Signature sg] let of_parsetree = function @@ -254,22 +248,24 @@ let is_recovered = function | _ -> false let check_node pos node = - let loc = node_merlin_loc node in - if Location_aux.compare_pos pos loc = 0 then true else false + let loc = node_merlin_loc node in + if Location_aux.compare_pos pos loc = 0 && loc.loc_ghost then true else false let get_children pos root = - let children = - match root with - | Structure str -> - of_structure_items (List.filter ~f:(fun x -> - check_node pos (Structure_item(x)) - ) str) - | Signature str -> - of_signature_items (List.filter ~f:(fun x -> - check_node pos (Signature_item(x)) - ) str) - | _ -> raise (Invalid_argument "Not a valid root node") - in children + let children = + List.map ~f:(fun x -> + match x with + | Structure str -> + of_structure_items (List.filter ~f:(fun x -> + check_node pos (Structure_item(x)) + ) str) + | Signature str -> + of_signature_items (List.filter ~f:(fun x -> + check_node pos (Signature_item(x)) + ) str) + | _ -> []) root + in children |> List.concat |> List.hd + let pprint_deriver_node () node = let ppf, to_string = Format.to_string () in @@ -281,6 +277,6 @@ let pprint_deriver_node () node = Format.pp_print_newline ppf (); to_string () -let pprint_deriver_nodes () nodes = - List.print (fun () node -> pprint_deriver_node () node) () nodes +let pprint_deriver_nodes = + List.print pprint_deriver_node \ No newline at end of file diff --git a/src/kernel/mbrowse_p.mli b/src/kernel/mbrowse_p.mli index 2dabf0d83..6fdf8a70c 100644 --- a/src/kernel/mbrowse_p.mli +++ b/src/kernel/mbrowse_p.mli @@ -80,5 +80,5 @@ val print_node : unit -> node -> string val print : unit -> t -> string val pprint_deriver_node : unit -> node -> string val pprint_deriver_nodes : unit -> node list -> string -val get_children : Lexing.position -> node -> node list +val get_children : Lexing.position -> node list -> node From a4db7b39a764cb98115ec99537e05a91c5002ac3 Mon Sep 17 00:00:00 2001 From: PizieDust Date: Wed, 8 May 2024 17:39:28 +0100 Subject: [PATCH 20/36] output deriver pos range for lsp --- src/frontend/ocamlmerlin/query_json.ml | 14 +++++++++++--- src/frontend/query_protocol.ml | 21 ++++++++++++++++----- 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/frontend/ocamlmerlin/query_json.ml b/src/frontend/ocamlmerlin/query_json.ml index 92df8c98d..19282e07d 100644 --- a/src/frontend/ocamlmerlin/query_json.ml +++ b/src/frontend/ocamlmerlin/query_json.ml @@ -395,10 +395,18 @@ let json_of_response (type a) (query : a t) (response : a) : json = ] | `No_documentation -> `String "No documentation found") | Expand_node _, resp -> - (match resp with - | `Found info -> `String info + let str = match resp with + | `Found ppx_info -> + `Assoc + [ + ("code", `String ppx_info.code); + ("deriver", `Assoc [ + ("start", Lexing.json_of_position ppx_info.deriver.a_start); + ("end", Lexing.json_of_position ppx_info.deriver.a_end); + ]) + ] | `No_deriver -> `String "No PPX deriver/extension node found on this position" - | `No_code -> `String "No code generated") + in str | Locate_type _, resp -> json_of_locate resp | Locate _, resp -> json_of_locate resp | Jump _, resp -> diff --git a/src/frontend/query_protocol.ml b/src/frontend/query_protocol.ml index 0964f7160..cde043272 100644 --- a/src/frontend/query_protocol.ml +++ b/src/frontend/query_protocol.ml @@ -103,6 +103,18 @@ type syntax_doc_result = documentation : string } +type ppx_deriver_pos = +{ + a_start : Lexing.position; + a_end : Lexing.position; +} + +type ppx_expand_result = +{ + code : string; + deriver : ppx_deriver_pos +} + type is_tail_position = [`No | `Tail_position | `Tail_call] type _ _bool = bool @@ -146,11 +158,10 @@ type _ t = | `No_documentation ] t | Expand_node - : Msource.position - -> [ `Found of string - | `No_deriver - | `No_code - ] t + : Msource.position + -> [ `Found of ppx_expand_result + | `No_deriver + ] t | Locate_type : Msource.position -> [ `Found of string option * Lexing.position From d916c5752c3e312fe1e82629b1e9f79943b58d48 Mon Sep 17 00:00:00 2001 From: PizieDust Date: Wed, 8 May 2024 17:39:39 +0100 Subject: [PATCH 21/36] refactor ppx-expand logic --- src/frontend/query_commands.ml | 54 ++++++++++++++++++++++++---------- 1 file changed, 38 insertions(+), 16 deletions(-) diff --git a/src/frontend/query_commands.ml b/src/frontend/query_commands.ml index 08c647993..675cfc673 100644 --- a/src/frontend/query_commands.ml +++ b/src/frontend/query_commands.ml @@ -507,31 +507,53 @@ let dispatch pipeline (type a) : a Query_protocol.t -> a = let typer = Mpipeline.typer_result pipeline in let pos = Mpipeline.get_lexing_pos pipeline pos in let node = Mtyper.node_at typer pos in - let res = Syntax_doc.get_syntax_doc pos node in + let res = Syntax_doc.get_syntax_doc pos node in (match res with - | Some res -> `Found res + | Some res -> `Found res | None -> `No_documentation) | Expand_node pos -> let pos = Mpipeline.get_lexing_pos pipeline pos in - let p_p = Mpipeline.ppx_parsetree pipeline in - let node = Mtyper.node_at_pp p_p pos in - let check_ppx (node: Mbrowse_p.node) : bool = + let typer = Mpipeline.typer_result pipeline in + let nodes = Mtyper.node_at_p typer pos in + let get_ppx_attribute attrs = + List.find_opt ~f:(fun (a:Parsetree.attribute) -> + let loc = a.attr_loc in + loc.loc_start.pos_cnum <= pos.pos_cnum && loc.loc_end.pos_cnum >= pos.pos_cnum + ) attrs + in + let check_ppx_deriver node = let has_a_deriver = Browse_raw_p.has_attr ~name:"deriving" node in let attrs = Browse_raw_p.node_attributes node in - List.exists ~f:(fun (a:Parsetree.attribute) -> - let loc = a.attr_loc in - loc.loc_start.pos_cnum <= pos.pos_cnum && loc.loc_end.pos_cnum >= pos.pos_cnum - ) attrs && has_a_deriver - in - let has_deriver = List.exists ~f:check_ppx node in - if has_deriver then - let root = (List.hd (List.rev node)) in - let derived_nodes = Mbrowse_p.get_children pos root in - `Found (Mbrowse_p.pprint_deriver_nodes () derived_nodes) - else + let attr = get_ppx_attribute attrs in + let check_attr_loc = + match attr with + | Some _ -> true + | None -> false + in + check_attr_loc && has_a_deriver + in + let deriver_node = List.find_opt ~f:check_ppx_deriver nodes in + begin + match deriver_node with + | Some node -> + let attribute = + Option.get (get_ppx_attribute (Browse_raw_p.node_attributes node)) + in + let derived_nodes = Mbrowse_p.get_children pos nodes in + `Found ( + { + code = (Mbrowse_p.pprint_deriver_node () derived_nodes); + deriver = + { + a_start = attribute.attr_loc.loc_start; + a_end = attribute.attr_loc.loc_end + } + }) + | None -> `No_deriver + end | Locate (patho, ml_or_mli, pos) -> let typer = Mpipeline.typer_result pipeline in From a2e98d6511bad24861c8ad26ce9c99de972706f1 Mon Sep 17 00:00:00 2001 From: PizieDust Date: Wed, 8 May 2024 17:50:59 +0100 Subject: [PATCH 22/36] better tests --- tests/test-dirs/expand_node/ppx-tests.t/run.t | 477 ++++++++++++++---- 1 file changed, 374 insertions(+), 103 deletions(-) diff --git a/tests/test-dirs/expand_node/ppx-tests.t/run.t b/tests/test-dirs/expand_node/ppx-tests.t/run.t index f9f06e659..d5b9e9675 100644 --- a/tests/test-dirs/expand_node/ppx-tests.t/run.t +++ b/tests/test-dirs/expand_node/ppx-tests.t/run.t @@ -50,7 +50,7 @@ on keyword type "notifications": [] } -on type name "point" +on type declaration name "point" $ $MERLIN single expand-node -position 2:9 -filename ./apt.ml < ./apt.ml { "class": "return", @@ -58,7 +58,7 @@ on type name "point" "notifications": [] } -on label x +on record label x $ $MERLIN single expand-node -position 2:16 -filename ./apt.ml < ./apt.ml { "class": "return", @@ -66,7 +66,7 @@ on label x "notifications": [] } -on core type "int" +on record core type "int" $ $MERLIN single expand-node -position 2:24 -filename ./apt.ml < ./apt.ml { "class": "return", @@ -78,20 +78,26 @@ on attribute name "deriving" $ $MERLIN single expand-node -position 2:36 -filename ./apt.ml < ./apt.ml { "class": "return", - "value": "[ module MyModule = + "value": { + "code": "include struct - type point = { + let _ = fun (_ : point) -> () + type point_renamed = { x: int ; - y: int }[@@deriving rename] - include - struct - let _ = fun (_ : point) -> () - type point_renamed = { - x: int ; - y: int } - end[@@ocaml.doc \"@inline\"][@@merlin.hide ] - end - ]", + y: int } + end[@@ocaml.doc \"@inline\"][@@merlin.hide ] + ", + "deriver": { + "start": { + "line": 2, + "col": 29 + }, + "end": { + "line": 2, + "col": 48 + } + } + }, "notifications": [] } @@ -99,20 +105,26 @@ on attribute payload name "rename" $ $MERLIN single expand-node -position 2:46 -filename ./apt.ml < ./apt.ml { "class": "return", - "value": "[ module MyModule = + "value": { + "code": "include struct - type point = { + let _ = fun (_ : point) -> () + type point_renamed = { x: int ; - y: int }[@@deriving rename] - include - struct - let _ = fun (_ : point) -> () - type point_renamed = { - x: int ; - y: int } - end[@@ocaml.doc \"@inline\"][@@merlin.hide ] - end - ]", + y: int } + end[@@ocaml.doc \"@inline\"][@@merlin.hide ] + ", + "deriver": { + "start": { + "line": 2, + "col": 29 + }, + "end": { + "line": 2, + "col": 48 + } + } + }, "notifications": [] } @@ -157,7 +169,7 @@ on keyword type "notifications": [] } -on type name "point" +on type declaration name "point" $ $MERLIN single expand-node -position 2:9 -filename ./apt.ml < ./apt.ml { "class": "return", @@ -165,7 +177,7 @@ on type name "point" "notifications": [] } -on label x +on record label x $ $MERLIN single expand-node -position 2:16 -filename ./apt.ml < ./apt.ml { "class": "return", @@ -173,7 +185,7 @@ on label x "notifications": [] } -on core type "int" +on record core type "int" $ $MERLIN single expand-node -position 2:24 -filename ./apt.ml < ./apt.ml { "class": "return", @@ -185,42 +197,52 @@ on attribute name "deriving" $ $MERLIN single expand-node -position 2:36 -filename ./apt.ml < ./apt.ml { "class": "return", - "value": "[ module type MyModuleSig = - sig - type tttt = - | Red - | Green [@@deriving rename] - include - sig [@@@ocaml.warning \"-32\"] type tttt_renamed = - | Red - | Green end[@@ocaml.doc \"@inline\"] - [@@merlin.hide ] - end - ]", + "value": { + "code": "include sig [@@@ocaml.warning \"-32\"] type tttt_renamed = + | Red + | Green end[@@ocaml.doc \"@inline\"] + [@@merlin.hide ] + ", + "deriver": { + "start": { + "line": 2, + "col": 26 + }, + "end": { + "line": 2, + "col": 45 + } + } + }, "notifications": [] } on attribute payload name "rename" - $ $MERLIN single expand-node -position 2:46 -filename ./apt.ml < ./apt.ml + $ $MERLIN single expand-node -position 2:40 -filename ./apt.ml < ./apt.ml { "class": "return", - "value": "[ module type MyModuleSig = - sig - type tttt = - | Red - | Green [@@deriving rename] - include - sig [@@@ocaml.warning \"-32\"] type tttt_renamed = - | Red - | Green end[@@ocaml.doc \"@inline\"] - [@@merlin.hide ] - end - ]", + "value": { + "code": "include sig [@@@ocaml.warning \"-32\"] type tttt_renamed = + | Red + | Green end[@@ocaml.doc \"@inline\"] + [@@merlin.hide ] + ", + "deriver": { + "start": { + "line": 2, + "col": 26 + }, + "end": { + "line": 2, + "col": 45 + } + } + }, "notifications": [] } -Type declaration in structure +Type declaration in structure B $ cat > apt.ml << EOF > type yyyy = int [@@deriving rename] > EOF @@ -230,16 +252,27 @@ Type declaration in structure $ $MERLIN single expand-node -position 1:23 -filename ./apt.ml < ./apt.ml { "class": "return", - "value": "[ type yyyy = int[@@deriving rename] - ; include struct let _ = fun (_ : yyyy) -> () + "value": { + "code": "include struct let _ = fun (_ : yyyy) -> () type yyyy_renamed = int end[@@ocaml.doc \"@inline\"][@@merlin.hide ] - ]", + ", + "deriver": { + "start": { + "line": 1, + "col": 16 + }, + "end": { + "line": 1, + "col": 35 + } + } + }, "notifications": [] } -Type declaration in signature +Type declaration in signature B $ cat > apt.mli << EOF > type yyyy = int [@@deriving rename] > EOF @@ -250,11 +283,22 @@ Type declaration in signature $ $MERLIN single expand-node -position 1:23 -filename ./apt.mli < ./apt.mli { "class": "return", - "value": "[ type yyyy = int[@@deriving rename] - ; include sig [@@@ocaml.warning \"-32\"] type yyyy_renamed = int end[@@ocaml.doc + "value": { + "code": "include sig [@@@ocaml.warning \"-32\"] type yyyy_renamed = int end[@@ocaml.doc \"@inline\"] [@@merlin.hide ] - ]", + ", + "deriver": { + "start": { + "line": 1, + "col": 16 + }, + "end": { + "line": 1, + "col": 35 + } + } + }, "notifications": [] } @@ -270,21 +314,42 @@ Type extension in structure $ $MERLIN single expand-node -position 1:22 -filename ./apy.ml < ./apy.ml { "class": "return", - "value": "[ type pppp = ..[@@deriving rename] - ; include struct let _ = fun (_ : pppp) -> () + "value": { + "code": "include struct let _ = fun (_ : pppp) -> () type pppp_renamed = .. end[@@ocaml.doc \"@inline\"][@@merlin.hide ] - ]", + ", + "deriver": { + "start": { + "line": 1, + "col": 15 + }, + "end": { + "line": 1, + "col": 34 + } + } + }, "notifications": [] } $ $MERLIN single expand-node -position 2:30 -filename ./apy.ml < ./apy.ml { "class": "return", - "value": "[ type pppp += - | Int of int [@@deriving rename] - ; include struct type pppp_renamed += + "value": { + "code": "include struct type pppp_renamed += | Int of int end[@@ocaml.doc \"@inline\"][@@merlin.hide ] - ]", + ", + "deriver": { + "start": { + "line": 2, + "col": 24 + }, + "end": { + "line": 2, + "col": 43 + } + } + }, "notifications": [] } @@ -299,11 +364,22 @@ Type extension in signature $ $MERLIN single expand-node -position 1:22 -filename ./apy.mli < ./apy.mli { "class": "return", - "value": "[ type pppp = ..[@@deriving rename] - ; include sig [@@@ocaml.warning \"-32\"] type pppp_renamed = .. end[@@ocaml.doc + "value": { + "code": "include sig [@@@ocaml.warning \"-32\"] type pppp_renamed = .. end[@@ocaml.doc \"@inline\"] [@@merlin.hide ] - ]", + ", + "deriver": { + "start": { + "line": 1, + "col": 15 + }, + "end": { + "line": 1, + "col": 34 + } + } + }, "notifications": [] } @@ -317,10 +393,21 @@ Exception in structure $ $MERLIN single expand-node -position 1:30 -filename ./apr.ml < ./apr.ml { "class": "return", - "value": "[ exception Foo of string [@@deriving rename] - ; include struct exception Foo_renamed of string end[@@ocaml.doc \"@inline\"] + "value": { + "code": "include struct exception Foo_renamed of string end[@@ocaml.doc \"@inline\"] [@@merlin.hide ] - ]", + ", + "deriver": { + "start": { + "line": 1, + "col": 24 + }, + "end": { + "line": 1, + "col": 43 + } + } + }, "notifications": [] } @@ -334,10 +421,21 @@ Exception in signature $ $MERLIN single expand-node -position 1:30 -filename ./apr.mli < ./apr.mli { "class": "return", - "value": "[ exception Foo of string [@@deriving rename] - ; include sig [@@@ocaml.warning \"-32\"] exception Foo_renamed of string end + "value": { + "code": "include sig [@@@ocaml.warning \"-32\"] exception Foo_renamed of string end [@@ocaml.doc \"@inline\"][@@merlin.hide ] - ]", + ", + "deriver": { + "start": { + "line": 1, + "col": 24 + }, + "end": { + "line": 1, + "col": 43 + } + } + }, "notifications": [] } @@ -360,17 +458,8 @@ Module type declaration in structure $ $MERLIN single expand-node -position 9:8 -filename ./apc.ml < ./apc.ml { "class": "return", - "value": "[ module type Stack = - sig - type t - type stack - val empty : stack - val is_empty : stack -> bool - val push : t -> stack -> stack - val pop : stack -> stack - val peek : stack -> t - end[@@deriving rename] - ; include + "value": { + "code": "include struct module type Stack_renamed = sig @@ -383,7 +472,18 @@ Module type declaration in structure val peek : stack -> t end end[@@ocaml.doc \"@inline\"][@@merlin.hide ] - ]", + ", + "deriver": { + "start": { + "line": 9, + "col": 4 + }, + "end": { + "line": 9, + "col": 23 + } + } + }, "notifications": [] } @@ -405,17 +505,8 @@ Module type declaration in signature $ $MERLIN single expand-node -position 9:8 -filename ./apc.mli < ./apc.mli { "class": "return", - "value": "[ module type Stack = - sig - type t - type stack - val empty : stack - val is_empty : stack -> bool - val push : t -> stack -> stack - val pop : stack -> stack - val peek : stack -> t - end[@@deriving rename] - ; include + "value": { + "code": "include sig [@@@ocaml.warning \"-32\"] module type Stack_renamed = @@ -429,7 +520,18 @@ Module type declaration in signature val peek : stack -> t end end[@@ocaml.doc \"@inline\"][@@merlin.hide ] - ]", + ", + "deriver": { + "start": { + "line": 9, + "col": 4 + }, + "end": { + "line": 9, + "col": 23 + } + } + }, "notifications": [] } @@ -446,3 +548,172 @@ Test for an attribute that's not deriving "value": "No PPX deriver/extension node found on this position", "notifications": [] } + + +Module type declaration in structure + $ cat > apcc.ml << EOF + > module type Queue = sig + > type t [@@deriving rename] + > type queue + > val enqueue : t -> queue -> queue + > val dequeue : queue -> queue + > val peek : queue -> t + > end [@@deriving rename] + > EOF + + $ dune build + + $ $MERLIN single expand-node -position 2:14 -filename ./apcc.ml < ./apcc.ml + { + "class": "return", + "value": { + "code": "include sig [@@@ocaml.warning \"-32\"] type t_renamed end[@@ocaml.doc + \"@inline\"][@@merlin.hide + ] + ", + "deriver": { + "start": { + "line": 2, + "col": 9 + }, + "end": { + "line": 2, + "col": 28 + } + } + }, + "notifications": [] + } + + + $ $MERLIN single expand-node -position 7:8 -filename ./apcc.ml < ./apcc.ml + { + "class": "return", + "value": { + "code": "include + struct + module type Queue_renamed = + sig + type t[@@deriving rename] + include sig [@@@ocaml.warning \"-32\"] type t_renamed end[@@ocaml.doc + \"@inline\"] + [@@merlin.hide ] + include sig [@@@ocaml.warning \"-32\"] type t_renamed end[@@ocaml.doc + \"@inline\"] + [@@merlin.hide ] + type queue + val enqueue : t -> queue -> queue + val dequeue : queue -> queue + val peek : queue -> t + end + end[@@ocaml.doc \"@inline\"][@@merlin.hide ] + ", + "deriver": { + "start": { + "line": 7, + "col": 4 + }, + "end": { + "line": 7, + "col": 23 + } + } + }, + "notifications": [] + } + +Type declaration in structure + $ cat > ttx.ml << EOF + > module M = struct + > module N = struct + > type t = int [@@deriving rename] + > let a = 1 + > end + > let b = 2 + > end + > let c = 3 + > EOF + + $ dune build + +on [@@deriving rename] + $ $MERLIN single expand-node -position 3:23 -filename ./ttx.ml < ./ttx.ml + { + "class": "return", + "value": { + "code": "include struct let _ = fun (_ : t) -> () + type t_renamed = int end[@@ocaml.doc \"@inline\"][@@merlin.hide + ] + ", + "deriver": { + "start": { + "line": 3, + "col": 18 + }, + "end": { + "line": 3, + "col": 37 + } + } + }, + "notifications": [] + } + + +Type declaration in structure + $ cat > aptt.ml << EOF + > type tont = float * float [@@deriving rename] + > type pont = float * float [@@deriving show] + > EOF + + $ dune build + +on [@@deriving rename] + $ $MERLIN single expand-node -position 1:37 -filename ./aptt.ml < ./aptt.ml + { + "class": "return", + "value": { + "code": "include + struct let _ = fun (_ : tont) -> () + type tont_renamed = (float * float) end[@@ocaml.doc \"@inline\"] + [@@merlin.hide ] + ", + "deriver": { + "start": { + "line": 1, + "col": 26 + }, + "end": { + "line": 1, + "col": 45 + } + } + }, + "notifications": [] + } +on [@@deriving show] + $ $MERLIN single expand-node -position 2:37 -filename ./aptt.ml < ./aptt.ml + { + "class": "return", + "value": { + "code": "include + struct + let _ = fun (_ : pont) -> () + [%%ocaml.error + \"Ppxlib.Deriving: 'show' is not a supported type deriving generator\"] + end[@@ocaml.doc \"@inline\"][@@merlin.hide ] + ", + "deriver": { + "start": { + "line": 2, + "col": 26 + }, + "end": { + "line": 2, + "col": 43 + } + } + }, + "notifications": [] + } + From 2290c173711a123e6162360d7423afe52af0710a Mon Sep 17 00:00:00 2001 From: PizieDust Date: Thu, 9 May 2024 13:30:44 +0100 Subject: [PATCH 23/36] return list of ppx nodes and not only the head --- src/frontend/query_commands.ml | 2 +- src/kernel/mbrowse_p.ml | 2 +- src/kernel/mbrowse_p.mli | 2 +- tests/test-dirs/expand_node/ppx-tests.t/run.t | 89 +++++++++++-------- 4 files changed, 56 insertions(+), 39 deletions(-) diff --git a/src/frontend/query_commands.ml b/src/frontend/query_commands.ml index 675cfc673..11c2b1ba8 100644 --- a/src/frontend/query_commands.ml +++ b/src/frontend/query_commands.ml @@ -544,7 +544,7 @@ let dispatch pipeline (type a) : a Query_protocol.t -> a = let derived_nodes = Mbrowse_p.get_children pos nodes in `Found ( { - code = (Mbrowse_p.pprint_deriver_node () derived_nodes); + code = (Mbrowse_p.pprint_deriver_nodes () derived_nodes); deriver = { a_start = attribute.attr_loc.loc_start; diff --git a/src/kernel/mbrowse_p.ml b/src/kernel/mbrowse_p.ml index 038c802ef..f855498de 100644 --- a/src/kernel/mbrowse_p.ml +++ b/src/kernel/mbrowse_p.ml @@ -264,7 +264,7 @@ let get_children pos root = check_node pos (Signature_item(x)) ) str) | _ -> []) root - in children |> List.concat |> List.hd + in children |> List.concat let pprint_deriver_node () node = diff --git a/src/kernel/mbrowse_p.mli b/src/kernel/mbrowse_p.mli index 6fdf8a70c..fd833bae8 100644 --- a/src/kernel/mbrowse_p.mli +++ b/src/kernel/mbrowse_p.mli @@ -80,5 +80,5 @@ val print_node : unit -> node -> string val print : unit -> t -> string val pprint_deriver_node : unit -> node -> string val pprint_deriver_nodes : unit -> node list -> string -val get_children : Lexing.position -> node list -> node +val get_children : Lexing.position -> node list -> node list diff --git a/tests/test-dirs/expand_node/ppx-tests.t/run.t b/tests/test-dirs/expand_node/ppx-tests.t/run.t index d5b9e9675..6953770e9 100644 --- a/tests/test-dirs/expand_node/ppx-tests.t/run.t +++ b/tests/test-dirs/expand_node/ppx-tests.t/run.t @@ -79,14 +79,14 @@ on attribute name "deriving" { "class": "return", "value": { - "code": "include + "code": "[ include struct let _ = fun (_ : point) -> () type point_renamed = { x: int ; y: int } end[@@ocaml.doc \"@inline\"][@@merlin.hide ] - ", + ]", "deriver": { "start": { "line": 2, @@ -106,14 +106,14 @@ on attribute payload name "rename" { "class": "return", "value": { - "code": "include + "code": "[ include struct let _ = fun (_ : point) -> () type point_renamed = { x: int ; y: int } end[@@ocaml.doc \"@inline\"][@@merlin.hide ] - ", + ]", "deriver": { "start": { "line": 2, @@ -198,11 +198,11 @@ on attribute name "deriving" { "class": "return", "value": { - "code": "include sig [@@@ocaml.warning \"-32\"] type tttt_renamed = + "code": "[ include sig [@@@ocaml.warning \"-32\"] type tttt_renamed = | Red | Green end[@@ocaml.doc \"@inline\"] [@@merlin.hide ] - ", + ]", "deriver": { "start": { "line": 2, @@ -222,11 +222,11 @@ on attribute payload name "rename" { "class": "return", "value": { - "code": "include sig [@@@ocaml.warning \"-32\"] type tttt_renamed = + "code": "[ include sig [@@@ocaml.warning \"-32\"] type tttt_renamed = | Red | Green end[@@ocaml.doc \"@inline\"] [@@merlin.hide ] - ", + ]", "deriver": { "start": { "line": 2, @@ -253,10 +253,10 @@ Type declaration in structure B { "class": "return", "value": { - "code": "include struct let _ = fun (_ : yyyy) -> () + "code": "[ include struct let _ = fun (_ : yyyy) -> () type yyyy_renamed = int end[@@ocaml.doc \"@inline\"][@@merlin.hide ] - ", + ]", "deriver": { "start": { "line": 1, @@ -284,10 +284,10 @@ Type declaration in signature B { "class": "return", "value": { - "code": "include sig [@@@ocaml.warning \"-32\"] type yyyy_renamed = int end[@@ocaml.doc + "code": "[ include sig [@@@ocaml.warning \"-32\"] type yyyy_renamed = int end[@@ocaml.doc \"@inline\"] [@@merlin.hide ] - ", + ]", "deriver": { "start": { "line": 1, @@ -315,10 +315,10 @@ Type extension in structure { "class": "return", "value": { - "code": "include struct let _ = fun (_ : pppp) -> () + "code": "[ include struct let _ = fun (_ : pppp) -> () type pppp_renamed = .. end[@@ocaml.doc \"@inline\"][@@merlin.hide ] - ", + ]", "deriver": { "start": { "line": 1, @@ -336,9 +336,9 @@ Type extension in structure { "class": "return", "value": { - "code": "include struct type pppp_renamed += + "code": "[ include struct type pppp_renamed += | Int of int end[@@ocaml.doc \"@inline\"][@@merlin.hide ] - ", + ]", "deriver": { "start": { "line": 2, @@ -365,10 +365,10 @@ Type extension in signature { "class": "return", "value": { - "code": "include sig [@@@ocaml.warning \"-32\"] type pppp_renamed = .. end[@@ocaml.doc + "code": "[ include sig [@@@ocaml.warning \"-32\"] type pppp_renamed = .. end[@@ocaml.doc \"@inline\"] [@@merlin.hide ] - ", + ]", "deriver": { "start": { "line": 1, @@ -394,9 +394,9 @@ Exception in structure { "class": "return", "value": { - "code": "include struct exception Foo_renamed of string end[@@ocaml.doc \"@inline\"] + "code": "[ include struct exception Foo_renamed of string end[@@ocaml.doc \"@inline\"] [@@merlin.hide ] - ", + ]", "deriver": { "start": { "line": 1, @@ -422,9 +422,9 @@ Exception in signature { "class": "return", "value": { - "code": "include sig [@@@ocaml.warning \"-32\"] exception Foo_renamed of string end + "code": "[ include sig [@@@ocaml.warning \"-32\"] exception Foo_renamed of string end [@@ocaml.doc \"@inline\"][@@merlin.hide ] - ", + ]", "deriver": { "start": { "line": 1, @@ -459,7 +459,7 @@ Module type declaration in structure { "class": "return", "value": { - "code": "include + "code": "[ include struct module type Stack_renamed = sig @@ -472,7 +472,7 @@ Module type declaration in structure val peek : stack -> t end end[@@ocaml.doc \"@inline\"][@@merlin.hide ] - ", + ]", "deriver": { "start": { "line": 9, @@ -506,7 +506,7 @@ Module type declaration in signature { "class": "return", "value": { - "code": "include + "code": "[ include sig [@@@ocaml.warning \"-32\"] module type Stack_renamed = @@ -520,7 +520,7 @@ Module type declaration in signature val peek : stack -> t end end[@@ocaml.doc \"@inline\"][@@merlin.hide ] - ", + ]", "deriver": { "start": { "line": 9, @@ -567,10 +567,27 @@ Module type declaration in structure { "class": "return", "value": { - "code": "include sig [@@@ocaml.warning \"-32\"] type t_renamed end[@@ocaml.doc + "code": "[ include sig [@@@ocaml.warning \"-32\"] type t_renamed end[@@ocaml.doc \"@inline\"][@@merlin.hide ] - ", + ; include + struct + module type Queue_renamed = + sig + type t[@@deriving rename] + include sig [@@@ocaml.warning \"-32\"] type t_renamed end[@@ocaml.doc + \"@inline\"] + [@@merlin.hide ] + include sig [@@@ocaml.warning \"-32\"] type t_renamed end[@@ocaml.doc + \"@inline\"] + [@@merlin.hide ] + type queue + val enqueue : t -> queue -> queue + val dequeue : queue -> queue + val peek : queue -> t + end + end[@@ocaml.doc \"@inline\"][@@merlin.hide ] + ]", "deriver": { "start": { "line": 2, @@ -590,7 +607,7 @@ Module type declaration in structure { "class": "return", "value": { - "code": "include + "code": "[ include struct module type Queue_renamed = sig @@ -607,7 +624,7 @@ Module type declaration in structure val peek : queue -> t end end[@@ocaml.doc \"@inline\"][@@merlin.hide ] - ", + ]", "deriver": { "start": { "line": 7, @@ -641,10 +658,10 @@ on [@@deriving rename] { "class": "return", "value": { - "code": "include struct let _ = fun (_ : t) -> () + "code": "[ include struct let _ = fun (_ : t) -> () type t_renamed = int end[@@ocaml.doc \"@inline\"][@@merlin.hide ] - ", + ]", "deriver": { "start": { "line": 3, @@ -673,11 +690,11 @@ on [@@deriving rename] { "class": "return", "value": { - "code": "include + "code": "[ include struct let _ = fun (_ : tont) -> () type tont_renamed = (float * float) end[@@ocaml.doc \"@inline\"] [@@merlin.hide ] - ", + ]", "deriver": { "start": { "line": 1, @@ -696,13 +713,13 @@ on [@@deriving show] { "class": "return", "value": { - "code": "include + "code": "[ include struct let _ = fun (_ : pont) -> () [%%ocaml.error \"Ppxlib.Deriving: 'show' is not a supported type deriving generator\"] end[@@ocaml.doc \"@inline\"][@@merlin.hide ] - ", + ]", "deriver": { "start": { "line": 2, From ecf359952dfc489ee3a648cc444a315367dc8db7 Mon Sep 17 00:00:00 2001 From: PizieDust Date: Fri, 10 May 2024 23:17:00 +0100 Subject: [PATCH 24/36] remove unused code --- src/kernel/mtyper.ml | 17 ++--------------- src/kernel/mtyper.mli | 5 +---- 2 files changed, 3 insertions(+), 19 deletions(-) diff --git a/src/kernel/mtyper.ml b/src/kernel/mtyper.ml index 0f5f2ae04..0c997e64b 100644 --- a/src/kernel/mtyper.ml +++ b/src/kernel/mtyper.ml @@ -237,21 +237,7 @@ let node_at ?(skip_recovered=false) t pos_cursor = path (* Get the node under the cursor in the Parsetree*) -let node_at_p ?(skip_recovered=false) t pos_cursor = - let node = Mbrowse_p.of_parsetree (get_parsetree t) in - let rec select = function - | _ :: (node' :: _ as ancestors) - when Mbrowse_p.is_recovered node' -> select ancestors - | l -> l - in - match Mbrowse_p.deepest_before pos_cursor [node] with - | [] -> [Browse_raw_p.Dummy] - | path when skip_recovered -> select path - | path -> path - - -(* Get the node under the cursor in the Ppxed-Parsetree*) -let node_at_pp ?(skip_recovered=false) p pos_cursor = +let node_at_p ?(skip_recovered=false) p pos_cursor = let node = Mbrowse_p.of_parsetree p in let rec select = function | _ :: (node' :: _ as ancestors) @@ -263,3 +249,4 @@ let node_at_pp ?(skip_recovered=false) p pos_cursor = | path when skip_recovered -> select path | path -> path + diff --git a/src/kernel/mtyper.mli b/src/kernel/mtyper.mli index f7b4671b1..97692d9e4 100644 --- a/src/kernel/mtyper.mli +++ b/src/kernel/mtyper.mli @@ -54,10 +54,7 @@ val node_at : (* Get the node under the cursor in the Parsetree*) val node_at_p : - ?skip_recovered:bool -> result -> Lexing.position -> Mbrowse_p.t - -(* Get the node under the cursor in the Ppxed-Parsetree*) -val node_at_pp : ?skip_recovered:bool -> Mreader.parsetree -> Lexing.position -> Mbrowse_p.t + From 8586b19a2f7e6cf174066822ef808c95bc637f51 Mon Sep 17 00:00:00 2001 From: PizieDust Date: Fri, 10 May 2024 23:17:26 +0100 Subject: [PATCH 25/36] proper naming --- src/kernel/mbrowse_p.ml | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/kernel/mbrowse_p.ml b/src/kernel/mbrowse_p.ml index f855498de..81d255261 100644 --- a/src/kernel/mbrowse_p.ml +++ b/src/kernel/mbrowse_p.ml @@ -249,21 +249,21 @@ let is_recovered = function let check_node pos node = let loc = node_merlin_loc node in - if Location_aux.compare_pos pos loc = 0 && loc.loc_ghost then true else false + Location_aux.compare_pos pos loc = 0 && loc.loc_ghost -let get_children pos root = +let get_children pos nodes = let children = - List.map ~f:(fun x -> - match x with + List.map ~f:(fun node -> + match node with | Structure str -> - of_structure_items (List.filter ~f:(fun x -> - check_node pos (Structure_item(x)) + of_structure_items (List.filter ~f:(fun n -> + check_node pos (Structure_item(n)) ) str) - | Signature str -> - of_signature_items (List.filter ~f:(fun x -> - check_node pos (Signature_item(x)) - ) str) - | _ -> []) root + | Signature sg -> + of_signature_items (List.filter ~f:(fun n -> + check_node pos (Signature_item(n)) + ) sg) + | _ -> []) nodes in children |> List.concat @@ -272,7 +272,7 @@ let pprint_deriver_node () node = begin match node with | Browse_raw_p.Structure_item n -> Pprintast.structure ppf [n] | Browse_raw_p.Signature_item n -> Pprintast.signature ppf [n] - | _ -> raise (Invalid_argument "Wrong nodes") + | _ -> () end; Format.pp_print_newline ppf (); to_string () From 03fbae7f139d3452b4e4091109daedd53e06fb29 Mon Sep 17 00:00:00 2001 From: PizieDust Date: Fri, 10 May 2024 23:19:22 +0100 Subject: [PATCH 26/36] add ppx_rewriter for test --- .../test-dirs/expand_node/ppx-tests.t/rewriter/dune | 4 ++++ .../expand_node/ppx-tests.t/rewriter/my_ppx.ml | 13 +++++++++++++ 2 files changed, 17 insertions(+) create mode 100644 tests/test-dirs/expand_node/ppx-tests.t/rewriter/dune create mode 100644 tests/test-dirs/expand_node/ppx-tests.t/rewriter/my_ppx.ml diff --git a/tests/test-dirs/expand_node/ppx-tests.t/rewriter/dune b/tests/test-dirs/expand_node/ppx-tests.t/rewriter/dune new file mode 100644 index 000000000..fcbdc1e39 --- /dev/null +++ b/tests/test-dirs/expand_node/ppx-tests.t/rewriter/dune @@ -0,0 +1,4 @@ +(library + (name my_ppx) + (kind ppx_rewriter) + (libraries ppxlib)) diff --git a/tests/test-dirs/expand_node/ppx-tests.t/rewriter/my_ppx.ml b/tests/test-dirs/expand_node/ppx-tests.t/rewriter/my_ppx.ml new file mode 100644 index 000000000..ded887bc8 --- /dev/null +++ b/tests/test-dirs/expand_node/ppx-tests.t/rewriter/my_ppx.ml @@ -0,0 +1,13 @@ +open Ppxlib + +let expand ~ctxt payload = + let loc = Expansion_context.Extension.extension_point_loc ctxt in + Ast_builder.Default.eint ~loc payload + +let my_extension = + Extension.V3.declare "get_int" Extension.Context.expression + Ast_pattern.(single_expr_payload (eint __)) + expand + +let rule = Ppxlib.Context_free.Rule.extension my_extension +let () = Driver.register_transformation ~rules:[ rule ] "get_int" From 9b5ec3a9b2ff31b43e4f220702ccfba9385c0fee Mon Sep 17 00:00:00 2001 From: PizieDust Date: Fri, 10 May 2024 23:19:36 +0100 Subject: [PATCH 27/36] add test for ppx extension nodes --- tests/test-dirs/expand_node/ppx-tests.t/run.t | 29 ++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/tests/test-dirs/expand_node/ppx-tests.t/run.t b/tests/test-dirs/expand_node/ppx-tests.t/run.t index 6953770e9..595be2315 100644 --- a/tests/test-dirs/expand_node/ppx-tests.t/run.t +++ b/tests/test-dirs/expand_node/ppx-tests.t/run.t @@ -6,7 +6,7 @@ Dune setup $ cat > dune << EOF > (executable > (name apt) - > (preprocess (pps c_ppx))) + > (preprocess (pps c_ppx my_ppx))) > EOF Type declaration in structure @@ -734,3 +734,30 @@ on [@@deriving show] "notifications": [] } + +PPx extension + $ cat > apttt.ml << EOF + > let p = 4 + [%get_int 98] + > EOF + + $ dune build + $ $MERLIN single expand-node -position 1:19 -filename ./apttt.ml < ./apttt.ml + { + "class": "return", + "value": { + "code": "4 + 98", + "deriver": { + "start": { + "line": 1, + "col": 13 + }, + "end": { + "line": 1, + "col": 26 + } + } + }, + "notifications": [] + } + + From 400a21d694e3c8880a963b8fa0faa6a992821a73 Mon Sep 17 00:00:00 2001 From: PizieDust Date: Fri, 10 May 2024 23:20:02 +0100 Subject: [PATCH 28/36] add extension nodes and refactor code --- src/frontend/query_commands.ml | 101 ++++++++++++++++++++------------- 1 file changed, 62 insertions(+), 39 deletions(-) diff --git a/src/frontend/query_commands.ml b/src/frontend/query_commands.ml index 11c2b1ba8..a2285cf6e 100644 --- a/src/frontend/query_commands.ml +++ b/src/frontend/query_commands.ml @@ -512,47 +512,70 @@ let dispatch pipeline (type a) : a Query_protocol.t -> a = | Some res -> `Found res | None -> `No_documentation) - | Expand_node pos -> + | Expand_node pos -> let pos = Mpipeline.get_lexing_pos pipeline pos in - let typer = Mpipeline.typer_result pipeline in - let nodes = Mtyper.node_at_p typer pos in - let get_ppx_attribute attrs = - List.find_opt ~f:(fun (a:Parsetree.attribute) -> - let loc = a.attr_loc in - loc.loc_start.pos_cnum <= pos.pos_cnum && loc.loc_end.pos_cnum >= pos.pos_cnum - ) attrs - in - let check_ppx_deriver node = - let has_a_deriver = - Browse_raw_p.has_attr ~name:"deriving" node in - let attrs = Browse_raw_p.node_attributes node in - let attr = get_ppx_attribute attrs in - let check_attr_loc = - match attr with - | Some _ -> true - | None -> false - in - check_attr_loc && has_a_deriver - in - let deriver_node = List.find_opt ~f:check_ppx_deriver nodes in - begin - match deriver_node with - | Some node -> - let attribute = - Option.get (get_ppx_attribute (Browse_raw_p.node_attributes node)) + let parsetree = Mpipeline.reader_parsetree pipeline in + let ppx_parsetree = Mpipeline.ppx_parsetree pipeline in + let nodes = Mtyper.node_at_p parsetree pos in + let ppx_expansion ~ppx ~a_start ~a_end = + `Found ({ + code = ppx; + deriver = { + a_start; + a_end + } + }) + in + let check_at_pos (loc:Warnings.loc) = + loc.loc_start.pos_cnum <= pos.pos_cnum && loc.loc_end.pos_cnum >= pos.pos_cnum + in + let has_ppx_deriver (attr:Parsetree.attribute) = + check_at_pos attr.attr_loc && + attr.attr_name.txt = "deriving" + in + let has_ppx_extension (node:Browse_raw_p.node) = + match node with + | Expression {pexp_desc = Pexp_extension _; pexp_loc = loc; _} -> + check_at_pos loc + | _ -> false + in + let extension = List.find_opt ~f:has_ppx_extension nodes in + begin match extension with + | Some (Expression ({pexp_desc = Pexp_extension _; _} as exp)) -> + let ppx = ref None in + let expr (self : Ast_iterator.iterator) (expr : Parsetree.expression) = + match check_at_pos expr.pexp_loc with + | true -> + ppx := Some expr + | false -> Ast_iterator.default_iterator.expr self expr + in + let iterator = { Ast_iterator.default_iterator with expr } in + let _ = + match ppx_parsetree with + | `Interface si -> iterator.signature iterator si + | `Implementation str -> iterator.structure iterator str + in + Option.map !ppx ~f:(fun expr -> + ppx_expansion ~ppx:(Pprintast.string_of_expression expr) + ~a_start:(exp.pexp_loc.loc_start) + ~a_end:(exp.pexp_loc.loc_end)) + |> Option.get + | Some _ | None -> + let nodes = Mtyper.node_at_p ppx_parsetree pos in + let has_deriving_attribute = + List.find_opt ~f:has_ppx_deriver + (List.concat_map ~f:(fun node -> + Browse_raw_p.node_attributes node) nodes) in - let derived_nodes = Mbrowse_p.get_children pos nodes in - `Found ( - { - code = (Mbrowse_p.pprint_deriver_nodes () derived_nodes); - deriver = - { - a_start = attribute.attr_loc.loc_start; - a_end = attribute.attr_loc.loc_end - } - }) - | None -> - `No_deriver + (match has_deriving_attribute with + | Some attribute -> + let derived_nodes = Mbrowse_p.get_children pos nodes in + ppx_expansion + ~ppx:(Mbrowse_p.pprint_deriver_nodes () derived_nodes) + ~a_start:(attribute.attr_loc.loc_start) + ~a_end:(attribute.attr_loc.loc_end) + | None -> + `No_deriver) end | Locate (patho, ml_or_mli, pos) -> From 26f97ed7965644f40000634ba75907fbf56bdefe Mon Sep 17 00:00:00 2001 From: PizieDust Date: Fri, 10 May 2024 23:38:28 +0100 Subject: [PATCH 29/36] ppx extension tests --- .../ppx-tests.t/rewriter/my_ppx.ml | 9 ++--- tests/test-dirs/expand_node/ppx-tests.t/run.t | 33 ++++++++++++++++--- 2 files changed, 34 insertions(+), 8 deletions(-) diff --git a/tests/test-dirs/expand_node/ppx-tests.t/rewriter/my_ppx.ml b/tests/test-dirs/expand_node/ppx-tests.t/rewriter/my_ppx.ml index ded887bc8..797578f42 100644 --- a/tests/test-dirs/expand_node/ppx-tests.t/rewriter/my_ppx.ml +++ b/tests/test-dirs/expand_node/ppx-tests.t/rewriter/my_ppx.ml @@ -1,13 +1,14 @@ open Ppxlib let expand ~ctxt payload = + let _p = payload in let loc = Expansion_context.Extension.extension_point_loc ctxt in - Ast_builder.Default.eint ~loc payload + Ast_builder.Default.estring ~loc "OCaml is so cool" let my_extension = - Extension.V3.declare "get_int" Extension.Context.expression - Ast_pattern.(single_expr_payload (eint __)) + Extension.V3.declare "tell_me" Extension.Context.expression + Ast_pattern.(__) expand let rule = Ppxlib.Context_free.Rule.extension my_extension -let () = Driver.register_transformation ~rules:[ rule ] "get_int" +let () = Driver.register_transformation ~rules:[ rule ] "tell_me" diff --git a/tests/test-dirs/expand_node/ppx-tests.t/run.t b/tests/test-dirs/expand_node/ppx-tests.t/run.t index 595be2315..b9ebfee31 100644 --- a/tests/test-dirs/expand_node/ppx-tests.t/run.t +++ b/tests/test-dirs/expand_node/ppx-tests.t/run.t @@ -737,27 +737,52 @@ on [@@deriving show] PPx extension $ cat > apttt.ml << EOF - > let p = 4 + [%get_int 98] + > let phrase = print_string ([%tell_me] ^ ":-)!") > EOF $ dune build + $ $MERLIN single expand-node -position 1:2 -filename ./apttt.ml < ./apttt.ml + { + "class": "return", + "value": "No PPX deriver/extension node found on this position", + "notifications": [] + } + $ $MERLIN single expand-node -position 1:7 -filename ./apttt.ml < ./apttt.ml + { + "class": "return", + "value": "No PPX deriver/extension node found on this position", + "notifications": [] + } $ $MERLIN single expand-node -position 1:19 -filename ./apttt.ml < ./apttt.ml + { + "class": "return", + "value": "No PPX deriver/extension node found on this position", + "notifications": [] + } + $ $MERLIN single expand-node -position 1:30 -filename ./apttt.ml < ./apttt.ml { "class": "return", "value": { - "code": "4 + 98", + "code": "print_string (\"OCaml is so cool\" ^ \":-)!\")", "deriver": { "start": { "line": 1, - "col": 13 + "col": 27 }, "end": { "line": 1, - "col": 26 + "col": 37 } } }, "notifications": [] } + $ $MERLIN single expand-node -position 1:41 -filename ./apttt.ml < ./apttt.ml + { + "class": "return", + "value": "No PPX deriver/extension node found on this position", + "notifications": [] + } + From 37315787cbf622791a87067e87afa94f3212b5a9 Mon Sep 17 00:00:00 2001 From: PizieDust Date: Sat, 11 May 2024 10:37:23 +0100 Subject: [PATCH 30/36] restrict selection only to expressions under the cursor --- src/frontend/query_commands.ml | 5 +- tests/test-dirs/expand_node/ppx-tests.t/run.t | 55 ++++++++++++++++++- 2 files changed, 56 insertions(+), 4 deletions(-) diff --git a/src/frontend/query_commands.ml b/src/frontend/query_commands.ml index a2285cf6e..dfb3df36f 100644 --- a/src/frontend/query_commands.ml +++ b/src/frontend/query_commands.ml @@ -527,7 +527,7 @@ let dispatch pipeline (type a) : a Query_protocol.t -> a = }) in let check_at_pos (loc:Warnings.loc) = - loc.loc_start.pos_cnum <= pos.pos_cnum && loc.loc_end.pos_cnum >= pos.pos_cnum + Location_aux.compare_pos pos loc = 0 in let has_ppx_deriver (attr:Parsetree.attribute) = check_at_pos attr.attr_loc && @@ -544,7 +544,8 @@ let dispatch pipeline (type a) : a Query_protocol.t -> a = | Some (Expression ({pexp_desc = Pexp_extension _; _} as exp)) -> let ppx = ref None in let expr (self : Ast_iterator.iterator) (expr : Parsetree.expression) = - match check_at_pos expr.pexp_loc with + match check_at_pos expr.pexp_loc && + Location_aux.compare exp.pexp_loc expr.pexp_loc = 0 with | true -> ppx := Some expr | false -> Ast_iterator.default_iterator.expr self expr diff --git a/tests/test-dirs/expand_node/ppx-tests.t/run.t b/tests/test-dirs/expand_node/ppx-tests.t/run.t index b9ebfee31..c3bd63534 100644 --- a/tests/test-dirs/expand_node/ppx-tests.t/run.t +++ b/tests/test-dirs/expand_node/ppx-tests.t/run.t @@ -6,7 +6,7 @@ Dune setup $ cat > dune << EOF > (executable > (name apt) - > (preprocess (pps c_ppx my_ppx))) + > (preprocess (pps c_ppx my_ppx ppx_here))) > EOF Type declaration in structure @@ -763,7 +763,7 @@ PPx extension { "class": "return", "value": { - "code": "print_string (\"OCaml is so cool\" ^ \":-)!\")", + "code": "\"OCaml is so cool\"", "deriver": { "start": { "line": 1, @@ -785,4 +785,55 @@ PPx extension } +Show only an output for the hover and not all extensions + $ cat > aptxc.ml << EOF + > let phrase = [%tell_me] ^ [%tell_me] + > EOF + + $ dune build +on the first [%tell_me] + $ $MERLIN single expand-node -position 1:16 -filename ./apttt.ml < ./aptxc.ml + { + "class": "return", + "value": { + "code": "\"OCaml is so cool\"", + "deriver": { + "start": { + "line": 1, + "col": 13 + }, + "end": { + "line": 1, + "col": 23 + } + } + }, + "notifications": [] + } +on the concatenator + $ $MERLIN single expand-node -position 1:24 -filename ./apttt.ml < ./aptxc.ml + { + "class": "return", + "value": "No PPX deriver/extension node found on this position", + "notifications": [] + } +on the second [%tell_me] + $ $MERLIN single expand-node -position 1:27 -filename ./apttt.ml < ./aptxc.ml + { + "class": "return", + "value": { + "code": "\"OCaml is so cool\"", + "deriver": { + "start": { + "line": 1, + "col": 26 + }, + "end": { + "line": 1, + "col": 36 + } + } + }, + "notifications": [] + } From 6ce434a4439a44ff7398f5d3907bc01f07c3dab8 Mon Sep 17 00:00:00 2001 From: PizieDust Date: Mon, 13 May 2024 09:06:29 +0100 Subject: [PATCH 31/36] add support for ppx_deriving --- src/frontend/query_commands.ml | 3 ++- src/kernel/mbrowse_p.ml | 3 +-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/frontend/query_commands.ml b/src/frontend/query_commands.ml index dfb3df36f..f251f3dbc 100644 --- a/src/frontend/query_commands.ml +++ b/src/frontend/query_commands.ml @@ -562,7 +562,7 @@ let dispatch pipeline (type a) : a Query_protocol.t -> a = ~a_end:(exp.pexp_loc.loc_end)) |> Option.get | Some _ | None -> - let nodes = Mtyper.node_at_p ppx_parsetree pos in + let nodes = Mtyper.node_at_p parsetree pos in let has_deriving_attribute = List.find_opt ~f:has_ppx_deriver (List.concat_map ~f:(fun node -> @@ -570,6 +570,7 @@ let dispatch pipeline (type a) : a Query_protocol.t -> a = in (match has_deriving_attribute with | Some attribute -> + let nodes = Mtyper.node_at_p ppx_parsetree pos in let derived_nodes = Mbrowse_p.get_children pos nodes in ppx_expansion ~ppx:(Mbrowse_p.pprint_deriver_nodes () derived_nodes) diff --git a/src/kernel/mbrowse_p.ml b/src/kernel/mbrowse_p.ml index 81d255261..538f6daf5 100644 --- a/src/kernel/mbrowse_p.ml +++ b/src/kernel/mbrowse_p.ml @@ -249,7 +249,7 @@ let is_recovered = function let check_node pos node = let loc = node_merlin_loc node in - Location_aux.compare_pos pos loc = 0 && loc.loc_ghost + Location_aux.compare_pos pos loc = 0 let get_children pos nodes = let children = @@ -266,7 +266,6 @@ let get_children pos nodes = | _ -> []) nodes in children |> List.concat - let pprint_deriver_node () node = let ppf, to_string = Format.to_string () in begin match node with From db3065a6dea27cc15ea68e18d2f57eb24d26c419 Mon Sep 17 00:00:00 2001 From: PizieDust Date: Mon, 13 May 2024 09:08:05 +0100 Subject: [PATCH 32/36] update tests --- tests/test-dirs/expand_node/ppx-tests.t/run.t | 196 ++++++++++++++---- 1 file changed, 151 insertions(+), 45 deletions(-) diff --git a/tests/test-dirs/expand_node/ppx-tests.t/run.t b/tests/test-dirs/expand_node/ppx-tests.t/run.t index c3bd63534..2147b9eb6 100644 --- a/tests/test-dirs/expand_node/ppx-tests.t/run.t +++ b/tests/test-dirs/expand_node/ppx-tests.t/run.t @@ -6,7 +6,7 @@ Dune setup $ cat > dune << EOF > (executable > (name apt) - > (preprocess (pps c_ppx my_ppx ppx_here))) + > (preprocess (pps c_ppx my_ppx))) > EOF Type declaration in structure @@ -79,13 +79,29 @@ on attribute name "deriving" { "class": "return", "value": { - "code": "[ include + "code": "[ type point = { + x: int ; + y: int }[@@deriving rename] + ; include struct let _ = fun (_ : point) -> () type point_renamed = { x: int ; y: int } end[@@ocaml.doc \"@inline\"][@@merlin.hide ] + ; module MyModule = + struct + type point = { + x: int ; + y: int }[@@deriving rename] + include + struct + let _ = fun (_ : point) -> () + type point_renamed = { + x: int ; + y: int } + end[@@ocaml.doc \"@inline\"][@@merlin.hide ] + end ]", "deriver": { "start": { @@ -106,13 +122,29 @@ on attribute payload name "rename" { "class": "return", "value": { - "code": "[ include + "code": "[ type point = { + x: int ; + y: int }[@@deriving rename] + ; include struct let _ = fun (_ : point) -> () type point_renamed = { x: int ; y: int } end[@@ocaml.doc \"@inline\"][@@merlin.hide ] + ; module MyModule = + struct + type point = { + x: int ; + y: int }[@@deriving rename] + include + struct + let _ = fun (_ : point) -> () + type point_renamed = { + x: int ; + y: int } + end[@@ocaml.doc \"@inline\"][@@merlin.hide ] + end ]", "deriver": { "start": { @@ -198,10 +230,24 @@ on attribute name "deriving" { "class": "return", "value": { - "code": "[ include sig [@@@ocaml.warning \"-32\"] type tttt_renamed = + "code": "[ type tttt = + | Red + | Green [@@deriving rename] + ; include sig [@@@ocaml.warning \"-32\"] type tttt_renamed = | Red | Green end[@@ocaml.doc \"@inline\"] [@@merlin.hide ] + ; module type MyModuleSig = + sig + type tttt = + | Red + | Green [@@deriving rename] + include + sig [@@@ocaml.warning \"-32\"] type tttt_renamed = + | Red + | Green end[@@ocaml.doc \"@inline\"] + [@@merlin.hide ] + end ]", "deriver": { "start": { @@ -222,10 +268,24 @@ on attribute payload name "rename" { "class": "return", "value": { - "code": "[ include sig [@@@ocaml.warning \"-32\"] type tttt_renamed = + "code": "[ type tttt = + | Red + | Green [@@deriving rename] + ; include sig [@@@ocaml.warning \"-32\"] type tttt_renamed = | Red | Green end[@@ocaml.doc \"@inline\"] [@@merlin.hide ] + ; module type MyModuleSig = + sig + type tttt = + | Red + | Green [@@deriving rename] + include + sig [@@@ocaml.warning \"-32\"] type tttt_renamed = + | Red + | Green end[@@ocaml.doc \"@inline\"] + [@@merlin.hide ] + end ]", "deriver": { "start": { @@ -253,7 +313,8 @@ Type declaration in structure B { "class": "return", "value": { - "code": "[ include struct let _ = fun (_ : yyyy) -> () + "code": "[ type yyyy = int[@@deriving rename] + ; include struct let _ = fun (_ : yyyy) -> () type yyyy_renamed = int end[@@ocaml.doc \"@inline\"][@@merlin.hide ] ]", @@ -284,7 +345,8 @@ Type declaration in signature B { "class": "return", "value": { - "code": "[ include sig [@@@ocaml.warning \"-32\"] type yyyy_renamed = int end[@@ocaml.doc + "code": "[ type yyyy = int[@@deriving rename] + ; include sig [@@@ocaml.warning \"-32\"] type yyyy_renamed = int end[@@ocaml.doc \"@inline\"] [@@merlin.hide ] ]", @@ -315,7 +377,8 @@ Type extension in structure { "class": "return", "value": { - "code": "[ include struct let _ = fun (_ : pppp) -> () + "code": "[ type pppp = ..[@@deriving rename] + ; include struct let _ = fun (_ : pppp) -> () type pppp_renamed = .. end[@@ocaml.doc \"@inline\"][@@merlin.hide ] ]", @@ -336,7 +399,9 @@ Type extension in structure { "class": "return", "value": { - "code": "[ include struct type pppp_renamed += + "code": "[ type pppp += + | Int of int [@@deriving rename] + ; include struct type pppp_renamed += | Int of int end[@@ocaml.doc \"@inline\"][@@merlin.hide ] ]", "deriver": { @@ -365,7 +430,8 @@ Type extension in signature { "class": "return", "value": { - "code": "[ include sig [@@@ocaml.warning \"-32\"] type pppp_renamed = .. end[@@ocaml.doc + "code": "[ type pppp = ..[@@deriving rename] + ; include sig [@@@ocaml.warning \"-32\"] type pppp_renamed = .. end[@@ocaml.doc \"@inline\"] [@@merlin.hide ] ]", @@ -394,7 +460,8 @@ Exception in structure { "class": "return", "value": { - "code": "[ include struct exception Foo_renamed of string end[@@ocaml.doc \"@inline\"] + "code": "[ exception Foo of string [@@deriving rename] + ; include struct exception Foo_renamed of string end[@@ocaml.doc \"@inline\"] [@@merlin.hide ] ]", "deriver": { @@ -422,7 +489,8 @@ Exception in signature { "class": "return", "value": { - "code": "[ include sig [@@@ocaml.warning \"-32\"] exception Foo_renamed of string end + "code": "[ exception Foo of string [@@deriving rename] + ; include sig [@@@ocaml.warning \"-32\"] exception Foo_renamed of string end [@@ocaml.doc \"@inline\"][@@merlin.hide ] ]", "deriver": { @@ -459,7 +527,17 @@ Module type declaration in structure { "class": "return", "value": { - "code": "[ include + "code": "[ module type Stack = + sig + type t + type stack + val empty : stack + val is_empty : stack -> bool + val push : t -> stack -> stack + val pop : stack -> stack + val peek : stack -> t + end[@@deriving rename] + ; include struct module type Stack_renamed = sig @@ -506,7 +584,17 @@ Module type declaration in signature { "class": "return", "value": { - "code": "[ include + "code": "[ module type Stack = + sig + type t + type stack + val empty : stack + val is_empty : stack -> bool + val push : t -> stack -> stack + val pop : stack -> stack + val peek : stack -> t + end[@@deriving rename] + ; include sig [@@@ocaml.warning \"-32\"] module type Stack_renamed = @@ -567,9 +655,21 @@ Module type declaration in structure { "class": "return", "value": { - "code": "[ include sig [@@@ocaml.warning \"-32\"] type t_renamed end[@@ocaml.doc + "code": "[ type t[@@deriving rename] + ; include sig [@@@ocaml.warning \"-32\"] type t_renamed end[@@ocaml.doc \"@inline\"][@@merlin.hide ] + ; module type Queue = + sig + type t[@@deriving rename] + include sig [@@@ocaml.warning \"-32\"] type t_renamed end[@@ocaml.doc + \"@inline\"] + [@@merlin.hide ] + type queue + val enqueue : t -> queue -> queue + val dequeue : queue -> queue + val peek : queue -> t + end[@@deriving rename] ; include struct module type Queue_renamed = @@ -607,7 +707,18 @@ Module type declaration in structure { "class": "return", "value": { - "code": "[ include + "code": "[ module type Queue = + sig + type t[@@deriving rename] + include sig [@@@ocaml.warning \"-32\"] type t_renamed end[@@ocaml.doc + \"@inline\"] + [@@merlin.hide ] + type queue + val enqueue : t -> queue -> queue + val dequeue : queue -> queue + val peek : queue -> t + end[@@deriving rename] + ; include struct module type Queue_renamed = sig @@ -658,9 +769,30 @@ on [@@deriving rename] { "class": "return", "value": { - "code": "[ include struct let _ = fun (_ : t) -> () + "code": "[ type t = int[@@deriving rename] + ; include struct let _ = fun (_ : t) -> () type t_renamed = int end[@@ocaml.doc \"@inline\"][@@merlin.hide ] + ; module N = + struct + type t = int[@@deriving rename] + include struct let _ = fun (_ : t) -> () + type t_renamed = int end[@@ocaml.doc \"@inline\"][@@merlin.hide + ] + let a = 1 + end + ; module M = + struct + module N = + struct + type t = int[@@deriving rename] + include struct let _ = fun (_ : t) -> () + type t_renamed = int end[@@ocaml.doc \"@inline\"] + [@@merlin.hide ] + let a = 1 + end + let b = 2 + end ]", "deriver": { "start": { @@ -680,7 +812,6 @@ on [@@deriving rename] Type declaration in structure $ cat > aptt.ml << EOF > type tont = float * float [@@deriving rename] - > type pont = float * float [@@deriving show] > EOF $ dune build @@ -690,7 +821,8 @@ on [@@deriving rename] { "class": "return", "value": { - "code": "[ include + "code": "[ type tont = (float * float)[@@deriving rename] + ; include struct let _ = fun (_ : tont) -> () type tont_renamed = (float * float) end[@@ocaml.doc \"@inline\"] [@@merlin.hide ] @@ -708,32 +840,6 @@ on [@@deriving rename] }, "notifications": [] } -on [@@deriving show] - $ $MERLIN single expand-node -position 2:37 -filename ./aptt.ml < ./aptt.ml - { - "class": "return", - "value": { - "code": "[ include - struct - let _ = fun (_ : pont) -> () - [%%ocaml.error - \"Ppxlib.Deriving: 'show' is not a supported type deriving generator\"] - end[@@ocaml.doc \"@inline\"][@@merlin.hide ] - ]", - "deriver": { - "start": { - "line": 2, - "col": 26 - }, - "end": { - "line": 2, - "col": 43 - } - } - }, - "notifications": [] - } - PPx extension $ cat > apttt.ml << EOF From 17b688685721225e9f3933003649c1e59d1fe4ed Mon Sep 17 00:00:00 2001 From: PizieDust Date: Mon, 13 May 2024 11:46:26 +0100 Subject: [PATCH 33/36] refactor ppx rewriter logic --- src/frontend/query_commands.ml | 25 +++++-------------- src/kernel/mbrowse_p.ml | 9 +++++++ src/kernel/mbrowse_p.mli | 1 + tests/test-dirs/expand_node/ppx-tests.t/run.t | 9 ++++--- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/src/frontend/query_commands.ml b/src/frontend/query_commands.ml index f251f3dbc..f45c47e66 100644 --- a/src/frontend/query_commands.ml +++ b/src/frontend/query_commands.ml @@ -542,25 +542,12 @@ let dispatch pipeline (type a) : a Query_protocol.t -> a = let extension = List.find_opt ~f:has_ppx_extension nodes in begin match extension with | Some (Expression ({pexp_desc = Pexp_extension _; _} as exp)) -> - let ppx = ref None in - let expr (self : Ast_iterator.iterator) (expr : Parsetree.expression) = - match check_at_pos expr.pexp_loc && - Location_aux.compare exp.pexp_loc expr.pexp_loc = 0 with - | true -> - ppx := Some expr - | false -> Ast_iterator.default_iterator.expr self expr - in - let iterator = { Ast_iterator.default_iterator with expr } in - let _ = - match ppx_parsetree with - | `Interface si -> iterator.signature iterator si - | `Implementation str -> iterator.structure iterator str - in - Option.map !ppx ~f:(fun expr -> - ppx_expansion ~ppx:(Pprintast.string_of_expression expr) - ~a_start:(exp.pexp_loc.loc_start) - ~a_end:(exp.pexp_loc.loc_end)) - |> Option.get + let nodes = Mtyper.node_at_p ppx_parsetree pos in + let derived_nodes = Mbrowse_p.get_ext_children pos exp.pexp_loc nodes in + ppx_expansion + ~ppx:(Mbrowse_p.pprint_deriver_nodes () derived_nodes) + ~a_start:(exp.pexp_loc.loc_start) + ~a_end:(exp.pexp_loc.loc_end) | Some _ | None -> let nodes = Mtyper.node_at_p parsetree pos in let has_deriving_attribute = diff --git a/src/kernel/mbrowse_p.ml b/src/kernel/mbrowse_p.ml index 538f6daf5..aa877cf05 100644 --- a/src/kernel/mbrowse_p.ml +++ b/src/kernel/mbrowse_p.ml @@ -266,11 +266,20 @@ let get_children pos nodes = | _ -> []) nodes in children |> List.concat +let get_ext_children (pos:Lexing.position) exp_loc nodes = + List.filter ~f:(fun node -> + match node with + | Expression exp -> + Location_aux.compare_pos pos exp.pexp_loc = 0 && Location_aux.compare exp.pexp_loc exp_loc = 0 + | _ -> false + ) nodes + let pprint_deriver_node () node = let ppf, to_string = Format.to_string () in begin match node with | Browse_raw_p.Structure_item n -> Pprintast.structure ppf [n] | Browse_raw_p.Signature_item n -> Pprintast.signature ppf [n] + | Browse_raw_p.Expression e -> Pprintast.expression ppf e | _ -> () end; Format.pp_print_newline ppf (); diff --git a/src/kernel/mbrowse_p.mli b/src/kernel/mbrowse_p.mli index fd833bae8..85859841a 100644 --- a/src/kernel/mbrowse_p.mli +++ b/src/kernel/mbrowse_p.mli @@ -81,4 +81,5 @@ val print : unit -> t -> string val pprint_deriver_node : unit -> node -> string val pprint_deriver_nodes : unit -> node list -> string val get_children : Lexing.position -> node list -> node list +val get_ext_children : Lexing.position -> Warnings.loc -> node list -> node list diff --git a/tests/test-dirs/expand_node/ppx-tests.t/run.t b/tests/test-dirs/expand_node/ppx-tests.t/run.t index 2147b9eb6..451f3ccfa 100644 --- a/tests/test-dirs/expand_node/ppx-tests.t/run.t +++ b/tests/test-dirs/expand_node/ppx-tests.t/run.t @@ -869,7 +869,8 @@ PPx extension { "class": "return", "value": { - "code": "\"OCaml is so cool\"", + "code": "[ \"OCaml is so cool\" + ]", "deriver": { "start": { "line": 1, @@ -902,7 +903,8 @@ on the first [%tell_me] { "class": "return", "value": { - "code": "\"OCaml is so cool\"", + "code": "[ \"OCaml is so cool\" + ]", "deriver": { "start": { "line": 1, @@ -928,7 +930,8 @@ on the second [%tell_me] { "class": "return", "value": { - "code": "\"OCaml is so cool\"", + "code": "[ \"OCaml is so cool\" + ]", "deriver": { "start": { "line": 1, From 5930014f682ffcce48b1c982c4ae8d1ff16afc2c Mon Sep 17 00:00:00 2001 From: PizieDust Date: Mon, 13 May 2024 11:59:40 +0100 Subject: [PATCH 34/36] linting --- src/frontend/query_commands.ml | 3 ++- src/kernel/mbrowse_p.ml | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/frontend/query_commands.ml b/src/frontend/query_commands.ml index c0c8f8967..59e0e271e 100644 --- a/src/frontend/query_commands.ml +++ b/src/frontend/query_commands.ml @@ -543,7 +543,8 @@ let dispatch pipeline (type a) : a Query_protocol.t -> a = begin match extension with | Some (Expression ({pexp_desc = Pexp_extension _; _} as exp)) -> let nodes = Mtyper.node_at_p ppx_parsetree pos in - let derived_nodes = Mbrowse_p.get_ext_children pos exp.pexp_loc nodes in + let derived_nodes = + Mbrowse_p.get_ext_children pos exp.pexp_loc nodes in ppx_expansion ~ppx:(Mbrowse_p.pprint_deriver_nodes () derived_nodes) ~a_start:(exp.pexp_loc.loc_start) diff --git a/src/kernel/mbrowse_p.ml b/src/kernel/mbrowse_p.ml index aa877cf05..8fd30a52b 100644 --- a/src/kernel/mbrowse_p.ml +++ b/src/kernel/mbrowse_p.ml @@ -270,7 +270,8 @@ let get_ext_children (pos:Lexing.position) exp_loc nodes = List.filter ~f:(fun node -> match node with | Expression exp -> - Location_aux.compare_pos pos exp.pexp_loc = 0 && Location_aux.compare exp.pexp_loc exp_loc = 0 + Location_aux.compare_pos pos exp.pexp_loc = 0 + && Location_aux.compare exp.pexp_loc exp_loc = 0 | _ -> false ) nodes From bec3e98f291a9a5332d419bb9a3cdff69805515e Mon Sep 17 00:00:00 2001 From: PizieDust Date: Mon, 13 May 2024 12:34:18 +0100 Subject: [PATCH 35/36] refactor logic, remove redundanct code --- src/frontend/query_commands.ml | 4 +- src/kernel/mbrowse_p.ml | 35 ++-- src/kernel/mbrowse_p.mli | 5 +- tests/test-dirs/expand_node/ppx-tests.t/run.t | 167 ++---------------- 4 files changed, 36 insertions(+), 175 deletions(-) diff --git a/src/frontend/query_commands.ml b/src/frontend/query_commands.ml index 59e0e271e..734224275 100644 --- a/src/frontend/query_commands.ml +++ b/src/frontend/query_commands.ml @@ -544,7 +544,7 @@ let dispatch pipeline (type a) : a Query_protocol.t -> a = | Some (Expression ({pexp_desc = Pexp_extension _; _} as exp)) -> let nodes = Mtyper.node_at_p ppx_parsetree pos in let derived_nodes = - Mbrowse_p.get_ext_children pos exp.pexp_loc nodes in + Mbrowse_p.get_children ~cursor_pos:pos ~expression_loc:(exp.pexp_loc) nodes in ppx_expansion ~ppx:(Mbrowse_p.pprint_deriver_nodes () derived_nodes) ~a_start:(exp.pexp_loc.loc_start) @@ -559,7 +559,7 @@ let dispatch pipeline (type a) : a Query_protocol.t -> a = (match has_deriving_attribute with | Some attribute -> let nodes = Mtyper.node_at_p ppx_parsetree pos in - let derived_nodes = Mbrowse_p.get_children pos nodes in + let derived_nodes = Mbrowse_p.get_children ~cursor_pos:pos nodes in ppx_expansion ~ppx:(Mbrowse_p.pprint_deriver_nodes () derived_nodes) ~a_start:(attribute.attr_loc.loc_start) diff --git a/src/kernel/mbrowse_p.ml b/src/kernel/mbrowse_p.ml index 8fd30a52b..bfe658390 100644 --- a/src/kernel/mbrowse_p.ml +++ b/src/kernel/mbrowse_p.ml @@ -250,30 +250,23 @@ let is_recovered = function let check_node pos node = let loc = node_merlin_loc node in Location_aux.compare_pos pos loc = 0 + && loc.loc_ghost -let get_children pos nodes = - let children = - List.map ~f:(fun node -> - match node with - | Structure str -> - of_structure_items (List.filter ~f:(fun n -> - check_node pos (Structure_item(n)) - ) str) - | Signature sg -> - of_signature_items (List.filter ~f:(fun n -> - check_node pos (Signature_item(n)) - ) sg) - | _ -> []) nodes - in children |> List.concat - -let get_ext_children (pos:Lexing.position) exp_loc nodes = - List.filter ~f:(fun node -> +let get_children ~cursor_pos ?(expression_loc = Location.none) nodes = + List.map nodes ~f:(fun node -> match node with | Expression exp -> - Location_aux.compare_pos pos exp.pexp_loc = 0 - && Location_aux.compare exp.pexp_loc exp_loc = 0 - | _ -> false - ) nodes + List.filter ~f:(fun _x -> + Location_aux.compare_pos cursor_pos exp.pexp_loc = 0 + && Location_aux.compare exp.pexp_loc expression_loc = 0) [Expression exp] + | Structure str -> + List.filter ~f:( fun n -> + check_node cursor_pos n) (of_structure_items str) + | Signature sg -> + List.filter ~f:( fun n -> + check_node cursor_pos n) (of_signature_items sg) + | _ -> [] + ) |> List.concat let pprint_deriver_node () node = let ppf, to_string = Format.to_string () in diff --git a/src/kernel/mbrowse_p.mli b/src/kernel/mbrowse_p.mli index 85859841a..02b3e766d 100644 --- a/src/kernel/mbrowse_p.mli +++ b/src/kernel/mbrowse_p.mli @@ -80,6 +80,7 @@ val print_node : unit -> node -> string val print : unit -> t -> string val pprint_deriver_node : unit -> node -> string val pprint_deriver_nodes : unit -> node list -> string -val get_children : Lexing.position -> node list -> node list -val get_ext_children : Lexing.position -> Warnings.loc -> node list -> node list +val get_children : cursor_pos:Lexing.position -> + ?expression_loc:Warnings.loc -> + node list -> node list diff --git a/tests/test-dirs/expand_node/ppx-tests.t/run.t b/tests/test-dirs/expand_node/ppx-tests.t/run.t index 451f3ccfa..2a19bfd4b 100644 --- a/tests/test-dirs/expand_node/ppx-tests.t/run.t +++ b/tests/test-dirs/expand_node/ppx-tests.t/run.t @@ -79,29 +79,13 @@ on attribute name "deriving" { "class": "return", "value": { - "code": "[ type point = { - x: int ; - y: int }[@@deriving rename] - ; include + "code": "[ include struct let _ = fun (_ : point) -> () type point_renamed = { x: int ; y: int } end[@@ocaml.doc \"@inline\"][@@merlin.hide ] - ; module MyModule = - struct - type point = { - x: int ; - y: int }[@@deriving rename] - include - struct - let _ = fun (_ : point) -> () - type point_renamed = { - x: int ; - y: int } - end[@@ocaml.doc \"@inline\"][@@merlin.hide ] - end ]", "deriver": { "start": { @@ -122,29 +106,13 @@ on attribute payload name "rename" { "class": "return", "value": { - "code": "[ type point = { - x: int ; - y: int }[@@deriving rename] - ; include + "code": "[ include struct let _ = fun (_ : point) -> () type point_renamed = { x: int ; y: int } end[@@ocaml.doc \"@inline\"][@@merlin.hide ] - ; module MyModule = - struct - type point = { - x: int ; - y: int }[@@deriving rename] - include - struct - let _ = fun (_ : point) -> () - type point_renamed = { - x: int ; - y: int } - end[@@ocaml.doc \"@inline\"][@@merlin.hide ] - end ]", "deriver": { "start": { @@ -230,24 +198,10 @@ on attribute name "deriving" { "class": "return", "value": { - "code": "[ type tttt = - | Red - | Green [@@deriving rename] - ; include sig [@@@ocaml.warning \"-32\"] type tttt_renamed = + "code": "[ include sig [@@@ocaml.warning \"-32\"] type tttt_renamed = | Red | Green end[@@ocaml.doc \"@inline\"] [@@merlin.hide ] - ; module type MyModuleSig = - sig - type tttt = - | Red - | Green [@@deriving rename] - include - sig [@@@ocaml.warning \"-32\"] type tttt_renamed = - | Red - | Green end[@@ocaml.doc \"@inline\"] - [@@merlin.hide ] - end ]", "deriver": { "start": { @@ -268,24 +222,10 @@ on attribute payload name "rename" { "class": "return", "value": { - "code": "[ type tttt = - | Red - | Green [@@deriving rename] - ; include sig [@@@ocaml.warning \"-32\"] type tttt_renamed = + "code": "[ include sig [@@@ocaml.warning \"-32\"] type tttt_renamed = | Red | Green end[@@ocaml.doc \"@inline\"] [@@merlin.hide ] - ; module type MyModuleSig = - sig - type tttt = - | Red - | Green [@@deriving rename] - include - sig [@@@ocaml.warning \"-32\"] type tttt_renamed = - | Red - | Green end[@@ocaml.doc \"@inline\"] - [@@merlin.hide ] - end ]", "deriver": { "start": { @@ -313,8 +253,7 @@ Type declaration in structure B { "class": "return", "value": { - "code": "[ type yyyy = int[@@deriving rename] - ; include struct let _ = fun (_ : yyyy) -> () + "code": "[ include struct let _ = fun (_ : yyyy) -> () type yyyy_renamed = int end[@@ocaml.doc \"@inline\"][@@merlin.hide ] ]", @@ -345,8 +284,7 @@ Type declaration in signature B { "class": "return", "value": { - "code": "[ type yyyy = int[@@deriving rename] - ; include sig [@@@ocaml.warning \"-32\"] type yyyy_renamed = int end[@@ocaml.doc + "code": "[ include sig [@@@ocaml.warning \"-32\"] type yyyy_renamed = int end[@@ocaml.doc \"@inline\"] [@@merlin.hide ] ]", @@ -377,8 +315,7 @@ Type extension in structure { "class": "return", "value": { - "code": "[ type pppp = ..[@@deriving rename] - ; include struct let _ = fun (_ : pppp) -> () + "code": "[ include struct let _ = fun (_ : pppp) -> () type pppp_renamed = .. end[@@ocaml.doc \"@inline\"][@@merlin.hide ] ]", @@ -399,9 +336,7 @@ Type extension in structure { "class": "return", "value": { - "code": "[ type pppp += - | Int of int [@@deriving rename] - ; include struct type pppp_renamed += + "code": "[ include struct type pppp_renamed += | Int of int end[@@ocaml.doc \"@inline\"][@@merlin.hide ] ]", "deriver": { @@ -430,8 +365,7 @@ Type extension in signature { "class": "return", "value": { - "code": "[ type pppp = ..[@@deriving rename] - ; include sig [@@@ocaml.warning \"-32\"] type pppp_renamed = .. end[@@ocaml.doc + "code": "[ include sig [@@@ocaml.warning \"-32\"] type pppp_renamed = .. end[@@ocaml.doc \"@inline\"] [@@merlin.hide ] ]", @@ -460,8 +394,7 @@ Exception in structure { "class": "return", "value": { - "code": "[ exception Foo of string [@@deriving rename] - ; include struct exception Foo_renamed of string end[@@ocaml.doc \"@inline\"] + "code": "[ include struct exception Foo_renamed of string end[@@ocaml.doc \"@inline\"] [@@merlin.hide ] ]", "deriver": { @@ -489,8 +422,7 @@ Exception in signature { "class": "return", "value": { - "code": "[ exception Foo of string [@@deriving rename] - ; include sig [@@@ocaml.warning \"-32\"] exception Foo_renamed of string end + "code": "[ include sig [@@@ocaml.warning \"-32\"] exception Foo_renamed of string end [@@ocaml.doc \"@inline\"][@@merlin.hide ] ]", "deriver": { @@ -527,17 +459,7 @@ Module type declaration in structure { "class": "return", "value": { - "code": "[ module type Stack = - sig - type t - type stack - val empty : stack - val is_empty : stack -> bool - val push : t -> stack -> stack - val pop : stack -> stack - val peek : stack -> t - end[@@deriving rename] - ; include + "code": "[ include struct module type Stack_renamed = sig @@ -584,17 +506,7 @@ Module type declaration in signature { "class": "return", "value": { - "code": "[ module type Stack = - sig - type t - type stack - val empty : stack - val is_empty : stack -> bool - val push : t -> stack -> stack - val pop : stack -> stack - val peek : stack -> t - end[@@deriving rename] - ; include + "code": "[ include sig [@@@ocaml.warning \"-32\"] module type Stack_renamed = @@ -655,21 +567,9 @@ Module type declaration in structure { "class": "return", "value": { - "code": "[ type t[@@deriving rename] - ; include sig [@@@ocaml.warning \"-32\"] type t_renamed end[@@ocaml.doc + "code": "[ include sig [@@@ocaml.warning \"-32\"] type t_renamed end[@@ocaml.doc \"@inline\"][@@merlin.hide ] - ; module type Queue = - sig - type t[@@deriving rename] - include sig [@@@ocaml.warning \"-32\"] type t_renamed end[@@ocaml.doc - \"@inline\"] - [@@merlin.hide ] - type queue - val enqueue : t -> queue -> queue - val dequeue : queue -> queue - val peek : queue -> t - end[@@deriving rename] ; include struct module type Queue_renamed = @@ -707,18 +607,7 @@ Module type declaration in structure { "class": "return", "value": { - "code": "[ module type Queue = - sig - type t[@@deriving rename] - include sig [@@@ocaml.warning \"-32\"] type t_renamed end[@@ocaml.doc - \"@inline\"] - [@@merlin.hide ] - type queue - val enqueue : t -> queue -> queue - val dequeue : queue -> queue - val peek : queue -> t - end[@@deriving rename] - ; include + "code": "[ include struct module type Queue_renamed = sig @@ -769,30 +658,9 @@ on [@@deriving rename] { "class": "return", "value": { - "code": "[ type t = int[@@deriving rename] - ; include struct let _ = fun (_ : t) -> () + "code": "[ include struct let _ = fun (_ : t) -> () type t_renamed = int end[@@ocaml.doc \"@inline\"][@@merlin.hide ] - ; module N = - struct - type t = int[@@deriving rename] - include struct let _ = fun (_ : t) -> () - type t_renamed = int end[@@ocaml.doc \"@inline\"][@@merlin.hide - ] - let a = 1 - end - ; module M = - struct - module N = - struct - type t = int[@@deriving rename] - include struct let _ = fun (_ : t) -> () - type t_renamed = int end[@@ocaml.doc \"@inline\"] - [@@merlin.hide ] - let a = 1 - end - let b = 2 - end ]", "deriver": { "start": { @@ -821,8 +689,7 @@ on [@@deriving rename] { "class": "return", "value": { - "code": "[ type tont = (float * float)[@@deriving rename] - ; include + "code": "[ include struct let _ = fun (_ : tont) -> () type tont_renamed = (float * float) end[@@ocaml.doc \"@inline\"] [@@merlin.hide ] From 429320288d9dd4a242c72b25ce868c6ad23f7b57 Mon Sep 17 00:00:00 2001 From: PizieDust Date: Mon, 13 May 2024 14:51:51 +0100 Subject: [PATCH 36/36] support ppx_deriving --- src/frontend/query_commands.ml | 2 +- src/kernel/mbrowse_p.ml | 15 +- src/kernel/mbrowse_p.mli | 6 +- tests/test-dirs/expand_node/ppx-tests.t/run.t | 169 ++++++++++++++++-- 4 files changed, 164 insertions(+), 28 deletions(-) diff --git a/src/frontend/query_commands.ml b/src/frontend/query_commands.ml index 734224275..6f22016e3 100644 --- a/src/frontend/query_commands.ml +++ b/src/frontend/query_commands.ml @@ -544,7 +544,7 @@ let dispatch pipeline (type a) : a Query_protocol.t -> a = | Some (Expression ({pexp_desc = Pexp_extension _; _} as exp)) -> let nodes = Mtyper.node_at_p ppx_parsetree pos in let derived_nodes = - Mbrowse_p.get_children ~cursor_pos:pos ~expression_loc:(exp.pexp_loc) nodes in + Mbrowse_p.get_ext_children ~cursor_pos:pos ~expression_loc:(exp.pexp_loc) nodes in ppx_expansion ~ppx:(Mbrowse_p.pprint_deriver_nodes () derived_nodes) ~a_start:(exp.pexp_loc.loc_start) diff --git a/src/kernel/mbrowse_p.ml b/src/kernel/mbrowse_p.ml index bfe658390..4ef3130ab 100644 --- a/src/kernel/mbrowse_p.ml +++ b/src/kernel/mbrowse_p.ml @@ -250,15 +250,10 @@ let is_recovered = function let check_node pos node = let loc = node_merlin_loc node in Location_aux.compare_pos pos loc = 0 - && loc.loc_ghost -let get_children ~cursor_pos ?(expression_loc = Location.none) nodes = +let get_children ~cursor_pos nodes = List.map nodes ~f:(fun node -> match node with - | Expression exp -> - List.filter ~f:(fun _x -> - Location_aux.compare_pos cursor_pos exp.pexp_loc = 0 - && Location_aux.compare exp.pexp_loc expression_loc = 0) [Expression exp] | Structure str -> List.filter ~f:( fun n -> check_node cursor_pos n) (of_structure_items str) @@ -268,6 +263,14 @@ let get_children ~cursor_pos ?(expression_loc = Location.none) nodes = | _ -> [] ) |> List.concat +let get_ext_children ~cursor_pos ~expression_loc nodes = + List.filter ~f:(fun node -> + match node with + | Expression exp -> + Location_aux.compare_pos cursor_pos exp.pexp_loc = 0 && + Location_aux.compare expression_loc exp.pexp_loc = 0 + | _ -> false) nodes + let pprint_deriver_node () node = let ppf, to_string = Format.to_string () in begin match node with diff --git a/src/kernel/mbrowse_p.mli b/src/kernel/mbrowse_p.mli index 02b3e766d..cc4052ea0 100644 --- a/src/kernel/mbrowse_p.mli +++ b/src/kernel/mbrowse_p.mli @@ -80,7 +80,7 @@ val print_node : unit -> node -> string val print : unit -> t -> string val pprint_deriver_node : unit -> node -> string val pprint_deriver_nodes : unit -> node list -> string -val get_children : cursor_pos:Lexing.position -> - ?expression_loc:Warnings.loc -> - node list -> node list +val get_children : cursor_pos:Lexing.position -> node list -> node list +val get_ext_children : cursor_pos:Lexing.position -> + expression_loc:Warnings.loc -> node list -> node list diff --git a/tests/test-dirs/expand_node/ppx-tests.t/run.t b/tests/test-dirs/expand_node/ppx-tests.t/run.t index 2a19bfd4b..9980eeecb 100644 --- a/tests/test-dirs/expand_node/ppx-tests.t/run.t +++ b/tests/test-dirs/expand_node/ppx-tests.t/run.t @@ -79,13 +79,29 @@ on attribute name "deriving" { "class": "return", "value": { - "code": "[ include + "code": "[ type point = { + x: int ; + y: int }[@@deriving rename] + ; include struct let _ = fun (_ : point) -> () type point_renamed = { x: int ; y: int } end[@@ocaml.doc \"@inline\"][@@merlin.hide ] + ; module MyModule = + struct + type point = { + x: int ; + y: int }[@@deriving rename] + include + struct + let _ = fun (_ : point) -> () + type point_renamed = { + x: int ; + y: int } + end[@@ocaml.doc \"@inline\"][@@merlin.hide ] + end ]", "deriver": { "start": { @@ -106,13 +122,29 @@ on attribute payload name "rename" { "class": "return", "value": { - "code": "[ include + "code": "[ type point = { + x: int ; + y: int }[@@deriving rename] + ; include struct let _ = fun (_ : point) -> () type point_renamed = { x: int ; y: int } end[@@ocaml.doc \"@inline\"][@@merlin.hide ] + ; module MyModule = + struct + type point = { + x: int ; + y: int }[@@deriving rename] + include + struct + let _ = fun (_ : point) -> () + type point_renamed = { + x: int ; + y: int } + end[@@ocaml.doc \"@inline\"][@@merlin.hide ] + end ]", "deriver": { "start": { @@ -198,10 +230,24 @@ on attribute name "deriving" { "class": "return", "value": { - "code": "[ include sig [@@@ocaml.warning \"-32\"] type tttt_renamed = + "code": "[ type tttt = + | Red + | Green [@@deriving rename] + ; include sig [@@@ocaml.warning \"-32\"] type tttt_renamed = | Red | Green end[@@ocaml.doc \"@inline\"] [@@merlin.hide ] + ; module type MyModuleSig = + sig + type tttt = + | Red + | Green [@@deriving rename] + include + sig [@@@ocaml.warning \"-32\"] type tttt_renamed = + | Red + | Green end[@@ocaml.doc \"@inline\"] + [@@merlin.hide ] + end ]", "deriver": { "start": { @@ -222,10 +268,24 @@ on attribute payload name "rename" { "class": "return", "value": { - "code": "[ include sig [@@@ocaml.warning \"-32\"] type tttt_renamed = + "code": "[ type tttt = + | Red + | Green [@@deriving rename] + ; include sig [@@@ocaml.warning \"-32\"] type tttt_renamed = | Red | Green end[@@ocaml.doc \"@inline\"] [@@merlin.hide ] + ; module type MyModuleSig = + sig + type tttt = + | Red + | Green [@@deriving rename] + include + sig [@@@ocaml.warning \"-32\"] type tttt_renamed = + | Red + | Green end[@@ocaml.doc \"@inline\"] + [@@merlin.hide ] + end ]", "deriver": { "start": { @@ -253,7 +313,8 @@ Type declaration in structure B { "class": "return", "value": { - "code": "[ include struct let _ = fun (_ : yyyy) -> () + "code": "[ type yyyy = int[@@deriving rename] + ; include struct let _ = fun (_ : yyyy) -> () type yyyy_renamed = int end[@@ocaml.doc \"@inline\"][@@merlin.hide ] ]", @@ -284,7 +345,8 @@ Type declaration in signature B { "class": "return", "value": { - "code": "[ include sig [@@@ocaml.warning \"-32\"] type yyyy_renamed = int end[@@ocaml.doc + "code": "[ type yyyy = int[@@deriving rename] + ; include sig [@@@ocaml.warning \"-32\"] type yyyy_renamed = int end[@@ocaml.doc \"@inline\"] [@@merlin.hide ] ]", @@ -315,7 +377,8 @@ Type extension in structure { "class": "return", "value": { - "code": "[ include struct let _ = fun (_ : pppp) -> () + "code": "[ type pppp = ..[@@deriving rename] + ; include struct let _ = fun (_ : pppp) -> () type pppp_renamed = .. end[@@ocaml.doc \"@inline\"][@@merlin.hide ] ]", @@ -336,7 +399,9 @@ Type extension in structure { "class": "return", "value": { - "code": "[ include struct type pppp_renamed += + "code": "[ type pppp += + | Int of int [@@deriving rename] + ; include struct type pppp_renamed += | Int of int end[@@ocaml.doc \"@inline\"][@@merlin.hide ] ]", "deriver": { @@ -365,7 +430,8 @@ Type extension in signature { "class": "return", "value": { - "code": "[ include sig [@@@ocaml.warning \"-32\"] type pppp_renamed = .. end[@@ocaml.doc + "code": "[ type pppp = ..[@@deriving rename] + ; include sig [@@@ocaml.warning \"-32\"] type pppp_renamed = .. end[@@ocaml.doc \"@inline\"] [@@merlin.hide ] ]", @@ -394,7 +460,8 @@ Exception in structure { "class": "return", "value": { - "code": "[ include struct exception Foo_renamed of string end[@@ocaml.doc \"@inline\"] + "code": "[ exception Foo of string [@@deriving rename] + ; include struct exception Foo_renamed of string end[@@ocaml.doc \"@inline\"] [@@merlin.hide ] ]", "deriver": { @@ -422,7 +489,8 @@ Exception in signature { "class": "return", "value": { - "code": "[ include sig [@@@ocaml.warning \"-32\"] exception Foo_renamed of string end + "code": "[ exception Foo of string [@@deriving rename] + ; include sig [@@@ocaml.warning \"-32\"] exception Foo_renamed of string end [@@ocaml.doc \"@inline\"][@@merlin.hide ] ]", "deriver": { @@ -459,7 +527,17 @@ Module type declaration in structure { "class": "return", "value": { - "code": "[ include + "code": "[ module type Stack = + sig + type t + type stack + val empty : stack + val is_empty : stack -> bool + val push : t -> stack -> stack + val pop : stack -> stack + val peek : stack -> t + end[@@deriving rename] + ; include struct module type Stack_renamed = sig @@ -506,7 +584,17 @@ Module type declaration in signature { "class": "return", "value": { - "code": "[ include + "code": "[ module type Stack = + sig + type t + type stack + val empty : stack + val is_empty : stack -> bool + val push : t -> stack -> stack + val pop : stack -> stack + val peek : stack -> t + end[@@deriving rename] + ; include sig [@@@ocaml.warning \"-32\"] module type Stack_renamed = @@ -567,9 +655,21 @@ Module type declaration in structure { "class": "return", "value": { - "code": "[ include sig [@@@ocaml.warning \"-32\"] type t_renamed end[@@ocaml.doc + "code": "[ type t[@@deriving rename] + ; include sig [@@@ocaml.warning \"-32\"] type t_renamed end[@@ocaml.doc \"@inline\"][@@merlin.hide ] + ; module type Queue = + sig + type t[@@deriving rename] + include sig [@@@ocaml.warning \"-32\"] type t_renamed end[@@ocaml.doc + \"@inline\"] + [@@merlin.hide ] + type queue + val enqueue : t -> queue -> queue + val dequeue : queue -> queue + val peek : queue -> t + end[@@deriving rename] ; include struct module type Queue_renamed = @@ -607,7 +707,18 @@ Module type declaration in structure { "class": "return", "value": { - "code": "[ include + "code": "[ module type Queue = + sig + type t[@@deriving rename] + include sig [@@@ocaml.warning \"-32\"] type t_renamed end[@@ocaml.doc + \"@inline\"] + [@@merlin.hide ] + type queue + val enqueue : t -> queue -> queue + val dequeue : queue -> queue + val peek : queue -> t + end[@@deriving rename] + ; include struct module type Queue_renamed = sig @@ -658,9 +769,30 @@ on [@@deriving rename] { "class": "return", "value": { - "code": "[ include struct let _ = fun (_ : t) -> () + "code": "[ type t = int[@@deriving rename] + ; include struct let _ = fun (_ : t) -> () type t_renamed = int end[@@ocaml.doc \"@inline\"][@@merlin.hide ] + ; module N = + struct + type t = int[@@deriving rename] + include struct let _ = fun (_ : t) -> () + type t_renamed = int end[@@ocaml.doc \"@inline\"][@@merlin.hide + ] + let a = 1 + end + ; module M = + struct + module N = + struct + type t = int[@@deriving rename] + include struct let _ = fun (_ : t) -> () + type t_renamed = int end[@@ocaml.doc \"@inline\"] + [@@merlin.hide ] + let a = 1 + end + let b = 2 + end ]", "deriver": { "start": { @@ -685,11 +817,12 @@ Type declaration in structure $ dune build on [@@deriving rename] - $ $MERLIN single expand-node -position 1:37 -filename ./aptt.ml < ./aptt.ml + $ $MERLIN single expand-node -position 1:34 -filename ./aptt.ml < ./aptt.ml { "class": "return", "value": { - "code": "[ include + "code": "[ type tont = (float * float)[@@deriving rename] + ; include struct let _ = fun (_ : tont) -> () type tont_renamed = (float * float) end[@@ocaml.doc \"@inline\"] [@@merlin.hide ]