aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorMarc Coquand <marc@mccd.space>2024-05-14 16:20:57 -0500
committerMarc Coquand <marc@mccd.space>2024-05-14 16:20:57 -0500
commit0bc0958e789847d3065f4d084a96117d62d18691 (patch)
tree7447ff4e8d22aaa69c94ffe44e0e6b9cbf3dfeec /lib
parent20aa8079b40ee794bc84ead7283622c78f49afb5 (diff)
downloadstitch-0bc0958e789847d3065f4d084a96117d62d18691.tar.gz
stitch-0bc0958e789847d3065f4d084a96117d62d18691.tar.bz2
stitch-0bc0958e789847d3065f4d084a96117d62d18691.zip
Add nice headings
Diffstat (limited to 'lib')
-rw-r--r--lib/grep.ml71
-rw-r--r--lib/headlines.ml63
-rw-r--r--lib/stitched_article.ml68
3 files changed, 148 insertions, 54 deletions
diff --git a/lib/grep.ml b/lib/grep.ml
index 9638c37..7e5c3b9 100644
--- a/lib/grep.ml
+++ b/lib/grep.ml
@@ -3,25 +3,33 @@ let execution_directory =
let grep_cmd = Sys.getenv_opt "STICH_GREP_CMD" |> Option.value ~default:"ugrep"
-let tag_pattern = Sys.getenv_opt "STITCH_TAG_PATTERN" |> Option.value ~default:":[a-z-]+:"
+
+let tag_pattern =
+ Sys.getenv_opt "STITCH_TAG_PATTERN" |> Option.value ~default:"\\:[a-z-]+\\:"
+
+
+let headline_pattern_regexp =
+ Sys.getenv_opt "STITCH_HEADLINE_PATTERN_REGEXP" |> Option.value ~default:"^\\* "
+
let headline_pattern =
- Sys.getenv_opt "STITCH_HEADLINE_PATTERN" |> Option.value ~default:"^\\* "
+ Sys.getenv_opt "STITCH_HEADLINE_PATTERN" |> Option.value ~default:"* "
+let headline_pattern_length = String.length headline_pattern
+
let find_sort_modification () =
let open Shexp_process in
let open Shexp_process.Infix in
call [ "find"; "."; "-printf"; "%Ts/%f\\n" ]
|- call [ "sort"; "-n" ]
|- call [ "cut"; "-c12-" ]
- |- call [ "sed"; "/^\\./d" ]
let find_sort_name () =
let open Shexp_process in
let open Shexp_process.Infix in
- call [ "find"; "." ] |- call [ "sort"; "-n" ] |- call [ "cut"; "-c3-" ]
+ call [ "find"; "." ] |- call [ "cut"; "-c3-" ]
let run_print ~dir args =
@@ -32,18 +40,13 @@ let run_print ~dir args =
let headline_args = [ "xargs"; grep_cmd; "^\\*"; "-H"; "-r"; "-n"; "--no-messages" ]
-(* type sort = *)
-(* | Name *)
-(* | LastModified *)
-
-(* let sort_to_cmd = function *)
-(* | Name -> find_sort_name () *)
-(* | LastModified -> find_sort_modification () *)
-
let get_headlines () =
let open Shexp_process in
let open Shexp_process.Infix in
- eval (chdir execution_directory (find_sort_name () |- call headline_args |- read_all))
+ eval
+ (chdir
+ execution_directory
+ (find_sort_name () |- call headline_args |- call [ "sort"; "-n"; "-r" ] |- read_all))
let get_tagged_headlines tag () =
@@ -54,7 +57,8 @@ let get_tagged_headlines tag () =
execution_directory
(find_sort_name ()
|- call headline_args
- |- call [ grep_cmd; "-E"; "--no-messages"; tag ]
+ |- call [ grep_cmd; "--no-messages"; "-E"; tag ]
+ |- call [ "sort"; "-n"; "-r" ]
|- read_all))
@@ -105,10 +109,41 @@ let pretty_format parsed_headlines =
(** Full body parsing *)
-let get_full_content () =
- run_print
- ~dir:execution_directory
- [ grep_cmd; "^\\*"; "-h"; "-r"; "-n"; "-C"; "9999"; "--separator='|'" ]
+let get_full_content_command file = [ "cat"; file ]
+
+let get_full_file_content_content file =
+ let open Shexp_process in
+ let open Shexp_process.Infix in
+ ( file
+ , eval
+ (chdir
+ execution_directory
+ (find_sort_name ()
+ |- call headline_args
+ |- call (get_full_content_command file)
+ |- read_all)) )
+
+
+let parse_full_content files =
+ List.concat_map
+ (fun ((file_name : string), content) ->
+ let content = String.split_on_char '\n' content in
+ List.mapi (fun line_number line -> file_name, line_number, line) content)
+ files
+
+
+type display_type =
+ | Bold of string
+ | Normal of string
+
+let pretty_print_parsed_content parsed_files =
+ let padding = String.make headline_pattern_length ' ' in
+ List.concat_map
+ (fun (file_name, line_number, line_content) ->
+ if line_number == 0
+ then [ Bold ("------- " ^ file_name); Normal line_content ]
+ else [ Normal (padding ^ line_content) ])
+ parsed_files
(* let parse_file_headline collection full = *)
(* match full with *)
diff --git a/lib/headlines.ml b/lib/headlines.ml
index aec8567..853603c 100644
--- a/lib/headlines.ml
+++ b/lib/headlines.ml
@@ -2,17 +2,16 @@ module Grep = Grep
module Common = Common
open Notty
module Input_screen = Input_screen
+module Stitched_article = Stitched_article
type state =
- { tag : string option
- ; pos : int * int
+ { pos : int * int
; scroll : int
; content : (string * string) array
; content_pretty : string array
}
-let rec headline_screen t ({ tag; pos; scroll; content; content_pretty } as state) =
- print_endline (Option.value ~default:"No tag" tag);
+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)
@@ -32,11 +31,11 @@ let rec headline_screen t ({ tag; pos; scroll; content; content_pretty } as stat
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
- headline_screen t @@ { state with pos = x, max (y - 1) 0; scroll }
+ 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
- headline_screen t @@ { state with pos = x, min (y + 1) (content_length - 1); scroll }
+ 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 ]) -> ()
@@ -44,42 +43,36 @@ let rec headline_screen t ({ tag; pos; scroll; content; content_pretty } as stat
(match s with
| `Down -> scroll_down ()
| `Up -> scroll_up ())
- | `Resize _ -> headline_screen t state
+ | `Resize _ -> render t state
| `Mouse ((`Press _ | `Drag), (_, y), _) ->
- headline_screen t { state with pos = 0, min y content_length }
- | `Key (`ASCII 't', []) ->
+ render t { state with pos = 0, min y content_length }
+ | `Key (`ASCII '@', []) ->
+ let content =
+ Array.map
+ (fun (file_name, _) -> Grep.get_full_file_content_content file_name)
+ content
+ |> Array.to_list
+ in
+ let content = Grep.parse_full_content content in
+ let content_pretty = Grep.pretty_print_parsed_content content |> Array.of_list in
+ Stitched_article.render
+ t
+ { pos = 0, 0; content = content |> Array.of_list; content_pretty; scroll = 0 }
+ | `Key (`ASCII 's', []) ->
let (input_state : Input_screen.state) =
{ screen = img
; user_input = ""
- ; prompt = "TAG: "
+ ; 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;
- headline_screen
- t
- { state with content; content_pretty; pos = 0, 0; scroll = 0 })
- ; on_cancel =
- (fun _ ->
- Common.Term.cursor t None;
- headline_screen t state)
- }
- in
- Input_screen.render t input_state
- | `Key (`ASCII 's', []) ->
- let (input_state : Input_screen.state) =
- { screen = img
- ; user_input = ""
- ; prompt = "SEARCH: "
- ; on_enter =
- (fun _ ->
- Common.Term.cursor t None;
- headline_screen t { state with pos = 0, 0; scroll = 0 })
+ render t { content; content_pretty; pos = 0, 0; scroll = 0 })
; on_cancel =
(fun _ ->
Common.Term.cursor t None;
- headline_screen t state)
+ render t state)
}
in
Input_screen.render t input_state
@@ -89,7 +82,7 @@ let rec headline_screen t ({ tag; pos; scroll; content; content_pretty } as stat
(match d with
| `Up -> scroll_up ()
| `Down -> scroll_down ()
- | _ -> headline_screen t state)
+ | _ -> 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) =
@@ -112,13 +105,13 @@ let rec headline_screen t ({ tag; pos; scroll; content; content_pretty } as stat
match Unix.wait () with
| _, _ ->
Common.Term.cursor t None;
- headline_screen t state
+ render t state
(* Capture resizing events *)
| exception Unix.Unix_error (Unix.EINTR, _, _) -> run_editor ()
| exception Unix.Unix_error (_, _, _) -> failwith "ERROR"
in
run_editor ()
- | _ -> headline_screen t state
+ | _ -> render t state
let start (tag : string) () =
@@ -134,6 +127,4 @@ let start (tag : string) () =
exit 0)
else (
let content_pretty = content |> Grep.pretty_format in
- headline_screen
- (Common.Term.create ())
- { tag; pos = 0, 0; scroll = 0; content; content_pretty })
+ render (Common.Term.create ()) { pos = 0, 0; scroll = 0; content; content_pretty })
diff --git a/lib/stitched_article.ml b/lib/stitched_article.ml
new file mode 100644
index 0000000..41efa75
--- /dev/null
+++ b/lib/stitched_article.ml
@@ -0,0 +1,68 @@
+module Grep = Grep
+module Common = Common
+open Notty
+module Input_screen = Input_screen
+
+type state =
+ { pos : int * int
+ ; scroll : int
+ ; content : (string * int * string) array
+ ; content_pretty : Grep.display_type array
+ }
+
+let render_line size_x y scroll (el : Grep.display_type) i =
+ match el with
+ | Bold el ->
+ if i == y - scroll
+ then (
+ let fill = String.make (max (size_x - String.length el) 0) ' ' in
+ I.strf "%s%s" ~attr:A.(st bold ++ st reverse) el fill |> I.pad ~l:0 ~t:i)
+ else I.strf "%s" ~attr:A.(st bold) el |> I.pad ~l:0 ~t:i
+ | Normal el ->
+ if i == y - scroll
+ then (
+ let fill = String.make (max (size_x - String.length el) 0) ' ' in
+ I.strf "%s%s" ~attr:A.(st reverse) el fill |> I.pad ~l:0 ~t:i)
+ else I.strf "%s" el |> I.pad ~l:0 ~t:i
+
+
+let rec render t ({ pos; scroll; content_pretty; _ } as state) =
+ let size_x, size_y = Common.Term.size t in
+ let x, y = pos in
+ let img =
+ let elements =
+ Array.mapi
+ (fun i el -> render_line size_x y scroll el i)
+ (* TODO: Fix this ugly slow conversion *)
+ (Array.to_seq content_pretty |> Seq.drop scroll |> Array.of_seq)
+ in
+ let open I in
+ Array.fold_left (fun sum el -> el </> sum) I.empty elements
+ 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 'j', []) | `Key (`ASCII 'N', [ `Ctrl ]) -> scroll_down ()
+ | `Key (`ASCII 'k', []) | `Key (`ASCII 'P', [ `Ctrl ]) -> scroll_up ()
+ | `Key (`Arrow d, _) ->
+ (match d with
+ | `Up -> scroll_up ()
+ | `Down -> scroll_down ()
+ | _ -> render t state)
+ | _ -> render t state