-
-
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) |
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.
I think there is a right parenthesis missing at the end of line 143