From 2c34ed818293dcea7c623242f3aaf3c2f8e515be Mon Sep 17 00:00:00 2001 From: djcb Date: Sun, 8 Apr 2012 20:28:49 +0300 Subject: [PATCH] * more work on the header/view split view, document it a bit (WIP2) --- NEWS | 15 ++++- emacs/mu4e-hdrs.el | 142 +++++++++++++++++++++++++-------------------- emacs/mu4e-view.el | 56 ++++++++++-------- emacs/mu4e.texi | 26 ++++++++- 4 files changed, 148 insertions(+), 91 deletions(-) diff --git a/NEWS b/NEWS index 64201b4f..6d82d5ba 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,14 @@ * NEWS (user visible changes) +** Release 0.9.9.4 + +*** mu4e + + - much faster header buffers + - fix off-by-one in number of matches shown + - split view mode (headers, view); see `mu4e-split-view'. + + ** Release 0.9.8.3 <2012-04-06> *NOTE*: existing mu/mu4e are recommended to run `mu index --rebuild' after @@ -29,9 +38,9 @@ - fix compiler warnings for newer/older gcc and clang/clang++ - fix unit tests (and some code) for Ubuntu 10.04 and FreeBSD9 - fix warnings for compilation with GTK+ 3.2 and recent glib (g_set_error) - - fixe mu_msg_move_to_maildir for top-level messages - - fixes in maildir scanning - - plugs some memleaks + - fix mu_msg_move_to_maildir for top-level messages + - fix in maildir scanning + - plug some memleaks ** Release 0.9.8.2 <2012-03-11> diff --git a/emacs/mu4e-hdrs.el b/emacs/mu4e-hdrs.el index addaa9b6..b4620cf1 100644 --- a/emacs/mu4e-hdrs.el +++ b/emacs/mu4e-hdrs.el @@ -69,8 +69,12 @@ results, otherwise, limit number of results to (switch-to-buffer buf) (mu4e-proc-find esc ;; '-1' means 'unlimited search' - (if full-search -1 mu4e-search-results-limit)))) + (if full-search -1 mu4e-search-results-limit)) + ;;; when we're starting a new search, we also kill the + ;;; view buffer, if any + (mu4e-view-kill-buffer-and-window))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; handler functions ;; @@ -212,7 +216,7 @@ if provided, or at the end of the buffer otherwise." (when (and thread-info mu4e-thread-info-map) (puthash docid thread-info mu4e-thread-info-map)) ;; now, append the header line - (mu4e-hdrs-add-header line docid point)))) + (mu4e-hdrs-add-header line docid point msg)))) (defun mu4e-hdrs-found-handler (count) "Create a one line description of the number of headers found @@ -228,9 +232,7 @@ after the end of the search results." (insert (propertize str 'face 'mu4e-system-face 'intangible t)) (unless (= 0 count) (message "Found %d matching message%s" - count (if (= 1 count) "" "s")))))))) - - + count (if (= 1 count) "" "s")))))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -247,8 +249,9 @@ after the end of the search results." (define-key map "b" 'mu4e-search-bookmark) (define-key map "B" 'mu4e-search-bookmark-edit-first) - (define-key map "q" 'mu4e-quit-buffer) - + (define-key map "q" 'mu4e-hdrs-kill-buffer-and-window) + (define-key map "z" 'mu4e-hdrs-kill-buffer-and-window) + (define-key map "r" 'mu4e-rerun-search) (define-key map "g" 'mu4e-rerun-search) ;; for compatibility @@ -291,7 +294,8 @@ after the end of the search results." (let ((menumap (make-sparse-keymap "Headers"))) (define-key map [menu-bar headers] (cons "Headers" menumap)) - (define-key menumap [quit-buffer] '("Quit view" . mu4e-quit-buffer)) + (define-key menumap [mu4e-hdrs-kill-buffer-and-window] + '("Quit view" . mu4e-hdrs-kill-buffer-and-window)) (define-key menumap [display-help] '("Help" . mu4e-display-manual)) (define-key menumap [sepa0] '("--")) @@ -382,7 +386,7 @@ after the end of the search results." 'face 'mu4e-header-title-face) " "))) mu4e-headers-fields)))) - ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (defun mu4e-select-headers-window-if-visible () "When there is a visible window for the headers buffer, make sure to select it. This is needed when adding new headers, otherwise @@ -390,7 +394,6 @@ adding a lot of new headers looks really choppy." (let ((win (get-buffer-window mu4e-hdrs-buffer))) (when win (select-window win)))) - ;;;; headers in the buffer are prefixed by an invisible string with the docid ;;;; followed by an EOT ('end-of-transmission', \004, ^D) non-printable ascii ;;;; character. this string also has a text-property with the docid. the former @@ -428,7 +431,6 @@ of the beginning of the line." - (defun mu4e--docid-pos (docid) "Return the pos of the beginning of the line with the header with docid DOCID, or nil if it cannot be found." @@ -453,28 +455,31 @@ docid DOCID, or nil if it cannot be found." ;; the area to write the marker. ;;(forward-char) ;; clear old marks, and add the new ones. - (delete-char 2) + (delete-char (length mu4e-hdrs-fringe)) (insert (propertize mark 'face 'mu4e-hdrs-marks-face) " ") (goto-char oldpoint)))) -(defun mu4e-hdrs-add-header (str docid point) +(defun mu4e-hdrs-add-header (str docid point &optional msg) "Add header STR with DOCID to the buffer at POINT if non-nil, or -at (point-max) otherwise." +at (point-max) otherwise. If MSG is not nil, add it as the text-property `msg'." (unless docid (error "Invalid message")) (when (buffer-live-p mu4e-hdrs-buffer) (with-current-buffer mu4e-hdrs-buffer - (let ((inhibit-read-only t) (point (if point point (point-max)))) - ;;(mu4e-select-headers-window-if-visible) + (let ((inhibit-read-only t) + (is-first-header (= (point-min) (point-max)))) (save-excursion - (goto-char point) - ;; make sure the output window is selected, which it wouldn't be if - ;; called from e.g. speedbar (output looks choppy when another window - ;; is selected). We use switch-to-buffer for its window-selecting - ;; side-effect - but only if the window is visible + (goto-char (if point point (point-max))) (insert - (mu4e--docid-cookie docid) - (propertize (concat mu4e-hdrs-fringe str "\n") 'docid docid))))))) + (propertize + (concat + (mu4e--docid-cookie docid) + mu4e-hdrs-fringe str "\n") + 'docid docid 'msg msg)) + ;; if it's the first header, highlight it + (when is-first-header + (goto-char (point-min)) + (hl-line-highlight))))))) (defun mu4e-hdrs-remove-header (docid) "Remove header with DOCID at POINT." @@ -619,13 +624,42 @@ work well." ;; in any case, clear the marks map (clrhash mu4e-marks-map)) -(defun mu4e-hdrs-view () - "View message at point." +(defun mu4e-view-message () + "View message at point. If there's an existing window for the +view, re-use that one. If not, create a new one, depending on the +value of `mu4e-split-view': if it's a symbol `horizontal' or +`vertical', split the window accordingly; if it is nil, replace the +current window. " + (interactive) (with-current-buffer mu4e-hdrs-buffer - (let ((docid (mu4e--docid-at-point))) + (let ((docid (mu4e--docid-at-point)) + (viewwin (get-buffer-window mu4e-view-buffer))) (unless docid (error "No message at point.")) + ;; is there a window already for the message view? + (unless (window-live-p viewwin) + ;; no view window yet; create one, based on the split settings etc. + (setq viewwin + (cond ;; is there are live window for the message view? + + ((eq mu4e-split-view 'horizontal) ;; split horizontally + (split-window nil mu4e-headers-visible-lines 'below)) + + ((eq mu4e-split-view 'vertical) ;; split vertically + (split-window nil mu4e-headers-visible-columns 'right)) + + (t ;; no splitting; just use the currently selected one + (selected-window))))) + ;; okay, now we should have a window for the message view + ;; we select it, and show the messages there. + (select-window viewwin) + (switch-to-buffer (get-buffer-create mu4e-view-buffer-name)) + (let ((inhibit-read-only t)) + (erase-buffer) + (insert (propertize "Waiting for message..." + 'face 'mu4e-system-face 'intangible t))) (mu4e-proc-view-msg docid)))) + (defun mu4e-hdrs-docid-is-marked (docid) "Is the given docid marked?" (when (gethash docid mu4e-marks-map) t)) @@ -694,12 +728,12 @@ otherwise, limit to up to `mu4e-search-results-limit'." (mu4e-hdrs-search expr current-prefix-arg))) -(defun mu4e-quit-buffer () - "Quit the current buffer." +(defun mu4e-hdrs-kill-buffer-and-window () + "Quit the message view and return to the main view." (interactive) - (when (mu4e-handle-marks) - (kill-buffer) - (mu4e))) + (message "KILL") + (mu4e-kill-buffer-and-window mu4e-hdrs-buffer) + (mu4e)) (defun mu4e-rerun-search () "Rerun the search for the last search expression; if none exists, @@ -709,43 +743,23 @@ do a new search." (if mu4e-last-expr (mu4e-hdrs-search mu4e-last-expr) (mu4e-search)))) - -(defun mu4e-view-message () - "View the message at point." - (interactive) - (let ((viewwin (when (buffer-live-p mu4e-view-buffer) - (get-buffer-window mu4e-view-buffer)))) - (unless (window-live-p viewwin) - ;; no view window yet; create one, based on the split settings etc. - (setq viewwin - (cond ;; is there are live window for the message view? - ;; split horizontally - ((eq mu4e-split-mode 'horizontal) - (split-window nil mu4e-headers-visible-lines 'below)) - ;; split vertically - ((eq mu4e-split-mode 'vertical) - (split-window nil mu4e-headers-visible-columns 'right)) - ;; no splitting; just use the currently selected one - (t - (selected-window))))) - ;; okay, now we have viewwin - (select-window viewwin) - (mu4e-hdrs-view))) - + (defun mu4e--hdrs-move (lines) "Move point LINES lines forward (if LINES is positive) or backward (if LINES is negative). If this succeeds, return the new docid. Otherwise, return nil." - (with-current-buffer mu4e-hdrs-buffer - (hl-line-unhighlight) - (let ((succeeded (= 0 (forward-line lines))) - (docid (mu4e--docid-at-point))) - ;; trick to move point, even if this function is called when this window - ;; is not visible - (set-window-point (get-buffer-window mu4e-hdrs-buffer) (point)) - (hl-line-highlight) - ;; return the docid only if the move succeeded - (when succeeded docid)))) + (with-current-buffer mu4e-hdrs-buffer + (unless (buffer-live-p mu4e-hdrs-buffer) + (error "Headers buffer is not alive %S" (current-buffer))) + (set-window-point (get-buffer-window mu4e-hdrs-buffer) (point)) + (hl-line-unhighlight) + (let ((succeeded (= 0 (forward-line lines))) + (docid (mu4e--docid-at-point))) + ;; trick to move point, even if this function is called when this window + ;; is not visible + (hl-line-highlight) + ;; return the docid only if the move succeeded + (when succeeded docid)))) (defun mu4e-next-header () "Move point to the next message header. If this succeeds, return diff --git a/emacs/mu4e-view.el b/emacs/mu4e-view.el index 3981fddd..caf0d40d 100644 --- a/emacs/mu4e-view.el +++ b/emacs/mu4e-view.el @@ -1,4 +1,4 @@ -;; mu4e-view.el -- part of mu4e, the mu mail user agent +;;; mu4e-view.el -- part of mu4e, the mu mail user agent ;; ;; Copyright (C) 2011-2012 Dirk-Jan C. Binnema @@ -41,7 +41,7 @@ (defvar mu4e-hdrs-buffer nil "*internal* Headers buffer connected to this view.") -(defvar mu4e-current-msg nil +(defvar mu4e--current-msg nil "*internal* The plist describing the current message.") (defun mu4e-view-message-with-msgid (msgid) @@ -111,7 +111,7 @@ marking if it still had that." (mu4e-view-mode) (setq ;; these are buffer-local buffer-read-only t - mu4e-current-msg msg + mu4e--current-msg msg mu4e-hdrs-buffer hdrsbuf mu4e-link-map (make-hash-table :size 32 :rehash-size 2 :weakness nil)) @@ -222,8 +222,13 @@ if IS-OPEN is nil, and otherwise open it." (unless mu4e-view-mode-map (setq mu4e-view-mode-map (let ((map (make-sparse-keymap))) - (define-key map "q" 'mu4e-view-quit-buffer) + + (define-key map "q" 'mu4e-view-kill-buffer-and-window) + ;; note, 'z' is by-default bound to 'bury-buffer' + ;; but that's not very useful in this case + (define-key map "z" 'mu4e-view-kill-buffer-and-window) + (define-key map "s" 'mu4e-search) (define-key map "b" 'mu4e-search-bookmark) @@ -289,7 +294,8 @@ if IS-OPEN is nil, and otherwise open it." (let ((menumap (make-sparse-keymap "View"))) (define-key map [menu-bar headers] (cons "View" menumap)) - (define-key menumap [quit-buffer] '("Quit view" . mu4e-view-quit-buffer)) + (define-key menumap [quit-buffer] + '("Quit view" . mu4e-view-kill-buffer-and-window)) (define-key menumap [display-help] '("Help" . mu4e-display-manual)) (define-key menumap [sepa0] '("--")) @@ -346,7 +352,7 @@ if IS-OPEN is nil, and otherwise open it." (use-local-map mu4e-view-mode-map) (make-local-variable 'mu4e-hdrs-buffer) - (make-local-variable 'mu4e-current-msg) + (make-local-variable 'mu4e--current-msg) (make-local-variable 'mu4e-link-map) (make-local-variable 'mu4e-lines-wrapped) @@ -366,9 +372,9 @@ if IS-OPEN is nil, and otherwise open it." (defun mu4e-view-mark-as-read-maybe () "Clear the current message's New/Unread status and set it to Seen; if the message is not New/Unread, do nothing." - (when mu4e-current-msg - (let ((flags (plist-get mu4e-current-msg :flags)) - (docid (plist-get mu4e-current-msg :docid))) + (when mu4e--current-msg + (let ((flags (plist-get mu4e--current-msg :flags)) + (docid (plist-get mu4e--current-msg :docid))) ;; is it a new message? (when (or (member 'unread flags) (member 'new flags)) (mu4e-proc-flag docid "+S-u-N"))))) @@ -550,9 +556,9 @@ See the `org-contacts' documentation for more details." (with-current-buffer mu4e-view-buffer-name (unless (eq major-mode 'mu4e-view-mode) (error "Not in mu4e-view mode.")) - (unless mu4e-current-msg + (unless mu4e--current-msg (error "No current message.")) - (let ((from (car-safe (plist-get mu4e-current-msg :from)))) + (let ((from (car-safe (plist-get mu4e--current-msg :from)))) (cond ((not from) "") ;; nothing found ((eq name-or-email 'name) @@ -601,18 +607,19 @@ See the `org-contacts' documentation for more details." "Redisplay the current message, without wrapped lines or hidden citations." (interactive) - (mu4e-view mu4e-current-msg mu4e-hdrs-buffer t) + (mu4e-view mu4e--current-msg mu4e-hdrs-buffer t) (setq mu4e-lines-wrapped nil mu4e-cited-hidden nil)) -(defun mu4e-view-quit-buffer () +(defun mu4e-view-kill-buffer-and-window () "Quit the message view and return to the headers." (interactive) - (kill-buffer-and-window) - (when (buffer-live-p mu4e-hdrs-buffer) - (switch-to-buffer mu4e-hdrs-buffer))) - + (when (buffer-live-p mu4e-view-buffer) + (with-current-buffer mu4e-view-buffer + ;; (mu4e-kill-buffer-and-window mu4e-view-buffer) + (kill-buffer-and-window)))) + (defun mu4e-view-next-header () "View the next header." (interactive) @@ -660,7 +667,7 @@ citations." (setq retry (and (file-exists-p path) (not (y-or-n-p (concat "Overwrite " path "?")))))) - (mu4e-proc-save (plist-get mu4e-current-msg :docid) id path))) + (mu4e-proc-save (plist-get mu4e--current-msg :docid) id path))) (defun mu4e-view-open-attachment (attnum) "Extract the attachment with ATTNUM" @@ -670,7 +677,7 @@ citations." (let* ((att (gethash attnum mu4e-attach-map)) (id (and att (plist-get att :index)))) (unless id (error "Not a valid attachment number")) - (mu4e-proc-open (plist-get mu4e-current-msg :docid) id))) + (mu4e-proc-open (plist-get mu4e--current-msg :docid) id))) (defun mu4e-view-unmark () "Warn user that unmarking only works in the header list." @@ -694,17 +701,20 @@ list." (defun mu4e-raw-view () "Show the the raw text of the current message." (interactive) - (unless mu4e-current-msg + (unless mu4e--current-msg (error "No current message")) - (mu4e-raw-view-message mu4e-current-msg (current-buffer))) + (mu4e-raw-view-message mu4e--current-msg (current-buffer))) (defun mu4e-view-pipe (cmd) "Pipe the message through shell command CMD, and display the results." (interactive "sShell command: ") - (unless mu4e-current-msg + (unless mu4e--current-msg (error "No current message")) - (mu4e-view-shell-command-on-raw-message mu4e-current-msg + (mu4e-view-shell-command-on-raw-message mu4e--current-msg (current-buffer) cmd)) + + + (provide 'mu4e-view) diff --git a/emacs/mu4e.texi b/emacs/mu4e.texi index 6b1af2c1..7ae4df20 100644 --- a/emacs/mu4e.texi +++ b/emacs/mu4e.texi @@ -538,7 +538,7 @@ H get help q leave the headers buffer @end verbatim -@subsection Some notes on marking +@subsection Marking messages Note, all mark/unmark commands support the current @emph{region} (i.e., selection) -- so, for example, if you the select a number of message and then @@ -554,6 +554,30 @@ have marked messages, normally you will be asked what to do with those marks @emph{cancel} the operation. This behavior can be influenced with the variable @code{`mu4e-headers-leave-behavior'} -- see its documentation. +@subsections Split view + +@emph{Split view} refers to viewing the @ref{Headers view} and the +@ref{Message view} next to each other, with the message selected in the +former, visible in the latter. Earlier versions of @t{mu4e} only showed one of +the views at the same time, but now it does support it - in fact, split view +is the default behavior after version 0.8.3.1. + +You can influence the way the splitting works by setting the variable +@code{mu4e-split-view} in your configuration to one of 3 values: +@itemize +@item @t{horizontal} (this is the default): display the message view below the +header view +@item @t{vertical} (this is the default): display the message view on the +right side of the header view +@item @t{nil}: don't do any splitting +@end itemize + +You can also determine the number of lines (when splitting horizontally) or +the number of columns (when splitting vertically) to reserve for the header +view with @t{mu4e-headers-visible-lines} (default: 8) and +@t{mu4e-headers-visible-columns} (default: 30). + + @node Message view @section Message view