Author: Kevin Schoon [kevinschoon@gmail.com]
Hash: a6dd74dc009561c9447458635e3c99dea5b3d4fa
Timestamp: Thu, 10 Sep 2020 18:13:43 +0000 (4 years ago)

+74 -85 +/-3 browse
improve completion support
1diff --git a/completion/note.bash b/completion/note.bash
2index 6dd1cf4..5b96695 100644
3--- a/completion/note.bash
4+++ b/completion/note.bash
5 @@ -1,11 +1,12 @@
6- # TODO: Core.Command completion is terrible
7- function _jsautocom_742571 {
8+ function _note_complete {
9 export COMP_CWORD
10 COMP_WORDS[0]=note
11 if type readarray > /dev/null
12 then readarray -t COMPREPLY < <("${COMP_WORDS[@]}")
13 else IFS="
14 " read -d "" -A COMPREPLY < <("${COMP_WORDS[@]}")
15+ echo $COMP_CWORD $COMP_WORDS
16 fi
17 }
18- complete -F _jsautocom_742571 note
19+
20+ complete -F _note_complete note
21 diff --git a/lib/cmd.ml b/lib/cmd.ml
22index abc4439..6893d5c 100644
23--- a/lib/cmd.ml
24+++ b/lib/cmd.ml
25 @@ -8,6 +8,22 @@ let init_config path =
26 Config.initialize config_path config;
27 config
28
29+ let get_slugs =
30+ let open Config in
31+ let cfg = init_config None in
32+ let slugs = Slug.load (get_exn cfg "state_dir") in
33+ List.map
34+ ~f:(fun s -> Filename.concat (get_exn cfg "state_dir") (Slug.to_string s))
35+ slugs
36+
37+ let get_notes_with_paths =
38+ List.map
39+ ~f:(fun path -> (Note.of_string (In_channel.read_all path), path))
40+ get_slugs
41+
42+ let get_notes =
43+ List.map ~f:(fun path -> Note.of_string (In_channel.read_all path)) get_slugs
44+
45 type encoding = Json | Yaml | Text
46
47 let encoding_argument =
48 @@ -18,6 +34,19 @@ let encoding_argument =
49 | "Text" | "text" | "TEXT" -> Text
50 | _ -> failwith "unsupported encoding type")
51
52+ let filter_arg =
53+ Command.Arg_type.create
54+ ~complete:(fun _ ~part ->
55+ let notes = get_notes in
56+ List.filter_map
57+ ~f:(fun note ->
58+ let title = Note.get_title note in
59+ if String.equal part "" then Some title
60+ else if String.is_substring ~substring:part title then Some title
61+ else None)
62+ notes)
63+ (fun filter -> filter)
64+
65 type value = Config of Config.t | Note of Note.t
66
67 let encode_value value = function
68 @@ -55,33 +84,20 @@ note cat fuubar
69 note cat -encoding json
70 |})
71 [%map_open
72- let filter_args = anon (sequence ("filters" %: string))
73+ let filter_args = anon (sequence ("filter" %: filter_arg))
74 and fulltext =
75- flag "fulltext" (no_arg) ~doc:"perform a fulltext search instead of just key comparison"
76+ flag "fulltext" no_arg
77+ ~doc:"perform a fulltext search instead of just key comparison"
78 and encoding =
79 flag "encoding"
80 (optional_with_default Text encoding_argument)
81 ~doc:"format [Text | Json | Yaml] (default: Text)"
82 in
83 fun () ->
84- let open Config in
85- let cfg = init_config None in
86- let slugs = Slug.load (get_exn cfg "state_dir") in
87- let paths =
88- List.map
89- ~f:(fun s ->
90- Filename.concat (get_exn cfg "state_dir") (Slug.to_string s))
91- slugs
92- in
93 let open Note.Filter in
94 let filter_kind = if fulltext then Some Fulltext else None in
95 let notes =
96- find_many
97- ?strategy:filter_kind
98- ~args: filter_args
99- (List.map
100- ~f:(fun path -> Note.of_string (In_channel.read_all path))
101- paths)
102+ find_many ?strategy:filter_kind ~args:filter_args get_notes
103 in
104 List.iter
105 ~f:(fun note -> print_endline (encode_value (Note note) encoding))
106 @@ -178,31 +194,21 @@ Examples
107 note delete fuubar
108 |})
109 [%map_open
110- let filter_args = anon (sequence ("filter" %: string))
111+ let filter_args = anon (sequence ("filter" %: filter_arg))
112 and fulltext =
113- flag "fulltext" (no_arg) ~doc:"perform a fulltext search instead of just key comparison"
114+ flag "fulltext" no_arg
115+ ~doc:"perform a fulltext search instead of just key comparison"
116 in
117 fun () ->
118 let open Config in
119 let cfg = init_config None in
120- let slugs = Slug.load (get_exn cfg "state_dir") in
121- let paths =
122- List.map
123- ~f:(fun s ->
124- Filename.concat (get_exn cfg "state_dir") (Slug.to_string s))
125- slugs
126- in
127 let open Note.Filter in
128 let filter_kind = if fulltext then Some Fulltext else None in
129 let note =
130- Note.Filter.find_one_with_paths
131- ?strategy: filter_kind
132- ~args: filter_args
133- (List.map
134- ~f:(fun path ->
135- (Note.of_string (In_channel.read_all path), path))
136- paths)
137+ Note.Filter.find_one_with_paths ?strategy:filter_kind
138+ ~args:filter_args get_notes_with_paths
139 in
140+
141 match note with
142 | Some (note, path) ->
143 Io.delete
144 @@ -223,30 +229,19 @@ Examples
145 note edit fuubar
146 |})
147 [%map_open
148- let filter_args = anon (sequence ("filter" %: string))
149+ let filter_args = anon (sequence ("filter" %: filter_arg))
150 and fulltext =
151- flag "fulltext" (no_arg) ~doc:"perform a fulltext search instead of just key comparison"
152+ flag "fulltext" no_arg
153+ ~doc:"perform a fulltext search instead of just key comparison"
154 in
155 fun () ->
156 let open Config in
157 let cfg = init_config None in
158- let slugs = Slug.load (get_exn cfg "state_dir") in
159- let paths =
160- List.map
161- ~f:(fun s ->
162- Filename.concat (get_exn cfg "state_dir") (Slug.to_string s))
163- slugs
164- in
165 let open Note.Filter in
166 let filter_kind = if fulltext then Some Fulltext else None in
167 let note =
168- Note.Filter.find_one_with_paths
169- ?strategy: filter_kind
170- ~args: filter_args
171- (List.map
172- ~f:(fun path ->
173- (Note.of_string (In_channel.read_all path), path))
174- paths)
175+ Note.Filter.find_one_with_paths ?strategy:filter_kind
176+ ~args:filter_args get_notes_with_paths
177 in
178 match note with
179 | Some (_, path) ->
180 @@ -270,35 +265,25 @@ note ls
181 ```
182 |})
183 [%map_open
184- let filter_args = anon (sequence ("args" %: string))
185+ let filter_args = anon (sequence ("filter" %: filter_arg))
186 and fulltext =
187- flag "fulltext" (no_arg) ~doc:"perform a fulltext search instead of just key comparison"
188+ flag "fulltext" no_arg
189+ ~doc:"perform a fulltext search instead of just key comparison"
190+ and simple =
191+ flag "simple" no_arg ~doc:"simple program output (machine readable)"
192 in
193 fun () ->
194- let open Config in
195- let cfg = init_config None in
196- let slugs = Slug.load (get_exn cfg "state_dir") in
197- let paths =
198- List.map
199- ~f:(fun s ->
200- Filename.concat (get_exn cfg "state_dir") (Slug.to_string s))
201- slugs
202- in
203 let open Note.Filter in
204+ let style = if simple then Simple else Fancy in
205 let filter_kind = if fulltext then Some Fulltext else None in
206 let notes =
207- Note.Filter.find_many
208- ?strategy: filter_kind
209- ~args: filter_args
210- (List.map
211- ~f:(fun path -> Note.of_string (In_channel.read_all path))
212- paths)
213+ Note.Filter.find_many ?strategy:filter_kind ~args:filter_args
214+ get_notes
215 in
216- print_short ~style:Fancy notes]
217+ print_short ~style notes]
218
219 let run =
220- Command.run
221- ~version: "%%VERSION%%"
222+ Command.run ~version:"%%VERSION%%"
223 (Command.group ~summary:"Note is a simple CLI based note taking application"
224 [
225 ("cat", cat_note);
226 diff --git a/lib/note.ml b/lib/note.ml
227index 66065cc..a1bc130 100644
228--- a/lib/note.ml
229+++ b/lib/note.ml
230 @@ -177,7 +177,7 @@ module Filter = struct
231 args
232 | Fulltext -> failwith "not implemented"
233
234- let find_one ?(strategy=Keys) ~args notes =
235+ let find_one ?(strategy = Keys) ~args notes =
236 let filters = of_strings strategy args in
237 List.find
238 ~f:(fun note ->
239 @@ -185,7 +185,7 @@ module Filter = struct
240 || List.length filters = 0)
241 notes
242
243- let find_one_with_paths ?(strategy=Keys) ~args notes =
244+ let find_one_with_paths ?(strategy = Keys) ~args notes =
245 let filters = of_strings strategy args in
246 List.find
247 ~f:(fun (note, _) ->
248 @@ -193,7 +193,7 @@ module Filter = struct
249 || List.length filters = 0)
250 notes
251
252- let find_many ?(strategy=Keys) ~args notes =
253+ let find_many ?(strategy = Keys) ~args notes =
254 let filters = of_strings strategy args in
255 List.filter
256 ~f:(fun note ->
257 @@ -233,23 +233,26 @@ module Display = struct
258
259 let print_short ~style notes =
260 let columns =
261- [ (([ Bold ], "title"), ([ Bold ], "tags"), ([ Bold ], "words")) ]
262- @ List.map
263- ~f:(fun note ->
264- let title = get_title note in
265- let tags = String.concat ~sep:"|" (get_tags note) in
266- let word_count = Core.sprintf "%d" (List.length (tokenize note)) in
267- (([], title), ([], tags), ([], word_count)))
268- notes
269+ List.map
270+ ~f:(fun note ->
271+ let title = get_title note in
272+ let tags = String.concat ~sep:"|" (get_tags note) in
273+ let word_count = Core.sprintf "%d" (List.length (tokenize note)) in
274+ (([], title), ([], tags), ([], word_count)))
275+ notes
276 in
277 match style with
278 | Simple ->
279 List.iter
280 ~f:(fun (col1, col2, col3) ->
281- let (_, text1), (_, text2), (_, text3) = (col1, col2, col3) in
282- print_endline (Core.sprintf "%s %s %s" text1 text2 text3))
283+ let (_, text1), (_, _), (_, _) = (col1, col2, col3) in
284+ print_endline (Core.sprintf "%s" text1))
285 columns
286 | Fancy ->
287+ let columns =
288+ [ (([ Bold ], "title"), ([ Bold ], "tags"), ([ Bold ], "words")) ]
289+ @ columns
290+ in
291 let max1, max2, max3 =
292 List.fold ~init:(0, 0, 0)
293 ~f:(fun accm pair ->