From 9f3d3d40ddf6db70f8395adf4290241f7c5735db Mon Sep 17 00:00:00 2001 From: Marc Coquand Date: Wed, 15 May 2024 12:32:39 -0500 Subject: Update state management --- bin/main.ml | 3 +-- lib/headlines.ml | 62 ++++++++++++++++--------------------------------- lib/shared_state.ml | 0 lib/stitch.ml | 32 +++++++++++++++++++++++++ lib/stitched_article.ml | 28 +++++----------------- lib/todos.ml | 15 ++++++++---- 6 files changed, 70 insertions(+), 70 deletions(-) create mode 100644 lib/shared_state.ml create mode 100644 lib/stitch.ml diff --git a/bin/main.ml b/bin/main.ml index 78d30ba..1fba7a3 100644 --- a/bin/main.ml +++ b/bin/main.ml @@ -1,4 +1,3 @@ -open Stitch open Cmdliner let tag_arg = @@ -6,7 +5,7 @@ let tag_arg = Arg.(value & opt string "" & info [ "t"; "tag" ] ~docv:"TAG" ~doc) -let headlines_t = Term.(const Headlines.start $ tag_arg $ const ()) +let headlines_t = Term.(const Stitch.start $ tag_arg $ const ()) let headlines_cmd = let doc = "Show titles in a condensed list" in diff --git a/lib/headlines.ml b/lib/headlines.ml index 4565f5f..7d410d7 100644 --- a/lib/headlines.ml +++ b/lib/headlines.ml @@ -4,18 +4,33 @@ open Notty module Input_screen = Input_screen module Stitched_article = Stitched_article module Help_screen = Help_screen -module Todos = Todos type state = { pos : int * int ; scroll : int ; content : (string * string) array ; content_pretty : string array - ; todo_state : Todos.state option + ; goto_todos_view : (unit -> unit) -> unit } +let init ~goto_todos_view ~regexp = + let tag = if String.equal regexp "" then None else Some regexp 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 "Regexp not found"; + exit 0) + else ( + let content_pretty = content |> Grep.pretty_format in + { pos = 0, 0; scroll = 0; content; content_pretty; goto_todos_view }) + + (* TODO: Add page title *) -let rec render t ({ pos; scroll; content; content_pretty; todo_state } as state) = +let rec render t ({ pos; scroll; content; content_pretty; goto_todos_view } as state) = let x, y = pos in let img = let dot = I.string A.(st bold) ">" |> I.pad ~l:0 ~t:(y - scroll) @@ -51,26 +66,7 @@ let rec render t ({ pos; scroll; content; content_pretty; todo_state } as 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 '2', []) -> - (match todo_state with - | Some todo_state -> - Todos.render - t - { todo_state with - goto_headlines = - (fun new_state -> render t { state with todo_state = Some new_state }) - } - | None -> - let todo_content = Grep.get_todos () |> Grep.parse_todo_string in - let todo_pretty = Grep.pretty_format_todo todo_content in - let todo_state = - Todos.init - ~goto_headlines:(fun new_state -> - render t { state with todo_state = Some new_state }) - ~content:(todo_content |> Array.of_list) - ~content_pretty:(todo_pretty |> Array.of_list) - in - Todos.render t todo_state) + | `Key (`ASCII '2', []) -> goto_todos_view (fun () -> render t state) | `Key (`ASCII 's', []) -> let content = Array.map @@ -89,7 +85,7 @@ let rec render t ({ pos; scroll; content; content_pretty; todo_state } as state) ; content_pretty = full_content_pretty ; scroll = 0 ; go_back = (fun () -> render t state) - ; todo_state + ; goto_todos_view } | `Key (`ASCII 'r', []) -> let (input_state : Input_screen.state) = @@ -145,21 +141,3 @@ let rec render t ({ pos; scroll; content; content_pretty; todo_state } as state) 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; todo_state = None }) diff --git a/lib/shared_state.ml b/lib/shared_state.ml new file mode 100644 index 0000000..e69de29 diff --git a/lib/stitch.ml b/lib/stitch.ml new file mode 100644 index 0000000..e49ed9c --- /dev/null +++ b/lib/stitch.ml @@ -0,0 +1,32 @@ +module Grep = Grep +module Common = Common +module Todos = Todos +module Headlines = Headlines + +let start (tag : string) () = + (* This is a rather funky state management that isn't maybe entirely functional. + What we do is store a function for each view that restores it's state. + + This allows us to remember the state of the view and restore it as we travel between different views. + + It does create a rather funky, cyclical state though. + *) + let term = Common.Term.create () in + let restore_headline_state = ref (fun () -> ()) in + let restore_todo_state = ref (fun () -> ()) in + let goto_headline_from_todo new_todo_state = + restore_todo_state := new_todo_state; + !restore_headline_state () + in + (restore_todo_state + := fun () -> + let todo = Todos.init ~goto_headlines:goto_headline_from_todo in + Todos.render term todo); + let headline = + Headlines.init + ~goto_todos_view:(fun new_state -> + restore_headline_state := new_state; + !restore_todo_state ()) + ~regexp:tag + in + Headlines.render term headline diff --git a/lib/stitched_article.ml b/lib/stitched_article.ml index c71ca14..66a108e 100644 --- a/lib/stitched_article.ml +++ b/lib/stitched_article.ml @@ -10,11 +10,14 @@ type state = ; content : (string * int * string * int) array ; go_back : unit -> unit ; content_pretty : Grep.display_type array - ; todo_state : Todos.state option + ; goto_todos_view : (unit -> unit) -> unit } (* TODO: Use grep -l to filter notes by regexp and rerender those files*) -let rec render t ({ pos; scroll; content_pretty; go_back; content; todo_state } as state) = +let rec render + t + ({ pos; scroll; content_pretty; go_back; content; goto_todos_view } as state) + = let size_x, size_y = Common.Term.size t in let x, y = pos in let img = @@ -50,26 +53,7 @@ let rec render t ({ pos; scroll; content_pretty; go_back; content; todo_state } | `Key (`ASCII 'j', []) | `Key (`ASCII 'N', [ `Ctrl ]) -> scroll_down () | `Key (`ASCII 'k', []) | `Key (`ASCII 'P', [ `Ctrl ]) -> scroll_up () | `Key (`ASCII '?', []) -> Help_screen.render t { go_back = (fun () -> render t state) } - | `Key (`ASCII '2', []) -> - (match todo_state with - | Some todo_state -> - Todos.render - t - { todo_state with - goto_headlines = - (fun new_state -> render t { state with todo_state = Some new_state }) - } - | None -> - let todo_content = Grep.get_todos () |> Grep.parse_todo_string in - let todo_pretty = Grep.pretty_format_todo todo_content in - let todo_state = - Todos.init - ~goto_headlines:(fun new_state -> - render t { state with todo_state = Some new_state }) - ~content:(todo_content |> Array.of_list) - ~content_pretty:(todo_pretty |> Array.of_list) - in - Todos.render t todo_state) + | `Key (`ASCII '2', []) -> goto_todos_view (fun () -> 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) = diff --git a/lib/todos.ml b/lib/todos.ml index 4e52cd4..d2af47f 100644 --- a/lib/todos.ml +++ b/lib/todos.ml @@ -8,11 +8,18 @@ type state = ; scroll : int ; content : (string * string) array ; content_pretty : string array - ; goto_headlines : state -> unit + ; goto_headlines : (unit -> unit) -> unit } -let init ~goto_headlines ~content ~content_pretty = - { pos = 0, 0; scroll = 0; content; content_pretty; goto_headlines } +let init ~goto_headlines = + let content = Grep.get_todos () |> Grep.parse_todo_string in + let content_pretty = Grep.pretty_format_todo content in + { pos = 0, 0 + ; scroll = 0 + ; content = content |> Array.of_list + ; content_pretty = content_pretty |> Array.of_list + ; goto_headlines + } let load_todos () = @@ -75,7 +82,7 @@ let rec render t ({ pos; scroll; content; content_pretty; goto_headlines } as st } in Input_screen.render t input_state - | `Key (`ASCII '1', []) -> goto_headlines state + | `Key (`ASCII '1', []) -> goto_headlines (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', []) -> -- cgit v1.2.3