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 227 228 229 230 231 232
|
(*
* zed_lines.ml
* ------------
* Copyright : (c) 2011, Jeremie Dimino <jeremie@dimino.org>
* Licence : BSD3
*
* This file is a part of Zed, an editor engine.
*)
open CamomileLibraryDyn.Camomile
exception Out_of_bounds
(* +-----------------------------------------------------------------+
| Representation |
+-----------------------------------------------------------------+ *)
(* Sets are represented by ropes. *)
type t =
| String of int
(* [String len] is a string of length [len] without newline
character. *)
| Return
(* A newline character. *)
| Concat of t * t * int * int * int
(* [Concat(t1, t2, len, count, depth)] *)
(* +-----------------------------------------------------------------+
| Basic functions |
+-----------------------------------------------------------------+ *)
let length = function
| String len -> len
| Return -> 1
| Concat(_, _, len, _, _) -> len
let count = function
| String _ -> 0
| Return -> 1
| Concat(_, _, _, count, _) -> count
let depth = function
| String _ | Return -> 0
| Concat(_, _, _, _, d) -> d
let is_empty = function
| String 0 -> true
| _ -> false
let empty = String 0
(* +-----------------------------------------------------------------+
| Offset/line resolution |
+-----------------------------------------------------------------+ *)
let rec line_index_rec set ofs acc =
match set with
| String _ ->
acc
| Return ->
if ofs = 0 then
acc
else
acc + 1
| Concat(s1, s2, _, _, _) ->
let len1 = length s1 in
if ofs < len1 then
line_index_rec s1 ofs acc
else
line_index_rec s2 (ofs - len1) (acc + count s1)
let line_index set ofs =
if ofs < 0 || ofs > length set then
raise Out_of_bounds
else
line_index_rec set ofs 0
let rec line_start_rec set idx acc =
match set with
| String _ ->
acc
| Return ->
if idx = 0 then
acc
else
acc + 1
| Concat(s1, s2, _, _, _) ->
let count1 = count s1 in
if idx <= count1 then
line_start_rec s1 idx acc
else
line_start_rec s2 (idx - count1) (acc + length s1)
let line_start set idx =
if idx < 0 || idx > count set then
raise Out_of_bounds
else
line_start_rec set idx 0
let line_stop set idx =
if idx = count set
then length set
else line_start set (idx + 1) - 1
let line_length set idx =
line_stop set idx - line_start set idx
(* +-----------------------------------------------------------------+
| Operations on sets |
+-----------------------------------------------------------------+ *)
let concat set1 set2 =
Concat(set1, set2,
length set1 + length set2,
count set1 + count set2,
1 + max (depth set1) (depth set2))
let append set1 set2 =
match set1, set2 with
| String 0, _ -> set2
| _, String 0 -> set1
| String len1, String len2 ->
String(len1 + len2)
| String len1, Concat(String len2, set, len, count, h) ->
Concat(String(len1 + len2), set, len + len1, count, h)
| Concat(set, String len1, len, count, h), String len2 ->
Concat(set, String(len1 + len2), len + len2, count, h)
| _ ->
let d1 = depth set1 and d2 = depth set2 in
if d1 > d2 + 2 then begin
match set1 with
| String _ | Return ->
assert false
| Concat(set1_1, set1_2, _, _, _) ->
if depth set1_1 >= depth set1_2 then
concat set1_1 (concat set1_2 set2)
else begin
match set1_2 with
| String _ | Return ->
assert false
| Concat(set1_2_1, set1_2_2, _, _, _) ->
concat (concat set1_1 set1_2_1) (concat set1_2_2 set2)
end
end else if d2 > d1 + 2 then begin
match set2 with
| String _ | Return ->
assert false
| Concat(set2_1, set2_2, _, _, _) ->
if depth set2_2 >= depth set2_1 then
concat (concat set1 set2_1) set2_2
else begin
match set2_1 with
| String _ | Return ->
assert false
| Concat(set2_1_1, set2_1_2, _, _, _) ->
concat (concat set1 set2_1_1) (concat set2_1_2 set2_2)
end
end else
concat set1 set2
let rec unsafe_sub set idx len =
match set with
| String _ ->
String len
| Return ->
if len = 1 then
Return
else
String 0
| Concat(set_l, set_r, len', _, _) ->
let len_l = length set_l in
if len = len' then
set
else if idx >= len_l then
unsafe_sub set_r (idx - len_l) len
else if idx + len <= len_l then
unsafe_sub set_l idx len
else
append
(unsafe_sub set_l idx (len_l - idx))
(unsafe_sub set_r 0 (len - len_l + idx))
let sub set idx len =
if idx < 0 || len < 0 || idx + len > length set then
raise Out_of_bounds
else
unsafe_sub set idx len
let break set ofs =
let len = length set in
if ofs < 0 || ofs > len then
raise Out_of_bounds
else
(unsafe_sub set 0 ofs, unsafe_sub set ofs (len - ofs))
let insert set ofs set' =
let set1, set2 = break set ofs in
append set1 (append set' set2)
let remove set ofs len =
append (sub set 0 ofs) (sub set (ofs + len) (length set - ofs - len))
let replace set ofs len repl =
append (sub set 0 ofs) (append repl (sub set (ofs + len) (length set - ofs - len)))
(* +-----------------------------------------------------------------+
| Sets from ropes |
+-----------------------------------------------------------------+ *)
let of_rope rope =
let rec loop zip len acc =
if Zed_rope.Zip.at_eos zip then
append acc (String len)
else
let ch, zip = Zed_rope.Zip.next zip in
if UChar.code ch = 10 then
loop0 zip (append (append acc (String len)) Return)
else
loop zip (len + 1) acc
and loop0 zip acc =
if Zed_rope.Zip.at_eos zip then
acc
else
let ch, zip = Zed_rope.Zip.next zip in
if UChar.code ch = 10 then
loop0 zip (append acc Return)
else
loop zip 1 acc
in
loop0 (Zed_rope.Zip.make_f rope 0) empty
|