outline循环折叠

干净基本版outshine 继续讨论:

outshine可以像org一样在三种折叠状态之间循环:全折叠,只显示子标题,全显示。

我写了两个函数实现这个功能:outline-cycleoutline-cycle-buffer

代码见下,也可以在

找到。

(defun outline--cycle-state ()
  "Return the cycle state of current heading.
Return either 'hide-all, 'headings-only, or 'show-all."
  (save-excursion
    (let (start end ov-list heading-end)
      (outline-back-to-heading)
      (setq start (point))
      (outline-end-of-heading)
      (setq heading-end (point))
      (outline-end-of-subtree)
      (setq end (point))
      (setq ov-list (cl-remove-if-not
                     (lambda (o) (eq (overlay-get o 'invisible) 'outline))
                     (overlays-in start end)))
      (cond ((eq ov-list nil) 'show-all)
            ;; (eq (length ov-list) 1) wouldn’t work: what if there is
            ;; one folded subheading?
            ((and (eq (overlay-end (car ov-list)) end)
                  (eq (overlay-start (car ov-list)) heading-end))
             'hide-all)
            (t 'headings-only)))))

(defun outline-has-subheading-p ()
  "Return t if this heading has subheadings, nil otherwise."
  (save-excursion
    (outline-back-to-heading)
    (< (save-excursion (outline-next-heading) (point))
       (save-excursion (outline-end-of-subtree) (point)))))

(defun outline-cycle ()
  "Cycle between “hide all”, “headings only” and “show all”.

“Hide all” means hide all subheadings and their bodies.
“Headings only” means show sub headings but not their bodies.
“Show all” means show all subheadings and their bodies."
  (interactive)
  (pcase (outline--cycle-state)
    ('hide-all (if (outline-has-subheading-p)
                   (progn (outline-show-children)
                          (message "Only headings"))
                 (outline-show-subtree)
                 (message "Show all")))
    ('headings-only (outline-show-subtree)
                    (message "Show all"))
    ('show-all (outline-hide-subtree)
               (message "Hide all"))))

(defvar-local outline--cycle-buffer-state 'show-all
  "Interval variable used for tracking buffer cycle state.")

(defun outline-cycle-buffer ()
  "Cycle the whole buffer like in ‘outline-cycle’."
  (interactive)
  (pcase outline--cycle-buffer-state
    ('show-all (save-excursion
                 (let ((start-point (point)))
                   (while (not (eq (point) start-point))
                     (outline-up-heading 1))
                   (outline-hide-sublevels
                    (progn (outline-back-to-heading)
                           (funcall 'outline-level)))))
               (setq outline--cycle-buffer-state 'top-level)
               (message "Top level headings"))
    ('top-level (outline-show-all)
                (outline-hide-region-body (point-min) (point-max))
                (setq outline--cycle-buffer-state 'all-heading)
                (message "All headings"))
    ('all-heading (outline-show-all)
                  (setq outline--cycle-buffer-state 'show-all)
                  (message "Show all"))))