]> git.notmuchmail.org Git - notmuch/blobdiff - emacs/notmuch.el
emacs: notmuch-search-refresh-view: reuse buffer
[notmuch] / emacs / notmuch.el
index 6564816f078659f23ab5f6a6c6fc51478aa695ae..079a3d1fdd1e9b7053d053f40b04e6607c4f335c 100644 (file)
@@ -1,4 +1,4 @@
-;; notmuch.el --- run notmuch within emacs
+;;; notmuch.el --- run notmuch within emacs
 ;;
 ;; Copyright © Carl Worth
 ;;
 ;; General Public License for more details.
 ;;
 ;; You should have received a copy of the GNU General Public License
-;; along with Notmuch.  If not, see <http://www.gnu.org/licenses/>.
+;; along with Notmuch.  If not, see <https://www.gnu.org/licenses/>.
 ;;
 ;; Authors: Carl Worth <cworth@cworth.org>
+;; Homepage: https://notmuchmail.org/
+
+;;; Commentary:
 
 ;; This is an emacs-based interface to the notmuch mail system.
 ;;
 ;; You will first need to have the notmuch program installed and have a
 ;; notmuch database built in order to use this. See
-;; http://notmuchmail.org for details.
+;; https://notmuchmail.org for details.
 ;;
 ;; To install this software, copy it to a directory that is on the
 ;; `load-path' variable within emacs (a good candidate is
@@ -45,7 +48,9 @@
 ;;
 ;; Have fun, and let us know if you have any comment, questions, or
 ;; kudos: Notmuch list <notmuch@notmuchmail.org> (subscription is not
-;; required, but is available from http://notmuchmail.org).
+;; required, but is available from https://notmuchmail.org).
+
+;;; Code:
 
 (eval-when-compile (require 'cl))
 (require 'mm-view)
 (require 'notmuch-message)
 (require 'notmuch-parser)
 
-(unless (require 'notmuch-version nil t)
-  (defconst notmuch-emacs-version "unknown"
-    "Placeholder variable when notmuch-version.el[c] is not available."))
-
 (defcustom notmuch-search-result-format
   `(("date" . "%12s ")
     ("count" . "%-7s ")
@@ -166,8 +167,9 @@ there will be called at other points of notmuch execution."
     (define-key map "o" 'notmuch-search-toggle-order)
     (define-key map "c" 'notmuch-search-stash-map)
     (define-key map "t" 'notmuch-search-filter-by-tag)
-    (define-key map "f" 'notmuch-search-filter)
+    (define-key map "l" 'notmuch-search-filter)
     (define-key map [mouse-1] 'notmuch-search-show-thread)
+    (define-key map "k" 'notmuch-tag-jump)
     (define-key map "*" 'notmuch-search-tag-all)
     (define-key map "a" 'notmuch-search-archive-thread)
     (define-key map "-" 'notmuch-search-remove-tag)
@@ -310,7 +312,27 @@ there will be called at other points of notmuch execution."
   :group 'notmuch-search
   :group 'notmuch-faces)
 
-(defun notmuch-search-mode ()
+(defface notmuch-search-flagged-face
+  '((t
+     (:weight bold)))
+  "Face used in search mode face for flagged threads.
+
+This face is the default value for the \"flagged\" tag in
+`notmuch-search-line-faces`."
+  :group 'notmuch-search
+  :group 'notmuch-faces)
+
+(defface notmuch-search-unread-face
+  '((t
+     (:foreground "blue")))
+  "Face used in search mode for unread threads.
+
+This face is the default value for the \"unread\" tag in
+`notmuch-search-line-faces`."
+  :group 'notmuch-search
+  :group 'notmuch-faces)
+
+(define-derived-mode notmuch-search-mode fundamental-mode "notmuch-search"
   "Major mode displaying results of a notmuch search.
 
 This buffer contains the results of a \"notmuch search\" of your
@@ -340,8 +362,6 @@ new, global search.
 Complete list of currently available key bindings:
 
 \\{notmuch-search-mode-map}"
-  (interactive)
-  (kill-all-local-variables)
   (make-local-variable 'notmuch-search-query-string)
   (make-local-variable 'notmuch-search-oldest-first)
   (make-local-variable 'notmuch-search-target-thread)
@@ -349,10 +369,7 @@ Complete list of currently available key bindings:
   (setq notmuch-buffer-refresh-function #'notmuch-search-refresh-view)
   (set (make-local-variable 'scroll-preserve-screen-position) t)
   (add-to-invisibility-spec (cons 'ellipsis t))
-  (use-local-map notmuch-search-mode-map)
   (setq truncate-lines t)
-  (setq major-mode 'notmuch-search-mode
-       mode-name "notmuch-search")
   (setq buffer-read-only t))
 
 (defun notmuch-search-get-result (&optional pos)
@@ -462,7 +479,11 @@ no messages in the region then return nil."
   (notmuch-search-properties-in-region :subject beg end))
 
 (defun notmuch-search-show-thread (&optional elide-toggle)
-  "Display the currently selected thread."
+  "Display the currently selected thread.
+
+With a prefix argument, invert the default value of
+`notmuch-show-only-matching-messages' when displaying the
+thread."
   (interactive "P")
   (let ((thread-id (notmuch-search-find-thread-id))
        (subject (notmuch-search-find-subject)))
@@ -649,9 +670,12 @@ of the result."
                  (goto-char (point-min))
                  (forward-line (1- notmuch-search-target-line)))))))))
 
-(defcustom notmuch-search-line-faces '(("unread" :weight bold)
-                                      ("flagged" :foreground "blue"))
-  "Tag/face mapping for line highlighting in notmuch-search.
+(defcustom notmuch-search-line-faces
+  '(("unread" 'notmuch-search-unread-face)
+    ("flagged" 'notmuch-search-flagged-face))
+  "Alist of tags to faces for line highlighting in notmuch-search.
+Each element looks like (TAG . FACE).
+A thread with TAG will have FACE applied.
 
 Here is an example of how to color search results based on tags.
  (the following text would be placed in your ~/.emacs file):
@@ -660,23 +684,24 @@ Here is an example of how to color search results based on tags.
                                    (\"deleted\" . (:foreground \"red\"
                                                  :background \"blue\"))))
 
-The attributes defined for matching tags are merged, with earlier
-attributes overriding later. A message having both \"deleted\"
-and \"unread\" tags with the above settings would have a green
-foreground and blue background."
+The FACE must be a face name (a symbol or string), a property
+list of face attributes, or a list of these.  The faces for
+matching tags are merged, with earlier attributes overriding
+later. A message having both \"deleted\" and \"unread\" tags with
+the above settings would have a green foreground and blue
+background."
   :type '(alist :key-type (string) :value-type (custom-face-edit))
   :group 'notmuch-search
   :group 'notmuch-faces)
 
 (defun notmuch-search-color-line (start end line-tag-list)
   "Colorize lines in `notmuch-show' based on tags."
-  (mapc (lambda (elem)
-         (let ((tag (car elem))
-               (attributes (cdr elem)))
-           (when (member tag line-tag-list)
-             (notmuch-apply-face nil attributes nil start end))))
-       ;; Reverse the list so earlier entries take precedence
-       (reverse notmuch-search-line-faces)))
+  ;; Reverse the list so earlier entries take precedence
+  (dolist (elem (reverse notmuch-search-line-faces))
+    (let ((tag (car elem))
+         (face (cdr elem)))
+      (when (member tag line-tag-list)
+       (notmuch-apply-face nil face nil start end)))))
 
 (defun notmuch-search-author-propertize (authors)
   "Split `authors' into matching and non-matching authors and
@@ -867,9 +892,10 @@ PROMPT is the string to prompt with."
                 (process-lines notmuch-command "search" "--output=tags" "*")))
        (completions
         (append (list "folder:" "path:" "thread:" "id:" "date:" "from:" "to:"
-                      "subject:" "attachment:" "mimetype:")
+                      "subject:" "attachment:")
                 (mapcar (lambda (tag) (concat "tag:" tag)) all-tags)
-                (mapcar (lambda (tag) (concat "is:" 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 (case major-mode
                           (notmuch-search-mode (notmuch-search-get-query))
@@ -900,7 +926,7 @@ PROMPT is the string to prompt with."
 
 ;;;###autoload
 (put 'notmuch-search 'notmuch-doc "Search for messages.")
-(defun notmuch-search (&optional query oldest-first target-thread target-line)
+(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.
@@ -911,6 +937,9 @@ Other optional parameters are used as follows:
                  current if it appears in the search results.
   TARGET-LINE: The line number to move to if the target thread does not
                appear in the search results.
+  NO-DISPLAY: Do not try to foreground the search results buffer. If it is
+              already foregrounded i.e. displayed in a window, this has no
+              effect, meaning the buffer will remain visible.
 
 When called interactively, this will prompt for a query and use
 the configured default sort order."
@@ -924,7 +953,9 @@ the configured default sort order."
 
   (let* ((query (or query (notmuch-read-query "Notmuch search: ")))
         (buffer (get-buffer-create (notmuch-search-buffer-title query))))
-    (switch-to-buffer buffer)
+    (if no-display
+       (set-buffer buffer)
+      (switch-to-buffer buffer))
     (notmuch-search-mode)
     ;; Don't track undo information for this buffer
     (set 'buffer-undo-list t)
@@ -960,17 +991,18 @@ the configured default sort order."
 (defun notmuch-search-refresh-view ()
   "Refresh the current view.
 
-Kills the current buffer and runs a new search with the same
+Erases the current buffer and runs a new search with the same
 query string as the current search. If the current thread is in
 the new search results, then point will be placed on the same
 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-bury-or-kill-this-buffer)
-    (notmuch-search query oldest-first target-thread target-line)
+    ;; notmuch-search erases the current buffer.
+    (notmuch-search query oldest-first target-thread target-line t)
     (goto-char (point-min))))
 
 (defun notmuch-search-toggle-order ()
@@ -982,18 +1014,28 @@ default sort order is defined by `notmuch-search-oldest-first'."
   (set '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)
+      (concat "( " query-string " )")
+    query-string))
+
 (defun notmuch-search-filter (query)
-  "Filter the current search results based on an additional query string.
+  "Filter or LIMIT the current search results based on an additional query string.
 
 Runs a new search matching only messages that match both the
 current search results AND the additional query string provided."
   (interactive (list (notmuch-read-query "Filter search: ")))
-  (let ((grouped-query (if (string-match-p notmuch-search-disjunctive-regexp query)
-                          (concat "( " query " )")
-                        query)))
-    (notmuch-search (if (string= notmuch-search-query-string "*")
+  (let ((grouped-query (notmuch-group-disjunctive-query-string query))
+       (grouped-original-query (notmuch-group-disjunctive-query-string
+                                notmuch-search-query-string)))
+    (notmuch-search (if (string= grouped-original-query "*")
                        grouped-query
-                     (concat notmuch-search-query-string " and " grouped-query)) notmuch-search-oldest-first)))
+                     (concat grouped-original-query " and " grouped-query))
+                   notmuch-search-oldest-first)))
 
 (defun notmuch-search-filter-by-tag (tag)
   "Filter the current search results based on a single tag.
@@ -1054,3 +1096,5 @@ notmuch buffers exist, run `notmuch'."
     (let ((init-file (locate-file notmuch-init-file '("/")
                                  (get-load-suffixes))))
       (if init-file (load init-file nil t t))))
+
+;;; notmuch.el ends here