From e6cd5a50d1f379c61c2a08722fec3e4e13cd9f71 Mon Sep 17 00:00:00 2001 From: Nicolas Goaziou Date: Sat, 18 Apr 2020 11:30:31 +0200 Subject: [PATCH] Drawer visibility tooling mimics blocks' * lisp/org.el (org-hide-drawer-toggle): New function. (org-flag-drawer): Assume either a parser drawer or buffer positions are provided. Remove unnecessary checks, since this is a low-level function. * testing/lisp/test-org.el (test-org/hide-drawer-toggle): New test. (test-org/flag-drawer): (test-org/show-set-visibility): Update tests. --- lisp/org.el | 75 +++++++++++++++++++++++++++++----------- testing/lisp/test-org.el | 71 ++++++++++++++++++++++++------------- 2 files changed, 102 insertions(+), 44 deletions(-) diff --git a/lisp/org.el b/lisp/org.el index 7576989e8..8f596b9ab 100644 --- a/lisp/org.el +++ b/lisp/org.el @@ -6042,33 +6042,68 @@ a list of strings specifying which drawers should not be hidden." ;; `org-drawer-regexp'. (goto-char (org-element-property :end drawer)))))))))) -(defun org-flag-drawer (flag &optional element beg end) +(defun org-flag-drawer (flag &optional drawer beg end) "When FLAG is non-nil, hide the drawer we are at. Otherwise make it visible. -When optional argument ELEMENT is a parsed drawer, as returned by +When optional argument DRAWER is a parsed drawer, as returned by `org-element-at-point', hide or show that drawer instead. When buffer positions BEG and END are provided, hide or show that -region as a drawer without further ado." - (if (and beg end) (org-flag-region beg end flag 'org-hide-drawer) - (let ((drawer (or element - (and (save-excursion - (beginning-of-line) - (looking-at-p org-drawer-regexp)) - (org-element-at-point))))) - (when (memq (org-element-type drawer) '(drawer property-drawer)) - (let ((post (org-element-property :post-affiliated drawer))) - (org-flag-region - (save-excursion (goto-char post) (line-end-position)) - (save-excursion (goto-char (org-element-property :end drawer)) - (skip-chars-backward " \t\n") - (line-end-position)) - flag 'org-hide-drawer) - ;; When the drawer is hidden away, make sure point lies in - ;; a visible part of the buffer. +region as a drawer without further ado. + +The function assumes either DRAWER, or BEG and END are non-nil." + (let ((beg (save-excursion + (goto-char (or beg + (org-element-property :post-affiliated drawer))) + (line-end-position))) + (end (save-excursion + (goto-char (or end (org-element-property :end drawer))) + (skip-chars-backward " \t\n") + (line-end-position)))) + (org-flag-region beg end flag 'org-hide-drawer))) + +(defun org-hide-drawer-toggle (&optional force no-error element) + "Toggle the visibility of the current drawer. + +When optional argument FORCE is `off', make drawer visible. If +it is non-nil, hide it unconditionally. Throw an error when not +at a drawer, unless NO-ERROR is non-nil. When optional argument +ELEMENT is provided, consider it instead of the current drawer. + +Return a non-nil value when toggling is successful." + (interactive) + (let ((element (or element (org-element-at-point)))) + (cond + ((memq (org-element-type element) '(drawer property-drawer)) + (let* ((post (org-element-property :post-affiliated element)) + (start (save-excursion + (goto-char post) + (line-end-position))) + (end (save-excursion + (goto-char (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))) + (let ((flag + (cond ((eq force 'off) nil) + (force t) + ((eq (get-char-property start 'invisible) + 'org-hide-drawer) + nil) + (t t)))) + (org-flag-drawer flag element)) + ;; When the drawer 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))))))) + (goto-char post)) + ;; Signal success. + t))) + (no-error nil) + (t (user-error "Not at a drawer"))))) ;;;; Visibility cycling diff --git a/testing/lisp/test-org.el b/testing/lisp/test-org.el index 6751610a4..923b3f9ba 100644 --- a/testing/lisp/test-org.el +++ b/testing/lisp/test-org.el @@ -7240,15 +7240,24 @@ CLOCK: [2012-03-29 Thu 10:00]--[2012-03-29 Thu 16:40] => 6:40" ;; Hide drawer. (should (org-test-with-temp-text ":DRAWER:\ncontents\n:END:" - (org-flag-drawer t) + (org-flag-drawer t (org-element-at-point)) + (get-char-property (line-end-position) 'invisible))) + (should + (org-test-with-temp-text ":DRAWER:\ncontents\n:END:" + (org-flag-drawer t nil (point-min) (point-max)) (get-char-property (line-end-position) 'invisible))) ;; Show drawer. (should-not (org-test-with-temp-text ":DRAWER:\ncontents\n:END:" - (org-flag-drawer t) - (org-flag-drawer nil) + (org-flag-drawer t nil (point-min) (point-max)) + (org-flag-drawer nil nil (point-min) (point-max)) (get-char-property (line-end-position) 'invisible))) - ;; Test optional argument. + (should-not + (org-test-with-temp-text ":DRAWER:\ncontents\n:END:" + (org-flag-drawer t nil (point-min) (point-max)) + (org-flag-drawer nil (org-element-at-point)) + (get-char-property (line-end-position) 'invisible))) + ;; Hide drawer remotely. (should (org-test-with-temp-text "Text\n:D1:\nc1\n:END:\n\n:D2:\nc2\n:END:" (let ((drawer (save-excursion (search-forward ":D2") @@ -7261,32 +7270,46 @@ CLOCK: [2012-03-29 Thu 10:00]--[2012-03-29 Thu 16:40] => 6:40" (let ((drawer (save-excursion (search-forward ":D2") (org-element-at-point)))) (org-flag-drawer t drawer) - (get-char-property (line-end-position) 'invisible)))) - ;; Do not hide fake drawers. - (should-not - (org-test-with-temp-text "#+begin_example\n:D:\nc\n:END:\n#+end_example" - (forward-line 1) - (org-flag-drawer t) + (get-char-property (line-end-position) 'invisible))))) + +(ert-deftest test-org/hide-drawer-toggle () + "Test `org-hide-drawer-toggle' specifications." + ;; Error when not at a drawer. + (should-error + (org-test-with-temp-text ":fake-drawer:\ncontents" + (org-hide-drawer-toggle 'off) (get-char-property (line-end-position) 'invisible))) - ;; Do not hide incomplete drawers. - (should-not - (org-test-with-temp-text ":D:\nparagraph" - (forward-line 1) - (org-flag-drawer t) + (should-error + (org-test-with-temp-text + "#+begin_example\n:D:\nc\n:END:\n#+end_example" + (org-hide-drawer-toggle t))) + ;; Hide drawer. + (should + (org-test-with-temp-text ":drawer:\ncontents\n:end:" + (org-hide-drawer-toggle) (get-char-property (line-end-position) 'invisible))) - ;; Do not hide drawers when called from final blank lines. + ;; Show drawer unconditionally when optional argument is `off'. (should-not - (org-test-with-temp-text ":DRAWER:\nA\n:END:\n\n" - (goto-char (point-max)) - (org-flag-drawer t) + (org-test-with-temp-text ":drawer:\ncontents\n:end:" + (org-hide-drawer-toggle) + (org-hide-drawer-toggle 'off) + (get-char-property (line-end-position) 'invisible))) + ;; Hide drawer unconditionally when optional argument is non-nil. + (should + (org-test-with-temp-text ":drawer:\ncontents\n:end:" + (org-hide-drawer-toggle t) + (get-char-property (line-end-position) 'invisible))) + ;; Do not hide drawer when called from final blank lines. + (should-not + (org-test-with-temp-text ":drawer:\ncontents\n:end:\n\n" + (org-hide-drawer-toggle) (goto-char (point-min)) (get-char-property (line-end-position) 'invisible))) ;; Don't leave point in an invisible part of the buffer when hiding ;; a drawer away. (should-not - (org-test-with-temp-text ":DRAWER:\ncontents\n:END:" - (goto-char (point-max)) - (org-flag-drawer t) + (org-test-with-temp-text ":drawer:\ncontents\n:end:" + (org-hide-drawer-toggle) (get-char-property (point) 'invisible)))) (ert-deftest test-org/hide-block-toggle () @@ -7406,14 +7429,14 @@ CLOCK: [2012-03-29 Thu 10:00]--[2012-03-29 Thu 16:40] => 6:40" (org-invisible-p2))) (should-not (org-test-with-temp-text ":DRAWER:\nText\n:END:" - (org-flag-drawer t) + (org-hide-drawer-toggle) (search-forward "Text") (org-show-set-visibility 'minimal) (org-invisible-p2))) (should-not (org-test-with-temp-text "#+BEGIN_QUOTE\n:DRAWER:\nText\n:END:\n#+END_QUOTE" - (org-flag-drawer t) + (org-hide-drawer-toggle) (forward-line -1) (org-hide-block-toggle) (search-forward "Text")