unofficial mirror of notmuch@notmuchmail.org
 help / color / mirror / code / Atom feed
* [PATCH v2] emacs: tree: support fold/unfold thread or sub-thread
@ 2019-03-25 15:25 Julien Masson
  2019-05-03 17:43 ` David Bremner
  2021-08-30 19:44 ` inwit
  0 siblings, 2 replies; 6+ messages in thread
From: Julien Masson @ 2019-03-25 15:25 UTC (permalink / raw)
  To: notmuch

This patch allow the user to fold/unfold a thread or a sub-thread in
the current tree buffer by pressing "t" key.

By default a string is displayed at the beginning of the overlay to
indicate that this thread/sub-thread is folded.
Pressing again "t" on a folded thread/sub-thread will unfold it.

This feature works accross all the queries which are in Tree View.

Signed-off-by: Julien Masson <massonju.eseo@gmail.com>
---

Changes from V1:
https://notmuchmail.org/pipermail/notmuch/2019/027571.html

-> handle sub-thread folding

-> With Universal Argument (C-u), the user can fold the whole thread
   when pressing "t"

-> replace seq-{find,filter} functions with their corresponding cl functions

 emacs/notmuch-tree.el | 93 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 93 insertions(+)

diff --git a/emacs/notmuch-tree.el b/emacs/notmuch-tree.el
index c00315e..8867542 100644
--- a/emacs/notmuch-tree.el
+++ b/emacs/notmuch-tree.el
@@ -71,6 +71,11 @@ Note the author string should not contain
   :type '(alist :key-type (string) :value-type (string))
   :group 'notmuch-tree)
 
+(defcustom notmuch-tree-overlay-string " [...]"
+  "String displayed at the beginning of the overlay"
+  :type 'string
+  :group 'notmuch-tree)
+
 ;; Faces for messages that match the query.
 (defface notmuch-tree-match-face
   '((t :inherit default))
@@ -159,6 +164,13 @@ Note the author string should not contain
   :group 'notmuch-tree
   :group 'notmuch-faces)
 
+;; Faces for overlays
+(defface notmuch-tree-overlay-fold-face
+  '((t :inherit 'font-lock-keyword-face))
+  "Default face used to display `notmuch-tree-overlay-string'"
+  :group 'notmuch-tree
+  :group 'notmuch-faces)
+
 (defvar notmuch-tree-previous-subject
   "The subject of the most recent result shown during the async display")
 (make-variable-buffer-local 'notmuch-tree-previous-subject)
@@ -196,6 +208,9 @@ if the user has loaded a different buffer in that window.")
 (make-variable-buffer-local 'notmuch-tree-message-buffer)
 (put 'notmuch-tree-message-buffer 'permanent-local t)
 
+(defvar notmuch-tree-overlays nil
+  "List of overlays used to fold/unfold thread")
+
 (defun notmuch-tree-to-message-pane (func)
   "Execute FUNC in message pane.
 
@@ -294,6 +309,7 @@ FUNC."
     (define-key map " " 'notmuch-tree-scroll-or-next)
     (define-key map (kbd "DEL") 'notmuch-tree-scroll-message-window-back)
     (define-key map "e" 'notmuch-tree-resume-message)
+    (define-key map "t" 'notmuch-tree-toggle-folding-thread)
     map))
 (fset 'notmuch-tree-mode-map notmuch-tree-mode-map)
 
@@ -415,6 +431,80 @@ NOT change the database."
 	(notmuch-draft-resume id)
       (message "No message to resume!"))))
 
+(defun notmuch-tree-find-overlay (buffer start end)
+  "Return the first overlay found in `notmuch-tree-overlays'.
+
+The overlay found is located between START and END position in BUFFER."
+  (cl-find-if (lambda (ov)
+	      (and (eq (overlay-buffer ov) buffer)
+		   (<= (overlay-start ov) start)
+		   (>= (overlay-end ov) end)))
+	    notmuch-tree-overlays))
+
+(defun notmuch-tree-clean-up-overlays ()
+  "Remove overlays not referenced to any buffer"
+  (setq notmuch-tree-overlays (cl-remove-if #'overlay-buffer notmuch-tree-overlays)))
+
+(defun notmuch-tree-remove-overlay (overlay)
+  "Delete OVERLAY and remove it from `notmuch-tree-overlays' list"
+  (setq notmuch-tree-overlays (remove overlay notmuch-tree-overlays))
+  (delete-overlay overlay))
+
+(defun notmuch-tree-add-overlay (start end)
+  "Add an overlay from START to END in the current buffer.
+
+If non nil, `notmuch-tree-overlay-string' is added at the end of the line.
+The overlay created is added to `notmuch-tree-overlays' list"
+  (let ((overlay (make-overlay start end)))
+    (add-to-list 'notmuch-tree-overlays overlay)
+    (overlay-put overlay 'invisible t)
+    (when notmuch-tree-overlay-string
+      (overlay-put overlay 'before-string
+		   (propertize notmuch-tree-overlay-string
+			       'face 'notmuch-tree-overlay-fold-face)))))
+
+(defun notmuch-tree-thread-range ()
+  "Return list of Start and End position of the current thread"
+  (let (start end)
+    (save-excursion
+      (while (not (or (notmuch-tree-get-prop :first) (eobp)))
+	(forward-line -1))
+      (setq start (line-end-position))
+      (notmuch-tree-next-thread)
+      (setq end (- (point) 1))
+      (list start end))))
+
+(defun notmuch-tree-sub-thread-range ()
+  "Return list of Start and End position of the current sub-thread"
+  (if (notmuch-tree-get-prop :first)
+      (notmuch-tree-thread-range)
+    (let ((level (length (notmuch-tree-get-prop :tree-status)))
+	  (start (line-end-position))
+	  end)
+      ;; find end position
+      (save-excursion
+	(forward-line)
+	(while (and (< level (length (notmuch-tree-get-prop :tree-status)))
+		    (not (eobp)))
+	  (forward-line))
+	(setq end (- (point) 1)))
+      (list start end))))
+
+(defun notmuch-tree-toggle-folding-thread (&optional arg)
+  "Fold / Unfold the current thread or sub-thread.
+
+With prefix arg (C-u) the whole thread is folded"
+  (interactive "p")
+  (cl-multiple-value-bind (start end)
+      (if (and arg (= arg 1))
+	  (notmuch-tree-sub-thread-range)
+	(notmuch-tree-thread-range))
+    (unless (= start end)
+      (let ((overlay (notmuch-tree-find-overlay (current-buffer) start end)))
+	(if overlay
+	    (notmuch-tree-remove-overlay overlay)
+	  (notmuch-tree-add-overlay start end))))))
+
 ;; The next two functions close the message window before calling
 ;; notmuch-search or notmuch-tree but they do so after the user has
 ;; entered the query (in case the user was basing the query on
@@ -966,6 +1056,9 @@ The arguments are:
   ;; Don't track undo information for this buffer
   (set 'buffer-undo-list t)
 
+  ;; clean-up overlays in case where some overlays reference to no buffer
+  (notmuch-tree-clean-up-overlays)
+
   (notmuch-tree-worker query query-context target open-target)
 
   (setq truncate-lines t))
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH v2] emacs: tree: support fold/unfold thread or sub-thread
  2019-03-25 15:25 [PATCH v2] emacs: tree: support fold/unfold thread or sub-thread Julien Masson
@ 2019-05-03 17:43 ` David Bremner
  2021-08-30 19:44 ` inwit
  1 sibling, 0 replies; 6+ messages in thread
From: David Bremner @ 2019-05-03 17:43 UTC (permalink / raw)
  To: Julien Masson, notmuch

Julien Masson <massonju.eseo@gmail.com> writes:
>  
> +(defcustom notmuch-tree-overlay-string " [...]"
> +  "String displayed at the beginning of the overlay"
> +  :type 'string
> +  :group 'notmuch-tree)

I initially thought this wasn't working because the end of my line was
outside the visible window in Emacs. I'm not sure if this is fixable
with a sensible amount of effort.

I think the docstring needs more context in order to be helpful to
users.

>  
> @@ -294,6 +309,7 @@ FUNC."
>      (define-key map " " 'notmuch-tree-scroll-or-next)
>      (define-key map (kbd "DEL") 'notmuch-tree-scroll-message-window-back)
>      (define-key map "e" 'notmuch-tree-resume-message)
> +    (define-key map "t" 'notmuch-tree-toggle-folding-thread)
>      map))

What do people think about the choice of 't'? It does something pretty
different in notmuch-show-mode

> +(defun notmuch-tree-clean-up-overlays ()
> +  "Remove overlays not referenced to any buffer"
> +  (setq notmuch-tree-overlays (cl-remove-if #'overlay-buffer notmuch-tree-overlays)))

This probably my confusion, but doesn't that remove all of the
non-deleted buffers?

> +(defun notmuch-tree-toggle-folding-thread (&optional arg)
> +  "Fold / Unfold the current thread or sub-thread.
> +
> +With prefix arg (C-u) the whole thread is folded"

Since this is the main entry point, I wonder if you should mention the
behaviour with notmuch-tree-overlay-string here.

In general we'd like at least one or two two tests for a new
feature. Maybe Mark or (other) David in copy can give some suggestions
for that.

David

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v2] emacs: tree: support fold/unfold thread or sub-thread
  2019-03-25 15:25 [PATCH v2] emacs: tree: support fold/unfold thread or sub-thread Julien Masson
  2019-05-03 17:43 ` David Bremner
@ 2021-08-30 19:44 ` inwit
  2021-08-31  0:00   ` David Bremner
  1 sibling, 1 reply; 6+ messages in thread
From: inwit @ 2021-08-30 19:44 UTC (permalink / raw)
  To: Julien Masson, notmuch

Hi all,

While being a total noob, I'm also interested in this patch, so after
getting it via David Bremner (thanks!), I have tried to apply it.
However, the code seems to have diverged and `git am 3` complains about
conflicts. After applying the patch manually, I get some warnings and
the following error:

`emacs/notmuch.el:76:1:Error: Symbol’s value as variable is void:
notmuch-tree-previous-message-button`

Seems like that function is missing in the current code, am I right?

It also seems to me that the patch is/was almost ready and it is a pity
it is not merged. Anyone would care to help me?

Regards,


On Mon Mar 25, 2019 at 4:25 PM CET, Julien Masson wrote:
> This patch allow the user to fold/unfold a thread or a sub-thread in
> the current tree buffer by pressing "t" key.
>
> By default a string is displayed at the beginning of the overlay to
> indicate that this thread/sub-thread is folded.
> Pressing again "t" on a folded thread/sub-thread will unfold it.
>
> This feature works accross all the queries which are in Tree View.
>
> Signed-off-by: Julien Masson <massonju.eseo@gmail.com>
> ---
>
> Changes from V1:
> https://notmuchmail.org/pipermail/notmuch/2019/027571.html
>
> -> handle sub-thread folding
>
> -> With Universal Argument (C-u), the user can fold the whole thread
> when pressing "t"
>
> -> replace seq-{find,filter} functions with their corresponding cl
> functions
>
> emacs/notmuch-tree.el | 93
> +++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 93 insertions(+)
>
> diff --git a/emacs/notmuch-tree.el b/emacs/notmuch-tree.el
> index c00315e..8867542 100644
> --- a/emacs/notmuch-tree.el
> +++ b/emacs/notmuch-tree.el
> @@ -71,6 +71,11 @@ Note the author string should not contain
> :type '(alist :key-type (string) :value-type (string))
> :group 'notmuch-tree)
>  
> +(defcustom notmuch-tree-overlay-string " [...]"
> + "String displayed at the beginning of the overlay"
> + :type 'string
> + :group 'notmuch-tree)
> +
> ;; Faces for messages that match the query.
> (defface notmuch-tree-match-face
> '((t :inherit default))
> @@ -159,6 +164,13 @@ Note the author string should not contain
> :group 'notmuch-tree
> :group 'notmuch-faces)
>  
> +;; Faces for overlays
> +(defface notmuch-tree-overlay-fold-face
> + '((t :inherit 'font-lock-keyword-face))
> + "Default face used to display `notmuch-tree-overlay-string'"
> + :group 'notmuch-tree
> + :group 'notmuch-faces)
> +
> (defvar notmuch-tree-previous-subject
> "The subject of the most recent result shown during the async display")
> (make-variable-buffer-local 'notmuch-tree-previous-subject)
> @@ -196,6 +208,9 @@ if the user has loaded a different buffer in that
> window.")
> (make-variable-buffer-local 'notmuch-tree-message-buffer)
> (put 'notmuch-tree-message-buffer 'permanent-local t)
>  
> +(defvar notmuch-tree-overlays nil
> + "List of overlays used to fold/unfold thread")
> +
> (defun notmuch-tree-to-message-pane (func)
> "Execute FUNC in message pane.
>  
> @@ -294,6 +309,7 @@ FUNC."
> (define-key map " " 'notmuch-tree-scroll-or-next)
> (define-key map (kbd "DEL") 'notmuch-tree-scroll-message-window-back)
> (define-key map "e" 'notmuch-tree-resume-message)
> + (define-key map "t" 'notmuch-tree-toggle-folding-thread)
> map))
> (fset 'notmuch-tree-mode-map notmuch-tree-mode-map)
>  
> @@ -415,6 +431,80 @@ NOT change the database."
> (notmuch-draft-resume id)
> (message "No message to resume!"))))
>  
> +(defun notmuch-tree-find-overlay (buffer start end)
> + "Return the first overlay found in `notmuch-tree-overlays'.
> +
> +The overlay found is located between START and END position in BUFFER."
> + (cl-find-if (lambda (ov)
> + (and (eq (overlay-buffer ov) buffer)
> + (<= (overlay-start ov) start)
> + (>= (overlay-end ov) end)))
> + notmuch-tree-overlays))
> +
> +(defun notmuch-tree-clean-up-overlays ()
> + "Remove overlays not referenced to any buffer"
> + (setq notmuch-tree-overlays (cl-remove-if #'overlay-buffer
> notmuch-tree-overlays)))
> +
> +(defun notmuch-tree-remove-overlay (overlay)
> + "Delete OVERLAY and remove it from `notmuch-tree-overlays' list"
> + (setq notmuch-tree-overlays (remove overlay notmuch-tree-overlays))
> + (delete-overlay overlay))
> +
> +(defun notmuch-tree-add-overlay (start end)
> + "Add an overlay from START to END in the current buffer.
> +
> +If non nil, `notmuch-tree-overlay-string' is added at the end of the
> line.
> +The overlay created is added to `notmuch-tree-overlays' list"
> + (let ((overlay (make-overlay start end)))
> + (add-to-list 'notmuch-tree-overlays overlay)
> + (overlay-put overlay 'invisible t)
> + (when notmuch-tree-overlay-string
> + (overlay-put overlay 'before-string
> + (propertize notmuch-tree-overlay-string
> + 'face 'notmuch-tree-overlay-fold-face)))))
> +
> +(defun notmuch-tree-thread-range ()
> + "Return list of Start and End position of the current thread"
> + (let (start end)
> + (save-excursion
> + (while (not (or (notmuch-tree-get-prop :first) (eobp)))
> + (forward-line -1))
> + (setq start (line-end-position))
> + (notmuch-tree-next-thread)
> + (setq end (- (point) 1))
> + (list start end))))
> +
> +(defun notmuch-tree-sub-thread-range ()
> + "Return list of Start and End position of the current sub-thread"
> + (if (notmuch-tree-get-prop :first)
> + (notmuch-tree-thread-range)
> + (let ((level (length (notmuch-tree-get-prop :tree-status)))
> + (start (line-end-position))
> + end)
> + ;; find end position
> + (save-excursion
> + (forward-line)
> + (while (and (< level (length (notmuch-tree-get-prop :tree-status)))
> + (not (eobp)))
> + (forward-line))
> + (setq end (- (point) 1)))
> + (list start end))))
> +
> +(defun notmuch-tree-toggle-folding-thread (&optional arg)
> + "Fold / Unfold the current thread or sub-thread.
> +
> +With prefix arg (C-u) the whole thread is folded"
> + (interactive "p")
> + (cl-multiple-value-bind (start end)
> + (if (and arg (= arg 1))
> + (notmuch-tree-sub-thread-range)
> + (notmuch-tree-thread-range))
> + (unless (= start end)
> + (let ((overlay (notmuch-tree-find-overlay (current-buffer) start
> end)))
> + (if overlay
> + (notmuch-tree-remove-overlay overlay)
> + (notmuch-tree-add-overlay start end))))))
> +
> ;; The next two functions close the message window before calling
> ;; notmuch-search or notmuch-tree but they do so after the user has
> ;; entered the query (in case the user was basing the query on
> @@ -966,6 +1056,9 @@ The arguments are:
> ;; Don't track undo information for this buffer
> (set 'buffer-undo-list t)
>  
> + ;; clean-up overlays in case where some overlays reference to no
> buffer
> + (notmuch-tree-clean-up-overlays)
> +
> (notmuch-tree-worker query query-context target open-target)
>  
> (setq truncate-lines t))
> --
> 2.7.4
>
> _______________________________________________
> notmuch mailing list
> notmuch@notmuchmail.org
> https://notmuchmail.org/mailman/listinfo/notmuch\r

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v2] emacs: tree: support fold/unfold thread or sub-thread
  2021-08-30 19:44 ` inwit
@ 2021-08-31  0:00   ` David Bremner
  2021-08-31  9:37     ` inwit
  0 siblings, 1 reply; 6+ messages in thread
From: David Bremner @ 2021-08-31  0:00 UTC (permalink / raw)
  To: inwit, Julien Masson, notmuch

"inwit" <inwit@sindominio.net> writes:

> After applying the patch manually, I get some warnings and
> the following error:
>
> `emacs/notmuch.el:76:1:Error: Symbol’s value as variable is void:
> notmuch-tree-previous-message-button`
>
> Seems like that function is missing in the current code, am I right?

That function is defined by a macro on line 272 of notmuch-tree.el.

I guess if nobody has a better idea, you can post your work-in-progress
and maybe it will be more clear what is going wrong.

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v2] emacs: tree: support fold/unfold thread or sub-thread
  2021-08-31  0:00   ` David Bremner
@ 2021-08-31  9:37     ` inwit
  2021-08-31 10:59       ` inwit
  0 siblings, 1 reply; 6+ messages in thread
From: inwit @ 2021-08-31  9:37 UTC (permalink / raw)
  To: David Bremner; +Cc: notmuch

On Tue Aug 31, 2021 at 2:00 AM CEST, David Bremner wrote:
> That function is defined by a macro on line 272 of notmuch-tree.el.
Clearly my knowledge of elisp is so far insufficient for this task.

> I guess if nobody has a better idea, you can post your work-in-progress
> and maybe it will be more clear what is going wrong.
I haven't done anything yet. Just tried to manually (and blindly) apply 
the patch, to no avail.

I'll give it another spin.

Regards,

> _______________________________________________
> notmuch mailing list -- notmuch@notmuchmail.org
> To unsubscribe send an email to notmuch-leave@notmuchmail.org

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH v2] emacs: tree: support fold/unfold thread or sub-thread
  2021-08-31  9:37     ` inwit
@ 2021-08-31 10:59       ` inwit
  0 siblings, 0 replies; 6+ messages in thread
From: inwit @ 2021-08-31 10:59 UTC (permalink / raw)
  To: inwit, David Bremner; +Cc: notmuch

On Tue Aug 31, 2021 at 11:37 AM CEST, inwit wrote:
> I'll give it another spin.
Ok, I have managed to merge it manually and now it works.

I'm sending the draft patch now. Sorry if I'm doing it wrong, it's my
first time. Please bear with me.

Regards,


>
> Regards,
>
> > _______________________________________________
> > notmuch mailing list -- notmuch@notmuchmail.org
> > To unsubscribe send an email to notmuch-leave@notmuchmail.org
> _______________________________________________
> notmuch mailing list -- notmuch@notmuchmail.org
> To unsubscribe send an email to notmuch-leave@notmuchmail.org

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2021-08-31 11:00 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-03-25 15:25 [PATCH v2] emacs: tree: support fold/unfold thread or sub-thread Julien Masson
2019-05-03 17:43 ` David Bremner
2021-08-30 19:44 ` inwit
2021-08-31  0:00   ` David Bremner
2021-08-31  9:37     ` inwit
2021-08-31 10:59       ` inwit

Code repositories for project(s) associated with this public inbox

	https://yhetil.org/notmuch.git/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).