From 62ef11504f361d058c5425ad5e1184be6e19bd6f Mon Sep 17 00:00:00 2001 From: Hugo Heagren Date: Mon, 12 Dec 2022 11:25:14 +0000 Subject: [PATCH 1/2] lisp/org-fold.el: Fold header lines in blocks * lisp/org-fold.el (org-fold--hide-wrapper-toggle): Cycle blocks between three folding states, potentially including headers in folding. Update docstring accordingly. --- lisp/org-fold.el | 65 +++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 53 insertions(+), 12 deletions(-) diff --git a/lisp/org-fold.el b/lisp/org-fold.el index 05ac71ea4..21cdc6fad 100644 --- a/lisp/org-fold.el +++ b/lisp/org-fold.el @@ -478,14 +478,27 @@ heading to appear." ;;;;; Blocks and drawers visibility (defun org-fold--hide-wrapper-toggle (element category force no-error) - "Toggle visibility for ELEMENT. + "Cycle visibility for ELEMENT. ELEMENT is a block or drawer type parsed element. CATEGORY is either `block' or `drawer'. When FORCE is `off', show the block or drawer. If it is non-nil, hide it unconditionally. Throw an error when not at a block or drawer, unless NO-ERROR is non-nil. -Return a non-nil value when toggling is successful." +A property drawer will cycle between open and closed states. + +A block with not extra header arguments (lines beginning +`#+header:', `#+name:' etc. directly above it) will cycle between +open and closed states. A block with such headers will cycle: +- everything closed (with only the top header visible) +- header stack folded (top header visible, other headers hidden, body + and content of block visible) +- everything open + +When there is only one header argument, no distinction is made +between the the second and third states. + +Return a non-nil value when cycling is successful." (let ((type (org-element-type element))) (cond ((memq type @@ -495,28 +508,56 @@ Return a non-nil value when toggling is successful." comment-block dynamic-block example-block export-block quote-block special-block src-block verse-block)) (_ (error "Unknown category: %S" category)))) - (let* ((post (org-element-property :post-affiliated element)) + (let* ((post (org-element-property :post-affiliated element)) ; bol of begin_x + (begin (org-element-property :begin element)) ; bol of first header + (content-begin ; bol of block content + (1+ (save-excursion (goto-char post) (line-end-position)))) + (state + (cond + ;; Everything is folded + ((org-fold-folded-p content-begin 'block) 'all) + ;; Everything is open + (t 'nothing))) (start (save-excursion - (goto-char post) + (goto-char + (if (equal category 'block) + (pcase state + ;; If there are no headers, start unfolding + ;; from the char before the char at bol + ;; of `begin_x', so that `begin_x' is + ;; displayed after unfolding. Otherwise + ;; begin unfolding from bol of that line. + ('all (if (= post begin) post (1- post))) + ('nothing begin)) + post)) (line-end-position))) (end (save-excursion - (goto-char (org-element-property :end element)) + (goto-char + (if (equal category 'block) + (pcase state + ('all (org-element-property :end element)) + ('nothing (org-element-property :end element))) + (org-element-property :end element))) (skip-chars-backward " \t\n") (line-end-position)))) ;; Do nothing when not before or at the block opening line or ;; at the block closing line. (unless (let ((eol (line-end-position))) - (and (> eol start) (/= eol end))) + (and (> eol content-begin) (/= eol end))) (org-fold-region start end - (cond ((eq force 'off) nil) - (force t) - ((org-fold-folded-p start category) nil) - (t t)) - category) + (cond ((eq force 'off) nil) + (force t) + ((org-fold-folded-p start category) nil) + (t t)) + category) ;; When the block is hidden away, make sure point is left in ;; a visible part of the buffer. (when (invisible-p (max (1- (point)) (point-min))) - (goto-char post)) + ;; The only state transition which hides point is from + ;; everything open to everything folded. In this case, the + ;; best place to leave point is at bol of first visible + ;; line, which is `begin'. + (goto-char begin)) ;; Signal success. t))) (no-error nil) -- 2.20.1