aboutsummaryrefslogtreecommitdiff
path: root/lib/done.ml
diff options
context:
space:
mode:
authorMarc Coquand <marc@mccd.space>2024-05-15 14:05:03 -0500
committerMarc Coquand <marc@mccd.space>2024-05-15 14:05:03 -0500
commit3f696169ab1a560d94d169c1a5b744346da4c081 (patch)
treed2aa5d6b0b9d70a594ede7795e423250a92729e1 /lib/done.ml
parent961339f0bd28c0f30bdb3c995a27927def8a991e (diff)
downloadstitch-3f696169ab1a560d94d169c1a5b744346da4c081.tar.gz
stitch-3f696169ab1a560d94d169c1a5b744346da4c081.tar.bz2
stitch-3f696169ab1a560d94d169c1a5b744346da4c081.zip
Add done view + visual
Diffstat (limited to 'lib/done.ml')
-rw-r--r--lib/done.ml141
1 files changed, 141 insertions, 0 deletions
diff --git a/lib/done.ml b/lib/done.ml
new file mode 100644
index 0000000..5daad60
--- /dev/null
+++ b/lib/done.ml
@@ -0,0 +1,141 @@
+module Grep = Grep
+module Common = Common
+open Notty
+module Help_screen = Help_screen
+
+type state =
+ { pos : int * int
+ ; scroll : int
+ ; content : (string * string) array
+ ; content_pretty : string array
+ ; goto_headlines : (unit -> unit) -> unit
+ ; goto_todo : (unit -> unit) -> unit
+ }
+
+let title = I.strf ~attr:A.(st bold) "%s" "Done View" |> I.pad ~l:0 ~t:0
+let content_start = 2
+
+let init ~goto_todo ~goto_headlines =
+ let content = Grep.get_done () |> Grep.parse_todo_string in
+ let content_pretty = Grep.pretty_format_todo content in
+ { pos = 0, content_start
+ ; scroll = 0
+ ; content = content |> Array.of_list
+ ; content_pretty = content_pretty |> Array.of_list
+ ; goto_headlines
+ ; goto_todo
+ }
+
+
+let load_done () =
+ let done_content = Grep.get_done () |> Grep.parse_todo_string in
+ let done_pretty = Grep.pretty_format_todo done_content in
+ done_content, done_pretty
+
+
+let rec render
+ t
+ ({ pos; scroll; content; content_pretty; goto_headlines; goto_todo } as state)
+ =
+ let x, y = pos in
+ let content_position = y - content_start in
+ let img =
+ let dot = I.string A.(st bold) ">" |> I.pad ~l:0 ~t:(y - scroll)
+ and elements =
+ Array.mapi
+ (fun i el ->
+ if i == y - scroll
+ then I.strf "%s" el |> I.pad ~l:2 ~t:(i + content_start)
+ else I.strf "%s" el |> I.pad ~l:2 ~t:(i + content_start))
+ (Array.to_seq content_pretty |> Seq.drop scroll |> Array.of_seq)
+ in
+ let open I in
+ Array.fold_left (fun sum el -> el </> sum) (title </> dot) elements
+ in
+ let _, size_y = Common.Term.size t in
+ Common.Term.image t img;
+ let content_end = Array.length content_pretty + (content_start - 1) in
+ let scroll_up () =
+ let scroll = if y - content_start - scroll = 0 then max (scroll - 1) 0 else scroll in
+ render t { state with pos = x, max (y - 1) content_start; scroll }
+ in
+ let scroll_down () =
+ let scroll = if y - scroll >= size_y - 1 then scroll + 1 else scroll in
+ render t { state with pos = x, min (y + 1) content_end; scroll }
+ in
+ match Common.Term.event t with
+ | `End | `Key (`Escape, []) | `Key (`ASCII 'q', []) | `Key (`ASCII 'C', [ `Ctrl ]) -> ()
+ | `Mouse (`Press (`Scroll s), _, _) ->
+ (match s with
+ | `Down -> scroll_down ()
+ | `Up -> scroll_up ())
+ | `Resize _ -> render t state
+ | `Mouse ((`Press _ | `Drag), (_, y), _) ->
+ render t { state with pos = 0, min y content_end }
+ | `Key (`ASCII '?', []) -> Help_screen.render t { go_back = (fun () -> render t state) }
+ | `Key (`ASCII 's', []) ->
+ let (input_state : Input_screen.state) =
+ { screen = img
+ ; user_input = ""
+ ; prompt = "GREP: "
+ ; on_enter =
+ (fun tag ->
+ let content = Grep.get_tagged_headlines tag () |> Grep.parse_headlines in
+ let content_pretty = Grep.pretty_format content in
+ Common.Term.cursor t None;
+ render t { state with content; content_pretty })
+ ; on_cancel =
+ (fun _ ->
+ Common.Term.cursor t None;
+ render t state)
+ }
+ in
+ Input_screen.render t input_state
+ | `Key (`ASCII '1', []) -> goto_headlines (fun () -> render t state)
+ | `Key (`ASCII '2', []) -> goto_todo (fun () -> render t state)
+ | `Key (`ASCII 'j', []) | `Key (`ASCII 'N', [ `Ctrl ]) -> scroll_down ()
+ | `Key (`ASCII 'k', []) | `Key (`ASCII 'P', [ `Ctrl ]) -> scroll_up ()
+ | `Key (`ASCII 't', []) ->
+ let selected_file, _ = Array.get content (y - content_start) in
+ let _ = Grep.toggle_todo selected_file in
+ let content, content_pretty = load_done () in
+ render
+ t
+ { state with
+ content = content |> Array.of_list
+ ; content_pretty = Array.of_list content_pretty
+ }
+ | `Key (`Arrow d, _) ->
+ (match d with
+ | `Up -> scroll_up ()
+ | `Down -> scroll_down ()
+ | _ -> render t state)
+ | `Key (`ASCII 'e', []) | `Key (`Enter, []) ->
+ (* Editor might be set with extra args, in that case we need to separate these *)
+ let[@warning "-8"] (editor :: args) =
+ String.split_on_char ' ' (Sys.getenv "EDITOR")
+ in
+ let selected_file, _ = Array.get content content_position in
+ let full_path_file = Grep.execution_directory ^ "/" ^ selected_file in
+ let full_args = Array.append (Array.of_list args) [| full_path_file |] in
+ Common.Term.cursor t (Some (0, 0));
+ let _ =
+ Unix.create_process_env
+ editor
+ full_args
+ (Unix.environment ())
+ Unix.stdin
+ Unix.stdout
+ Unix.stderr
+ in
+ let rec run_editor () =
+ match Unix.wait () with
+ | _, _ ->
+ Common.Term.cursor t None;
+ render t state
+ (* Capture resizing events *)
+ | exception Unix.Unix_error (Unix.EINTR, _, _) -> run_editor ()
+ | exception Unix.Unix_error (_, _, _) -> failwith "ERROR"
+ in
+ run_editor ()
+ | _ -> render t state