1 Return-Path: <tomi.ollila@iki.fi>
\r
2 X-Original-To: notmuch@notmuchmail.org
\r
3 Delivered-To: notmuch@notmuchmail.org
\r
4 Received: from localhost (localhost [127.0.0.1])
\r
5 by olra.theworths.org (Postfix) with ESMTP id 13AFF431FAF
\r
6 for <notmuch@notmuchmail.org>; Tue, 4 Jun 2013 12:41:11 -0700 (PDT)
\r
7 X-Virus-Scanned: Debian amavisd-new at olra.theworths.org
\r
11 X-Spam-Status: No, score=0 tagged_above=-999 required=5 tests=[none]
\r
13 Received: from olra.theworths.org ([127.0.0.1])
\r
14 by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)
\r
15 with ESMTP id FDEIQQ8cywXP for <notmuch@notmuchmail.org>;
\r
16 Tue, 4 Jun 2013 12:40:59 -0700 (PDT)
\r
17 Received: from guru.guru-group.fi (guru.guru-group.fi [46.183.73.34])
\r
18 by olra.theworths.org (Postfix) with ESMTP id B2045431FAE
\r
19 for <notmuch@notmuchmail.org>; Tue, 4 Jun 2013 12:40:58 -0700 (PDT)
\r
20 Received: from guru.guru-group.fi (localhost [IPv6:::1])
\r
21 by guru.guru-group.fi (Postfix) with ESMTP id 75800100030;
\r
22 Tue, 4 Jun 2013 22:40:54 +0300 (EEST)
\r
23 From: Tomi Ollila <tomi.ollila@iki.fi>
\r
24 To: Mark Walters <markwalters1009@gmail.com>, notmuch@notmuchmail.org
\r
25 Subject: Re: [PATCH] emacs: search: allow command line args as part of query
\r
26 In-Reply-To: <1370359319-2140-1-git-send-email-markwalters1009@gmail.com>
\r
27 References: <1370292776-24535-1-git-send-email-markwalters1009@gmail.com>
\r
28 <1370359319-2140-1-git-send-email-markwalters1009@gmail.com>
\r
29 User-Agent: Notmuch/0.15.2+172~g0a61aef (http://notmuchmail.org) Emacs/24.3.1
\r
30 (x86_64-unknown-linux-gnu)
\r
31 X-Face: HhBM'cA~<r"^Xv\KRN0P{vn'Y"Kd;zg_y3S[4)KSN~s?O\"QPoL
\r
32 $[Xv_BD:i/F$WiEWax}R(MPS`^UaptOGD`*/=@\1lKoVa9tnrg0TW?"r7aRtgk[F
\r
33 !)g;OY^,BjTbr)Np:%c_o'jj,Z
\r
34 Date: Tue, 04 Jun 2013 22:40:54 +0300
\r
35 Message-ID: <m28v2p7jg9.fsf@guru.guru-group.fi>
\r
37 Content-Type: text/plain
\r
38 X-BeenThere: notmuch@notmuchmail.org
\r
39 X-Mailman-Version: 2.1.13
\r
41 List-Id: "Use and development of the notmuch mail system."
\r
42 <notmuch.notmuchmail.org>
\r
43 List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,
\r
44 <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>
\r
45 List-Archive: <http://notmuchmail.org/pipermail/notmuch>
\r
46 List-Post: <mailto:notmuch@notmuchmail.org>
\r
47 List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>
\r
48 List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,
\r
49 <mailto:notmuch-request@notmuchmail.org?subject=subscribe>
\r
50 X-List-Received-Date: Tue, 04 Jun 2013 19:41:11 -0000
\r
52 On Tue, Jun 04 2013, Mark Walters <markwalters1009@gmail.com> wrote:
\r
54 > This allows command line arguments for notmuch-search to be part of
\r
55 > the query-string.
\r
57 Nice feature -- some comments below:
\r
59 > The string must be of the form
\r
60 > [:blank:]*--cli-arguments -- query. I hope this doesn't clash with
\r
62 One problem requiring trailing '--' noticed after reviewed -- if one
\r
63 forgets that the whole string is going to be query string.
\r
65 > xapian: I believe that queries shouldn't start with a "-".
\r
66 > The cli-arguments must be arguments in a whitelist of arguments: this
\r
67 > adds a slight maintenance burden but means we don't have to deal with
\r
68 > users who passed "--format=text" or other incompatible options.
\r
70 > Correctly parsed example queries are
\r
71 > --sort=oldest-first -- tag:inbox
\r
72 > --exclude=false -- from:fred
\r
74 > Some options (currently only sort-order) we parse in emacs, the rest
\r
75 > we just pass to the cli. In light testing it seems to work.
\r
77 > A full custom parser would be nicer but at least here we are only parsing
\r
78 > the non-query part of a string which is relatively simple: indeed we
\r
79 > already do that in the c code.
\r
81 > We could just implement the option for sort-order, but I thought for
\r
82 > interface consistency making all the sensible options (sort-order
\r
83 > exclude limit and offset) work was worth the extra hassle.
\r
86 > This is a slight change to
\r
87 > 1370292776-24535-1-git-send-email-markwalters1009@gmail.com The change
\r
88 > is that we add a whitelist of allowed cli options; other options are
\r
89 > removed and the user is warned (but the query is not aborted).
\r
91 > One other tiny change is that a query starting with "[[:blank:]]*-- "
\r
92 > is allowed: everything after the -- is part of the real qeury so if
\r
93 > any strange query is accidentally being misparsed the user can prefix
\r
94 > with "-- " and it will give the current behaviour.
\r
101 > emacs/notmuch-hello.el | 5 +++--
\r
102 > emacs/notmuch-lib.el | 44 ++++++++++++++++++++++++++++++++++++++++++++
\r
103 > emacs/notmuch.el | 36 +++++++++++++++++++++++++-----------
\r
104 > 3 files changed, 72 insertions(+), 13 deletions(-)
\r
106 > diff --git a/emacs/notmuch-hello.el b/emacs/notmuch-hello.el
\r
107 > index c1c6f4b..bcc1843 100644
\r
108 > --- a/emacs/notmuch-hello.el
\r
109 > +++ b/emacs/notmuch-hello.el
\r
110 > @@ -383,10 +383,11 @@ options will be handled as specified for
\r
111 > `notmuch-hello-insert-searches'."
\r
112 > (with-temp-buffer
\r
113 > (dolist (elem query-alist nil)
\r
114 > - (let ((count-query (if (consp (cdr elem))
\r
115 > + (let* ((full-count-query (if (consp (cdr elem))
\r
116 > ;; do we have a different query for the message count?
\r
120 > + (count-query (car (notmuch-parse-query full-count-query))))
\r
122 > (notmuch-hello-filtered-query count-query
\r
123 > (or (plist-get options :filter-count)
\r
124 > diff --git a/emacs/notmuch-lib.el b/emacs/notmuch-lib.el
\r
125 > index 28f78e0..65c489e 100644
\r
126 > --- a/emacs/notmuch-lib.el
\r
127 > +++ b/emacs/notmuch-lib.el
\r
128 > @@ -189,6 +189,50 @@ user-friendly queries."
\r
129 > "Return a query that matches the message with id ID."
\r
130 > (concat "id:" (notmuch-escape-boolean-term id)))
\r
132 > +(defun notmuch-search-parse-sort-order (args oldest-first)
\r
133 > + (dolist (arg args nil)
\r
134 > + (when (equal arg "--sort=oldest-first")
\r
135 > + (setq oldest-first t))
\r
136 > + (when (equal arg "--sort=newest-first")
\r
137 > + (setq oldest-first nil)))
\r
138 > + (setq args (delete "--sort=oldest-first" args))
\r
139 > + (setq args (delete "--sort=newest-first" args))
\r
140 > + (cons oldest-first args))
\r
142 If one gave --sort=oldest-first --sort=newest-first oldest-first is
\r
143 chosen instead of newest-first as both are removed from args -- that
\r
144 should be pretty easy to fix by putting deletes inside whens.
\r
146 > +(defvar notmuch-parse-option-whitelist
\r
147 > + '("^--sort=oldest-first$"
\r
148 > + "^--sort=newest-first$"
\r
149 > + "^--exclude=true$"
\r
150 > + "^--exclude=false$"
\r
151 > + "^--exclude=flag$"
\r
152 > + "^--limit=[0-9]*$"
\r
153 > + "^--offset=[0-9]*$"
\r
156 is zero numbers of numbers for --limit & --offset good ([0-9]*) ?
\r
158 > +(defun notmuch-parse-in-whitelist-p (arg)
\r
159 > + (let ((allowed nil))
\r
160 > + (dolist (opt notmuch-parse-option-whitelist nil)
\r
161 > + (setq allowed (or allowed (string-match-p opt arg))))
\r
164 > +(defun notmuch-parse-query (query)
\r
165 > + "Parse a query into a search and cli arguments
\r
167 > +Returns a list consisting of query followed by the cli-args (as a
\r
168 > +list). If the string does not have cli-args then this will be nil."
\r
170 > + (if (or (string-match "^[[:blank:]]*--.*? -- " query)
\r
171 > + (string-match "^[[:blank:]]*-- " query))
\r
172 > + (let ((actual-query (substring query (match-end 0)))
\r
173 > + (args (split-string (match-string 0 query) " " t)))
\r
175 Should the whitespace matching in locations above be consistent:
\r
176 like "^[[:blank:]]*--.*?[[:blank:]]--[[:blank:]]". Blank matches
\r
177 space & tab (according to http://www.gnu.org/software/emacs/manual/html_node/elisp/Char-Classes.html#Char-Classes )
\r
179 Hmm, learned a bit:
\r
181 (split-string " foo bar ") -> ("foo" "bar")
\r
182 (split-string " foo bar " " ") -> ("" "" "foo" "" "" "bar" "" "")
\r
183 (split-string " foo bar " " " t) -> ("foo" "bar")
\r
184 (split-string " foo bar " " +") -> ("" "foo" "bar" "")
\r
185 (split-string " foo bar " " +" t) -> ("foo" "bar")
\r
187 -> ... (args (split-string (match-string 0 query) "[[:blank:]]" t)))
\r
189 > + (message "Parsing query")
\r
190 > + (dolist (arg args nil)
\r
191 > + (unless (notmuch-parse-in-whitelist-p arg)
\r
192 > + (setq args (delete arg args))
\r
193 > + (message "Removing unknown option %s" arg)))
\r
195 And finally, I'd suggest it is an error to encounter unknown
\r
196 option and in that case operation is aborted.
\r
202 > + (cons actual-query args))
\r
203 > + ;; no cli arguments
\r
207 > (defun notmuch-common-do-stash (text)
\r
208 > diff --git a/emacs/notmuch.el b/emacs/notmuch.el
\r
209 > index 7994d74..6a4052e 100644
\r
210 > --- a/emacs/notmuch.el
\r
211 > +++ b/emacs/notmuch.el
\r
212 > @@ -255,6 +255,7 @@ For a mouse binding, return nil."
\r
213 > (notmuch-common-do-stash (notmuch-search-find-thread-id)))
\r
215 > (defvar notmuch-search-query-string)
\r
216 > +(defvar notmuch-search-query-args)
\r
217 > (defvar notmuch-search-target-thread)
\r
218 > (defvar notmuch-search-target-line)
\r
219 > (defvar notmuch-search-continuation)
\r
220 > @@ -409,6 +410,7 @@ Complete list of currently available key bindings:
\r
222 > (kill-all-local-variables)
\r
223 > (make-local-variable 'notmuch-search-query-string)
\r
224 > + (make-local-variable 'notmuch-search-query-args)
\r
225 > (make-local-variable 'notmuch-search-oldest-first)
\r
226 > (make-local-variable 'notmuch-search-target-thread)
\r
227 > (make-local-variable 'notmuch-search-target-line)
\r
228 > @@ -897,7 +899,7 @@ PROMPT is the string to prompt with."
\r
229 > 'notmuch-search-history nil nil)))))
\r
232 > -(defun notmuch-search (&optional query oldest-first target-thread target-line continuation)
\r
233 > +(defun notmuch-search (&optional query oldest-first target-thread target-line continuation cli-args)
\r
234 > "Run \"notmuch search\" with the given `query' and display results.
\r
236 > If `query' is nil, it is read interactively from the minibuffer.
\r
237 > @@ -909,13 +911,20 @@ Other optional parameters are used as follows:
\r
238 > target-line: The line number to move to if the target thread does not
\r
239 > appear in the search results."
\r
241 > - (let* ((query (or query (notmuch-read-query "Notmuch search: ")))
\r
242 > + (let* ((full-query (or query (notmuch-read-query "Notmuch search: ")))
\r
243 > + (parsed-query (notmuch-parse-query full-query))
\r
244 > + (query (car parsed-query))
\r
245 > + (cli-args (or cli-args (cdr parsed-query)))
\r
246 > + (combined-order-query (notmuch-search-parse-sort-order cli-args oldest-first))
\r
247 > + (oldest-first (car combined-order-query))
\r
248 > + (cli-args (cdr combined-order-query))
\r
249 > (buffer (get-buffer-create (notmuch-search-buffer-title query))))
\r
250 > (switch-to-buffer buffer)
\r
251 > (notmuch-search-mode)
\r
252 > ;; Don't track undo information for this buffer
\r
253 > (set 'buffer-undo-list t)
\r
254 > (set 'notmuch-search-query-string query)
\r
255 > + (set 'notmuch-search-query-args cli-args)
\r
256 > (set 'notmuch-search-oldest-first oldest-first)
\r
257 > (set 'notmuch-search-target-thread target-thread)
\r
258 > (set 'notmuch-search-target-line target-line)
\r
259 > @@ -928,13 +937,13 @@ Other optional parameters are used as follows:
\r
261 > (goto-char (point-min))
\r
263 > - (let ((proc (notmuch-start-notmuch
\r
264 > + (let ((proc (apply #'notmuch-start-notmuch
\r
265 > "notmuch-search" buffer #'notmuch-search-process-sentinel
\r
266 > "search" "--format=sexp" "--format-version=1"
\r
267 > - (if oldest-first
\r
268 > + (if notmuch-search-oldest-first
\r
269 > "--sort=oldest-first"
\r
270 > "--sort=newest-first")
\r
272 > + (append cli-args (list query))))
\r
273 > ;; Use a scratch buffer to accumulate partial output.
\r
274 > ;; This buffer will be killed by the sentinel, which
\r
275 > ;; should be called no matter how the process dies.
\r
276 > @@ -957,9 +966,10 @@ same relative position within the new buffer."
\r
277 > (oldest-first notmuch-search-oldest-first)
\r
278 > (target-thread (notmuch-search-find-thread-id 'bare))
\r
279 > (query notmuch-search-query-string)
\r
280 > - (continuation notmuch-search-continuation))
\r
281 > + (continuation notmuch-search-continuation)
\r
282 > + (cli-args notmuch-search-query-args))
\r
283 > (notmuch-kill-this-buffer)
\r
284 > - (notmuch-search query oldest-first target-thread target-line continuation)
\r
285 > + (notmuch-search query oldest-first target-thread target-line continuation cli-args)
\r
286 > (goto-char (point-min))))
\r
288 > (defcustom notmuch-poll-script nil
\r
289 > @@ -1024,18 +1034,22 @@ search."
\r
290 > (set 'notmuch-search-oldest-first (not notmuch-search-oldest-first))
\r
291 > (notmuch-search-refresh-view))
\r
293 > -(defun notmuch-search-filter (query)
\r
294 > +(defun notmuch-search-filter (full-query)
\r
295 > "Filter the current search results based on an additional query string.
\r
297 > Runs a new search matching only messages that match both the
\r
298 > current search results AND the additional query string provided."
\r
299 > (interactive (list (notmuch-read-query "Filter search: ")))
\r
300 > - (let ((grouped-query (if (string-match-p notmuch-search-disjunctive-regexp query)
\r
301 > + (let* ((parsed-query (notmuch-parse-query full-query))
\r
302 > + (query (car parsed-query))
\r
303 > + (grouped-query (if (string-match-p notmuch-search-disjunctive-regexp query)
\r
304 > (concat "( " query " )")
\r
307 > + (extra-cli-args (cdr parsed-query))
\r
308 > + (cli-args (append notmuch-search-query-args extra-cli-args)))
\r
309 > (notmuch-search (if (string= notmuch-search-query-string "*")
\r
311 > - (concat notmuch-search-query-string " and " grouped-query)) notmuch-search-oldest-first)))
\r
312 > + (concat notmuch-search-query-string " and " grouped-query)) notmuch-search-oldest-first nil nil nil cli-args)))
\r
314 > (defun notmuch-search-filter-by-tag (tag)
\r
315 > "Filter the current search results based on a single tag.
\r
319 > _______________________________________________
\r
320 > notmuch mailing list
\r
321 > notmuch@notmuchmail.org
\r
322 > http://notmuchmail.org/mailman/listinfo/notmuch
\r