-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.rkt
206 lines (178 loc) · 7.34 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
#lang racket
;; TODO try #lang hacket - haskell + racket
;; https://lexi-lambda.github.io/hackett/index.html
(module+ test
(require rackunit
racket/match))
;; 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
;; Code here
(module+ test
;; Any code in this `test` submodule runs when this file is run using DrRacket
;; or with `raco test`. The code here does not run when this file is
;; required by another module.
(check-equal? (+ 2 2) 4))
(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
(require
;; (prefix-in com: "common.rkt")
"notes.rkt" ;; is used indeed
"notes-reader.rkt"
racket/runtime-path #| for define-runtime-path |#
ansi-color)
(define pattern-param (make-parameter ""))
(define matching-files-param (make-parameter ""))
(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 -f shells -p title
racket main.rkt -fp shells title
racket main.rkt -n f shells -p title
racket main.rkt -nfp shells title
racket main.rkt -f \"shells|linux\" -p title
"
#:once-each
;; see also .spacemacs definition
[("-f" "--files") REGEXP
"Regexp matching a list of file-names in the org-roam
directory to search in."
(matching-files-param REGEXP)]
[("-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))
(define-runtime-path runtime-dir
(build-path
;; 1.
;; (find-user-pkgs-dir) ;; can't be found even after (require setup/dirs)
;; 2. this is not needed:
;; (find-system-path 'addon-dir) (version)
;; 3. only this is needed:
"notes"))
(define dir (path->string runtime-dir))
(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?
(with-colors 'red (lambda () (color-display ptrn)))
(display ptrn))
(colorize colorize-matches? display-fn
(cdr matches) (cdr patterns)))]))
(define regexp-split-match
(regexp (format "(?~a:~a)" (case-sensitivity-params) (pattern-param))))
(define colorize-matches? (colorize-matches-param))
(define display-fn (if colorize-matches? color-display display))
((compose
(lambda (_) (display ""))
(curry map
(lambda (all-file-strings)
(let ((relevant-file-strings (cdr all-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 'white
(lambda ()
(color-displayln first-file-string)))
(displayln first-file-string))
(colorize colorize-matches?
display-fn
(regexp-split regexp-split-match
relevant-file-strings-joined)
(regexp-match* regexp-split-match
relevant-file-strings-joined))
(printf "\n\n")))
relevant-file-strings)))
(curry map
(lambda (f)
(let ((strs (call-with-input-file f
(lambda (input-file)
(define expression
`(notes
,@((compose
(curry cons (pattern-param))
(curry cons (case-sensitivity-params))
(curry cons (colorize-matches-param)))
(parse-notes add-src-location-info
input-file))))
(eval expression namespace)))))
(if (empty? strs)
(list f)
(list f (string-join strs "\n\n"))))))
(lambda (predicate-filter-fun)
"Return a list of files forming the search space."
(let ([all-files (for/list ([f (in-directory dir)])
(path->string f))])
(filter predicate-filter-fun all-files)))
(lambda (regexp)
"Return a predicate function."
(lambda (f)
"Always exclude 'notes.scrbl' from the search space."
(if (equal? f (string-append dir "notes.scrbl"))
#f
(regexp-match (format ".*(~a).*\\.(org|scrbl)" regexp) f)))))
(matching-files-param)))