-;;; notmuch.el --- run notmuch within emacs
+;;; notmuch.el --- run notmuch within emacs -*- lexical-binding: t -*-
;;
;; Copyright © Carl Worth
;;
;;; Code:
-(eval-when-compile (require 'cl-lib))
-
(require 'mm-view)
(require 'message)
+(require 'hl-line)
+
(require 'notmuch-lib)
(require 'notmuch-tag)
(require 'notmuch-show)
(require 'notmuch-message)
(require 'notmuch-parser)
+;;; Options
+
(defcustom notmuch-search-result-format
`(("date" . "%12s ")
("count" . "%-7s ")
("authors" . "%-20s ")
("subject" . "%s ")
("tags" . "(%s)"))
- "Search result formatting. Supported fields are:
- date, count, authors, subject, tags
-For example:
- (setq notmuch-search-result-format \(\(\"authors\" . \"%-40s\"\)
- \(\"subject\" . \"%s\"\)\)\)
+ "Search result formatting.
+
+List of pairs of (field . format-string). Supported field
+strings are: \"date\", \"count\", \"authors\", \"subject\",
+\"tags\". It is also supported to pass a function in place of a
+field name. In this case the function is passed the thread
+object (plist) and format string.
+
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
+ (choice
+ (const :tag "Date" "date")
+ (const :tag "Count" "count")
+ (const :tag "Authors" "authors")
+ (const :tag "Subject" "subject")
+ (const :tag "Tags" "tags")
+ function)
+ :value-type (string :tag "Format"))
:group 'notmuch-search)
;; The name of this variable `notmuch-init-file' is consistent with the
:type 'file
:group 'notmuch)
-(defvar notmuch-query-history nil
- "Variable to store minibuffer history for notmuch queries.")
+(defcustom notmuch-search-hook '(notmuch-hl-line-mode)
+ "List of functions to call when notmuch displays the search results."
+ :type 'hook
+ :options '(notmuch-hl-line-mode)
+ :group 'notmuch-search
+ :group 'notmuch-hooks)
+
+;;; Mime Utilities
(defun notmuch-foreach-mime-part (function mm-handle)
(cond ((stringp (car mm-handle))
(mm-save-part p))))
mm-handle))
-(require 'hl-line)
-
-(defun notmuch-hl-line-mode ()
- (prog1 (hl-line-mode)
- (when hl-line-overlay
- (overlay-put hl-line-overlay 'priority 1))))
-
-(defcustom notmuch-search-hook '(notmuch-hl-line-mode)
- "List of functions to call when notmuch displays the search results."
- :type 'hook
- :options '(notmuch-hl-line-mode)
- :group 'notmuch-search
- :group 'notmuch-hooks)
+;;; Keymap
(defvar notmuch-search-mode-map
(let ((map (make-sparse-keymap)))
(set-keymap-parent map notmuch-common-keymap)
(define-key map "x" 'notmuch-bury-or-kill-this-buffer)
- (define-key map (kbd "<DEL>") 'notmuch-search-scroll-down)
+ (define-key map (kbd "DEL") 'notmuch-search-scroll-down)
(define-key map "b" 'notmuch-search-scroll-down)
(define-key map " " 'notmuch-search-scroll-up)
(define-key map "<" 'notmuch-search-first-thread)
(define-key map "c" 'notmuch-search-stash-map)
(define-key map "t" 'notmuch-search-filter-by-tag)
(define-key map "l" 'notmuch-search-filter)
+ (define-key map "E" 'notmuch-search-edit-search)
(define-key map [mouse-1] 'notmuch-search-show-thread)
(define-key map "k" 'notmuch-tag-jump)
(define-key map "*" 'notmuch-search-tag-all)
map)
"Keymap for \"notmuch search\" buffers.")
+;;; Internal Variables
+
+(defvar notmuch-query-history nil
+ "Variable to store minibuffer history for notmuch queries.")
+
+(defvar-local notmuch-search-query-string nil)
+(defvar-local notmuch-search-target-thread nil)
+(defvar-local notmuch-search-target-line nil)
+
+;;; Stashing
+
(defvar notmuch-search-stash-map
(let ((map (make-sparse-keymap)))
(define-key map "i" 'notmuch-search-stash-thread-id)
(defun notmuch-stash-query ()
"Copy current query to kill-ring."
(interactive)
- (notmuch-common-do-stash (notmuch-search-get-query)))
+ (notmuch-common-do-stash notmuch-search-query-string))
-(defvar notmuch-search-query-string)
-(defvar notmuch-search-target-thread)
-(defvar notmuch-search-target-line)
-
-(defvar notmuch-search-disjunctive-regexp "\\<[oO][rR]\\>")
+;;; Movement
(defun notmuch-search-scroll-up ()
"Move forward through search results by one window's worth."
(interactive)
(goto-char (point-min)))
+;;; Faces
+
(defface notmuch-message-summary-face
`((((class color) (background light))
,@(and (>= emacs-major-version 27) '(:extend t))
:group 'notmuch-search
:group 'notmuch-faces)
+;;; Mode
+
(define-derived-mode notmuch-search-mode fundamental-mode "notmuch-search"
"Major mode displaying results of a notmuch search.
Complete list of currently available key bindings:
\\{notmuch-search-mode-map}"
- (make-local-variable 'notmuch-search-query-string)
- (make-local-variable 'notmuch-search-oldest-first)
- (make-local-variable 'notmuch-search-target-thread)
- (make-local-variable 'notmuch-search-target-line)
(setq notmuch-buffer-refresh-function #'notmuch-search-refresh-view)
(setq-local scroll-preserve-screen-position t)
(add-to-invisibility-spec (cons 'ellipsis t))
(setq imenu-extract-index-name-function
#'notmuch-search-imenu-extract-index-name-function))
+;;; Search Results
+
(defun notmuch-search-get-result (&optional pos)
"Return the result object for the thread at POS (or point).
(defun notmuch-search-find-stable-query ()
"Return the stable queries for the current thread.
-This returns a list (MATCHED-QUERY UNMATCHED-QUERY) for the
+Return a list (MATCHED-QUERY UNMATCHED-QUERY) for the
matched and unmatched messages in the current thread."
(plist-get (notmuch-search-get-result) :query))
With a prefix argument, invert the default value of
`notmuch-show-only-matching-messages' when displaying the
-thread."
+thread.
+
+Return non-nil on success."
(interactive "P")
- (let ((thread-id (notmuch-search-find-thread-id))
- (subject (notmuch-search-find-subject)))
- (if (> (length thread-id) 0)
+ (let ((thread-id (notmuch-search-find-thread-id)))
+ (if thread-id
(notmuch-show thread-id
elide-toggle
(current-buffer)
notmuch-search-query-string
;; Name the buffer based on the subject.
- (concat "*"
- (truncate-string-to-width subject 30 nil nil t)
- "*"))
- (message "End of search results."))))
+ (format "*%s*" (truncate-string-to-width
+ (notmuch-search-find-subject)
+ 30 nil nil t)))
+ (message "End of search results.")
+ nil)))
(defun notmuch-tree-from-search-current-query ()
- "Call notmuch tree with the current query."
+ "Tree view of current query."
(interactive)
(notmuch-tree notmuch-search-query-string))
(defun notmuch-unthreaded-from-search-current-query ()
- "Call notmuch tree with the current query."
+ "Unthreaded view of current query."
(interactive)
(notmuch-unthreaded notmuch-search-query-string))
(defun notmuch-search-reply-to-thread (&optional prompt-for-sender)
"Begin composing a reply-all to the entire current thread in a new buffer."
(interactive "P")
- (let ((message-id (notmuch-search-find-thread-id)))
- (notmuch-mua-new-reply message-id prompt-for-sender t)))
+ (notmuch-mua-new-reply (notmuch-search-find-thread-id)
+ prompt-for-sender t))
(defun notmuch-search-reply-to-thread-sender (&optional prompt-for-sender)
"Begin composing a reply to the entire current thread in a new buffer."
(interactive "P")
- (let ((message-id (notmuch-search-find-thread-id)))
- (notmuch-mua-new-reply message-id prompt-for-sender nil)))
+ (notmuch-mua-new-reply (notmuch-search-find-thread-id)
+ prompt-for-sender nil))
+
+;;; Tags
(defun notmuch-search-set-tags (tags &optional pos)
- (let ((new-result (plist-put (notmuch-search-get-result pos) :tags tags)))
- (notmuch-search-update-result new-result pos)))
+ (notmuch-search-update-result
+ (plist-put (notmuch-search-get-result pos) :tags tags)
+ pos))
(defun notmuch-search-get-tags (&optional pos)
(plist-get (notmuch-search-get-result pos) :tags))
(notmuch-search-foreach-result beg end
(lambda (pos)
(setq output (append output (notmuch-search-get-tags pos)))))
- output))
+ (delete-dups output)))
(defun notmuch-search-interactive-tag-changes (&optional initial-input)
"Prompt for tag changes for the current thread or region.
-Returns (TAG-CHANGES REGION-BEGIN REGION-END)."
+Return (TAG-CHANGES REGION-BEGIN REGION-END)."
(pcase-let ((`(,beg ,end) (notmuch-interactive-region)))
(list (notmuch-read-tag-changes (notmuch-search-get-tags-region beg end)
(if (= beg end) "Tag thread" "Tag region")
(when (eq beg end)
(notmuch-search-next-thread)))
+;;; Search Results
+
(defun notmuch-search-update-result (result &optional pos)
"Replace the result object of the thread at POS (or point) by
RESULT and redraw it.
(min init-point (- new-end 1)))))
(goto-char new-point)))))
-(defun notmuch-search-process-sentinel (proc msg)
+(defun notmuch-search-process-sentinel (proc _msg)
"Add a message to let user know when \"notmuch search\" exits."
(let ((buffer (process-buffer proc))
(status (process-status proc))
(throw 'return nil))
(when (and atbob
(not (string= notmuch-search-target-thread "found")))
- (set 'never-found-target-thread t)))))
+ (setq never-found-target-thread t)))))
(when (and never-found-target-thread
notmuch-search-target-line)
(goto-char (point-min))
(setq invisible-string (notmuch-search-author-propertize invisible-string)))
;; If there is any invisible text, add it as a tooltip to the
;; visible text.
- (unless (string= invisible-string "")
+ (unless (string-empty-p invisible-string)
(setq visible-string
(propertize visible-string
'help-echo (concat "..." invisible-string))))
;; Insert the visible and, if present, invisible author strings.
(insert visible-string)
- (unless (string= invisible-string "")
+ (unless (string-empty-p invisible-string)
(let ((start (point))
overlay)
(insert invisible-string)
(setq overlay (make-overlay start (point)))
+ (overlay-put overlay 'evaporate t)
(overlay-put overlay 'invisible 'ellipsis)
(overlay-put overlay 'isearch-open-invisible #'delete-overlay)))
(insert padding))))
(defun notmuch-search-insert-field (field format-string result)
- (cond
- ((string-equal field "date")
- (insert (propertize (format format-string (plist-get result :date_relative))
- 'face 'notmuch-search-date)))
- ((string-equal field "count")
- (insert (propertize (format format-string
- (format "[%s/%s]" (plist-get result :matched)
- (plist-get result :total)))
- 'face 'notmuch-search-count)))
- ((string-equal field "subject")
- (insert (propertize (format format-string
- (notmuch-sanitize (plist-get result :subject)))
- 'face 'notmuch-search-subject)))
- ((string-equal field "authors")
- (notmuch-search-insert-authors
- format-string (notmuch-sanitize (plist-get result :authors))))
- ((string-equal field "tags")
- (let ((tags (plist-get result :tags))
- (orig-tags (plist-get result :orig-tags)))
- (insert (format format-string (notmuch-tag-format-tags tags orig-tags)))))))
+ (pcase field
+ ((pred functionp)
+ (insert (funcall field format-string result)))
+ ("date"
+ (insert (propertize (format format-string (plist-get result :date_relative))
+ 'face 'notmuch-search-date)))
+ ("count"
+ (insert (propertize (format format-string
+ (format "[%s/%s]" (plist-get result :matched)
+ (plist-get result :total)))
+ 'face 'notmuch-search-count)))
+ ("subject"
+ (insert (propertize (format format-string
+ (notmuch-sanitize (plist-get result :subject)))
+ 'face 'notmuch-search-subject)))
+ ("authors"
+ (notmuch-search-insert-authors format-string
+ (notmuch-sanitize (plist-get result :authors))))
+ ("tags"
+ (let ((tags (plist-get result :tags))
+ (orig-tags (plist-get result :orig-tags)))
+ (insert (format format-string (notmuch-tag-format-tags tags orig-tags)))))))
(defun notmuch-search-show-result (result pos)
"Insert RESULT at POS."
(setq notmuch-search-target-thread "found")
(goto-char pos))))
+(defvar-local notmuch--search-hook-run nil
+ "Flag used to ensure the notmuch-search-hook is only run once per buffer")
+
+(defun notmuch--search-hook-wrapper ()
+ (unless notmuch--search-hook-run
+ (setq notmuch--search-hook-run t)
+ (run-hooks 'notmuch-search-hook)))
+
(defun notmuch-search-process-filter (proc string)
"Process and filter the output of \"notmuch search\"."
(let ((results-buf (process-buffer proc))
(parse-buf (process-get proc 'parse-buf))
- (inhibit-read-only t)
- done)
+ (inhibit-read-only t))
(when (buffer-live-p results-buf)
(with-current-buffer parse-buf
;; Insert new data
(goto-char (point-max))
(insert string))
(notmuch-sexp-parse-partial-list 'notmuch-search-append-result
- results-buf)))))
+ results-buf))
+ (with-current-buffer results-buf
+ (notmuch--search-hook-wrapper)))))
+
+;;; Commands (and some helper functions used by them)
(defun notmuch-search-tag-all (tag-changes)
"Add/remove tags from all messages in current search buffer.
(notmuch-search-get-tags-region (point-min) (point-max)) "Tag all")))
(notmuch-search-tag tag-changes (point-min) (point-max) t))
-(defun notmuch-search-buffer-title (query)
+(defcustom notmuch-search-buffer-name-format "*notmuch-%t-%s*"
+ "Format for the name of search results buffers.
+
+In this spec, %s will be replaced by a description of the search
+query and %t by its type (search, tree or unthreaded). The
+buffer name is formatted using `format-spec': see its docstring
+for additional parameters for the s and t format specifiers.
+
+See also `notmuch-saved-search-buffer-name-format'"
+ :type 'string
+ :group 'notmuch-search)
+
+(defcustom notmuch-saved-search-buffer-name-format "*notmuch-saved-%t-%s*"
+ "Format for the name of search results buffers for saved searches.
+
+In this spec, %s will be replaced by the saved search name and %t
+by its type (search, tree or unthreaded). The buffer name is
+formatted using `format-spec': see its docstring for additional
+parameters for the s and t format specifiers.
+
+See also `notmuch-search-buffer-name-format'"
+ :type 'string
+ :group 'notmuch-search)
+
+(defun notmuch-search-format-buffer-name (query type saved)
+ "Compose a buffer name for the given QUERY, TYPE (search, tree,
+unthreaded) and whether it's SAVED (t or nil)."
+ (let ((fmt (if saved
+ notmuch-saved-search-buffer-name-format
+ notmuch-search-buffer-name-format)))
+ (format-spec fmt `((?t . ,(or type "search")) (?s . ,query)))))
+
+(defun notmuch-search-buffer-title (query &optional type)
"Returns the title for a buffer with notmuch search results."
(let* ((saved-search
(let (longest
do (setq longest tuple))
longest))
(saved-search-name (notmuch-saved-search-get saved-search :name))
+ (saved-search-type (notmuch-saved-search-get saved-search :search-type))
(saved-search-query (notmuch-saved-search-get saved-search :query)))
(cond ((and saved-search (equal saved-search-query query))
;; Query is the same as saved search (ignoring case)
- (concat "*notmuch-saved-search-" saved-search-name "*"))
+ (notmuch-search-format-buffer-name saved-search-name
+ saved-search-type
+ t))
(saved-search
- (concat "*notmuch-search-"
- (replace-regexp-in-string
- (concat "^" (regexp-quote saved-search-query))
- (concat "[ " saved-search-name " ]")
- query)
- "*"))
- (t
- (concat "*notmuch-search-" query "*")))))
+ (let ((query (replace-regexp-in-string
+ (concat "^" (regexp-quote saved-search-query))
+ (concat "[ " saved-search-name " ]")
+ query)))
+ (notmuch-search-format-buffer-name query saved-search-type t)))
+ (t (notmuch-search-format-buffer-name query type nil)))))
(defun notmuch-read-query (prompt)
"Read a notmuch-query from the minibuffer with completion.
PROMPT is the string to prompt with."
- (let*
- ((all-tags
- (mapcar (lambda (tag) (notmuch-escape-boolean-term tag))
- (process-lines notmuch-command "search" "--output=tags" "*")))
- (completions
- (append (list "folder:" "path:" "thread:" "id:" "date:" "from:" "to:"
- "subject:" "attachment:")
- (mapcar (lambda (tag) (concat "tag:" tag)) all-tags)
- (mapcar (lambda (tag) (concat "is:" tag)) all-tags)
- (mapcar (lambda (mimetype) (concat "mimetype:" mimetype))
- (mailcap-mime-types)))))
- (let ((keymap (copy-keymap minibuffer-local-map))
- (current-query (cl-case major-mode
- (notmuch-search-mode (notmuch-search-get-query))
- (notmuch-show-mode (notmuch-show-get-query))
- (notmuch-tree-mode (notmuch-tree-get-query))))
- (minibuffer-completion-table
- (completion-table-dynamic
- (lambda (string)
- ;; generate a list of possible completions for the current input
- (cond
- ;; this ugly regexp is used to get the last word of the input
- ;; possibly preceded by a '('
- ((string-match "\\(^\\|.* (?\\)\\([^ ]*\\)$" string)
- (mapcar (lambda (compl)
- (concat (match-string-no-properties 1 string) compl))
- (all-completions (match-string-no-properties 2 string)
- completions)))
- (t (list string)))))))
- ;; this was simpler than convincing completing-read to accept spaces:
- (define-key keymap (kbd "TAB") 'minibuffer-complete)
- (let ((history-delete-duplicates t))
- (read-from-minibuffer prompt nil keymap nil
- 'notmuch-search-history current-query nil)))))
+ (let* ((all-tags
+ (mapcar (lambda (tag) (notmuch-escape-boolean-term tag))
+ (notmuch--process-lines notmuch-command "search" "--output=tags" "*")))
+ (completions
+ (append (list "folder:" "path:" "thread:" "id:" "date:" "from:" "to:"
+ "subject:" "attachment:")
+ (mapcar (lambda (tag) (concat "tag:" tag)) all-tags)
+ (mapcar (lambda (tag) (concat "is:" tag)) all-tags)
+ (mapcar (lambda (mimetype) (concat "mimetype:" mimetype))
+ (mailcap-mime-types))))
+ (keymap (copy-keymap minibuffer-local-map))
+ (current-query (cl-case major-mode
+ (notmuch-search-mode (notmuch-search-get-query))
+ (notmuch-show-mode (notmuch-show-get-query))
+ (notmuch-tree-mode (notmuch-tree-get-query))))
+ (minibuffer-completion-table
+ (completion-table-dynamic
+ (lambda (string)
+ ;; Generate a list of possible completions for the current input.
+ (cond
+ ;; This ugly regexp is used to get the last word of the input
+ ;; possibly preceded by a '('.
+ ((string-match "\\(^\\|.* (?\\)\\([^ ]*\\)$" string)
+ (mapcar (lambda (compl)
+ (concat (match-string-no-properties 1 string) compl))
+ (all-completions (match-string-no-properties 2 string)
+ completions)))
+ (t (list string)))))))
+ ;; This was simpler than convincing completing-read to accept spaces:
+ (define-key keymap (kbd "TAB") 'minibuffer-complete)
+ (let ((history-delete-duplicates t))
+ (read-from-minibuffer prompt nil keymap nil
+ 'notmuch-search-history current-query nil))))
(defun notmuch-search-get-query ()
"Return the current query in this search buffer."
(put 'notmuch-search 'notmuch-doc "Search for messages.")
;;;###autoload
-(defun notmuch-search (&optional query oldest-first target-thread target-line no-display)
+(defun notmuch-search (&optional query oldest-first target-thread target-line
+ no-display)
"Display threads matching QUERY in a notmuch-search buffer.
If QUERY is nil, it is read interactively from the minibuffer.
(if no-display
(set-buffer buffer)
(pop-to-buffer-same-window buffer))
- ;; avoid wiping out third party buffer-local variables in the case
- ;; where we're just refreshing or changing the sort order of an
- ;; existing search results buffer
- (unless (eq major-mode 'notmuch-search-mode)
- (notmuch-search-mode))
+ (notmuch-search-mode)
;; Don't track undo information for this buffer
- (set 'buffer-undo-list t)
- (set 'notmuch-search-query-string query)
- (set 'notmuch-search-oldest-first oldest-first)
- (set 'notmuch-search-target-thread target-thread)
- (set 'notmuch-search-target-line target-line)
+ (setq buffer-undo-list t)
+ (setq notmuch-search-query-string query)
+ (setq notmuch-search-oldest-first oldest-first)
+ (setq notmuch-search-target-thread target-thread)
+ (setq notmuch-search-target-line target-line)
(notmuch-tag-clear-cache)
- (let ((proc (get-buffer-process (current-buffer)))
- (inhibit-read-only t))
- (when proc
- (error "notmuch search process already running for query `%s'" query))
+ (when (get-buffer-process buffer)
+ (error "notmuch search process already running for query `%s'" query))
+ (let ((inhibit-read-only t))
(erase-buffer)
(goto-char (point-min))
(save-excursion
(let ((proc (notmuch-start-notmuch
"notmuch-search" buffer #'notmuch-search-process-sentinel
- "search" "--format=sexp" "--format-version=4"
+ "search" "--format=sexp" "--format-version=5"
(if oldest-first
"--sort=oldest-first"
"--sort=newest-first")
- query))
- ;; Use a scratch buffer to accumulate partial output.
- ;; This buffer will be killed by the sentinel, which
- ;; should be called no matter how the process dies.
- (parse-buf (generate-new-buffer " *notmuch search parse*")))
- (process-put proc 'parse-buf parse-buf)
+ query)))
+ ;; Use a scratch buffer to accumulate partial output.
+ ;; This buffer will be killed by the sentinel, which
+ ;; should be called no matter how the process dies.
+ (process-put proc 'parse-buf
+ (generate-new-buffer " *notmuch search parse*"))
(set-process-filter proc 'notmuch-search-process-filter)
- (set-process-query-on-exit-flag proc nil))))
- (run-hooks 'notmuch-search-hook)))
+ (set-process-query-on-exit-flag proc nil))))))
(defun notmuch-search-refresh-view ()
"Refresh the current view.
thread. Otherwise, point will be moved to attempt to be in the
same relative position within the new buffer."
(interactive)
- (let ((target-line (line-number-at-pos))
- (oldest-first notmuch-search-oldest-first)
- (target-thread (notmuch-search-find-thread-id 'bare))
- (query notmuch-search-query-string))
- ;; notmuch-search erases the current buffer.
- (notmuch-search query oldest-first target-thread target-line t)
- (goto-char (point-min))))
+ (notmuch-search notmuch-search-query-string
+ notmuch-search-oldest-first
+ (notmuch-search-find-thread-id 'bare)
+ (line-number-at-pos)
+ t)
+ (goto-char (point-min)))
(defun notmuch-search-toggle-order ()
"Toggle the current search order.
This command toggles the sort order for the current search. The
default sort order is defined by `notmuch-search-oldest-first'."
(interactive)
- (set 'notmuch-search-oldest-first (not notmuch-search-oldest-first))
+ (setq notmuch-search-oldest-first (not notmuch-search-oldest-first))
(notmuch-search-refresh-view))
(defun notmuch-group-disjunctive-query-string (query-string)
"Group query if it contains a complex expression.
-
-Enclose QUERY-STRING in parentheses if it matches
-`notmuch-search-disjunctive-regexp'."
- (if (string-match-p notmuch-search-disjunctive-regexp query-string)
+Enclose QUERY-STRING in parentheses if contains \"OR\" operators."
+ (if (string-match-p "\\<[oO][rR]\\>" query-string)
(concat "( " query-string " )")
query-string))
notmuch-search-oldest-first)))
(defun notmuch-search-filter-by-tag (tag)
- "Filter the current search results based on a single tag.
+ "Filter the current search results based on a single TAG.
-Runs a new search matching only messages that match both the
-current search results AND that are tagged with the given tag."
+Run a new search matching only messages that match the current
+search results and that are also tagged with the given TAG."
(interactive
(list (notmuch-select-tag-with-completion "Filter by tag: "
notmuch-search-query-string)))
(list (notmuch-select-tag-with-completion "Notmuch search tag: ")))
(notmuch-search (concat "tag:" tag)))
+(defun notmuch-search-edit-search (query)
+ "Edit the current search"
+ (interactive (list (read-from-minibuffer "Edit search: "
+ notmuch-search-query-string)))
+ (notmuch-search query notmuch-search-oldest-first))
+
;;;###autoload
(defun notmuch ()
"Run notmuch and display saved searches, known tags, etc."
(notmuch-hello))
(defun notmuch-interesting-buffer (b)
- "Is the current buffer of interest to a notmuch user?"
+ "Whether the current buffer's major-mode is a notmuch mode."
(with-current-buffer b
(memq major-mode '(notmuch-show-mode
notmuch-search-mode
(defun notmuch-cycle-notmuch-buffers ()
"Cycle through any existing notmuch buffers (search, show or hello).
-If the current buffer is the only notmuch buffer, bury it. If no
-notmuch buffers exist, run `notmuch'."
+If the current buffer is the only notmuch buffer, bury it.
+If no notmuch buffers exist, run `notmuch'."
(interactive)
(let (start first)
;; If the current buffer is a notmuch buffer, remember it and then
(pop-to-buffer-same-window first))
(notmuch))))
+;;; Integrations
+;;;; Hl-line Support
+
+(defun notmuch-hl-line-mode ()
+ (prog1 (hl-line-mode)
+ (when hl-line-overlay
+ (overlay-put hl-line-overlay 'priority 1))))
+
;;;; Imenu Support
(defun notmuch-search-imenu-prev-index-position-function ()
"Move point to previous message in notmuch-search buffer.
-This function is used as a value for
-`imenu-prev-index-position-function'."
+Used as`imenu-prev-index-position-function' in notmuch buffers."
(notmuch-search-previous-thread))
(defun notmuch-search-imenu-extract-index-name-function ()
"Return imenu name for line at point.
-This function is used as a value for
-`imenu-extract-index-name-function'. Point should be at the
-beginning of the line."
- (let ((subject (notmuch-search-find-subject))
- (author (notmuch-search-find-authors)))
- (format "%s (%s)" subject author)))
-
-(setq mail-user-agent 'notmuch-user-agent)
+Used as `imenu-extract-index-name-function' in notmuch buffers.
+Point should be at the beginning of the line."
+ (format "%s (%s)"
+ (notmuch-search-find-subject)
+ (notmuch-search-find-authors)))
+
+;;; _
(provide 'notmuch)