]> git.notmuchmail.org Git - notmuch/blobdiff - notmuch.el
Use 'forward-line' instead of 'next-line' while walking search display
[notmuch] / notmuch.el
index 05e7c4d1cece5e075e0e6b3f23d7a618750549a1..8894a8e194716f444489b1676c3630c545c82c95 100644 (file)
@@ -19,7 +19,8 @@
 ;
 ; Authors: Carl Worth <cworth@cworth.org>
 
-(load "cl-seq")
+(require 'cl)
+(require 'mm-view)
 
 (defvar notmuch-show-mode-map
   (let ((map (make-sparse-keymap)))
@@ -35,6 +36,7 @@
     (define-key map "b" 'notmuch-show-toggle-body-read-visible)
     (define-key map "c" 'notmuch-show-toggle-citations-visible)
     (define-key map "h" 'notmuch-show-toggle-headers-visible)
+    (define-key map "m" 'message-mail)
     (define-key map "n" 'notmuch-show-next-message)
     (define-key map "N" 'notmuch-show-mark-read-then-next-open-message)
     (define-key map "p" 'notmuch-show-previous-message)
@@ -43,6 +45,7 @@
     (define-key map "q" 'kill-this-buffer)
     (define-key map "r" 'notmuch-show-reply)
     (define-key map "s" 'notmuch-show-toggle-signatures-visible)
+    (define-key map "v" 'notmuch-show-view-all-mime-parts)
     (define-key map "w" 'notmuch-show-view-raw-message)
     (define-key map "x" 'kill-this-buffer)
     (define-key map "+" 'notmuch-show-add-tag)
   "Keymap for \"notmuch show\" buffers.")
 (fset 'notmuch-show-mode-map notmuch-show-mode-map)
 
-(defvar notmuch-show-signature-lines-max 6
+(defvar notmuch-show-signature-regexp "\\(-- ?\\|_+\\)$"
+  "Pattern to match a line that separates content from signature.
+
+The regexp can (and should) include $ to match the end of the
+line, but should not include ^ to match the beginning of the
+line. This is because notmuch may have inserted additional space
+for indentation at the beginning of the line. But notmuch will
+move past the indentation when testing this pattern, (so that the
+pattern can still test against the entire line).")
+
+(defvar notmuch-show-signature-lines-max 12
   "Maximum length of signature that will be hidden by default.")
 
 (set 'notmuch-show-message-begin-regexp    "\fmessage{")
@@ -70,6 +83,7 @@
 (set 'notmuch-show-marker-regexp "\f\\(message\\|header\\|body\\|attachment\\|part\\)[{}].*$")
 
 (set 'notmuch-show-id-regexp "\\(id:[^ ]*\\)")
+(set 'notmuch-show-depth-regexp " depth:\\([0-9]*\\) ")
 (set 'notmuch-show-filename-regexp "filename:\\(.*\\)$")
 (set 'notmuch-show-tags-regexp "(\\([^)]*\\))$")
 
@@ -222,6 +236,18 @@ buffer."
   (interactive)
   (view-file (notmuch-show-get-filename)))
 
+(defun notmuch-show-view-all-mime-parts ()
+  "Use external viewers (according to mailcap) to view all MIME-encoded parts."
+  (interactive)
+  (save-excursion
+    (let ((filename (notmuch-show-get-filename)))
+      (switch-to-buffer (generate-new-buffer (concat "*notmuch-mime-"
+                                                    filename
+                                                    "*")))
+      (insert-file-contents filename nil nil nil t)
+      (mm-display-parts (mm-dissect-buffer))
+      (kill-this-buffer))))
+
 (defun notmuch-show-reply ()
   "Begin composing a reply to the current message in a new buffer."
   (interactive)
@@ -410,65 +436,103 @@ which this thread was originally shown."
        (if last
            (notmuch-show-archive-thread))))))
 
-(defun notmuch-show-markup-citations-region (beg end)
+(defun notmuch-show-markup-citations-region (beg end depth)
   (goto-char beg)
   (beginning-of-line)
   (while (< (point) end)
-    (let ((beg-sub (point)))
-      (if (looking-at ">")
+    (let ((beg-sub (point-marker))
+         (indent (make-string depth ? ))
+         (citation "[[:space:]]*>"))
+      (if (looking-at citation)
          (progn
-           (while (looking-at ">")
+           (while (looking-at citation)
              (forward-line))
            (let ((overlay (make-overlay beg-sub (point))))
              (overlay-put overlay 'invisible 'notmuch-show-citation)
              (overlay-put overlay 'before-string
-                          (concat "[" (number-to-string (count-lines beg-sub (point)))
+                          (concat indent
+                                  "[" (number-to-string (count-lines beg-sub (point)))
                                   "-line citation. Press 'c' to show.]\n")))))
-      (if (looking-at "--[ ]?$")
-         (let ((sig-lines (count-lines beg-sub end)))
+      (move-to-column depth)
+      (if (looking-at notmuch-show-signature-regexp)
+         (let ((sig-lines (- (count-lines beg-sub end) 1)))
            (if (<= sig-lines notmuch-show-signature-lines-max)
                (progn
-                 (overlay-put (make-overlay beg-sub (+ beg-sub 1))
-                              'before-string
-                              (concat "[" (number-to-string sig-lines)
-                                      "-line signature. Press 's' to show.]"))
-                 (overlay-put (make-overlay (+ beg-sub 2) end)
+                 (overlay-put (make-overlay beg-sub end)
                               'invisible 'notmuch-show-signature)
+                 (overlay-put (make-overlay beg (- beg-sub 1))
+                              'after-string
+                              (concat "\n" indent
+                                      "[" (number-to-string sig-lines)
+                                      "-line signature. Press 's' to show.]"))
                  (goto-char end)))))
       (forward-line))))
 
-(defun notmuch-show-markup-body ()
+(defun notmuch-show-markup-part (beg end depth)
+  (if (re-search-forward notmuch-show-part-begin-regexp nil t)
+      (progn
+       (forward-line)
+       (let ((beg (point-marker)))
+         (re-search-forward notmuch-show-part-end-regexp)
+         (let ((end (copy-marker (match-beginning 0))))
+           (goto-char end)
+           (if (not (bolp))
+               (insert "\n"))
+           (indent-rigidly beg end depth)
+           (notmuch-show-markup-citations-region beg end depth)
+           ; Advance to the next part (if any) (so the outer loop can
+           ; determine whether we've left the current message.
+           (if (re-search-forward notmuch-show-part-begin-regexp nil t)
+               (beginning-of-line)))))
+    (goto-char end)))
+
+(defun notmuch-show-markup-parts-region (beg end depth)
+  (save-excursion
+    (goto-char beg)
+    (while (< (point) end)
+      (notmuch-show-markup-part beg end depth))))
+
+(defun notmuch-show-markup-body (depth)
   (re-search-forward notmuch-show-body-begin-regexp)
-  (next-line 1)
-  (beginning-of-line)
-  (let ((beg (point)))
+  (forward-line)
+  (let ((beg (point-marker)))
     (re-search-forward notmuch-show-body-end-regexp)
-    (let ((end (match-beginning 0)))
-      (notmuch-show-markup-citations-region beg end)
+    (let ((end (copy-marker (match-beginning 0))))
+      (notmuch-show-markup-parts-region beg end depth)
       (if (not (notmuch-show-message-unread-p))
          (overlay-put (make-overlay beg end)
-                      'invisible 'notmuch-show-body-read)))))
+                      'invisible 'notmuch-show-body-read))
+      (set-marker beg nil)
+      (set-marker end nil)
+      )))
 
-(defun notmuch-show-markup-header ()
+(defun notmuch-show-markup-header (depth)
   (re-search-forward notmuch-show-header-begin-regexp)
-  (forward-line 1)
-  (beginning-of-line)
-  (let ((beg (point)))
+  (forward-line)
+  (let ((beg (point-marker)))
     (end-of-line)
     ; Inverse video for subject
     (overlay-put (make-overlay beg (point)) 'face '((cons :inverse-video t)))
-    (beginning-of-line)
     (forward-line 2)
-    (set 'beg (point))
-    (re-search-forward notmuch-show-header-end-regexp)
-    (overlay-put (make-overlay beg (match-beginning 0))
-                'invisible 'notmuch-show-header)))
+    (let ((beg-hidden (point-marker)))
+      (re-search-forward notmuch-show-header-end-regexp)
+      (beginning-of-line)
+      (let ((end (point-marker)))
+       (indent-rigidly beg end depth)
+       (overlay-put (make-overlay beg-hidden end)
+                    'invisible 'notmuch-show-header)
+       (set-marker beg nil)
+       (set-marker beg-hidden nil)
+       (set-marker end nil)
+       ))))
 
 (defun notmuch-show-markup-message ()
   (if (re-search-forward notmuch-show-message-begin-regexp nil t)
       (progn
-       (notmuch-show-markup-header)
-       (notmuch-show-markup-body))
+       (re-search-forward notmuch-show-depth-regexp)
+       (let ((depth (string-to-number (buffer-substring (match-beginning 1) (match-end 1)))))
+         (notmuch-show-markup-header depth)
+         (notmuch-show-markup-body depth)))
     (goto-char (point-max))))
 
 (defun notmuch-show-hide-markers ()
@@ -609,12 +673,17 @@ thread from that buffer can be show when done with this one)."
                  (notmuch-show-toggle-body-read-visible)))))
       )))
 
+(defvar notmuch-search-authors-width 40
+  "Number of columns to use to diplay authors in a notmuch-search buffer.")
+
 (defvar notmuch-search-mode-map
   (let ((map (make-sparse-keymap)))
     (define-key map "a" 'notmuch-search-archive-thread)
     (define-key map "b" 'notmuch-search-scroll-down)
     (define-key map "f" 'notmuch-search-filter)
+    (define-key map "m" 'message-mail)
     (define-key map "n" 'next-line)
+    (define-key map "o" 'notmuch-search-toggle-order)
     (define-key map "p" 'previous-line)
     (define-key map "q" 'kill-this-buffer)
     (define-key map "s" 'notmuch-search)
@@ -705,14 +774,24 @@ global search.
     (if (re-search-forward "thread:[a-fA-F0-9]*" nil t)
        (progn
          (forward-char)
-         (overlay-put (make-overlay beg (point)) 'invisible 'notmuch-search)))))
+         (overlay-put (make-overlay beg (point)) 'invisible 'notmuch-search)
+         (re-search-forward ".*\\[[0-9]*/[0-9]*\\] \\([^;]*\\)\\(;\\)")
+         (let* ((authors (buffer-substring (match-beginning 1) (match-end 1)))
+                (authors-length (length authors)))
+           ;; Drop the semi-colon
+           (replace-match "" t nil nil 2)
+           (if (<= authors-length notmuch-search-authors-width)
+               (replace-match (concat authors (make-string
+                                               (- notmuch-search-authors-width
+                                                  authors-length) ? )) t t nil 1)
+             (replace-match (concat (substring authors 0 (- notmuch-search-authors-width 3)) "...") t t nil 1)))))))
 
 (defun notmuch-search-markup-thread-ids ()
   (save-excursion
     (goto-char (point-min))
     (while (not (eobp))
       (notmuch-search-markup-this-thread-id)
-      (next-line))))
+      (forward-line))))
 
 (defun notmuch-search-show-thread ()
   (interactive)
@@ -820,6 +899,24 @@ same relative position within the new buffer."
        (beginning-of-line)
       (goto-char here))))
 
+(defun notmuch-search-toggle-order ()
+  "Toggle the current search order.
+
+By default, the \"inbox\" view created by `notmuch' is displayed
+in chronological order (oldest thread at the beginning of the
+buffer), while any global searches created by `notmuch-search'
+are displayed in reverse-chronological order (newest thread at
+the beginning of the buffer).
+
+This command toggles the sort order for the current search.
+
+Note that any fitlered searches created by
+`notmuch-search-filter' retain the search order of the parent
+search."
+  (interactive)
+  (set 'notmuch-search-oldest-first (not notmuch-search-oldest-first))
+  (notmuch-search-refresh-view))
+
 (defun notmuch-search-filter (query)
   "Filter the current search results based on an additional query string.