aboutsummaryrefslogtreecommitdiff
path: root/lib/grep.ml
blob: 9855b6f798ac97fe5e5a8bf0b0368381a3c6191d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
let execution_directory =
  Sys.getenv_opt "STICH_DIRECTORY" |> Option.value ~default:"/home/mccd/notes-example"


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 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:"* "


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-" ]


let find_sort_name () =
  let open Shexp_process in
  let open Shexp_process.Infix in
  call [ "find"; "." ] |- call [ "cut"; "-c3-" ]


let run_print ~dir args =
  let open Shexp_process in
  let open Shexp_process.Infix in
  eval (chdir dir (call args |- read_all))


let headline_args = [ "xargs"; grep_cmd; "^\\*"; "-H"; "-r"; "-n"; "--no-messages" ]

let get_headlines () =
  let open Shexp_process in
  let open Shexp_process.Infix in
  eval
    (chdir
       execution_directory
       (find_sort_name () |- call headline_args |- call [ "sort"; "-n"; "-r" ] |- read_all))


let get_tagged_headlines tag () =
  let open Shexp_process in
  let open Shexp_process.Infix in
  eval
    (chdir
       execution_directory
       (find_sort_name ()
        |- call headline_args
        |- call [ grep_cmd; "--no-messages"; "-E"; tag ]
        |- call [ "sort"; "-n"; "-r" ]
        |- read_all))


let get_tags () =
  let open Shexp_process in
  let open Shexp_process.Infix in
  eval
    (chdir
       execution_directory
       (call headline_args |- call [ grep_cmd; "-E"; tag_pattern; "-o" ] |- read_all))


exception Not_A_Tuple of string * string

(** Returns a tuple of file name and Content *)
let parse_headlines s =
  String.split_on_char '\n' s
  (* Testing in utop it seems like there is maybe a bug with bounded_split, 1 doesn't work for ':'. Therefore using a slower implementation. *)
  |> List.filter_map (fun message ->
    if String.equal message ""
    then None
    else (
      let split = Str.bounded_split (Str.regexp ":[0-9]+:") message 2 in
      match split with
      (* file, line, content *)
      | [ file_name; content ] -> Some (file_name, content)
      | _ -> raise (Not_A_Tuple (String.concat " SPLIT " split, message))))
  |> Array.of_list


(** Used for pretty printing *)
let get_padding list =
  Array.fold_left (fun n (file_name, _) -> Int.max n (String.length file_name)) 0 list


let pad str n =
  let padding = n - String.length str in
  String.concat "" [ str; String.make padding ' ' ]


(** Turns "2024-03-05.org:* Hello world" into "2024-03-05    | * Hello world" *)
let pretty_format parsed_headlines =
  let padding = get_padding parsed_headlines in
  Array.map
    (fun (file_name, content) -> String.concat " | " [ pad file_name padding; content ])
    parsed_headlines


(** Full body parsing *)

let get_full_content_command file = [ "cat"; file ]

let read_whole_file filename =
  (* open_in_bin works correctly on Unix and Windows *)
  let ch = open_in_bin filename in
  let s = really_input_string ch (in_channel_length ch) in
  close_in ch;
  s


let get_full_file_content_content file =
  file, read_whole_file (execution_directory ^ "/" ^ file)


let parse_full_content files =
  List.concat
  @@ List.mapi
       (fun file_number ((file_name : string), content) ->
         let content = String.split_on_char '\n' content in
         List.mapi
           (fun line_number line -> file_name, line_number, line, file_number)
           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