-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.rkt
301 lines (269 loc) · 11.6 KB
/
main.rkt
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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
#lang racket
;; TODO try #lang hacket - haskell + racket
;; https://lexi-lambda.github.io/hackett/index.html
;; Notice
;; To install (from within the package directory):
;; $ raco pkg install
;; To install (once uploaded to pkgs.racket-lang.org):
;; $ raco pkg install <<name>>
;; To uninstall:
;; $ raco pkg remove <<name>>
;; To view documentation:
;; $ raco docs <<name>>
;;
;; For your convenience, we have included LICENSE-MIT and LICENSE-APACHE files.
;; If you would prefer to use a different license, replace those files with the
;; desired license.
;;
;; Some users like to add a `private/` directory, place auxiliary files there,
;; and require them in `main.rkt`.
;;
;; See the current version of the racket style guide here:
;; http://docs.racket-lang.org/style/index.html
(module+ main
;; (Optional) main submodule. Put code here if you need it to be executed when
;; this file is run using DrRacket or the `racket` executable. The code here
;; does not run when this file is required by another module. Documentation:
;; http://docs.racket-lang.org/guide/Module_Syntax.html#%28part._main-and-test%29
(provide
create-pattern-with-diacritics
create-regexp-split-match
regexp-normalize-match*
regexp-normalize-split)
(require
;; (prefix-in com: "common.rkt")
"notes.rkt" ;; is used indeed
"notes-reader.rkt"
ansi-color
;; for string-replace
racket/string
)
(define diacritic-map
(hash "a" "[aáäàâæ]"
"c" "[cčç]"
"d" "[dď]"
"e" "[eéèêë]"
"i" "[iíîï]"
"l" "[lĺľ]"
"n" "[nň]"
"o" "[oóôöœ]"
"r" "[rŕř]"
"s" "[sš]"
"t" "[tť]"
"u" "[uúûüù]"
"y" "[yý]"
"z" "[zž]"
"A" "[AÁÄÀÂÆ]"
"C" "[CČÇ]"
"D" "[DĎ]"
"E" "[EÉÈÊË]"
"I" "[IÍÎÏ]"
"L" "[LĹĽ]"
"N" "[NŇ]"
"O" "[OÓÔÖŒ]"
"R" "[RŔŘ]"
"S" "[SŠ]"
"T" "[TŤ]"
"U" "[UÚÛÜÙ]"
"Y" "[YÝ]"
"Z" "[ZŽ]"
"ß" "ß")) ; German sharp S
(define (string-normalize s)
;; Normalization Form C, Canonical Decomposition followed by Canonical
;; Composition:
;; Decompose characters and then recomposes them using canonical
;; equivalence. E.g., 'é' would first be split into 'e' and the combining
;; accent, and then recomposed back into 'é'.
;; Use this when you want to normalize characters to their composed forms
;; while still respecting canonical equivalence.
(string-normalize-nfc s))
(define (regexp-normalize-match* regex target-str)
;; (printf "[regexp-normalize-match*] regex: ~a\n" regex)
;; (printf "[regexp-normalize-match*] target-str : ~a\n" target-str)
(let* ((normalized-target (string-normalize target-str)))
;; (printf "[regexp-normalize-match*] normalized-target: ~a\n" normalized-target)
(regexp-match* regex normalized-target)))
(define (regexp-normalize-split regex target-str)
;; (printf "[regexp-normalize-split] regex: ~a\n" regex)
;; (printf "[regexp-normalize-split] target-str : ~a\n" target-str)
(let* ((normalized-target (string-normalize target-str)))
;; (printf "[regexp-normalize-split] normalized-target: ~a\n" normalized-target)
(regexp-split regex normalized-target)))
(define pattern-param (make-parameter ""))
(define filepaths-param (make-parameter ""))
(define filepath-param (make-parameter (list)))
(define case-sensitive "i")
(define case-insensitive "-i")
;; passed directly to the regexp as a flag for case-sensitivity-params flag
(define case-sensitivity-params (make-parameter case-sensitive))
(define case-sensitivity-params-help-text
(format "case-sensitive `~a`~a or case-insensitive `~a`~a search."
case-sensitive
(if (equal? case-sensitive (case-sensitivity-params))
" (default)" "")
case-insensitive
(if (equal? case-sensitive (case-sensitivity-params))
"" " (default)")))
(define colorize-matches-param (make-parameter #t))
(define colorize-matches-param-help-text
"If omitted the result is colorized")
(command-line
#:program "search-notes"
#:usage-help
"Search in note-file(s) for a pattern. Return note-block(s).
E.g.:
racket main.rkt -p title
racket main.rkt -n -p title
racket main.rkt -np title
racket main.rkt -p rackjure
racket main.rkt -e ./main.rkt -p \"\\bfile\\b\" # \\b match word boundaries
racket main.rkt -e ./main.rkt -p [[:blank:]]install([[:cntrl:]]|[[:blank:]])
racket main.rkt -e ./main.rkt -p \"[eeeee]\"
racket main.rkt -e ./main.rkt -p \"[eéèêë]\"
racket main.rkt -e \"/home/bost/der/search-notes/main.rkt /home/bost/der/search-notes/README.md\" -p subdir
"
#:multi
[("-f" "--filepath")
FILEPATH
"Exact filepath on the file system."
(filepath-param (cons FILEPATH (filepath-param)))]
#:once-each
;; see also .spacemacs definition
[("-e" "--exact-filepaths")
FILEPATHS
"String of exact filepaths on the file system, separated by spaces."
(filepaths-param FILEPATHS)]
[("-c" "--case-sensitivity-params") CS
(case-sensitivity-params-help-text)
(case-sensitivity-params CS)]
[("-n" "--no-colors")
(colorize-matches-param-help-text)
(colorize-matches-param #f)]
[("-p" "--pattern") NAME
"Search pattern"
(pattern-param NAME)]
;; no other arguments are accepted
#;#;#;#:args () (void))
(define-namespace-anchor a)
;; the expression must be evaluated in a namespace.
;; Thanks to https://stackoverflow.com/q/16266934 for a hint
(define namespace (namespace-anchor->namespace a))
;; This is the default location of the org-roam directory. See Makefile
(define dir (format "~a/org-roam/" (getenv "HOME")))
(define add-src-location-info #f)
;; TODO implement interleave
;; (interleave (repeat "a") [1 2 3])
;; =>("a" 1 "a" 2 "a" 3)
;; (require racket/list)
(define (interpose elem ls)
;; TODO implement tail-call version of `interpose`; see also string-join
(if (or (empty? (cdr ls)) (empty? ls))
ls
(append (list (car ls) elem) (interpose elem (cdr ls)))))
(define (colorize colorize-matches? display-fn matches patterns)
(match matches
[(list) (display-fn "")]
[(list l) (display-fn l)]
[_
(let ((txt (car matches))
(ptrn (car patterns)))
(display-fn txt)
(if colorize-matches?
;; b-red means 'bold and red'
(with-colors 'b-red (lambda () (color-display ptrn)))
(display ptrn))
(colorize colorize-matches? display-fn
(cdr matches) (cdr patterns)))]))
(define (create-pattern-with-diacritics pattern)
(string-append*
(map (lambda (char)
(hash-ref diacritic-map (string char) (string char)))
(string->list pattern))))
(define pattern-with-diacritics (create-pattern-with-diacritics (pattern-param)))
;; - For regexp vs. pregexp - the same must be used also in notes.rkt;
;; - In contrary to the regexp-defining string in the notes.rkt no '.*' must
;; be used.
(define (create-regexp-split-match case-sensitivity diacritic-pattern)
;; (printf "case-sensitivity : ~a\n" case-sensitivity)
;; (printf "diacritic-pattern : ~a\n" diacritic-pattern)
;; (printf "pattern : ~a\n" pattern)
(let* [
(rgx (pregexp (format "(?~a:~a)"
case-sensitivity diacritic-pattern)))]
;; (printf "diacritic-pattern : ~a\n" diacritic-pattern)
;; (printf "rgx : ~a\n" rgx)
rgx))
(define regexp-split-match (create-regexp-split-match
(case-sensitivity-params)
pattern-with-diacritics))
(define colorize-matches? (colorize-matches-param))
(define display-fn (if colorize-matches? color-display display))
((compose
(lambda (_) (display ""))
(curry map
(lambda (all-file-strings)
;; (printf "all-file-strings : ~a\n" all-file-strings)
(let [(relevant-file-strings (cdr all-file-strings))]
;; (printf "relevant-file-strings : ~a\n" relevant-file-strings)
;; (printf "(empty? relevant-file-strings) : ~a\n" (empty? relevant-file-strings))
(unless (empty? relevant-file-strings)
(let ((first-file-string (car all-file-strings))
(relevant-file-strings-joined
(string-join relevant-file-strings "\n")))
(if colorize-matches?
(with-colors 'magenta
(lambda ()
(color-displayln first-file-string)))
(displayln first-file-string))
(colorize colorize-matches?
display-fn
(regexp-normalize-split
regexp-split-match relevant-file-strings-joined)
(regexp-normalize-match*
regexp-split-match relevant-file-strings-joined))
(printf "\n\n")))
relevant-file-strings)))
;; (lambda (files) (printf "3. files:\n~a\n" files) files)
(curry map
(lambda (f)
(let ((strs (call-with-input-file f
(lambda (input-file)
;; (printf "1. input-file:\n~a\n" input-file)
(define expression
`(notes-syntax-parser
,@((compose
;; (lambda (p) (printf "13. ~a\n" p) p)
(curry cons pattern-with-diacritics)
;; (lambda (p) (printf "12. ~a\n" p) p)
(curry cons (case-sensitivity-params))
;; (lambda (p) (printf "11. ~a\n" p) p)
(curry cons (colorize-matches-param))
;; (lambda (p) (printf "10. ~a\n" p) p)
)
(parse-notes add-src-location-info
input-file))))
;; (printf "1. expression:\n~a\n" expression)
;; (printf "1. namespace:\n~a\n" namespace)
(eval expression namespace)))))
(if (empty? strs)
(list f)
(list f (string-join strs "\n\n"))))))
;; (lambda (files) (printf "2. files:\n~a\n" files) files)
remove-duplicates
;; environment variable in the path is expanded by the shell
(curry map (compose
;; - Unix and Mac OS: convert by decoding path’s byte-string
;; encoding using the current locale
;; - Windows: convert by using UTF-8
;; path->string
;; convert by using a UTF-8 encoding
some-system-path->string
;; handle up- or same-directory indicators
simple-form-path
;; convert tilda '~'
expand-user-path))
;; (lambda (files) (printf "1. files:\n~a\n" files) files)
(curry append (filepath-param))
;; (lambda (files) (printf "0. files:\n~a\n" files) files)
string-split)
(filepaths-param)))