198 lines
6.1 KiB
EmacsLisp
198 lines
6.1 KiB
EmacsLisp
;; 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 :meta))
|
||
(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-fold-all)
|
||
|