diff options
| author | Federico Igne <undyamon@disroot.org> | 2024-01-28 23:42:12 +0100 |
|---|---|---|
| committer | Federico Igne <undyamon@disroot.org> | 2024-01-28 23:42:12 +0100 |
| commit | f5c33507a74d83692c028d1e1659d3506399138e (patch) | |
| tree | 15abadcda66931770003a7ec5b403695f4f81fd0 /lib/editor.ml | |
| parent | 633fb26ed21f7208517aa29dbaab9f0cf3bb2047 (diff) | |
| download | sandy-f5c33507a74d83692c028d1e1659d3506399138e.tar.gz sandy-f5c33507a74d83692c028d1e1659d3506399138e.zip | |
Diffstat (limited to 'lib/editor.ml')
| -rw-r--r-- | lib/editor.ml | 106 |
1 files changed, 95 insertions, 11 deletions
diff --git a/lib/editor.ml b/lib/editor.ml index ea2e68a..315067b 100644 --- a/lib/editor.ml +++ b/lib/editor.ml | |||
| @@ -14,7 +14,7 @@ type editor = { | |||
| 14 | mode : Mode.t; | 14 | mode : Mode.t; |
| 15 | offset : int * int; | 15 | offset : int * int; |
| 16 | cursor : cursor; | 16 | cursor : cursor; |
| 17 | buffer : Buffer.t; | 17 | buffer : Buffer.t Tipper.t; |
| 18 | rendered : bool; | 18 | rendered : bool; |
| 19 | istream : Command.t Sequence.t; | 19 | istream : Command.t Sequence.t; |
| 20 | nstream : Command.t Sequence.t; | 20 | nstream : Command.t Sequence.t; |
| @@ -39,7 +39,8 @@ let init (c : Config.t) : editor = | |||
| 39 | buffer = | 39 | buffer = |
| 40 | List.hd c.files | 40 | List.hd c.files |
| 41 | |> Option.map ~f:Buffer.from_file | 41 | |> Option.map ~f:Buffer.from_file |
| 42 | |> Option.value ~default:Buffer.empty; | 42 | |> Option.value_or_thunk ~default:Buffer.empty |
| 43 | |> Tipper.create; | ||
| 43 | rendered = true; | 44 | rendered = true; |
| 44 | istream = Command.i_stream; | 45 | istream = Command.i_stream; |
| 45 | nstream = Command.n_stream; | 46 | nstream = Command.n_stream; |
| @@ -59,10 +60,11 @@ let statusbar e = | |||
| 59 | let w = e.term.size |> snd in | 60 | let w = e.term.size |> snd in |
| 60 | let status = | 61 | let status = |
| 61 | let mode = e.mode |> Mode.to_string |> sequence_of_string in | 62 | let mode = e.mode |> Mode.to_string |> sequence_of_string in |
| 62 | let lsize = Sequence.length mode | 63 | let lsize = Sequence.length mode in |
| 63 | and c = e.buffer.kind |> Buffer.string_of_kind |> sequence_of_string | 64 | let buf = Tipper.focus e.buffer in |
| 64 | and br, bc = Buffer.size e.buffer | 65 | let c = buf.kind |> Buffer.string_of_kind |> sequence_of_string |
| 65 | and cr, cc = Buffer.cursor ~rendered:false e.buffer in | 66 | and br, bc = Buffer.size buf |
| 67 | and cr, cc = Buffer.cursor ~rendered:false buf in | ||
| 66 | let perc = | 68 | let perc = |
| 67 | match cr with | 69 | match cr with |
| 68 | | 0 -> "Top" | 70 | | 0 -> "Top" |
| @@ -136,15 +138,25 @@ module Action = struct | |||
| 136 | let dx, dy = e.offset and rs, cs = e.term.size in | 138 | let dx, dy = e.offset and rs, cs = e.term.size in |
| 137 | (* Limit cursor to buffer view *) | 139 | (* Limit cursor to buffer view *) |
| 138 | let rs = rs - e.status_size in | 140 | let rs = rs - e.status_size in |
| 139 | let cx, cy = Buffer.cursor e.buffer in | 141 | let cx, cy = e.buffer |> Tipper.focus |> Buffer.cursor in |
| 140 | let dx' = Int.clamp_exn ~min:(cx - rs + 1) ~max:cx dx | 142 | let dx' = Int.clamp_exn ~min:(cx - rs + 1) ~max:cx dx |
| 141 | and dy' = Int.clamp_exn ~min:(cy - cs + 1) ~max:cy dy in | 143 | and dy' = Int.clamp_exn ~min:(cy - cs + 1) ~max:cy dy in |
| 142 | { e with cursor = (cx - dx' + 1, cy - dy' + 1); offset = (dx', dy') } | 144 | { e with cursor = (cx - dx' + 1, cy - dy' + 1); offset = (dx', dy') } |
| 143 | in | 145 | in |
| 144 | modify ~f:aux | 146 | modify ~f:aux |
| 145 | 147 | ||
| 146 | let get_focused_buffer e = (e.buffer, e) | 148 | let get_focused_buffer_history e = (e.buffer, e) |
| 147 | let set_focused_buffer b e = ((), { e with buffer = b }) | 149 | |
| 150 | let set_focused_buffer_history h = | ||
| 151 | (fun e -> ((), { e with buffer = h })) *> update_cursor | ||
| 152 | |||
| 153 | let on_focused_buffer_history f = | ||
| 154 | get_focused_buffer_history >>| f >>= set_focused_buffer_history | ||
| 155 | |||
| 156 | let get_focused_buffer e = (Tipper.focus e.buffer, e) | ||
| 157 | |||
| 158 | let set_focused_buffer b e = | ||
| 159 | ((), { e with buffer = Tipper.set_focus b e.buffer }) | ||
| 148 | 160 | ||
| 149 | let on_focused_buffer f = | 161 | let on_focused_buffer f = |
| 150 | let* b = get_focused_buffer in | 162 | let* b = get_focused_buffer in |
| @@ -196,7 +208,7 @@ module Action = struct | |||
| 196 | in | 208 | in |
| 197 | let ssize = e.status_size in | 209 | let ssize = e.status_size in |
| 198 | let bufview = | 210 | let bufview = |
| 199 | e.buffer | 211 | e.buffer |> Tipper.focus |
| 200 | |> limit x y (r - ssize) c | 212 | |> limit x y (r - ssize) c |
| 201 | |> Text.extend ~fill r | 213 | |> Text.extend ~fill r |
| 202 | |> Fn.flip Sequence.take (r - ssize) | 214 | |> Fn.flip Sequence.take (r - ssize) |
| @@ -246,6 +258,48 @@ module Action = struct | |||
| 246 | set_message (Printf.sprintf "Pattern not found: %s" word) | 258 | set_message (Printf.sprintf "Pattern not found: %s" word) |
| 247 | | Some (r, c) -> Buffer.Action.goto ~r ~c |> on_focused_buffer | 259 | | Some (r, c) -> Buffer.Action.goto ~r ~c |> on_focused_buffer |
| 248 | 260 | ||
| 261 | (* History *) | ||
| 262 | let take_buffer_snapshot = | ||
| 263 | let* h = get_focused_buffer_history in | ||
| 264 | let buf = { (Tipper.focus h) with last_modified = Unix.gettimeofday () } in | ||
| 265 | let h = Tipper.(h |> push (create buf) |> down) in | ||
| 266 | set_focused_buffer_history h | ||
| 267 | |||
| 268 | let undo = | ||
| 269 | let* h = get_focused_buffer_history in | ||
| 270 | if Tipper.is_root h then set_message "Already at the oldest change" | ||
| 271 | else set_focused_buffer_history (Tipper.up h) | ||
| 272 | |||
| 273 | let redo = | ||
| 274 | let* h = get_focused_buffer_history in | ||
| 275 | if Tipper.is_leaf h then set_message "Already at the newest change" | ||
| 276 | else set_focused_buffer_history (Tipper.down h) | ||
| 277 | |||
| 278 | let timetravel ?(later = false) ?(_secs = 0.) = | ||
| 279 | let last_modified (n : Buffer.t Tipper.t) = | ||
| 280 | (Tipper.focus n).last_modified | ||
| 281 | in | ||
| 282 | let find ts h = | ||
| 283 | let f a t = | ||
| 284 | let tmax = | ||
| 285 | if later then | ||
| 286 | Option.(map ~f:last_modified a |> value ~default:Float.max_value) | ||
| 287 | else ts | ||
| 288 | in | ||
| 289 | let tmin = | ||
| 290 | if later then ts | ||
| 291 | else Option.(map ~f:last_modified a |> value ~default:0.) | ||
| 292 | and cur = last_modified t in | ||
| 293 | if Float.(tmin < cur && cur < tmax) then Some t else a | ||
| 294 | in | ||
| 295 | Tipper.fold ~a:None ~f h | ||
| 296 | in | ||
| 297 | let* h = get_focused_buffer_history in | ||
| 298 | let ts = last_modified h in | ||
| 299 | match find ts (Tipper.root h) with | ||
| 300 | | None -> set_message "Already at the oldest change" | ||
| 301 | | Some h -> set_focused_buffer_history h | ||
| 302 | |||
| 249 | (* Debug *) | 303 | (* Debug *) |
| 250 | let get_rendered e = (e.rendered, e) | 304 | let get_rendered e = (e.rendered, e) |
| 251 | let set_rendered r e = ((), { e with rendered = r }) | 305 | let set_rendered r e = ((), { e with rendered = r }) |
| @@ -372,6 +426,7 @@ let handle_normal_command c = | |||
| 372 | (* Change *) | 426 | (* Change *) |
| 373 | | Shortcut (r, n, Change) -> | 427 | | Shortcut (r, n, Change) -> |
| 374 | let n = Option.value ~default:1 n - 1 in | 428 | let n = Option.value ~default:1 n - 1 in |
| 429 | let* () = take_buffer_snapshot in | ||
| 375 | let* out = Buffer.Action.delete_to_eol ~n |> on_focused_buffer in | 430 | let* out = Buffer.Action.delete_to_eol ~n |> on_focused_buffer in |
| 376 | let* () = set_register ?r (Glyphwise out) in | 431 | let* () = set_register ?r (Glyphwise out) in |
| 377 | set_mode Insert | 432 | set_mode Insert |
| @@ -383,6 +438,7 @@ let handle_normal_command c = | |||
| 383 | and* () = insert_line ~before:true *> move_up in | 438 | and* () = insert_line ~before:true *> move_up in |
| 384 | return out | 439 | return out |
| 385 | in | 440 | in |
| 441 | let* () = take_buffer_snapshot in | ||
| 386 | let* out = act |> on_focused_buffer in | 442 | let* out = act |> on_focused_buffer in |
| 387 | let* () = set_register ?r (Linewise out) in | 443 | let* () = set_register ?r (Linewise out) in |
| 388 | set_mode Insert | 444 | set_mode Insert |
| @@ -394,16 +450,19 @@ let handle_normal_command c = | |||
| 394 | and* () = insert_line ~before:true *> move_up in | 450 | and* () = insert_line ~before:true *> move_up in |
| 395 | return out | 451 | return out |
| 396 | in | 452 | in |
| 453 | let* () = take_buffer_snapshot in | ||
| 397 | let* out = act |> on_focused_buffer in | 454 | let* out = act |> on_focused_buffer in |
| 398 | let* () = set_register ?r (Linewise out) in | 455 | let* () = set_register ?r (Linewise out) in |
| 399 | set_mode Insert | 456 | set_mode Insert |
| 400 | | Chord (r, n1, Change, n2, Left) -> | 457 | | Chord (r, n1, Change, n2, Left) -> |
| 401 | let n = Option.(value ~default:1 n1 * value ~default:1 n2) in | 458 | let n = Option.(value ~default:1 n1 * value ~default:1 n2) in |
| 459 | let* () = take_buffer_snapshot in | ||
| 402 | let* out = Buffer.Action.delete_before ~n |> on_focused_buffer in | 460 | let* out = Buffer.Action.delete_before ~n |> on_focused_buffer in |
| 403 | let* () = set_register ?r (Glyphwise out) in | 461 | let* () = set_register ?r (Glyphwise out) in |
| 404 | set_mode Insert | 462 | set_mode Insert |
| 405 | | Chord (r, n1, Change, n2, Right) -> | 463 | | Chord (r, n1, Change, n2, Right) -> |
| 406 | let n = Option.(value ~default:1 n1 * value ~default:1 n2) in | 464 | let n = Option.(value ~default:1 n1 * value ~default:1 n2) in |
| 465 | let* () = take_buffer_snapshot in | ||
| 407 | let* out = Buffer.Action.delete_after ~n |> on_focused_buffer in | 466 | let* out = Buffer.Action.delete_after ~n |> on_focused_buffer in |
| 408 | let* () = set_register ?r (Glyphwise out) in | 467 | let* () = set_register ?r (Glyphwise out) in |
| 409 | set_mode Insert | 468 | set_mode Insert |
| @@ -415,41 +474,50 @@ let handle_normal_command c = | |||
| 415 | and* () = insert_line ~before:true *> move_up in | 474 | and* () = insert_line ~before:true *> move_up in |
| 416 | return out | 475 | return out |
| 417 | in | 476 | in |
| 477 | let* () = take_buffer_snapshot in | ||
| 418 | let* out = act |> on_focused_buffer in | 478 | let* out = act |> on_focused_buffer in |
| 419 | let* () = set_register ?r (Linewise out) in | 479 | let* () = set_register ?r (Linewise out) in |
| 420 | set_mode Insert | 480 | set_mode Insert |
| 421 | | Chord (r, _, Change, _, To_bol) -> | 481 | | Chord (r, _, Change, _, To_bol) -> |
| 482 | let* () = take_buffer_snapshot in | ||
| 422 | let* out = Buffer.Action.delete_to_bol |> on_focused_buffer in | 483 | let* out = Buffer.Action.delete_to_bol |> on_focused_buffer in |
| 423 | let* () = set_register ?r (Glyphwise out) in | 484 | let* () = set_register ?r (Glyphwise out) in |
| 424 | set_mode Insert | 485 | set_mode Insert |
| 425 | | Chord (r, n1, Change, n2, To_eol) -> | 486 | | Chord (r, n1, Change, n2, To_eol) -> |
| 426 | let n = Option.((value ~default:1 n1 * value ~default:1 n2) - 1) in | 487 | let n = Option.((value ~default:1 n1 * value ~default:1 n2) - 1) in |
| 488 | let* () = take_buffer_snapshot in | ||
| 427 | let* out = Buffer.Action.delete_to_eol ~n |> on_focused_buffer in | 489 | let* out = Buffer.Action.delete_to_eol ~n |> on_focused_buffer in |
| 428 | let* () = set_register ?r (Glyphwise out) in | 490 | let* () = set_register ?r (Glyphwise out) in |
| 429 | set_mode Insert | 491 | set_mode Insert |
| 430 | (* Delete *) | 492 | (* Delete *) |
| 431 | | Shortcut (r, n, Delete) -> | 493 | | Shortcut (r, n, Delete) -> |
| 432 | let n = Option.value ~default:1 n - 1 in | 494 | let n = Option.value ~default:1 n - 1 in |
| 495 | let* () = take_buffer_snapshot in | ||
| 433 | let* out = Buffer.Action.delete_to_eol ~n |> on_focused_buffer in | 496 | let* out = Buffer.Action.delete_to_eol ~n |> on_focused_buffer in |
| 434 | set_register ?r (Glyphwise out) | 497 | set_register ?r (Glyphwise out) |
| 435 | | Chord (r, n1, Delete, n2, Line) -> | 498 | | Chord (r, n1, Delete, n2, Line) -> |
| 436 | let n = Option.(value ~default:1 n1 * value ~default:1 n2) in | 499 | let n = Option.(value ~default:1 n1 * value ~default:1 n2) in |
| 500 | let* () = take_buffer_snapshot in | ||
| 437 | let* out = Buffer.Action.delete_lines ~n |> on_focused_buffer in | 501 | let* out = Buffer.Action.delete_lines ~n |> on_focused_buffer in |
| 438 | set_register ?r (Linewise out) | 502 | set_register ?r (Linewise out) |
| 439 | | Chord (r, n1, Delete, n2, Down) -> | 503 | | Chord (r, n1, Delete, n2, Down) -> |
| 440 | let n = Option.((value ~default:1 n1 * value ~default:1 n2) + 1) in | 504 | let n = Option.((value ~default:1 n1 * value ~default:1 n2) + 1) in |
| 505 | let* () = take_buffer_snapshot in | ||
| 441 | let* out = Buffer.Action.delete_lines ~n |> on_focused_buffer in | 506 | let* out = Buffer.Action.delete_lines ~n |> on_focused_buffer in |
| 442 | set_register ?r (Linewise out) | 507 | set_register ?r (Linewise out) |
| 443 | | Chord (r, n1, Delete, n2, Left) -> | 508 | | Chord (r, n1, Delete, n2, Left) -> |
| 444 | let n = Option.(value ~default:1 n1 * value ~default:1 n2) in | 509 | let n = Option.(value ~default:1 n1 * value ~default:1 n2) in |
| 510 | let* () = take_buffer_snapshot in | ||
| 445 | let* out = Buffer.Action.delete_before ~n |> on_focused_buffer in | 511 | let* out = Buffer.Action.delete_before ~n |> on_focused_buffer in |
| 446 | set_register ?r (Glyphwise out) | 512 | set_register ?r (Glyphwise out) |
| 447 | | Chord (r, n1, Delete, n2, Right) -> | 513 | | Chord (r, n1, Delete, n2, Right) -> |
| 448 | let n = Option.(value ~default:1 n1 * value ~default:1 n2) in | 514 | let n = Option.(value ~default:1 n1 * value ~default:1 n2) in |
| 515 | let* () = take_buffer_snapshot in | ||
| 449 | let* out = Buffer.Action.delete_after ~n |> on_focused_buffer in | 516 | let* out = Buffer.Action.delete_after ~n |> on_focused_buffer in |
| 450 | set_register ?r (Glyphwise out) | 517 | set_register ?r (Glyphwise out) |
| 451 | | Chord (r, n1, Delete, n2, Up) -> | 518 | | Chord (r, n1, Delete, n2, Up) -> |
| 452 | let n = Option.(value ~default:1 n1 * value ~default:1 n2) in | 519 | let n = Option.(value ~default:1 n1 * value ~default:1 n2) in |
| 520 | let* () = take_buffer_snapshot in | ||
| 453 | let* out = | 521 | let* out = |
| 454 | Buffer.Action.(move_up ~n *> delete_lines ~n:(n + 1)) | 522 | Buffer.Action.(move_up ~n *> delete_lines ~n:(n + 1)) |
| 455 | |> on_focused_buffer | 523 | |> on_focused_buffer |
| @@ -457,37 +525,47 @@ let handle_normal_command c = | |||
| 457 | set_register ?r (Linewise out) | 525 | set_register ?r (Linewise out) |
| 458 | | Shortcut (r, n, Erase_before) -> | 526 | | Shortcut (r, n, Erase_before) -> |
| 459 | let n = Option.value ~default:1 n in | 527 | let n = Option.value ~default:1 n in |
| 528 | let* () = take_buffer_snapshot in | ||
| 460 | let* out = Buffer.Action.delete_before ~n |> on_focused_buffer in | 529 | let* out = Buffer.Action.delete_before ~n |> on_focused_buffer in |
| 461 | set_register ?r (Glyphwise out) | 530 | set_register ?r (Glyphwise out) |
| 462 | | Shortcut (r, n, Erase_after) -> | 531 | | Shortcut (r, n, Erase_after) -> |
| 463 | let n = Option.value ~default:1 n in | 532 | let n = Option.value ~default:1 n in |
| 533 | let* () = take_buffer_snapshot in | ||
| 464 | let* out = Buffer.Action.delete_after ~n |> on_focused_buffer in | 534 | let* out = Buffer.Action.delete_after ~n |> on_focused_buffer in |
| 465 | set_register ?r (Glyphwise out) | 535 | set_register ?r (Glyphwise out) |
| 466 | | Chord (r, _, Delete, _, To_bol) -> | 536 | | Chord (r, _, Delete, _, To_bol) -> |
| 537 | let* () = take_buffer_snapshot in | ||
| 467 | let* out = Buffer.Action.delete_to_bol |> on_focused_buffer in | 538 | let* out = Buffer.Action.delete_to_bol |> on_focused_buffer in |
| 468 | set_register ?r (Glyphwise out) | 539 | set_register ?r (Glyphwise out) |
| 469 | | Chord (r, n1, Delete, n2, To_eol) -> | 540 | | Chord (r, n1, Delete, n2, To_eol) -> |
| 470 | let n = Option.((value ~default:1 n1 * value ~default:1 n2) - 1) in | 541 | let n = Option.((value ~default:1 n1 * value ~default:1 n2) - 1) in |
| 542 | let* () = take_buffer_snapshot in | ||
| 471 | let* out = Buffer.Action.delete_to_eol ~n |> on_focused_buffer in | 543 | let* out = Buffer.Action.delete_to_eol ~n |> on_focused_buffer in |
| 472 | set_register ?r (Glyphwise out) | 544 | set_register ?r (Glyphwise out) |
| 473 | (* Paste *) | 545 | (* Paste *) |
| 474 | | Shortcut (r, n, Paste_after) -> ( | 546 | | Shortcut (r, n, Paste_after) -> ( |
| 475 | get_register ?r >>= function | 547 | get_register ?r >>= function |
| 476 | | Empty -> noop | 548 | | Empty -> noop |
| 477 | | Glyphwise z -> Buffer.Action.paste ?n z |> on_focused_buffer | 549 | | Glyphwise z -> |
| 550 | let* () = take_buffer_snapshot in | ||
| 551 | Buffer.Action.paste ?n z |> on_focused_buffer | ||
| 478 | | Linewise z -> | 552 | | Linewise z -> |
| 553 | let* () = take_buffer_snapshot in | ||
| 479 | Buffer.Action.paste ~linewise:true ?n z |> on_focused_buffer) | 554 | Buffer.Action.paste ~linewise:true ?n z |> on_focused_buffer) |
| 480 | | Shortcut (r, n, Paste_before) -> ( | 555 | | Shortcut (r, n, Paste_before) -> ( |
| 481 | get_register ?r >>= function | 556 | get_register ?r >>= function |
| 482 | | Empty -> noop | 557 | | Empty -> noop |
| 483 | | Glyphwise z -> | 558 | | Glyphwise z -> |
| 559 | let* () = take_buffer_snapshot in | ||
| 484 | Buffer.Action.paste ~before:true ?n z |> on_focused_buffer | 560 | Buffer.Action.paste ~before:true ?n z |> on_focused_buffer |
| 485 | | Linewise z -> | 561 | | Linewise z -> |
| 562 | let* () = take_buffer_snapshot in | ||
| 486 | Buffer.Action.paste ~before:true ~linewise:true ?n z | 563 | Buffer.Action.paste ~before:true ~linewise:true ?n z |
| 487 | |> on_focused_buffer) | 564 | |> on_focused_buffer) |
| 488 | (* Join *) | 565 | (* Join *) |
| 489 | | Shortcut (_, n, Join) -> | 566 | | Shortcut (_, n, Join) -> |
| 490 | let n = Option.(value ~default:2 n) in | 567 | let n = Option.(value ~default:2 n) in |
| 568 | let* () = take_buffer_snapshot in | ||
| 491 | Buffer.Action.join_lines ~n |> on_focused_buffer | 569 | Buffer.Action.join_lines ~n |> on_focused_buffer |
| 492 | (* Control *) | 570 | (* Control *) |
| 493 | | Simple (Key ':' as k) -> | 571 | | Simple (Key ':' as k) -> |
| @@ -512,6 +590,12 @@ let handle_normal_command c = | |||
| 512 | | None -> set_message "No search history" | 590 | | None -> set_message "No search history" |
| 513 | | Some (dir, word) -> search (not dir) word |> repeat ?n) | 591 | | Some (dir, word) -> search (not dir) word |> repeat ?n) |
| 514 | | Simple (Ctrl 'Q') -> quit 0 | 592 | | Simple (Ctrl 'Q') -> quit 0 |
| 593 | (* History *) | ||
| 594 | | Shortcut (_, n, Undo) -> repeat ?n undo | ||
| 595 | | Shortcut (_, n, Redo) -> repeat ?n redo | ||
| 596 | | Shortcut (_, n, Earlier) -> repeat ?n timetravel | ||
| 597 | | Shortcut (_, n, Later) -> repeat ?n (timetravel ~later:true) | ||
| 598 | (* | Shortcut (_, n, Redo) -> repeat ?n redo *) | ||
| 515 | (* Misc *) | 599 | (* Misc *) |
| 516 | | Simple (Key 'A') -> | 600 | | Simple (Key 'A') -> |
| 517 | (Buffer.Action.eol |> on_focused_buffer) *> set_mode Insert | 601 | (Buffer.Action.eol |> on_focused_buffer) *> set_mode Insert |
