package merlin-lib
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>
Merlin's libraries
Install
dune-project
Dependency
Authors
Maintainers
Sources
merlin-5.9.tbz
sha256=de395dd37d246e3ec303e1af35ff8421665fa4f0f0005d95732124b22e3417ac
sha512=74756b28767cdfe10288720540582e818a9d946dd2406606724ebc7b68d1cf6623142e56a1e457f7862839732e7add365aec056930c7ac89be7036c22e5f29ae
doc/src/merlin-lib.analysis/enclosing.ml.html
Source file enclosing.ml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226(* This computes the "enclosing" ranges a given location. Those ranges do not necessarily correspond to a node in the AST. The ranges are purely syntactic, however the AST used here is the typedtree. This creates several issues, where some information has been lost during a translation phase. We worked around those limitations, but for the record, here is a (potentially non-exhaustive) list of things that would be improved by switching to the (non-preprocessed) parsetree instead: - [let%map ... in ...] could be recogized without having to recognize the rewritten expression - Parenthesized expression could recover their inner/outer locations without [node_loc_stack] (which we could get rid of) - Let bindings turned into match node in the typedtree would not need special handling. On the other hand, a "parsetree" version of [MBrowse] does not exist yet, and we use it to get the node at cursor, as well as some of its helper functions. So, reader, whenever the parsetree version get added, rewrite this module! *) open Std let ( << ) (x : Location.t) (y : Location.t) = x.loc_end.pos_cnum <= y.loc_start.pos_cnum let contains (loc1 : Location.t) (loc2 : Location.t) = if Location.is_none loc1 then false else loc1.loc_start.pos_cnum <= loc2.loc_start.pos_cnum && loc2.loc_end.pos_cnum <= loc1.loc_end.pos_cnum let ( <= ) x y = contains y x let merge (loc1 : Location.t) (loc2 : Location.t) = let resolve_non_pos (pos1 : Lexing.position) (pos2 : Lexing.position) f = if pos1.pos_cnum < 0 then pos2 else if pos2.pos_cnum < 0 then pos1 else f () in let min_pos pos1 pos2 = resolve_non_pos pos1 pos2 @@ fun () -> if pos1.pos_cnum < pos2.pos_cnum then pos1 else pos2 in let max_pos pos1 pos2 = resolve_non_pos pos1 pos2 @@ fun () -> if pos1.pos_cnum < pos2.pos_cnum then pos2 else pos1 in let loc_start = min_pos loc1.loc_start loc2.loc_start in let loc_end = max_pos loc1.loc_end loc2.loc_end in (* I don't think loc_ghosts are a problem, as this is generated by Mbrowse module it should not contain any ghost location (apart from the initial [Location.none]). *) { Location.loc_start; loc_end; loc_ghost = false } let ( ++ ) = merge (* In some cases, we don't want to go "up" to a full node, but include new location little by little. For instance: {[ let a = 1 in let b = 2 in let c = 3 in ... ]} We don't want to go go up from [2] to [let b = 2 in let c 3 in ...], we want intermediate stages that do not correspond to actual nodes: [let b = 2 in], then [let b = 2 in let c = 3 in], ... Similar example can be done with chains of [;] sequences, and other cases. All those cases share a similar setting: they look like a sequence (of [let ... in ...], of [;], etc) that are actually represented with binary operators, with "right" priority for parentheses. In all those cases, we split the location in the left part and the right part (using [merlin_loc] to split at the right point), include the left part first, and continue "going down-right". *) let rec go_down_right acc ~current_loc (expr : Typedtree.expression) = if Mbrowse.node_loc (Expression expr) <= current_loc then (current_loc, acc) else let go right_expr = (* The location without [begin ... end] or parenthesis delimiters. *) let undelimited_loc = match Mbrowse.node_loc_stack (Expression expr) with | [] -> Some (Mbrowse.node_loc (Expression expr)) | innermost :: _ -> (* If we are in a delimited environment, and we don't come from below, we don't want to continue. Example, where brackets are current_loc and curlies are innermost: {[ [let x = 1 in {let y = 2 in] x + y} ]}*) if current_loc <= innermost then Some innermost else None in match undelimited_loc with | None -> (current_loc, acc) | Some full_loc -> let left_loc = let right_merlin_loc = Mbrowse.node_merlin_loc (Expression right_expr) in { full_loc with loc_end = right_merlin_loc.loc_start } in let current_loc = current_loc ++ left_loc in let acc = current_loc :: acc in let is_splittable = Mbrowse.node_loc_stack (Expression right_expr) |> List.is_empty in if not is_splittable then (current_loc, acc) else go_down_right acc ~current_loc right_expr in match expr with (* [let x = exp1 in][ exp2] *) | { exp_desc = Texp_let (_, _, right_expr); _ } -> go right_expr (* [exp1 ;][ exp2] *) | { exp_desc = Texp_sequence (_, right_expr); _ } -> go right_expr (* [let%map x = exp1 in][ exp2] *) | { exp_desc = Texp_apply ( _map_or_bind, [ (Nolabel, Arg value_binded); ( Labelled "f", Arg { exp_desc = Texp_function ( [ { fp_kind = Tparam_pat pat; _ } ], Tfunction_body right_expr ); _ } ) ] ); _ } when pat.pat_loc << value_binded.exp_loc && value_binded.exp_loc << right_expr.exp_loc -> go right_expr (* [let () = exp1 in][ exp2] *) | { exp_desc = Texp_match ( exp, [ { c_lhs; c_rhs = right_expr; c_guard = None; c_cont = _ } ], [], _ ); _ } when c_lhs.pat_loc << exp.exp_loc -> go right_expr (* [let* x = exp1 in][ exp2] *) | { exp_desc = Texp_letop { body = { c_rhs = right_expr; _ }; _ }; _ } -> go right_expr | _ -> (current_loc, acc) let add_intermediate_locs (current_loc, acc) locs = List.fold_left ~f:(fun (current_loc, acc) loc -> let current_loc = current_loc ++ loc in (current_loc, current_loc :: acc)) ~init:(current_loc, acc) locs let with_extra ~current_loc acc extra node = let loc_stack = Mbrowse.node_loc_stack node in let node_loc = Mbrowse.node_loc node in let node_locs = loc_stack @ [ node_loc ] in (* Exclude locations not fully containing current_loc. This is to avoid when the cursor is eg on a closing parenthesis, including the innermost location would not take the closing parenthesis. *) let node_locs = List.filter ~f:(fun loc -> current_loc <= loc) node_locs in let acc = add_intermediate_locs (current_loc, acc) node_locs in add_intermediate_locs acc (List.rev_map ~f:(fun (_, loc, _) -> loc) extra) let expr_locs acc ~current_loc (exp : Typedtree.expression) = match exp with | { exp_extra; _ } -> with_extra ~current_loc acc exp_extra (Expression exp) let pat_locs acc ~current_loc pat = match pat with | { Typedtree.pat_extra; _ } -> with_extra ~current_loc acc pat_extra (Pattern pat) let typ_locs acc ~current_loc typ = let loc_stack = Mbrowse.node_loc_stack (Core_type typ) |> List.filter ~f:(fun loc -> current_loc <= loc) in let typ_loc = Mbrowse.node_loc (Core_type typ) in add_intermediate_locs (current_loc, acc) (loc_stack @ [ typ_loc ]) let rec expand_node acc ~current_loc (nodes : Browse_raw.node list) = match nodes with | [] -> List.rev acc (* Skipping node already included *) | node :: nodes when Mbrowse.node_loc node <= current_loc -> expand_node acc ~current_loc nodes (* skipping "[let x = 5] in exp2" location. TODO: would we want to add "let [x = 5] in exp2"? *) | Value_binding _ :: (Expression _ :: _ as nodes) -> expand_node acc ~current_loc nodes | Expression exp :: nodes -> let current_loc, acc = go_down_right acc ~current_loc exp in let current_loc, acc = expr_locs acc ~current_loc exp in expand_node acc ~current_loc nodes | Pattern pat :: nodes -> let current_loc, acc = pat_locs acc ~current_loc pat in expand_node acc ~current_loc nodes | Core_type typ :: nodes -> let current_loc, acc = typ_locs acc ~current_loc typ in expand_node acc ~current_loc nodes | node :: q -> let loc = Mbrowse.node_loc node in let current_loc = current_loc ++ loc in let acc = current_loc :: acc in expand_node acc ~current_loc q let locs current_loc (mbrowse : Mbrowse.t) = mbrowse |> List.map ~f:snd |> expand_node [] ~current_loc |> List.fold_left ~init:[] ~f:(fun acc loc -> match acc with | hd :: _ as acc when Location_aux.compare hd loc = 0 -> acc | _ -> loc :: acc) |> List.rev
sectionYPositions = computeSectionYPositions($el), 10)"
x-init="setTimeout(() => sectionYPositions = computeSectionYPositions($el), 10)"
>