forked from rescript-lang/rescript
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathloc.ml
187 lines (165 loc) · 5.27 KB
/
loc.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
(*
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*)
open Primitive_deriving
(* line numbers are 1-indexed; column numbers are 0-indexed *)
type position = {
line: int;
column: int;
}
[@@deriving_inline equal]
let _ = fun (_ : position) -> ()
let equal_position =
(fun a__001_ ->
fun b__002_ ->
if Ppx_compare_lib.phys_equal a__001_ b__002_
then true
else
Ppx_compare_lib.(&&) (equal_int a__001_.line b__002_.line)
(equal_int a__001_.column b__002_.column) : position ->
position -> bool)
let _ = equal_position
[@@@end]
(* start is inclusive; end is exclusive *)
(* If you are modifying this record, go look at ALoc.ml and make sure you understand the
* representation there. *)
type t = {
source: File_key.t option;
start: position;
_end: position;
}
let none = { source = None; start = { line = 0; column = 0 }; _end = { line = 0; column = 0 } }
let is_none (x : t) =
x == none
||
match x with
| { source = None; start = { line = 0; column = 0 }; _end = { line = 0; column = 0 } } -> true
| _ -> false
let is_none_ignore_source (x : t) =
x == none
||
match x with
| { source = _; start = { line = 0; column = 0 }; _end = { line = 0; column = 0 } } -> true
| _ -> false
let btwn loc1 loc2 = { source = loc1.source; start = loc1.start; _end = loc2._end }
(* Returns the position immediately before the start of the given loc. If the
given loc is at the beginning of a line, return the position of the first
char on the same line. *)
let char_before loc =
let start =
let { line; column } = loc.start in
let column =
if column > 0 then
column - 1
else
column
in
{ line; column }
in
let _end = loc.start in
{ loc with start; _end }
(* Returns the location of the first character in the given loc. Not accurate if the
* first line is a newline character, but is still consistent with loc orderings. *)
let first_char loc =
let start = loc.start in
let _end = { start with column = start.column + 1 } in
{ loc with _end }
let pos_cmp a b =
let k = a.line - b.line in
if k = 0 then
a.column - b.column
else
k
(**
* If `a` spans (completely contains) `b`, then returns 0.
* If `b` starts before `a` (even if it ends inside), returns < 0.
* If `b` ends after `a` (even if it starts inside), returns > 0.
*)
let span_compare a b =
let k = File_key.compare_opt a.source b.source in
if k = 0 then
let k = pos_cmp a.start b.start in
if k <= 0 then
let k = pos_cmp a._end b._end in
if k >= 0 then
0
else
-1
else
1
else
k
(** [contains loc1 loc2] returns true if [loc1] entirely overlaps [loc2] *)
let contains loc1 loc2 = span_compare loc1 loc2 = 0
(** [intersects loc1 loc2] returns true if [loc1] intersects [loc2] at all *)
let intersects loc1 loc2 =
File_key.compare_opt loc1.source loc2.source = 0
&& not (pos_cmp loc1._end loc2.start < 0 || pos_cmp loc1.start loc2._end > 0)
(** [lines_intersect loc1 loc2] returns true if [loc1] and [loc2] cover any part of
the same line, even if they don't actually intersect.
For example, if [loc1] ends and then [loc2] begins later on the same line,
[intersects loc1 loc2] is false, but [lines_intersect loc1 loc2] is true. *)
let lines_intersect loc1 loc2 =
File_key.compare_opt loc1.source loc2.source = 0
&& not (loc1._end.line < loc2.start.line || loc1.start.line > loc2._end.line)
let compare_ignore_source loc1 loc2 =
match pos_cmp loc1.start loc2.start with
| 0 -> pos_cmp loc1._end loc2._end
| k -> k
let compare loc1 loc2 =
let k = File_key.compare_opt loc1.source loc2.source in
if k = 0 then
compare_ignore_source loc1 loc2
else
k
let equal loc1 loc2 = compare loc1 loc2 = 0
(**
* This is mostly useful for debugging purposes.
* Please don't dead-code delete this!
*)
let debug_to_string ?(include_source = false) loc =
let source =
if include_source then
Printf.sprintf
"%S: "
(match loc.source with
| Some src -> File_key.to_string src
| None -> "<NONE>")
else
""
in
let pos =
Printf.sprintf
"(%d, %d) to (%d, %d)"
loc.start.line
loc.start.column
loc._end.line
loc._end.column
in
source ^ pos
let to_string_no_source loc =
let line = loc.start.line in
let start = loc.start.column + 1 in
let end_ = loc._end.column in
if line <= 0 then
"0:0"
else if line = loc._end.line && start = end_ then
Printf.sprintf "%d:%d" line start
else if line != loc._end.line then
Printf.sprintf "%d:%d,%d:%d" line start loc._end.line end_
else
Printf.sprintf "%d:%d-%d" line start end_
let mk_loc ?source (start_line, start_column) (end_line, end_column) =
{
source;
start = { line = start_line; column = start_column };
_end = { line = end_line; column = end_column };
}
let source loc = loc.source
(** Produces a zero-width Loc.t, where start = end *)
let cursor source line column = { source; start = { line; column }; _end = { line; column } }
let start_loc loc = { loc with _end = loc.start }
let end_loc loc = { loc with start = loc._end }