stitch

Note taking for messy minimalists

git clone git://mccd.space/stitch
commit f805a04bd0600653f71e5e4e006104892cf7b6b9
parent fbbbdca67c79145d43d6fdd59202a36fddfc8cd0
Author: Marc Coquand <marc@mccd.space>
Date:   Sat, 18 May 2024 13:19:26 -0500

Add run command to done; fix content bug

Diffstat:
Mlib/done.ml | 62++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Mlib/headlines.ml | 5+----
Mlib/todos.ml | 5+----
3 files changed, 58 insertions(+), 14 deletions(-)
diff --git a/lib/done.ml b/lib/done.ml
@@ -10,6 +10,8 @@ type state =
   ; content_pretty : string array
   ; goto_headlines : (unit -> unit) -> unit
   ; goto_todo : (unit -> unit) -> unit
+  ; tag : string
+  ; output : string option
   }
 
 let title = I.strf ~attr:A.(st bold) "%s" "Done View" |> I.pad ~l:0 ~t:0
@@ -24,6 +26,8 @@ let init ~goto_todo ~goto_headlines =
   ; content_pretty = content_pretty |> Array.of_list
   ; goto_headlines
   ; goto_todo
+  ; tag = ""
+  ; output = None
   }
 
 
@@ -35,12 +39,19 @@ let load_done () =
 
 let rec render
   t
-  ({ pos; scroll; content; content_pretty; goto_headlines; goto_todo } as state)
+  ({ pos; scroll; content; content_pretty; goto_headlines; goto_todo; output; _ } as state)
   =
   let x, y = pos in
+  let size_x, size_y = Common.Term.size t in
   let content_position = y - content_start in
   let img =
-    let dot =
+    let output_info =
+      match output with
+      | Some line ->
+        I.strf "%s%s" (String.escaped line) (String.make size_x ' ')
+        |> I.pad ~t:(size_y - 1)
+      | None -> I.empty
+    and dot =
       if Array.length content_pretty = 0
       then I.empty
       else I.string A.(st bold) ">" |> I.pad ~l:0 ~t:(y - scroll)
@@ -53,18 +64,17 @@ let rec render
         (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
+    Array.fold_left (fun sum el -> el </> sum) (title </> dot </> output_info) 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 }
+    render t { state with pos = x, max (y - 1) content_start; scroll; output = None }
   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 }
+    render t { state with pos = x, min (y + 1) content_end; scroll; output = None }
   in
   match Common.Term.event t with
   | `End | `Key (`Escape, []) | `Key (`ASCII 'q', []) | `Key (`ASCII 'C', [ `Ctrl ]) -> ()
@@ -91,6 +101,7 @@ let rec render
               { state with
                 content = content |> Array.of_list
               ; content_pretty = content_pretty |> Array.of_list
+              ; tag
               })
       ; on_cancel =
           (fun _ ->
@@ -111,6 +122,45 @@ let rec render
       ; content = content |> Array.of_list
       ; content_pretty = Array.of_list content_pretty
       }
+  | `Key (`ASCII '!', []) ->
+    let selected_file, content = Array.get content content_position in
+    let selected_file = Grep.execution_directory ^ "/" ^ selected_file in
+    let (input_state : Input_prompt.state) =
+      { screen = img
+      ; user_input = ""
+      ; prompt = "COMMAND"
+      ; on_enter =
+          (fun command ->
+            if String.equal (String.trim command) String.empty
+            then (
+              Common.Term.cursor t None;
+              render t state)
+            else
+              Arbitrary_command.run
+                t
+                ~command:(Scanf.unescaped command)
+                ~content
+                ~selected_file
+                ~on_return:(fun result ->
+                  let content, content_pretty = load_done () in
+                  let y = min (List.length content_pretty + content_start) y in
+                  Common.Term.cursor t None;
+                  render
+                    t
+                    { state with
+                      content = Array.of_list content
+                    ; content_pretty = Array.of_list content_pretty
+                    ; pos = 0, y
+                    ; scroll = 0
+                    ; output = Some result
+                    }))
+      ; on_cancel =
+          (fun _ ->
+            Common.Term.cursor t None;
+            render t state)
+      }
+    in
+    Input_prompt.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', [ `Ctrl ]) ->
diff --git a/lib/headlines.ml b/lib/headlines.ml
@@ -152,11 +152,8 @@ let rec render
      | `Down -> scroll_down ()
      | _ -> render t state)
   | `Key (`ASCII '!', []) ->
-    let selected_file, _ = Array.get content content_position in
+    let selected_file, content = Array.get content content_position in
     let selected_file = Grep.execution_directory ^ "/" ^ selected_file in
-    let content =
-      Array.map (fun (_, c) -> c) content |> Array.to_list |> String.concat "\n"
-    in
     let (input_state : Input_prompt.state) =
       { screen = img
       ; user_input = ""
diff --git a/lib/todos.ml b/lib/todos.ml
@@ -126,11 +126,8 @@ let rec render
   | `Key (`ASCII 'j', []) | `Key (`ASCII 'N', [ `Ctrl ]) -> scroll_down ()
   | `Key (`ASCII 'k', []) | `Key (`ASCII 'P', [ `Ctrl ]) -> scroll_up ()
   | `Key (`ASCII '!', []) ->
-    let selected_file, _ = Array.get content content_position in
+    let selected_file, content = Array.get content content_position in
     let selected_file = Grep.execution_directory ^ "/" ^ selected_file in
-    let content =
-      Array.map (fun (_, c) -> c) content |> Array.to_list |> String.concat "\n"
-    in
     let (input_state : Input_prompt.state) =
       { screen = img
       ; user_input = ""