aboutsummaryrefslogtreecommitdiff
path: root/lib/todos.ml
diff options
context:
space:
mode:
authorMarc Coquand <marc@mccd.space>2024-05-15 10:45:54 -0500
committerMarc Coquand <marc@mccd.space>2024-05-15 10:45:54 -0500
commit47892663040f7e295cc4052438cf804b040f0389 (patch)
treed299cb4f0741904b1a8d484a6fba365b41592bf5 /lib/todos.ml
parent7038e0073e7f7f672644ca949eeda399d6a533fb (diff)
downloadstitch-47892663040f7e295cc4052438cf804b040f0389.tar.gz
stitch-47892663040f7e295cc4052438cf804b040f0389.tar.bz2
stitch-47892663040f7e295cc4052438cf804b040f0389.zip
Add TODOs, update help, rebind keys
Diffstat (limited to 'lib/todos.ml')
-rw-r--r--lib/todos.ml136
1 files changed, 136 insertions, 0 deletions
diff --git a/lib/todos.ml b/lib/todos.ml
new file mode 100644
index 0000000..3162615
--- /dev/null
+++ b/lib/todos.ml
@@ -0,0 +1,136 @@
+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
+ }
+
+let init ~content ~content_pretty = { pos = 0, 0; scroll = 0; content; content_pretty }
+
+let load_todos () =
+ let todo_content = Grep.get_todos () |> Grep.parse_todo_string in
+ let todo_pretty = Grep.pretty_format_todo todo_content in
+ todo_content, todo_pretty
+
+
+let rec render t ({ pos; scroll; content; content_pretty } as state) =
+ let x, y = pos 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 ~attr:A.(st underline) "%s" el |> I.pad ~l:2 ~t:i
+ else I.strf "%s" el |> I.pad ~l:2 ~t:i)
+ (Array.to_seq content_pretty |> Seq.drop scroll |> Array.of_seq)
+ in
+ let open I in
+ Array.fold_left (fun sum el -> el </> sum) dot elements
+ in
+ let _, size_y = Common.Term.size t in
+ Common.Term.image t img;
+ let content_length = Array.length content_pretty in
+ let scroll_up () =
+ let scroll = if y - scroll = 0 then max (scroll - 1) 0 else scroll in
+ render t @@ { state with pos = x, max (y - 1) 0; 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_length - 1); 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_length }
+ | `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 { content; content_pretty; pos = 0, 0; scroll = 0 })
+ ; on_cancel =
+ (fun _ ->
+ Common.Term.cursor t None;
+ render t state)
+ }
+ in
+ Input_screen.render t input_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 in
+ let _ = Grep.toggle_done selected_file in
+ let content, content_pretty = load_todos () 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 y 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
+
+
+let start (tag : string) () =
+ let tag = if String.equal tag "" then None else Some tag in
+ let content =
+ match tag with
+ | None -> Grep.get_headlines () |> Grep.parse_headlines
+ | Some tag -> Grep.get_tagged_headlines tag () |> Grep.parse_headlines
+ in
+ if Array.length content == 0
+ then (
+ print_endline "No entry for tag";
+ exit 0)
+ else (
+ let content_pretty = content |> Grep.pretty_format in
+ render (Common.Term.create ()) { pos = 0, 0; scroll = 0; content; content_pretty })