Source file rawDocument.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
open Lsp.Types
type text_edit = Range.t * string
type t = {
text : string;
lines : int array;
is_ascii : bool array;
}
let compute_lines text =
let len = String.length text in
let rec loop idx current_ascii acc_lines acc_ascii =
if idx >= len then
let final_lines = Array.of_list (List.rev acc_lines) in
let final_ascii = Array.of_list (List.rev (current_ascii :: acc_ascii)) in
(final_lines, final_ascii)
else
let c = String.unsafe_get text idx in
if c = '\n' then
loop (idx + 1) true ((idx + 1) :: acc_lines) (current_ascii :: acc_ascii)
else
loop (idx + 1) (current_ascii && Char.code c < 128) acc_lines acc_ascii
in
loop 0 true [0] []
let create text =
let lines, is_ascii = compute_lines text in
{ text; lines; is_ascii }
let text t = t.text
let line_count raw =
Array.length raw.lines
let line_span raw i =
if i + 1 < Array.length raw.lines then
(raw.lines.(i), raw.lines.(i+1) - raw.lines.(i))
else
(raw.lines.(i), String.length raw.text - raw.lines.(i))
let code_unit_pos_of_loc raw line_idx loc =
let line_start, line_len = line_span raw line_idx in
let loc = max 0 (min loc line_len) in
if raw.is_ascii.(line_idx) then
loc
else
let rec loop byte_offset utf16_count =
if byte_offset >= loc then
utf16_count
else
let d = String.get_utf_8_uchar raw.text (line_start + byte_offset) in
let byte_len = Uchar.utf_decode_length d in
let u = Uchar.utf_decode_uchar d in
let units = if Uchar.to_int u > 0xFFFF then 2 else 1 in
loop (byte_offset + byte_len) (utf16_count + units)
in
loop 0 0
let loc_of_code_unit_pos raw line_idx pos =
let line_start, line_len = line_span raw line_idx in
let pos = max 0 (min pos line_len) in
if raw.is_ascii.(line_idx) then
pos
else
let rec loop byte_offset utf16_count =
if utf16_count >= pos then
byte_offset
else
let d = String.get_utf_8_uchar raw.text (line_start + byte_offset) in
let byte_len = Uchar.utf_decode_length d in
let u = Uchar.utf_decode_uchar d in
let units = if Uchar.to_int u > 0xFFFF then 2 else 1 in
loop (byte_offset + byte_len) (utf16_count + units)
in
loop 0 0
let line_of_loc raw loc =
let nlines = line_count raw in
let rec aux low high =
if low > high then max 0 high
else
let mid = low + (high - low) / 2 in
if raw.lines.(mid) <= loc then
if mid = nlines - 1 || loc < raw.lines.(mid + 1) then
mid
else
aux (mid + 1) high
else
aux low (mid - 1)
in
aux 0 (nlines - 1)
let position_of_loc raw loc =
let line = line_of_loc raw loc in
let character = code_unit_pos_of_loc raw line (loc - raw.lines.(line)) in
Position.{ line; character }
let loc_of_position raw Position.{ line; character } =
let nlines = line_count raw in
let line = max 0 (min line (nlines - 1)) in
let charloc = loc_of_code_unit_pos raw line character in
raw.lines.(line) + charloc
let end_loc raw =
String.length raw.text
let range_of_loc raw loc =
let open Range in
{ start = position_of_loc raw loc.Loc.bp;
end_ = position_of_loc raw loc.Loc.ep;
}
let word_back_reg = Str.regexp {|[^a-zA-Z_0-9.']|}
let word_forward_reg = Str.regexp {|[^a-zA-Z_0-9']|}
let word_at_loc raw loc : string option =
try
let start_ind = loc in
let first_non_word_ind = Str.search_backward word_back_reg raw.text start_ind in
let first_word_ind = first_non_word_ind + 1 in
let last_word_ind = Str.search_forward word_forward_reg raw.text start_ind in
let word = String.sub raw.text first_word_ind (last_word_ind - first_word_ind) in
Some word
with _ ->
None
let string_in_range raw start end_ =
try
String.sub raw.text start (end_ - start)
with _ ->
""
let apply_text_edit raw (Range.{start; end_}, editText) =
let start = loc_of_position raw start in
let stop = loc_of_position raw end_ in
let before = String.sub raw.text 0 start in
let after = String.sub raw.text stop (String.length raw.text - stop) in
let new_text = before ^ editText ^ after in
let new_lines, new_is_ascii = compute_lines new_text in
{ text = new_text; lines = new_lines; is_ascii = new_is_ascii }, start