summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorFederico Igne <undyamon@disroot.org>2024-01-26 20:38:37 +0100
committerFederico Igne <undyamon@disroot.org>2024-01-26 20:38:37 +0100
commitbabeb42c12a3f1bffaefd87d9f2b18e8ae2e8939 (patch)
tree3789e8fa79fc273e972e17c11ba44e4fcf5a1e43
parent51db77f719d2d9fe3160b91a6c7cb9a6e9f256f8 (diff)
downloadsandy-babeb42c12a3f1bffaefd87d9f2b18e8ae2e8939.tar.gz
sandy-babeb42c12a3f1bffaefd87d9f2b18e8ae2e8939.zip
refactor: turn editor buffer into a state monad
-rw-r--r--lib/editorBuffer.ml271
1 files changed, 189 insertions, 82 deletions
diff --git a/lib/editorBuffer.ml b/lib/editorBuffer.ml
index 295e6e7..52b1115 100644
--- a/lib/editorBuffer.ml
+++ b/lib/editorBuffer.ml
@@ -55,15 +55,54 @@ let cursor ?(rendered = true) b =
55 in 55 in
56 (left_length c, apply_focus_or ~default:0 hlen c) 56 (left_length c, apply_focus_or ~default:0 hlen c)
57 57
58type 'a action = t -> 'a * t
59
58module Action = struct 60module Action = struct
59 let on_content f b = { b with content = Result.map ~f b.content } 61 include Applicative.Make (struct
60 let on_rendered f b = { b with rendered = f b.rendered } 62 type 'a t = 'a action
63
64 let return a b = (a, b)
65
66 let apply f a b =
67 let g, b = f b in
68 let x, b = a b in
69 (g x, b)
70
71 let map = `Define_using_apply
72 end)
73
74 include Monad.Make (struct
75 type 'a t = 'a action
76
77 let return x b = (x, b)
78
79 let bind a ~f x =
80 let y, a' = a x in
81 f y a'
82
83 let map = `Define_using_bind
84 end)
85
86 let ( let* ) b f = bind b ~f
87 let ( and* ) = both
88 let ( let+ ) b f = map b ~f
89 let get b = (b, b)
90 let put b _ = ((), b)
91 let modify ~f = get >>= (put &> f)
92 let on_content f b = ((), { b with content = Result.map ~f b.content })
93
94 let on_content_with_output ~default f b =
95 match b.content with
96 | Error _ -> (default, b)
97 | Ok z ->
98 let a, z = f z in
99 (a, { b with content = Ok z })
61 100
62 (* let on_content_stored ?(register = '"') ?(append = false) f b = ... *) 101 let on_rendered f b = ((), { b with rendered = f b.rendered })
63 102
64 let update_render ?(before = false) ~n b = 103 let update_render ?(before = false) ~n b =
65 match b.content with 104 match b.content with
66 | Error _ -> b 105 | Error _ -> ((), b)
67 | Ok c -> 106 | Ok c ->
68 let step = if before then left else right in 107 let step = if before then left else right in
69 let rec aux i r c = 108 let rec aux i r c =
@@ -74,51 +113,66 @@ module Action = struct
74 let c' = step c and r' = swap_focus l r |> step in 113 let c' = step c and r' = swap_focus l r |> step in
75 aux (i - 1) r' c' 114 aux (i - 1) r' c'
76 in 115 in
77 { b with rendered = aux n b.rendered c |> goto (left_length c) } 116 ((), { b with rendered = aux n b.rendered c |> goto (left_length c) })
78 117
79 let update_render_at_cursor = update_render ~n:1 118 let update_render_at_cursor = update_render ~before:false ~n:1
80 119
81 let move_up ?(n = 1) = 120 let move_up ?(n = 1) =
82 let change_content = 121 let change_content =
83 on_content (fun z -> 122 let act z =
84 let col = apply_focus_or ~default:0 left_length z in 123 let col = apply_focus_or ~default:0 left_length z in
85 Fn.apply_n_times ~n left z |> map_focus (goto col)) 124 Fn.apply_n_times ~n left z |> map_focus (goto col)
125 in
126 on_content act
86 and change_rendered = on_rendered (Fn.apply_n_times ~n left) in 127 and change_rendered = on_rendered (Fn.apply_n_times ~n left) in
87 change_content &> change_rendered 128 change_content *> change_rendered
88 129
89 let move_down ?(n = 1) = 130 let move_down ?(n = 1) =
90 let change_content = 131 let change_content =
91 on_content (fun z -> 132 let act z =
92 let col = apply_focus_or ~default:0 left_length z in 133 let col = apply_focus_or ~default:0 left_length z in
93 Fn.apply_n_times ~n right z |> map_focus (goto col)) 134 Fn.apply_n_times ~n right z |> map_focus (goto col)
135 in
136 on_content act
94 and change_rendered = on_rendered (Fn.apply_n_times ~n right) in 137 and change_rendered = on_rendered (Fn.apply_n_times ~n right) in
95 change_content &> change_rendered 138 change_content *> change_rendered
96 139
97 let move_left, move_right = 140 let move_left, move_right =
98 let horizontal f ?(n = 1) = 141 let horizontal f ?(n = 1) =
99 let change_content = map_focus (Fn.apply_n_times ~n f) |> on_content in 142 map_focus (Fn.apply_n_times ~n f) |> on_content
100 change_content &> update_render_at_cursor
101 in 143 in
102 (horizontal left, horizontal right) 144 (horizontal left, horizontal right)
103 145
104 let bol = map_focus far_left |> on_content 146 let bol ?(n = 0) = move_down ~n *> (map_focus far_left |> on_content)
105 let eol = map_focus far_right |> on_content 147 let eol ?(n = 0) = move_down ~n *> (map_focus far_right |> on_content)
106 148
107 let bof = 149 let bof =
108 let change_content = far_left |> on_content 150 let change_content = far_left |> on_content
109 and change_rendered = far_left |> on_rendered in 151 and change_rendered = far_left |> on_rendered in
110 change_content &> change_rendered 152 change_content *> change_rendered
111 153
112 let eof = 154 let eof =
113 let change_content = far_right |> on_content 155 let change_content = far_right |> on_content
114 and change_rendered = far_right |> on_rendered in 156 and change_rendered = far_right |> on_rendered in
115 change_content &> change_rendered 157 change_content *> change_rendered
116 158
117 let insert k = 159 let insert k =
118 let change_content = map_focus (push_before k) |> on_content in 160 let change_content = map_focus (push_before k) |> on_content in
119 change_content &> update_render_at_cursor 161 change_content *> update_render_at_cursor
120 162
121 (* let replace k = () *) 163 let replace k =
164 let change_content = map_focus (swap_focus k) |> on_content in
165 change_content *> update_render_at_cursor
166
167 let insert_line ?(before = false) =
168 let change_content =
169 let push = if before then push_before else push_after in
170 push Zipper.empty |> on_content
171 and change_rendered =
172 let push = if before then push_before else push_after in
173 push Sequence.empty |> on_rendered
174 in
175 change_content *> change_rendered
122 176
123 let join_lines ?(n = 2) = 177 let join_lines ?(n = 2) =
124 let change_content = 178 let change_content =
@@ -126,14 +180,14 @@ module Action = struct
126 if right_length z < 2 then z 180 if right_length z < 2 then z
127 else 181 else
128 let to_join = right z |> focus_or ~default:Zipper.empty in 182 let to_join = right z |> focus_or ~default:Zipper.empty in
129 map_focus (join ~z2:to_join) z |> pop_after 183 map_focus (Zipper.join ~z2:to_join) z |> pop_after |> snd
130 in 184 in
131 Fn.apply_n_times ~n:(n - 1) join_2_lines |> on_content 185 Fn.apply_n_times ~n:(n - 1) join_2_lines |> on_content
132 and change_rendered = pop_after ~n:(n - 1) |> on_rendered in 186 and change_rendered = pop_after ~n:(n - 1) &> snd |> on_rendered in
133 change_content &> change_rendered &> update_render_at_cursor 187 change_content *> change_rendered *> update_render_at_cursor
134 188
135 let delete_after ?(cross_lines = false) ~n b = 189 let delete_after ?(cross_lines = false) ~n =
136 let lines_to_delete = 190 let lines_to_delete b =
137 match b.content with 191 match b.content with
138 | Error _ -> 0 192 | Error _ -> 0
139 | Ok z -> ( 193 | Ok z -> (
@@ -149,23 +203,38 @@ module Action = struct
149 |> Sequence.findi ~f:(fun _ a -> n <= a) 203 |> Sequence.findi ~f:(fun _ a -> n <= a)
150 |> Option.map ~f:fst 204 |> Option.map ~f:fst
151 |> Option.value ~default:(Sequence.length t)) 205 |> Option.value ~default:(Sequence.length t))
206 and change_content =
207 let default = Sequence.empty in
208 on_content_with_output ~default (fun z ->
209 let rec aux i acc z =
210 let line = focus_or ~default:Zipper.empty z in
211 if right_length line >= i || (not cross_lines) || right_length z < 2
212 then
213 let s, z1 = pop ~n:i line in
214 (s :: acc, swap_focus z1 z)
215 else
216 let rlen = right_length line in
217 let s, z1 = pop ~n:rlen line in
218 let z = swap_focus z1 z in
219 let to_join = right z |> focus_or ~default:Zipper.empty in
220 let z =
221 map_focus (Zipper.join ~z2:to_join) z |> pop_after |> snd
222 in
223 aux (i - rlen - 1) (s :: acc) z
224 in
225 let acc, z = aux n [] z in
226 (List.rev acc |> Sequence.of_list, z))
227 and change_rendered n =
228 if cross_lines then pop_after ~n &> snd |> on_rendered else return ()
152 in 229 in
153 let change_content = 230 let* n = get >>| lines_to_delete in
154 let delete_1_after z = 231 let* out = change_content
155 let line = focus_or ~default:Zipper.empty z in 232 and* () = change_rendered n
156 if cross_lines && is_far_right line && not (is_far_right z) then 233 and* () = update_render_at_cursor in
157 let to_join = right z |> focus_or ~default:Zipper.empty in 234 return out
158 map_focus (join ~z2:to_join) z |> pop_after 235
159 else map_focus pop z 236 let delete_before ?(cross_lines = false) ~n =
160 in 237 let lines_to_delete b =
161 Fn.apply_n_times ~n delete_1_after |> on_content
162 and change_rendered =
163 if cross_lines then pop_after ~n:lines_to_delete |> on_rendered else Fn.id
164 in
165 b |> change_content |> change_rendered |> update_render_at_cursor
166
167 let delete_before ?(cross_lines = false) ~n b =
168 let lines_to_delete =
169 match b.content with 238 match b.content with
170 | Error _ -> 0 239 | Error _ -> 0
171 | Ok z -> 240 | Ok z ->
@@ -178,55 +247,87 @@ module Action = struct
178 |> Sequence.findi ~f:(fun _ a -> n <= a) 247 |> Sequence.findi ~f:(fun _ a -> n <= a)
179 |> Option.map ~f:fst 248 |> Option.map ~f:fst
180 |> Option.value ~default:(left_length z) 249 |> Option.value ~default:(left_length z)
250 and change_content =
251 let default = Sequence.empty in
252 on_content_with_output ~default (fun z ->
253 let rec aux i acc z =
254 let line = focus_or ~default:Zipper.empty z in
255 if left_length line >= i || (not cross_lines) || is_far_left z then
256 let s, z1 = pop_before ~n:i line in
257 let z' = swap_focus z1 z in
258 (s :: acc, z')
259 else
260 let llen = left_length line in
261 let s, z1 = pop_before ~n:llen line in
262 let z' = swap_focus z1 z in
263 let to_join = focus_or ~default:Zipper.empty z' in
264 let z'' =
265 left z'
266 |> map_focus (Zipper.join ~z2:to_join)
267 |> pop_after |> snd
268 in
269 aux (i - llen - 1) (s :: acc) z''
270 in
271 let acc, z' = aux n [] z in
272 (List.rev acc |> Sequence.of_list, z'))
273 and change_rendered n =
274 if cross_lines then pop_before ~n &> snd |> on_rendered else return ()
181 in 275 in
182 let change_content = 276 let* n = get >>| lines_to_delete in
183 let delete_1_before z = 277 let* out = change_content
184 let line = focus_or ~default:Zipper.empty z in 278 and* () = change_rendered n
185 if cross_lines && is_far_left line && not (is_far_left z) then 279 and* () = update_render_at_cursor in
186 left z |> map_focus (join ~z2:line) |> pop_after 280 return out
187 else map_focus pop_before z
188 in
189 Fn.apply_n_times ~n delete_1_before |> on_content
190 and change_rendered =
191 if cross_lines then pop_before ~n:lines_to_delete |> on_rendered
192 else Fn.id
193 in
194 b |> change_content |> change_rendered |> update_render_at_cursor
195
196 let delete_to_eol =
197 map_focus (split &> fst) |> on_content &> update_render_at_cursor
198
199 let delete_to_bol =
200 map_focus (split &> snd) |> on_content &> update_render_at_cursor
201 281
202 let delete_lines ~n = 282 let delete_lines ~n =
203 let change_content = pop ~n |> on_content 283 let default = Sequence.empty in
204 and change_rendered = pop ~n |> on_rendered in 284 let* out = pop ~n |> on_content_with_output ~default
205 change_content &> change_rendered 285 and* () = pop ~n &> snd |> on_rendered in
286 return (Sequence.map ~f:to_seq out)
287
288 (* TODO: maybe not needed *)
289 (* let delete_lines_before ~n b = *)
290 (* let default = Sequence.empty in *)
291 (* let out, b = on_content_with_output ~default (pop_before ~n) b in *)
292 (* (Sequence.map ~f:to_seq out, on_rendered (pop_before ~n &> snd) b) *)
293
294 let delete_to_eol ?(n = 0) =
295 let act z =
296 let default = (Zipper.empty, Zipper.empty) in
297 let z1, z2 = apply_focus_or ~default split z in
298 (to_seq z2, swap_focus z1 z)
299 in
300 let default = Sequence.empty in
301 let* h = act |> on_content_with_output ~default
302 and* t = move_down *> delete_lines ~n
303 and* () = move_up *> eol
304 and* () = update_render_at_cursor in
305 return (Sequence.shift_right t h)
206 306
207 let delete_lines_before ~n = 307 let delete_to_bol =
208 let change_content = pop_before ~n |> on_content 308 let act z =
209 and change_rendered = pop_before ~n |> on_rendered in 309 let default = (Zipper.empty, Zipper.empty) in
210 change_content &> change_rendered 310 let z1, z2 = apply_focus_or ~default split z in
311 (Sequence.singleton (to_seq z1), swap_focus z2 z)
312 in
313 let default = Sequence.empty in
314 let* out = act |> on_content_with_output ~default
315 and* () = update_render_at_cursor in
316 return out
211 317
212 let newline = 318 let newline =
213 let change_content = 319 let change_content =
214 let aux z = 320 let aux z =
215 let default = (Zipper.empty, Zipper.empty) in 321 let default = (Zipper.empty, Zipper.empty) in
216 let l1, l2 = apply_focus_or ~default split z in 322 let z1, z2 = apply_focus_or ~default split z in
217 push_before l1 z |> swap_focus l2 323 push_before z1 z |> swap_focus z2
218 in 324 in
219 on_content aux 325 on_content aux
220 and change_rendered b = 326 and change_rendered =
221 match b.content with 327 (push_before Sequence.empty |> on_rendered)
222 | Error _ -> b 328 *> update_render ~before:true ~n:2
223 | Ok c -> (
224 match Sequence.next (before c) with
225 | None -> failwith "newline > change_rendered: unreachable"
226 | Some (h, _) ->
227 { b with rendered = push_before (to_seq h |> render) b.rendered })
228 in 329 in
229 change_content &> change_rendered &> update_render_at_cursor 330 change_content *> change_rendered
230 331
231 let paste ?(before = false) ?(linewise = false) ?(n = 1) s = 332 let paste ?(before = false) ?(linewise = false) ?(n = 1) s =
232 let change_content = 333 let change_content =
@@ -263,9 +364,15 @@ module Action = struct
263 let len = Sequence.length s in 364 let len = Sequence.length s in
264 let push = if before then push_before else push in 365 let push = if before then push_before else push in
265 let aux z = Fn.apply_n_times ~n:(len - 1) (push Sequence.empty) z in 366 let aux z = Fn.apply_n_times ~n:(len - 1) (push Sequence.empty) z in
266 on_rendered aux &> update_render ~before ~n:len 367 on_rendered aux *> update_render ~before ~n:len
368 in
369 let rec bind_n_times ~n act =
370 match n with
371 | _ when n <= 0 -> return ()
372 | 1 -> act
373 | _ -> act *> bind_n_times ~n:(n - 1) act
267 in 374 in
268 Fn.apply_n_times ~n (change_content &> change_rendered) 375 bind_n_times ~n (change_content *> change_rendered)
269 376
270 (* let save_history_to ?(clear = true) r = () *) 377 (* let save_history_to ?(clear = true) r = () *)
271end 378end