diff --git a/CHANGES.md b/CHANGES.md index ea217dc137..4916a4d56e 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -8,6 +8,7 @@ merlin NEXT_VERSION - Ignore SIGPIPE in the Merlin server process (#1746) - Fix lexing of quoted strings in comments (#1754, fixes #1753) - Improve cursor position detection in longidents (#1756) + - Implement new expand-node command for expanding PPX annotations (#1745) merlin 4.14 =========== diff --git a/src/frontend/ocamlmerlin/new/new_commands.ml b/src/frontend/ocamlmerlin/new/new_commands.ml index a1b753dd7d..f6f2db88cb 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 69515bc1f5..19282e07dd 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,19 @@ 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 -> + 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" + in str | Locate_type _, resp -> json_of_locate resp | Locate _, resp -> json_of_locate resp | Jump _, resp -> diff --git a/src/frontend/query_commands.ml b/src/frontend/query_commands.ml index 350d3e7586..6f22016e3e 100644 --- a/src/frontend/query_commands.ml +++ b/src/frontend/query_commands.ml @@ -512,6 +512,62 @@ let dispatch pipeline (type a) : a Query_protocol.t -> a = | Some res -> `Found res | None -> `No_documentation) + | Expand_node pos -> + let pos = Mpipeline.get_lexing_pos pipeline pos in + 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) = + Location_aux.compare_pos pos loc = 0 + 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 nodes = Mtyper.node_at_p ppx_parsetree pos in + let derived_nodes = + 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) + ~a_end:(exp.pexp_loc.loc_end) + | Some _ | None -> + 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 -> + Browse_raw_p.node_attributes node) nodes) + 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 ~cursor_pos: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) -> let typer = Mpipeline.typer_result pipeline in let local_defs = Mtyper.get_typedtree typer in diff --git a/src/frontend/query_protocol.ml b/src/frontend/query_protocol.ml index cd8871e476..cde043272a 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 @@ -145,6 +157,11 @@ type _ t = -> [ `Found of syntax_doc_result | `No_documentation ] t + | Expand_node + : Msource.position + -> [ `Found of ppx_expand_result + | `No_deriver + ] t | Locate_type : Msource.position -> [ `Found of string option * Lexing.position diff --git a/src/kernel/mbrowse_p.ml b/src/kernel/mbrowse_p.ml new file mode 100644 index 0000000000..4ef3130ab8 --- /dev/null +++ b/src/kernel/mbrowse_p.ml @@ -0,0 +1,287 @@ +(* {{{ 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 of_structure_items lst = + List.map ~f:(fun head -> Browse_raw_p.Structure_item head) lst + +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 + | `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 + Location_aux.compare_pos pos loc = 0 + +let get_children ~cursor_pos nodes = + List.map nodes ~f:(fun node -> + match node with + | 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 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 + | 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 (); + to_string () + +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 new file mode 100644 index 0000000000..cc4052ea05 --- /dev/null +++ b/src/kernel/mbrowse_p.mli @@ -0,0 +1,86 @@ +(* {{{ 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 : 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/src/kernel/mtyper.ml b/src/kernel/mtyper.ml index 034cb10c7d..0c997e64be 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,18 @@ 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) 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 fd6a7a6b77..97692d9e48 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,10 @@ 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 -> Mreader.parsetree -> Lexing.position -> Mbrowse_p.t + + + diff --git a/src/ocaml/merlin_specific/browse_raw.ml b/src/ocaml/merlin_specific/browse_raw.ml index 88caa4bed6..c63863bd3a 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 = 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 0000000000..126f04192c --- /dev/null +++ b/src/ocaml/merlin_specific/browse_raw_p.ml @@ -0,0 +1,734 @@ +(* {{{ 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 + | Type_exception of type_exception + | 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} + | 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 _ + | 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 + | 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 + | 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 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 (Type_exception texn) + | 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 (Type_exception texn) + | 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 + | 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 + | 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" + | Type_exception _ -> "type_exception" + | 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 + +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 new file mode 100644 index 0000000000..f7ad76cd94 --- /dev/null +++ b/src/ocaml/merlin_specific/browse_raw_p.mli @@ -0,0 +1,112 @@ +(* {{{ 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 + | Type_exception of type_exception + | 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 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 0000000000..8c0ec0b0fc --- /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 with loc_ghost = true}; + 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 with loc_ghost = true}} + }] + ) 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 with loc_ghost = true}; + 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 with loc_ghost = true}} + }] + ) 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 with loc_ghost = true})}; + ptyext_params = type_extension.ptyext_params; + ptyext_constructors = type_extension.ptyext_constructors; + ptyext_private = type_extension.ptyext_private; + ptyext_loc = {type_extension.ptyext_loc with loc_ghost = true}; + 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 with loc_ghost = true})}; + ptyext_params = type_extension.ptyext_params; + ptyext_constructors = type_extension.ptyext_constructors; + ptyext_private = type_extension.ptyext_private; + ptyext_loc = {type_extension.ptyext_loc with loc_ghost = true}; + 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 with loc_ghost = true}}; + pext_kind = type_exception.ptyexn_constructor.pext_kind; + pext_loc = {loc with loc_ghost = true}; + pext_attributes = []; + }; + ptyexn_loc = {loc with loc_ghost = true}; + 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 with loc_ghost = true}}; + pext_kind = type_exception.ptyexn_constructor.pext_kind; + pext_loc = {loc with loc_ghost = true}; + pext_attributes = []; + }; + ptyexn_loc = {loc with loc_ghost = true}; + 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 with loc_ghost = true};}; + pmtd_type = module_type_declaration.pmtd_type; + pmtd_attributes = []; + pmtd_loc = {loc with loc_ghost = true}; + } + ] + +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 with loc_ghost = true};}; + pmtd_type = module_type_declaration.pmtd_type; + pmtd_attributes = []; + pmtd_loc = {loc with loc_ghost = true}; + } + ] + + +(* 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 0000000000..c6fb4575a2 --- /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)) 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 0000000000..fcbdc1e39c --- /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 0000000000..797578f42e --- /dev/null +++ b/tests/test-dirs/expand_node/ppx-tests.t/rewriter/my_ppx.ml @@ -0,0 +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.estring ~loc "OCaml is so cool" + +let my_extension = + 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 ] "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 new file mode 100644 index 0000000000..9980eeecb9 --- /dev/null +++ b/tests/test-dirs/expand_node/ppx-tests.t/run.t @@ -0,0 +1,948 @@ +Dune setup + $ cat > dune-project << EOF + > (lang dune 2.9) + > EOF + + $ cat > dune << EOF + > (executable + > (name apt) + > (preprocess (pps c_ppx my_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 declaration 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 record 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 record 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": { + "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": { + "line": 2, + "col": 29 + }, + "end": { + "line": 2, + "col": 48 + } + } + }, + "notifications": [] + } + +on attribute payload name "rename" + $ $MERLIN single expand-node -position 2:46 -filename ./apt.ml < ./apt.ml + { + "class": "return", + "value": { + "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": { + "line": 2, + "col": 29 + }, + "end": { + "line": 2, + "col": 48 + } + } + }, + "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 declaration 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 record 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 record 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": { + "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": { + "line": 2, + "col": 26 + }, + "end": { + "line": 2, + "col": 45 + } + } + }, + "notifications": [] + } + +on attribute payload name "rename" + $ $MERLIN single expand-node -position 2:40 -filename ./apt.ml < ./apt.ml + { + "class": "return", + "value": { + "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": { + "line": 2, + "col": 26 + }, + "end": { + "line": 2, + "col": 45 + } + } + }, + "notifications": [] + } + + +Type declaration in structure B + $ 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": { + "code": "[ type yyyy = int[@@deriving rename] + ; 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 B + $ 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": { + "code": "[ type yyyy = int[@@deriving rename] + ; 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": [] + } + + +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": { + "code": "[ type pppp = ..[@@deriving rename] + ; 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": { + "code": "[ type pppp += + | Int of int [@@deriving rename] + ; 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": [] + } + + +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": { + "code": "[ type pppp = ..[@@deriving rename] + ; 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": [] + } + +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": { + "code": "[ exception Foo of string [@@deriving rename] + ; 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": [] + } + +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": { + "code": "[ exception Foo of string [@@deriving rename] + ; 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": [] + } + + +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] + > EOF + + $ dune build + + $ $MERLIN single expand-node -position 9:8 -filename ./apc.ml < ./apc.ml + { + "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 + 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 ] + ]", + "deriver": { + "start": { + "line": 9, + "col": 4 + }, + "end": { + "line": 9, + "col": 23 + } + } + }, + "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] + > EOF + + $ dune build + + $ $MERLIN single expand-node -position 9:8 -filename ./apc.mli < ./apc.mli + { + "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 + 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 ] + ]", + "deriver": { + "start": { + "line": 9, + "col": 4 + }, + "end": { + "line": 9, + "col": 23 + } + } + }, + "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": [] + } + + +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": "[ 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 = + 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, + "col": 9 + }, + "end": { + "line": 2, + "col": 28 + } + } + }, + "notifications": [] + } + + + $ $MERLIN single expand-node -position 7:8 -filename ./apcc.ml < ./apcc.ml + { + "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 + 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": "[ 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": { + "line": 3, + "col": 18 + }, + "end": { + "line": 3, + "col": 37 + } + } + }, + "notifications": [] + } + + +Type declaration in structure + $ cat > aptt.ml << EOF + > type tont = float * float [@@deriving rename] + > EOF + + $ dune build + +on [@@deriving rename] + $ $MERLIN single expand-node -position 1:34 -filename ./aptt.ml < ./aptt.ml + { + "class": "return", + "value": { + "code": "[ type tont = (float * float)[@@deriving rename] + ; 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": [] + } + +PPx extension + $ cat > apttt.ml << EOF + > 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": "[ \"OCaml is so cool\" + ]", + "deriver": { + "start": { + "line": 1, + "col": 27 + }, + "end": { + "line": 1, + "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": [] + } + + +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": [] + } +