-
-
Save rougier/98e83fb50e19fb73fe34a7ecc5fc1ccc to your computer and use it in GitHub Desktop.
;; mu4e thread fast folding -*- lexical-binding: t; -*- | |
;; This file is not part of GNU Emacs. | |
;; | |
;; This program is free software: you can redistribute it and/or | |
;; modify it under the terms of the GNU General Public License as | |
;; published by the Free Software Foundation, either version 3 of the | |
;; License, or (at your option) any later version. | |
;; | |
;; This program is distributed in the hope that it will be useful, but | |
;; WITHOUT ANY WARRANTY; without even the implied warranty of | |
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
;; General Public License for more details. | |
;; | |
;; You should have received a copy of the GNU General Public License | |
;; along with this program. If not, see <http://www.gnu.org/licenses/> | |
(require 'mu4e) | |
(defun mu4e-fast-folding-info (msg) | |
(let* ((thread (mu4e-message-field msg :thread)) | |
(prefix (mu4e~headers-thread-prefix thread)) | |
(unread (memq 'unread (mu4e-message-field msg :flags)))) | |
(concat | |
(if (= (length prefix) 0) " " " ") ;; Normal space vs Non-breaking space | |
(if unread "•" " ")))) ;; Specific character to later detect unread | |
(add-to-list 'mu4e-header-info-custom | |
'(:fast-folding . (:name "fast-folding" | |
:shortname "" | |
:function mu4e-fast-folding-info))) | |
(setq mu4e-headers-fields '((:fast-folding . 2) | |
(:human-date . 12) | |
(:flags . 6) | |
(:mailing-list . 10) | |
(:from . 22) | |
(:subject))) | |
(defun mu4e-fast-folding-is-unfolded-child () | |
"Check if the line at point is an unfolded thread child. | |
This is detected by the presence of non-breaking space." | |
(interactive) | |
(save-excursion | |
(beginning-of-line) | |
(and (not (mu4e-fast-folding-is-folded-children)) | |
(search-forward " " (line-end-position) t)))) | |
(defun mu4e-fast-folding-is-folded-children () | |
"Check if the line at point is a folded thread. | |
This is detected by the presence of an overlay with value 'overlay." | |
(interactive) | |
(save-excursion | |
(beginning-of-line) | |
(let ((overlays (overlays-at (point))) | |
(found nil)) | |
(while overlays | |
(if (overlay-get (car overlays) 'overlay) | |
(setq found t)) | |
(setq overlays (cdr overlays))) | |
found))) | |
(defun mu4e-fast-folding-is-root () | |
"Check if the line at point is a thread root." | |
(interactive) | |
(and (not (mu4e-fast-folding-is-unfolded-child)) | |
(not (mu4e-fast-folding-is-folded-children)))) | |
(defun mu4e-fast-folding-is-unread () | |
"Check if the line at point is an unread message." | |
(save-excursion | |
(beginning-of-line) | |
(search-forward "•" (line-end-position) t))) | |
(defun mu4e-fast-folding-thread-toggle () | |
"Toggle thread at point." | |
(interactive) | |
(save-excursion | |
(beginning-of-line) | |
(if (mu4e-fast-folding-is-root) | |
(forward-line)) | |
(cond ((mu4e-fast-folding-is-folded-children) | |
(mu4e-fast-folding-thread-unfold)) | |
((mu4e-fast-folding-is-unfolded-child) | |
(mu4e-fast-folding-thread-fold))))) | |
(defun mu4e-fast-folding-thread-unfold () | |
"Unfold thread at point." | |
(interactive) | |
(if (mu4e-fast-folding-is-root) | |
(forward-line)) | |
(let ((overlays (overlays-at (point)))) | |
(while overlays | |
(let ((overlay (car overlays))) | |
(if (overlay-get overlay 'overlay) | |
(delete-overlay (overlay-get overlay 'overlay)))) | |
(setq overlays (cdr overlays))))) | |
(defun mu4e-fast-folding-thread-fold () | |
"Fold thread at point." | |
(interactive) | |
;; Move to thread start | |
(beginning-of-line) | |
(while (and (> (point) (point-min)) | |
(mu4e-fast-folding-is-unfolded-child)) | |
(forward-line -1)) | |
(forward-line +1) | |
;; Hide all children, count them and count unread | |
(beginning-of-line) | |
(let ((start (point)) | |
(end (+ (point) 1)) | |
(unread 0) | |
(count 0)) | |
(while (and (< (point) (point-max)) | |
(mu4e-fast-folding-is-unfolded-child)) | |
;; Count unread | |
(beginning-of-line) | |
(if (mu4e-fast-folding-is-unread) | |
(setq unread (+ unread 1))) | |
;; Count thread | |
(setq count (+ count 1)) | |
;; Set new end for the overlay | |
(setq end (+ (line-end-position) 1)) | |
(forward-line +1) | |
(beginning-of-line)) | |
;; Add overlay | |
(let* ((overlay (make-overlay start (- end 1))) | |
(face (if (> unread 0) 'mu4e-unread-face 'mu4e-system-face)) | |
(text (if (> unread 0) | |
(format " --- %d hidden messages (%d unread) --- " count unread) | |
(format " --- %d hidden messages --- " count)))) | |
;; No overlay if only 1 child | |
(when (> count 1) | |
(overlay-put overlay 'display (propertize text 'face face)) | |
(overlay-put overlay 'overlay overlay))))) | |
(defun mu4e-fast-folding-thread-fold-all () | |
"Fold all threads independently of their current state." | |
(interactive) | |
(save-excursion | |
(goto-char (point-min)) | |
(while (not (eobp)) | |
(mu4e-fast-folding-thread-fold) | |
(forward-line)))) | |
(defun mu4e-fast-folding-thread-unfold-all () | |
"Unfold all threads, independently of their current state." | |
(interactive) | |
(save-excursion | |
(goto-char (point-min)) | |
(while (not (eobp)) | |
(mu4e-fast-folding-thread-unfold) | |
(forward-line)))) | |
(defvar mu4e-fast-folding-thread-folding-state nil | |
"Global folding state") | |
(defun mu4e-fast-folding-thread-toggle-all () | |
"Toggle global folding state." | |
(interactive) | |
(when mu4e-headers-include-related | |
(setq mu4e-fast-folding-thread-folding-state | |
(not mu4e-fast-folding-thread-folding-state)) | |
(mu4e-fast-folding-thread-apply-folding))) | |
(defun mu4e-fast-folding-thread-apply-folding () | |
"Apply folding according to the global folding state." | |
(interactive) | |
(if mu4e-fast-folding-thread-folding-state | |
(mu4e-fast-folding-thread-fold-all) | |
(mu4e-fast-folding-thread-unfold-all))) | |
(add-hook 'mu4e-headers-found-hook | |
#'mu4e-fast-folding-thread-apply-folding) | |
(define-key mu4e-headers-mode-map (kbd "TAB") | |
#'mu4e-fast-folding-thread-toggle) | |
(define-key mu4e-headers-mode-map (kbd "<backtab>") | |
#'mu4e-fast-folding-thread-toggle-all) |
rougier
commented
Sep 23, 2021
I think there is a right parenthesis missing at the end of line 143
Thanks, fixed.
For some reason nothing seems to happen when loading this file. Has anyone experienced this before and/or has a suggestion on how to get it to work?
For some reason nothing seems to happen when loading this file. Has anyone experienced this before and/or has a suggestion on how to get it to work?
Same here. Have you found a solution?
No, unfortunately not.
You mean no change at all in layout and no error messages?
correct, no change at all. Though I just tried again, and it just makes mu4e-headers unresponsive
You mean no change at all in layout and no error messages?
I have saved the .el file in my path that I load upon startup. This does not throw an error, but I also don't see threads being folded. Manually evaluating mu4e-fast-folding.el
also does not work. I am on Emacs 29.0.50 and mu4e 1.9.0.
Ok after investigating, this line (let* ((thread (mu4e-message-field msg :thread))
has to be changed to (let* ((thread (mu4e-message-field msg :meta))
for it to work. But there is an issue that you cannot forward-line if point is at the beginning of the line when threads are folded...
You're using next-line
?
Yes. Good catch - will rebind next-line
to mu4e-headers-next
. Using C-n
instead of n
was causing issues.