doomemacs/modules/editor/fold/autoload/fold.el
Henrik Lissner 3a89c3f79d
editor/fold: fix void-variable hs-block-start-regexp
Which will happen if +fold/next is called before hideshow is loaded.
2019-10-14 13:19:32 -04:00

156 lines
5.0 KiB
EmacsLisp

;;; editor/fold/autoload/fold.el -*- lexical-binding: t; -*-
;; `hideshow' is a decent code folding implementation, but it won't let you
;; create custom folds. `vimish-fold' offers custom folds, but essentially
;; ignores any other type of folding (indent or custom markers, which hideshow
;; and `outline-mode' give you). This is my effort to combine them.
;;
;;; Helpers
(defun +fold--ensure-hideshow-mode ()
(unless (bound-and-true-p hs-minor-mode)
(hs-minor-mode +1)))
(defun +fold--vimish-fold-p ()
(and (featurep 'vimish-fold)
(cl-some #'vimish-fold--vimish-overlay-p
(overlays-at (point)))))
(defun +fold--outline-fold-p ()
(and (or (bound-and-true-p outline-minor-mode)
(derived-mode-p 'outline-mode))
(outline-on-heading-p)))
(defun +fold--hideshow-fold-p ()
(+fold--ensure-hideshow-mode)
(save-excursion
(ignore-errors
(or (hs-looking-at-block-start-p)
(hs-find-block-beginning)))))
(defun +fold--invisible-points (count)
(let (points)
(save-excursion
(catch 'abort
(if (< count 0) (beginning-of-line))
(while (re-search-forward hs-block-start-regexp nil t
(if (> count 0) 1 -1))
(unless (invisible-p (point))
(end-of-line)
(when (hs-already-hidden-p)
(push (point) points)
(when (>= (length points) count)
(throw 'abort nil))))
(forward-line (if (> count 0) 1 -1)))))
points))
(defmacro +fold-from-eol (&rest body)
"Perform action after moving to the end of the line."
`(save-excursion
(end-of-line)
,@body))
;;
;;; Commands
;;;###autoload
(defun +fold/toggle ()
"Toggle the fold at point.
Targets `vimmish-fold', `hideshow' and `outline' folds."
(interactive)
(save-excursion
(cond ((+fold--vimish-fold-p) (vimish-fold-toggle))
((+fold--outline-fold-p)
(cl-letf (((symbol-function #'outline-hide-subtree)
(symbol-function #'outline-hide-entry)))
(outline-toggle-children)))
((+fold--hideshow-fold-p) (+fold-from-eol (hs-toggle-hiding))))))
;;;###autoload
(defun +fold/open ()
"Open the folded region at point.
Targets `vimmish-fold', `hideshow' and `outline' folds."
(interactive)
(save-excursion
(cond ((+fold--vimish-fold-p) (vimish-fold-unfold))
((+fold--outline-fold-p)
(outline-show-children)
(outline-show-entry))
((+fold--hideshow-fold-p) (+fold-from-eol (hs-show-block))))))
;;;###autoload
(defun +fold/close ()
"Close the folded region at point.
Targets `vimmish-fold', `hideshow' and `outline' folds."
(interactive)
(save-excursion
(cond ((+fold--vimish-fold-p) (vimish-fold-refold))
((+fold--hideshow-fold-p) (+fold-from-eol (hs-hide-block)))
((+fold--outline-fold-p) (outline-hide-subtree)))))
;;;###autoload
(defun +fold/open-all (&optional level)
"Open folds at LEVEL (or all folds if LEVEL is nil)."
(interactive
(list (if current-prefix-arg (prefix-numeric-value current-prefix-arg))))
(when (featurep 'vimish-fold)
(vimish-fold-unfold-all))
(save-excursion
(+fold--ensure-hideshow-mode)
(if (integerp level)
(progn
(outline-hide-sublevels (max 1 (1- level)))
(hs-life-goes-on
(hs-hide-level-recursive (1- level) (point-min) (point-max))))
(hs-show-all)
(when (fboundp 'outline-show-all)
(outline-show-all)))))
;;;###autoload
(defun +fold/close-all (&optional level)
"Close folds at LEVEL (or all folds if LEVEL is nil)."
(interactive
(list (if current-prefix-arg (prefix-numeric-value current-prefix-arg))))
(save-excursion
(when (featurep 'vimish-fold)
(vimish-fold-refold-all))
(+fold--ensure-hideshow-mode)
(hs-life-goes-on
(if (integerp level)
(hs-hide-level-recursive (1- level) (point-min) (point-max))
(hs-hide-all)))))
;;;###autoload
(defun +fold/next (count)
"Jump to the next vimish fold, outline heading or folded region."
(interactive "p")
(cl-loop with orig-pt = (point)
for fn
in (list (lambda ()
(when (bound-and-true-p hs-block-start-regexp)
(car (+fold--invisible-points count))))
(lambda ()
(when (featurep 'vimish-fold)
(if (> count 0)
(evil-vimish-fold/next-fold count)
(evil-vimish-fold/previous-fold (- count))))
(if (/= (point) orig-pt) (point))))
if (save-excursion (funcall fn))
collect it into points
finally do
(if-let* ((pt (car (sort points (if (> count 0) #'< #'>)))))
(goto-char pt)
(message "No more folds %s point" (if (> count 0) "after" "before"))
(goto-char orig-pt))))
;;;###autoload
(defun +fold/previous (count)
"Jump to the previous vimish fold, outline heading or folded region."
(interactive "p")
(+fold/next (- count)))