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.
This commit is contained in:
Nicolas Goaziou 2020-04-18 11:30:31 +02:00
parent e515a7cec8
commit e6cd5a50d1
2 changed files with 102 additions and 44 deletions

View File

@ -6042,33 +6042,68 @@ a list of strings specifying which drawers should not be hidden."
;; `org-drawer-regexp'. ;; `org-drawer-regexp'.
(goto-char (org-element-property :end drawer)))))))))) (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. "When FLAG is non-nil, hide the drawer we are at.
Otherwise make it visible. 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. `org-element-at-point', hide or show that drawer instead.
When buffer positions BEG and END are provided, hide or show that When buffer positions BEG and END are provided, hide or show that
region as a drawer without further ado." region as a drawer without further ado.
(if (and beg end) (org-flag-region beg end flag 'org-hide-drawer)
(let ((drawer (or element The function assumes either DRAWER, or BEG and END are non-nil."
(and (save-excursion (let ((beg (save-excursion
(beginning-of-line) (goto-char (or beg
(looking-at-p org-drawer-regexp)) (org-element-property :post-affiliated drawer)))
(org-element-at-point))))) (line-end-position)))
(when (memq (org-element-type drawer) '(drawer property-drawer)) (end (save-excursion
(let ((post (org-element-property :post-affiliated drawer))) (goto-char (or end (org-element-property :end drawer)))
(org-flag-region (skip-chars-backward " \t\n")
(save-excursion (goto-char post) (line-end-position)) (line-end-position))))
(save-excursion (goto-char (org-element-property :end drawer)) (org-flag-region beg end flag 'org-hide-drawer)))
(skip-chars-backward " \t\n")
(line-end-position)) (defun org-hide-drawer-toggle (&optional force no-error element)
flag 'org-hide-drawer) "Toggle the visibility of the current drawer.
;; When the drawer is hidden away, make sure point lies in
;; a visible part of the buffer. 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))) (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 ;;;; Visibility cycling

View File

@ -7240,15 +7240,24 @@ CLOCK: [2012-03-29 Thu 10:00]--[2012-03-29 Thu 16:40] => 6:40"
;; Hide drawer. ;; Hide drawer.
(should (should
(org-test-with-temp-text ":DRAWER:\ncontents\n:END:" (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))) (get-char-property (line-end-position) 'invisible)))
;; Show drawer. ;; Show drawer.
(should-not (should-not
(org-test-with-temp-text ":DRAWER:\ncontents\n:END:" (org-test-with-temp-text ":DRAWER:\ncontents\n:END:"
(org-flag-drawer t) (org-flag-drawer t nil (point-min) (point-max))
(org-flag-drawer nil) (org-flag-drawer nil nil (point-min) (point-max))
(get-char-property (line-end-position) 'invisible))) (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 (should
(org-test-with-temp-text "Text\n:D1:\nc1\n:END:\n\n:D2:\nc2\n:END:" (org-test-with-temp-text "Text\n:D1:\nc1\n:END:\n\n:D2:\nc2\n:END:"
(let ((drawer (save-excursion (search-forward ":D2") (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") (let ((drawer (save-excursion (search-forward ":D2")
(org-element-at-point)))) (org-element-at-point))))
(org-flag-drawer t drawer) (org-flag-drawer t drawer)
(get-char-property (line-end-position) 'invisible)))) (get-char-property (line-end-position) 'invisible)))))
;; Do not hide fake drawers.
(should-not (ert-deftest test-org/hide-drawer-toggle ()
(org-test-with-temp-text "#+begin_example\n:D:\nc\n:END:\n#+end_example" "Test `org-hide-drawer-toggle' specifications."
(forward-line 1) ;; Error when not at a drawer.
(org-flag-drawer t) (should-error
(org-test-with-temp-text ":fake-drawer:\ncontents"
(org-hide-drawer-toggle 'off)
(get-char-property (line-end-position) 'invisible))) (get-char-property (line-end-position) 'invisible)))
;; Do not hide incomplete drawers. (should-error
(should-not (org-test-with-temp-text
(org-test-with-temp-text ":D:\nparagraph" "#+begin_example\n<point>:D:\nc\n:END:\n#+end_example"
(forward-line 1) (org-hide-drawer-toggle t)))
(org-flag-drawer t) ;; Hide drawer.
(should
(org-test-with-temp-text ":drawer:\ncontents\n:end:"
(org-hide-drawer-toggle)
(get-char-property (line-end-position) 'invisible))) (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 (should-not
(org-test-with-temp-text ":DRAWER:\nA\n:END:\n\n" (org-test-with-temp-text ":drawer:\ncontents\n:end:"
(goto-char (point-max)) (org-hide-drawer-toggle)
(org-flag-drawer t) (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<point>"
(org-hide-drawer-toggle)
(goto-char (point-min)) (goto-char (point-min))
(get-char-property (line-end-position) 'invisible))) (get-char-property (line-end-position) 'invisible)))
;; Don't leave point in an invisible part of the buffer when hiding ;; Don't leave point in an invisible part of the buffer when hiding
;; a drawer away. ;; a drawer away.
(should-not (should-not
(org-test-with-temp-text ":DRAWER:\ncontents\n:END:" (org-test-with-temp-text ":drawer:\ncontents\n<point>:end:"
(goto-char (point-max)) (org-hide-drawer-toggle)
(org-flag-drawer t)
(get-char-property (point) 'invisible)))) (get-char-property (point) 'invisible))))
(ert-deftest test-org/hide-block-toggle () (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))) (org-invisible-p2)))
(should-not (should-not
(org-test-with-temp-text ":DRAWER:\nText\n:END:" (org-test-with-temp-text ":DRAWER:\nText\n:END:"
(org-flag-drawer t) (org-hide-drawer-toggle)
(search-forward "Text") (search-forward "Text")
(org-show-set-visibility 'minimal) (org-show-set-visibility 'minimal)
(org-invisible-p2))) (org-invisible-p2)))
(should-not (should-not
(org-test-with-temp-text (org-test-with-temp-text
"#+BEGIN_QUOTE\n<point>:DRAWER:\nText\n:END:\n#+END_QUOTE" "#+BEGIN_QUOTE\n<point>:DRAWER:\nText\n:END:\n#+END_QUOTE"
(org-flag-drawer t) (org-hide-drawer-toggle)
(forward-line -1) (forward-line -1)
(org-hide-block-toggle) (org-hide-block-toggle)
(search-forward "Text") (search-forward "Text")