From 482e8b80fa66e328e252567c915f5e96e727f7cf Mon Sep 17 00:00:00 2001 From: Federico Igne Date: Tue, 23 Jan 2024 18:08:56 +0100 Subject: feat: add simple status bar with timed status message support --- lib/editor.ml | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 80 insertions(+), 5 deletions(-) (limited to 'lib/editor.ml') diff --git a/lib/editor.ml b/lib/editor.ml index 69dc666..c34d558 100644 --- a/lib/editor.ml +++ b/lib/editor.ml @@ -2,7 +2,7 @@ open Base module Buffer = EditorBuffer open Util -type mode = Normal | Insert +type mode = Normal | Insert | Control type cursor = int * int type editor = { @@ -15,6 +15,10 @@ type editor = { pending : Key.t Sequence.t; i_pending : Command.t Sequence.t; n_pending : Command.t Sequence.t; + status_size : int; + message : string option; + message_timestamp : float; + message_duration : float; } type t = editor @@ -30,8 +34,57 @@ let init (c : Config.t) : editor = pending = Key.stream; i_pending = Command.i_stream; n_pending = Command.n_stream; + status_size = 2; + message = Some "Hello, control line!"; + message_timestamp = Unix.time (); + message_duration = 5.; } +let string_of_mode = function + | Insert -> " I " + | Normal -> " N " + | Control -> " C " + +let statusbar e = + let open Text in + (* let open Sequence.Infix in *) + let w = e.term.size |> snd in + let status = + let mode = e.mode |> string_of_mode |> sequence_of_string in + let msize = Sequence.length mode + and path = + Buffer.( + e.buffer |> Option.map ~f:kind + |> Option.value ~default:No_name + |> string_of_kind |> sequence_of_string) + and br, bc = + Option.(e.buffer |> map ~f:Buffer.size |> value ~default:(0, 0)) + and cr, cc = + Option.( + e.buffer + |> map ~f:(Buffer.cursor ~rendered:false) + |> value ~default:(0, 0)) + in + let perc = + match cr with + | 0 -> "Top" + | n when n = br -> "Bot" + | n -> Printf.sprintf "%2d%%" (100 * n / br) + in + let nav = + Printf.sprintf "%d/%d %2d/%2d [%s] " cr br cc bc perc + |> sequence_of_string + in + let nsize = Sequence.length nav in + spread ~l:(bold mode) ~lsize:msize ~c:path ~r:(bold nav) ~rsize:nsize + ~fill:' ' w + |> invert + and control = + let msg = Option.value ~default:"" e.message |> sequence_of_string in + spread ~l:msg ~fill:' ' w + in + Sequence.(take (of_list [ status; control ]) e.status_size) + type 'a action = t -> 'a * t module Action = struct @@ -71,6 +124,8 @@ module Action = struct let update_cursor = let aux e = let dx, dy = e.offset and rs, cs = e.term.size in + (* Limit cursor to buffer view *) + let rs = rs - e.status_size in match Option.map ~f:Buffer.cursor e.buffer with | None -> { e with cursor = (1, 1); offset = (0, 0) } | Some (cx, cy) -> @@ -107,22 +162,41 @@ module Action = struct let x, y = e.offset and ((r, c) as size) = e.term.size and fill = Sequence.singleton '~' + and status = statusbar e and limit = Buffer.(if e.rendered then rendered_view else unrendered_view) in - let view = + let ssize = e.status_size in + let bufview = Option.( - e.buffer >>| limit x y r c + e.buffer + >>| limit x y (r - ssize) c |> value ~default:(welcome size) - |> Text.extend ~fill r) + |> Text.extend ~fill r + |> Fn.flip Sequence.take (r - ssize)) in - Terminal.redraw view e.cursor + let screen = Sequence.append bufview status in + Terminal.redraw screen e.cursor in get >>| aux (* TODO: save logic *) let quit n = Stdlib.exit n + (* Statusbar *) + let set_message m e = + ((), { e with message = m; message_timestamp = Unix.time () }) + + + let tick = + let check_message_timestamp e = + let now = Unix.time () in + let expired = Float.(e.message_timestamp < now - e.message_duration) in + if Option.is_some e.message && expired then { e with message = None } + else e + in + get >>| check_message_timestamp >>= put + (* Debug *) let get_rendered e = (e.rendered, e) let set_rendered r e = ((), { e with rendered = r }) @@ -376,6 +450,7 @@ let handle_next_command m e = match Sequence.next e.n_pending with | None -> ((), e) | Some (h, t) -> handle_normal_command h { e with n_pending = t }) + | Control -> failwith "unimplemented" let handle_next_command = let open Action in -- cgit v1.2.3