diff options
| author | Federico Igne <undyamon@disroot.org> | 2024-01-28 01:22:52 +0100 |
|---|---|---|
| committer | Federico Igne <undyamon@disroot.org> | 2024-01-28 01:22:52 +0100 |
| commit | 53916fc3f7dcbefd90e3d0340a2a8f32bf331d1d (patch) | |
| tree | baf9b5d6bdfdc77d0338e230adf64f7f52aefa4e /lib/editor.ml | |
| parent | 722a3d4d2d0d0630f57cfbfdddc229dee341505b (diff) | |
| download | sandy-53916fc3f7dcbefd90e3d0340a2a8f32bf331d1d.tar.gz sandy-53916fc3f7dcbefd90e3d0340a2a8f32bf331d1d.zip | |
feat: add plain search functionality (with history)
Diffstat (limited to 'lib/editor.ml')
| -rw-r--r-- | lib/editor.ml | 137 |
1 files changed, 120 insertions, 17 deletions
diff --git a/lib/editor.ml b/lib/editor.ml index 043c00e..5a68e53 100644 --- a/lib/editor.ml +++ b/lib/editor.ml | |||
| @@ -2,8 +2,6 @@ open Base | |||
| 2 | module Buffer = EditorBuffer | 2 | module Buffer = EditorBuffer |
| 3 | open Util | 3 | open Util |
| 4 | 4 | ||
| 5 | type mode = Normal | Insert | Control | ||
| 6 | |||
| 7 | type selection = | 5 | type selection = |
| 8 | | Empty | 6 | | Empty |
| 9 | | Glyphwise of char Sequence.t Sequence.t | 7 | | Glyphwise of char Sequence.t Sequence.t |
| @@ -13,7 +11,7 @@ type cursor = int * int | |||
| 13 | 11 | ||
| 14 | type editor = { | 12 | type editor = { |
| 15 | term : Terminal.state; | 13 | term : Terminal.state; |
| 16 | mode : mode; | 14 | mode : Mode.t; |
| 17 | offset : int * int; | 15 | offset : int * int; |
| 18 | cursor : cursor; | 16 | cursor : cursor; |
| 19 | buffer : Buffer.t; | 17 | buffer : Buffer.t; |
| @@ -26,6 +24,8 @@ type editor = { | |||
| 26 | message_duration : float; | 24 | message_duration : float; |
| 27 | pending_command : string; | 25 | pending_command : string; |
| 28 | registers : selection Array.t; | 26 | registers : selection Array.t; |
| 27 | control : Control.t; | ||
| 28 | search_history : (bool * char Sequence.t) Zipper.t; | ||
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | type t = editor | 31 | type t = editor |
| @@ -49,19 +49,16 @@ let init (c : Config.t) : editor = | |||
| 49 | message_duration = 5.; | 49 | message_duration = 5.; |
| 50 | pending_command = ""; | 50 | pending_command = ""; |
| 51 | registers = Array.create ~len:128 Empty; | 51 | registers = Array.create ~len:128 Empty; |
| 52 | control = Control.create Key.Nul; | ||
| 53 | search_history = Zipper.empty; | ||
| 52 | } | 54 | } |
| 53 | 55 | ||
| 54 | let string_of_mode = function | ||
| 55 | | Insert -> " I " | ||
| 56 | | Normal -> " N " | ||
| 57 | | Control -> " C " | ||
| 58 | |||
| 59 | let statusbar e = | 56 | let statusbar e = |
| 60 | let open Text in | 57 | let open Text in |
| 61 | (* let open Sequence.Infix in *) | 58 | (* let open Sequence.Infix in *) |
| 62 | let w = e.term.size |> snd in | 59 | let w = e.term.size |> snd in |
| 63 | let status = | 60 | let status = |
| 64 | let mode = e.mode |> string_of_mode |> sequence_of_string in | 61 | let mode = e.mode |> Mode.to_string |> sequence_of_string in |
| 65 | let lsize = Sequence.length mode | 62 | let lsize = Sequence.length mode |
| 66 | and c = e.buffer.kind |> Buffer.string_of_kind |> sequence_of_string | 63 | and c = e.buffer.kind |> Buffer.string_of_kind |> sequence_of_string |
| 67 | and br, bc = Buffer.size e.buffer | 64 | and br, bc = Buffer.size e.buffer |
| @@ -79,9 +76,12 @@ let statusbar e = | |||
| 79 | let rsize = Sequence.length nav in | 76 | let rsize = Sequence.length nav in |
| 80 | spread ~l:(bold mode) ~lsize ~c ~r:(bold nav) ~rsize ~fill:' ' w |> invert | 77 | spread ~l:(bold mode) ~lsize ~c ~r:(bold nav) ~rsize ~fill:' ' w |> invert |
| 81 | and control = | 78 | and control = |
| 82 | let msg = Option.value ~default:"" e.message |> sequence_of_string | 79 | match e.mode with |
| 83 | and cmd = e.pending_command |> sequence_of_string in | 80 | | Control -> Control.render e.control |
| 84 | spread ~l:msg ~r:cmd ~fill:' ' w | 81 | | _ -> |
| 82 | let msg = Option.value ~default:"" e.message |> sequence_of_string | ||
| 83 | and cmd = e.pending_command |> sequence_of_string in | ||
| 84 | spread ~l:msg ~r:cmd ~fill:' ' w | ||
| 85 | in | 85 | in |
| 86 | Sequence.(take (of_list [ status; control ]) e.status_size) | 86 | Sequence.(take (of_list [ status; control ]) e.status_size) |
| 87 | 87 | ||
| @@ -117,8 +117,16 @@ module Action = struct | |||
| 117 | let map = `Define_using_bind | 117 | let map = `Define_using_bind |
| 118 | end) | 118 | end) |
| 119 | 119 | ||
| 120 | let ( let+ ) e f = map e ~f | ||
| 120 | let ( let* ) e f = bind e ~f | 121 | let ( let* ) e f = bind e ~f |
| 121 | let ( and* ) = both | 122 | let ( and* ) = both |
| 123 | |||
| 124 | let rec repeat ?(n = 1) a = | ||
| 125 | match n with | ||
| 126 | | _ when n <= 0 -> return () | ||
| 127 | | 1 -> a | ||
| 128 | | _ -> a *> repeat ~n:(n - 1) a | ||
| 129 | |||
| 122 | let get e = (e, e) | 130 | let get e = (e, e) |
| 123 | let put e _ = ((), e) | 131 | let put e _ = ((), e) |
| 124 | let modify ~f e = ((), f e) | 132 | let modify ~f e = ((), f e) |
| @@ -149,8 +157,22 @@ module Action = struct | |||
| 149 | let* b = get_focused_buffer in | 157 | let* b = get_focused_buffer in |
| 150 | return (f b |> fst) | 158 | return (f b |> fst) |
| 151 | 159 | ||
| 160 | let get_control_buffer e = (e.control, e) | ||
| 161 | let set_control_buffer c e = ((), { e with control = c }) | ||
| 162 | |||
| 163 | let on_control_buffer f = | ||
| 164 | let* c = get_control_buffer in | ||
| 165 | set_control_buffer (f c) | ||
| 166 | |||
| 152 | let get_mode e = (e.mode, e) | 167 | let get_mode e = (e.mode, e) |
| 153 | let set_mode m e = ((), { e with mode = m }) | 168 | let set_mode m e = ((), { e with mode = m }) |
| 169 | let get_search_history e = (e.search_history, e) | ||
| 170 | let set_search_history h e = ((), { e with search_history = h }) | ||
| 171 | let on_search_history f = get_search_history >>| f >>= set_search_history | ||
| 172 | |||
| 173 | let set_last_search dir word = | ||
| 174 | Zipper.(far_left &> swap_focus (dir, word)) |> on_search_history | ||
| 175 | |||
| 154 | let get_terminal_size e = (e.term.size, e) | 176 | let get_terminal_size e = (e.term.size, e) |
| 155 | 177 | ||
| 156 | let get_register ?(r = '"') e = | 178 | let get_register ?(r = '"') e = |
| @@ -179,8 +201,14 @@ module Action = struct | |||
| 179 | |> Text.extend ~fill r | 201 | |> Text.extend ~fill r |
| 180 | |> Fn.flip Sequence.take (r - ssize) | 202 | |> Fn.flip Sequence.take (r - ssize) |
| 181 | in | 203 | in |
| 182 | let screen = Sequence.append bufview status in | 204 | let screen = Sequence.append bufview status |
| 183 | Terminal.redraw screen e.cursor | 205 | and cursor = |
| 206 | let open Mode in | ||
| 207 | match e.mode with | ||
| 208 | | Control -> (r, Control.cursor e.control) | ||
| 209 | | _ -> e.cursor | ||
| 210 | in | ||
| 211 | Terminal.redraw screen cursor | ||
| 184 | in | 212 | in |
| 185 | get >>| aux | 213 | get >>| aux |
| 186 | 214 | ||
| @@ -189,7 +217,7 @@ module Action = struct | |||
| 189 | 217 | ||
| 190 | (* Statusbar *) | 218 | (* Statusbar *) |
| 191 | let set_message m e = | 219 | let set_message m e = |
| 192 | ((), { e with message = m; message_timestamp = Unix.time () }) | 220 | ((), { e with message = Some m; message_timestamp = Unix.time () }) |
| 193 | 221 | ||
| 194 | let get_pending_command e = (e.pending_command, e) | 222 | let get_pending_command e = (e.pending_command, e) |
| 195 | let set_pending_command p e = ((), { e with pending_command = p }) | 223 | let set_pending_command p e = ((), { e with pending_command = p }) |
| @@ -209,6 +237,15 @@ module Action = struct | |||
| 209 | in | 237 | in |
| 210 | get >>| check_message_timestamp >>= put | 238 | get >>| check_message_timestamp >>= put |
| 211 | 239 | ||
| 240 | (* Control line *) | ||
| 241 | let search dir word = | ||
| 242 | let* coords = Buffer.Action.(search dir word |> on_focused_buffer) in | ||
| 243 | match coords with | ||
| 244 | | None -> | ||
| 245 | let word = word |> Sequence.to_list |> String.of_list in | ||
| 246 | set_message (Printf.sprintf "Pattern not found: %s" word) | ||
| 247 | | Some (r, c) -> Buffer.Action.goto r c |> on_focused_buffer | ||
| 248 | |||
| 212 | (* Debug *) | 249 | (* Debug *) |
| 213 | let get_rendered e = (e.rendered, e) | 250 | let get_rendered e = (e.rendered, e) |
| 214 | let set_rendered r e = ((), { e with rendered = r }) | 251 | let set_rendered r e = ((), { e with rendered = r }) |
| @@ -448,7 +485,28 @@ let handle_normal_command c = | |||
| 448 | | Shortcut (_, n, Join) -> | 485 | | Shortcut (_, n, Join) -> |
| 449 | let n = Option.(value ~default:2 n) in | 486 | let n = Option.(value ~default:2 n) in |
| 450 | Buffer.Action.join_lines ~n |> on_focused_buffer | 487 | Buffer.Action.join_lines ~n |> on_focused_buffer |
| 451 | (* Quit *) | 488 | (* Control *) |
| 489 | | Simple (Key ':' as k) -> | ||
| 490 | let c = Control.create k in | ||
| 491 | set_control_buffer c *> set_mode Control | ||
| 492 | | Simple (Key '/' as k) -> | ||
| 493 | let c = Control.create k in | ||
| 494 | (Zipper.(far_left &> push (true, Sequence.empty)) |> on_search_history) | ||
| 495 | *> set_control_buffer c *> set_mode Control | ||
| 496 | | Simple (Key '?' as k) -> | ||
| 497 | let c = Control.create k in | ||
| 498 | (Zipper.(far_left &> push (false, Sequence.empty)) |> on_search_history) | ||
| 499 | *> set_control_buffer c *> set_mode Control | ||
| 500 | | Shortcut (_, n, Search) -> ( | ||
| 501 | let* h = get_search_history in | ||
| 502 | match Zipper.focus h with | ||
| 503 | | None -> set_message "No search history" | ||
| 504 | | Some (dir, word) -> search dir word |> repeat ?n) | ||
| 505 | | Shortcut (_, n, Search_rev) -> ( | ||
| 506 | let* h = get_search_history in | ||
| 507 | match Zipper.focus h with | ||
| 508 | | None -> set_message "No search history" | ||
| 509 | | Some (dir, word) -> search (not dir) word |> repeat ?n) | ||
| 452 | | Simple (Ctrl 'Q') -> quit 0 | 510 | | Simple (Ctrl 'Q') -> quit 0 |
| 453 | (* Misc *) | 511 | (* Misc *) |
| 454 | | Simple (Key 'A') -> | 512 | | Simple (Key 'A') -> |
| @@ -467,7 +525,49 @@ let handle_normal_command c = | |||
| 467 | in | 525 | in |
| 468 | compute_action *> update_command_cue | 526 | compute_action *> update_command_cue |
| 469 | 527 | ||
| 528 | let handle_control_command = | ||
| 529 | let open Command in | ||
| 530 | let open Action in | ||
| 531 | function | ||
| 532 | | Simple Arrow_down -> | ||
| 533 | let* c = get_control_buffer in | ||
| 534 | if Control.is_search c then | ||
| 535 | let* () = Zipper.left |> on_search_history | ||
| 536 | and* h = get_search_history in | ||
| 537 | match Zipper.focus h with | ||
| 538 | | None -> noop | ||
| 539 | | Some (_, word) -> Control.set_content word |> on_control_buffer | ||
| 540 | else failwith "Control line command history unimplemented!" | ||
| 541 | | Simple Arrow_left -> Control.move_left |> on_control_buffer | ||
| 542 | | Simple Arrow_right -> Control.move_right |> on_control_buffer | ||
| 543 | | Simple Arrow_up -> | ||
| 544 | let* c = get_control_buffer in | ||
| 545 | if Control.is_search c then | ||
| 546 | let* () = Zipper.right |> on_search_history | ||
| 547 | and* h = get_search_history in | ||
| 548 | match Zipper.focus h with | ||
| 549 | | None -> noop | ||
| 550 | | Some (_, word) -> Control.set_content word |> on_control_buffer | ||
| 551 | else failwith "Control line command history unimplemented!" | ||
| 552 | | Simple Backspace -> Control.delete_before |> on_control_buffer | ||
| 553 | | Simple Delete -> Control.delete_after |> on_control_buffer | ||
| 554 | | Simple Enter -> ( | ||
| 555 | let* () = set_mode Normal and* c = get_control_buffer in | ||
| 556 | match Control.get_result c with | ||
| 557 | | Search (dir, word) -> search dir word *> set_last_search dir word | ||
| 558 | | No_result -> noop) | ||
| 559 | | Simple Esc -> ( | ||
| 560 | let* () = set_mode Normal and* c = get_control_buffer in | ||
| 561 | match Control.get_result c with | ||
| 562 | | Search _ -> Zipper.(far_left &> pop &> snd) |> on_search_history | ||
| 563 | | No_result -> noop) | ||
| 564 | | Simple Home -> Control.bol |> on_control_buffer | ||
| 565 | | Simple End -> Control.eol |> on_control_buffer | ||
| 566 | | Type k -> Control.insert k |> on_control_buffer | ||
| 567 | | _ -> noop | ||
| 568 | |||
| 470 | let handle_next_command m e = | 569 | let handle_next_command m e = |
| 570 | let open Mode in | ||
| 471 | match m with | 571 | match m with |
| 472 | | Insert -> ( | 572 | | Insert -> ( |
| 473 | match Sequence.next e.istream with | 573 | match Sequence.next e.istream with |
| @@ -477,7 +577,10 @@ let handle_next_command m e = | |||
| 477 | match Sequence.next e.nstream with | 577 | match Sequence.next e.nstream with |
| 478 | | None -> ((), e) | 578 | | None -> ((), e) |
| 479 | | Some (h, t) -> handle_normal_command h { e with nstream = t }) | 579 | | Some (h, t) -> handle_normal_command h { e with nstream = t }) |
| 480 | | Control -> failwith "unimplemented" | 580 | | Control -> ( |
| 581 | match Sequence.next e.istream with | ||
| 582 | | None -> ((), e) | ||
| 583 | | Some (h, t) -> handle_control_command h { e with istream = t }) | ||
| 481 | 584 | ||
| 482 | let handle_next_command = | 585 | let handle_next_command = |
| 483 | let open Action in | 586 | let open Action in |
