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
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
{ Location.loc_start; loc_end; loc_ghost = false }
let ( ++ ) = merge
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 =
let undelimited_loc =
match Mbrowse.node_loc_stack (Expression expr) with
| [] -> Some (Mbrowse.node_loc (Expression expr))
| innermost :: _ ->
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
| { exp_desc = Texp_let (_, _, right_expr); _ } -> go right_expr
| { exp_desc = Texp_sequence (_, right_expr); _ } -> go right_expr
| { 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
| { 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
| { 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 ~current_loc acc 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
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
| { ; _ } -> with_extra ~current_loc acc exp_extra (Expression exp)
let pat_locs acc ~current_loc pat =
match pat with
| { ; _ } ->
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
| node :: nodes when Mbrowse.node_loc node <= current_loc ->
expand_node acc ~current_loc nodes
| 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