emacs: Use result text properties for search result iteration

This simplifies the traversal of regions of results and eliminates the
need for save-excursions (which tend to get in the way of maintaining
point when we make changes to the buffer).  It also fixes some strange
corner cases in the old line-based code where results that bordered
the region but were not included in it could be affected by region
commands.  Coincidentally, this also essentially enables multi-line
search result formats; the only remaining non-multi-line-capable
functions are notmuch-search-{next,previous}-thread, which are only
used for interactive navigation.
This commit is contained in:
Austin Clements 2012-07-21 13:37:08 -04:00 committed by David Bremner
parent 2a91f636d8
commit 7ba5c86399

View file

@ -69,7 +69,13 @@
date, count, authors, subject, tags date, count, authors, subject, tags
For example: For example:
(setq notmuch-search-result-format \(\(\"authors\" . \"%-40s\"\) (setq notmuch-search-result-format \(\(\"authors\" . \"%-40s\"\)
\(\"subject\" . \"%s\"\)\)\)" \(\"subject\" . \"%s\"\)\)\)
Line breaks are permitted in format strings (though this is
currently experimental). Note that a line break at the end of an
\"authors\" field will get elided if the authors list is long;
place it instead at the beginning of the following field. To
enter a line break when setting this variable with setq, use \\n.
To enter a line break in customize, press \\[quoted-insert] C-j."
:type '(alist :key-type (string) :value-type (string)) :type '(alist :key-type (string) :value-type (string))
:group 'notmuch-search) :group 'notmuch-search)
@ -427,17 +433,40 @@ returns nil"
(next-single-property-change (or pos (point)) 'notmuch-search-result (next-single-property-change (or pos (point)) 'notmuch-search-result
nil (point-max)))) nil (point-max))))
(defun notmuch-search-foreach-result (beg end function)
"Invoke FUNCTION for each result between BEG and END.
FUNCTION should take one argument. It will be applied to the
character position of the beginning of each result that overlaps
the region between points BEG and END. As a special case, if (=
BEG END), FUNCTION will be applied to the result containing point
BEG."
(lexical-let ((pos (notmuch-search-result-beginning beg))
;; End must be a marker in case function changes the
;; text.
(end (copy-marker end))
;; Make sure we examine at least one result, even if
;; (= beg end).
(first t))
;; We have to be careful if the region extends beyond the results.
;; In this case, pos could be null or there could be no result at
;; pos.
(while (and pos (or (< pos end) first))
(when (notmuch-search-get-result pos)
(funcall function pos))
(setq pos (notmuch-search-result-end pos)
first nil))))
;; Unindent the function argument of notmuch-search-foreach-result so
;; the indentation of callers doesn't get out of hand.
(put 'notmuch-search-foreach-result 'lisp-indent-function 2)
(defun notmuch-search-properties-in-region (property beg end) (defun notmuch-search-properties-in-region (property beg end)
(save-excursion (let (output)
(let ((output nil) (notmuch-search-foreach-result beg end
(last-line (line-number-at-pos end)) (lambda (pos)
(max-line (- (line-number-at-pos (point-max)) 2))) (push (get-text-property pos property) output)))
(goto-char beg) output))
(beginning-of-line)
(while (<= (line-number-at-pos) (min last-line max-line))
(setq output (cons (get-text-property (point) property) output))
(forward-line 1))
output)))
(defun notmuch-search-find-thread-id () (defun notmuch-search-find-thread-id ()
"Return the thread for the current thread" "Return the thread for the current thread"
@ -517,28 +546,21 @@ and will also appear in a buffer named \"*Notmuch errors*\"."
(plist-get (notmuch-search-get-result pos) :tags)) (plist-get (notmuch-search-get-result pos) :tags))
(defun notmuch-search-get-tags-region (beg end) (defun notmuch-search-get-tags-region (beg end)
(save-excursion (let (output)
(let ((output nil) (notmuch-search-foreach-result beg end
(last-line (line-number-at-pos end)) (lambda (pos)
(max-line (- (line-number-at-pos (point-max)) 2))) (setq output (append output (notmuch-search-get-tags pos)))))
(goto-char beg) output))
(while (<= (line-number-at-pos) (min last-line max-line))
(setq output (append output (notmuch-search-get-tags)))
(forward-line 1))
output)))
(defun notmuch-search-tag-region (beg end &optional tag-changes) (defun notmuch-search-tag-region (beg end &optional tag-changes)
"Change tags for threads in the given region." "Change tags for threads in the given region."
(let ((search-string (notmuch-search-find-thread-id-region-search beg end))) (let ((search-string (notmuch-search-find-thread-id-region-search beg end)))
(setq tag-changes (funcall 'notmuch-tag search-string tag-changes)) (setq tag-changes (funcall 'notmuch-tag search-string tag-changes))
(save-excursion (notmuch-search-foreach-result beg end
(let ((last-line (line-number-at-pos end)) (lambda (pos)
(max-line (- (line-number-at-pos (point-max)) 2))) (notmuch-search-set-tags
(goto-char beg) (notmuch-update-tags (notmuch-search-get-tags pos) tag-changes)
(while (<= (line-number-at-pos) (min last-line max-line)) pos)))))
(notmuch-search-set-tags
(notmuch-update-tags (notmuch-search-get-tags) tag-changes))
(forward-line))))))
(defun notmuch-search-tag (&optional tag-changes) (defun notmuch-search-tag (&optional tag-changes)
"Change tags for the currently selected thread or region. "Change tags for the currently selected thread or region.