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 | |
| parent | 633fb26ed21f7208517aa29dbaab9f0cf3bb2047 (diff) | |
| download | sandy-f5c33507a74d83692c028d1e1659d3506399138e.tar.gz sandy-f5c33507a74d83692c028d1e1659d3506399138e.zip | |
| -rw-r--r-- | lib/command.ml | 17 | ||||
| -rw-r--r-- | lib/editor.ml | 106 | ||||
| -rw-r--r-- | lib/editorBuffer.ml | 17 |
3 files changed, 123 insertions, 17 deletions
diff --git a/lib/command.ml b/lib/command.ml index 2d7b51b..6439165 100644 --- a/lib/command.ml +++ b/lib/command.ml | |||
| @@ -17,6 +17,10 @@ type operation = | |||
| 17 | | Search | 17 | | Search |
| 18 | | Search_rev | 18 | | Search_rev |
| 19 | | Goto | 19 | | Goto |
| 20 | | Undo | ||
| 21 | | Redo | ||
| 22 | | Earlier | ||
| 23 | | Later | ||
| 20 | 24 | ||
| 21 | type scope = Line | To_bol | To_eol | Down | Left | Right | Up | 25 | type scope = Line | To_bol | To_eol | Down | Left | Right | Up |
| 22 | 26 | ||
| @@ -85,6 +89,8 @@ let instant_operation = | |||
| 85 | Key 'X'; | 89 | Key 'X'; |
| 86 | Key 'p'; | 90 | Key 'p'; |
| 87 | Key 'x'; | 91 | Key 'x'; |
| 92 | Key 'u'; | ||
| 93 | Ctrl 'R'; | ||
| 88 | ] | 94 | ] |
| 89 | 95 | ||
| 90 | let chord_operation = [ Key 'c'; Key 'd'; Key 'y' ] | 96 | let chord_operation = [ Key 'c'; Key 'd'; Key 'y' ] |
| @@ -101,6 +107,8 @@ let to_op = function | |||
| 101 | | Key 'P' -> Paste_before | 107 | | Key 'P' -> Paste_before |
| 102 | | Key 'x' -> Erase_after | 108 | | Key 'x' -> Erase_after |
| 103 | | Key 'X' -> Erase_before | 109 | | Key 'X' -> Erase_before |
| 110 | | Key 'u' -> Undo | ||
| 111 | | Ctrl 'R' -> Redo | ||
| 104 | | _ -> failwith "Invalid operation in chord." | 112 | | _ -> failwith "Invalid operation in chord." |
| 105 | 113 | ||
| 106 | let is_chord_operation k = List.mem ~equal:Poly.equal chord_operation k | 114 | let is_chord_operation k = List.mem ~equal:Poly.equal chord_operation k |
| @@ -129,6 +137,15 @@ let n_stream = | |||
| 129 | (`start, shortcut ~r (to_op k)) | 137 | (`start, shortcut ~r (to_op k)) |
| 130 | | `chord_fst_n (r, n), k when is_instant_operation k -> | 138 | | `chord_fst_n (r, n), k when is_instant_operation k -> |
| 131 | (`start, shortcut ?r ~n (to_op k)) | 139 | (`start, shortcut ?r ~n (to_op k)) |
| 140 | (* Special operations *) | ||
| 141 | | `start, Key 'g' -> (`special (None, None), Partial k) | ||
| 142 | | `chord_reg r, Key 'g' -> (`special (Some r, None), Partial k) | ||
| 143 | | `chord_fst_n (r, n), Key 'g' -> (`special (r, Some n), Partial k) | ||
| 144 | | `special (r, n), Key 'g' -> | ||
| 145 | let n = Option.value ~default:0 n in | ||
| 146 | (`start, shortcut ?r ~n Goto) | ||
| 147 | | `special (r, n), Key '-' -> (`start, shortcut ?r ?n Earlier) | ||
| 148 | | `special (r, n), Key '+' -> (`start, shortcut ?r ?n Later) | ||
| 132 | (* Chord operation (first) *) | 149 | (* Chord operation (first) *) |
| 133 | | `start, k when is_chord_operation k -> | 150 | | `start, k when is_chord_operation k -> |
| 134 | (`chord_cmd (None, None, to_op k), Partial k) | 151 | (`chord_cmd (None, None, to_op k), Partial k) |
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 |
diff --git a/lib/editorBuffer.ml b/lib/editorBuffer.ml index 959c04a..8aacd71 100644 --- a/lib/editorBuffer.ml +++ b/lib/editorBuffer.ml | |||
| @@ -10,15 +10,17 @@ type buffer = { | |||
| 10 | kind : kind; | 10 | kind : kind; |
| 11 | content : (char zipper zipper, error) Result.t; | 11 | content : (char zipper zipper, error) Result.t; |
| 12 | rendered : char Sequence.t zipper; | 12 | rendered : char Sequence.t zipper; |
| 13 | last_modified : float; | ||
| 13 | } | 14 | } |
| 14 | 15 | ||
| 15 | type t = buffer | 16 | type t = buffer |
| 16 | 17 | ||
| 17 | let empty = | 18 | let empty () = |
| 18 | { | 19 | { |
| 19 | kind = No_name; | 20 | kind = No_name; |
| 20 | content = empty |> push empty |> Result.return; | 21 | content = empty |> push empty |> Result.return; |
| 21 | rendered = push Sequence.empty empty; | 22 | rendered = push Sequence.empty empty; |
| 23 | last_modified = Unix.gettimeofday (); | ||
| 22 | } | 24 | } |
| 23 | 25 | ||
| 24 | let kind b = b.kind | 26 | let kind b = b.kind |
| @@ -106,13 +108,14 @@ module Action = struct | |||
| 106 | match b.content with | 108 | match b.content with |
| 107 | | Error _ -> ((), b) | 109 | | Error _ -> ((), b) |
| 108 | | Ok c -> | 110 | | Ok c -> |
| 109 | let step = if before then left else right in | 111 | let rstep = if before then left else right ~by_one:false in |
| 112 | let cstep = if before then left else right ~by_one:false in | ||
| 110 | let rec aux i r c = | 113 | let rec aux i r c = |
| 111 | if i = 0 then r | 114 | if i = 0 then r |
| 112 | else | 115 | else |
| 113 | let default = Sequence.empty in | 116 | let default = Sequence.empty in |
| 114 | let l = apply_focus_or ~default (to_seq &> render) c in | 117 | let l = apply_focus_or ~default (to_seq &> render) c in |
| 115 | let c' = step c and r' = swap_focus l r |> step in | 118 | let c' = cstep c and r' = swap_focus l r |> rstep in |
| 116 | aux (i - 1) r' c' | 119 | aux (i - 1) r' c' |
| 117 | in | 120 | in |
| 118 | ((), { b with rendered = aux n b.rendered c |> goto (left_length c) }) | 121 | ((), { b with rendered = aux n b.rendered c |> goto (left_length c) }) |
| @@ -444,9 +447,11 @@ let from_file f = | |||
| 444 | Sequence.(of_list lines |> map ~f:line_to_seq) | 447 | Sequence.(of_list lines |> map ~f:line_to_seq) |
| 445 | with Unix.Unix_error (ENOENT, _, _) -> Sequence.empty | 448 | with Unix.Unix_error (ENOENT, _, _) -> Sequence.empty |
| 446 | in | 449 | in |
| 447 | let rendered = Sequence.map ~f:render lines |> of_seq in | 450 | let kind = File f |
| 448 | let content = Sequence.map ~f:of_seq lines |> of_seq in | 451 | and content = Sequence.map ~f:of_seq lines |> of_seq |> Result.return |
| 449 | { kind = File f; content = Ok content; rendered } | 452 | and rendered = Sequence.map ~f:render lines |> of_seq |
| 453 | and last_modified = Unix.gettimeofday () in | ||
| 454 | { kind; content; rendered; last_modified } | ||
| 450 | 455 | ||
| 451 | let unrendered_view x y h w b = | 456 | let unrendered_view x y h w b = |
| 452 | match b.content with | 457 | match b.content with |
