Skip to content

Commit ccee213

Browse files
committed
feat: outline
1 parent ea9a545 commit ccee213

File tree

8 files changed

+205
-41
lines changed

8 files changed

+205
-41
lines changed

Eask

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
(source 'melpa)
1717
(source 'jcs-elpa)
1818

19+
(source-priority 'jcs-elpa 90)
20+
1921
(depends-on "emacs" "26.1")
2022
(depends-on "elenv")
2123
(depends-on "fringe-helper")

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
A list of supported backends:
1010

11+
- [outline-minor-mode][]
1112
- [hideshow][]
1213
- [origami.el][]
1314
- [ts-fold][] / [treesit-fold][]
@@ -86,6 +87,7 @@ See [`LICENSE`](./LICENSE) for details.
8687

8788
<!-- Links -->
8889

90+
[outline-minor-mode]: https://www.gnu.org/software/emacs/manual/html_node/emacs/Outline-Minor-Mode.html
8991
[hideshow]: https://www.gnu.org/software/emacs/manual/html_node/emacs/Hideshow.html
9092

9193
[origami.el]: https://github.com/gregsexton/origami.el

foldvis-hideshow.el

Lines changed: 39 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@
4444
(remove-hook 'hs-hide-hook #'foldvis-hideshow--refresh t))
4545

4646
;;;###autoload
47-
(defun foldvis-hideshow--valid ()
47+
(defun foldvis-hideshow--valid-p ()
4848
"Return non-nil if the backend is valid."
4949
(and (featurep 'hideshow) hs-minor-mode))
5050

@@ -62,11 +62,11 @@
6262
;; XXX: This section is mostly copy-pasted code. It’s admittedly messy,
6363
;; but I’m too lazy to clean it up right now.
6464

65-
(defvar foldvis-hideshow--nodes nil
65+
(defvar-local foldvis-hideshow--nodes nil
6666
"Store a list of nodes to fold.")
6767

6868
(defun foldvis-hideshow--make-overlay (b e kind &optional b-offset e-offset)
69-
"Modified from the function `hs-make-overlay'."
69+
"To override the function `hs-make-overlay'."
7070
(push (list b e kind b-offset e-offset) foldvis-hideshow--nodes))
7171

7272
(defun foldvis-hideshow--hide-comment-region (beg end &optional repos-end)
@@ -106,42 +106,44 @@
106106
(foldvis-hideshow--make-overlay p q 'code (- header-end p)))
107107
(goto-char (if end q (min p header-end)))))))
108108

109-
(defun foldvis-hideshow--nodes ()
110-
"Return a list of foldable nodes.
109+
(defun foldvis-hideshow--hide-all ()
110+
"Modified from the function `hs-hide-all'."
111+
(hs-life-goes-on
112+
(save-excursion
113+
(goto-char (point-min))
114+
(syntax-propertize (point-max))
115+
(let ((re (concat "\\("
116+
hs-block-start-regexp
117+
"\\)"
118+
(if hs-hide-comments-when-hiding-all
119+
(concat "\\|\\("
120+
hs-c-start-regexp
121+
"\\)")
122+
""))))
123+
(while (funcall hs-find-next-block-func re (point-max)
124+
hs-hide-comments-when-hiding-all)
125+
(if (match-beginning 1)
126+
;; We have found a block beginning.
127+
(progn
128+
(goto-char (match-beginning 1))
129+
(unless (if hs-hide-all-non-comment-function
130+
(funcall hs-hide-all-non-comment-function)
131+
(foldvis-hideshow--hide-block-at-point t))
132+
;; Go to end of matched data to prevent from getting stuck
133+
;; with an endless loop.
134+
(when (looking-at hs-block-start-regexp)
135+
(goto-char (match-end 0)))))
136+
;; found a comment, probably
137+
(let ((c-reg (hs-inside-comment-p)))
138+
(when (and c-reg (car c-reg))
139+
(if (> (count-lines (car c-reg) (nth 1 c-reg)) 1)
140+
(foldvis-hideshow--hide-block-at-point t c-reg)
141+
(goto-char (nth 1 c-reg)))))))))))
111142

112-
Modified from the function `hs-hide-all'."
143+
(defun foldvis-hideshow--nodes ()
144+
"Return a list of foldable nodes."
113145
(let (foldvis-hideshow--nodes)
114-
(hs-life-goes-on
115-
(save-excursion
116-
(goto-char (point-min))
117-
(syntax-propertize (point-max))
118-
(let ((re (concat "\\("
119-
hs-block-start-regexp
120-
"\\)"
121-
(if hs-hide-comments-when-hiding-all
122-
(concat "\\|\\("
123-
hs-c-start-regexp
124-
"\\)")
125-
""))))
126-
(while (funcall hs-find-next-block-func re (point-max)
127-
hs-hide-comments-when-hiding-all)
128-
(if (match-beginning 1)
129-
;; We have found a block beginning.
130-
(progn
131-
(goto-char (match-beginning 1))
132-
(unless (if hs-hide-all-non-comment-function
133-
(funcall hs-hide-all-non-comment-function)
134-
(foldvis-hideshow--hide-block-at-point t))
135-
;; Go to end of matched data to prevent from getting stuck
136-
;; with an endless loop.
137-
(when (looking-at hs-block-start-regexp)
138-
(goto-char (match-end 0)))))
139-
;; found a comment, probably
140-
(let ((c-reg (hs-inside-comment-p)))
141-
(when (and c-reg (car c-reg))
142-
(if (> (count-lines (car c-reg) (nth 1 c-reg)) 1)
143-
(foldvis-hideshow--hide-block-at-point t c-reg)
144-
(goto-char (nth 1 c-reg))))))))))
146+
(ignore-errors (foldvis-hideshow--hide-all))
145147
foldvis-hideshow--nodes))
146148

147149
;;

foldvis-origami.el

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@
4141
;;; Entry
4242

4343
;;;###autoload
44-
(defun foldvis-origami--valid ()
44+
(defun foldvis-origami--valid-p ()
4545
"Return non-nil if the backend is valid."
4646
(and (featurep 'origami) origami-mode))
4747

foldvis-outline.el

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
;;; foldvis-outline.el --- Display indicators for outline -*- lexical-binding: t; -*-
2+
3+
;; Copyright (C) 2025 Shen, Jen-Chieh
4+
5+
;; This file is not part of GNU Emacs.
6+
7+
;; This program is free software: you can redistribute it and/or modify
8+
;; it under the terms of the GNU General Public License as published by
9+
;; the Free Software Foundation, either version 3 of the License, or
10+
;; (at your option) any later version.
11+
12+
;; This program is distributed in the hope that it will be useful,
13+
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
;; GNU General Public License for more details.
16+
17+
;; You should have received a copy of the GNU General Public License
18+
;; along with this program. If not, see <https://www.gnu.org/licenses/>.
19+
20+
;;; Commentary:
21+
;;
22+
;; Display indicators for outline.
23+
;;
24+
25+
;;; Code:
26+
27+
(require 'outline)
28+
29+
(require 'foldvis)
30+
31+
;;
32+
;;; Entry
33+
34+
;;;###autoload
35+
(defun foldvis-outline--enable ()
36+
"Enable the folding minor mode."
37+
(add-hook 'outline-view-change-hook #'foldvis-hideshow--refresh nil t))
38+
39+
;;;###autoload
40+
(defun foldvis-outline--disable ()
41+
"Disable the folding minor mode."
42+
(remove-hook 'outline-view-change-hook #'foldvis-hideshow--refresh t))
43+
44+
;;;###autoload
45+
(defun foldvis-outline--valid-p ()
46+
"Return non-nil if the backend is valid."
47+
(and (featurep 'outline) outline-minor-mode))
48+
49+
;;
50+
;;; Events
51+
52+
;;;###autoload
53+
(defun foldvis-outline--toggle ()
54+
"Event to toggle folding on and off."
55+
(outline-toggle-children))
56+
57+
;;
58+
;;; Overwrite
59+
60+
(defvar-local foldvis-outline--nodes nil
61+
"Store a list of nodes to fold.")
62+
63+
(defun foldvis-outline--flag-region (from to flag)
64+
"To override the function `outline-flag-region'."
65+
(push (list from to flag) foldvis-outline--nodes))
66+
67+
(defun foldvis-outline--show-heading ()
68+
""
69+
(foldvis-outline--flag-region (- (point)
70+
(if (bobp) 0
71+
(if (and outline-blank-line
72+
(eq (char-before (1- (point))) ?\n))
73+
2 1)))
74+
(progn (outline-end-of-heading) (point))
75+
nil))
76+
77+
(defun foldvis-outline--hide-sublevels (levels)
78+
""
79+
(if (< levels 1)
80+
(error "Must keep at least one level of headers"))
81+
(save-excursion
82+
(let* (outline-view-change-hook
83+
(beg (progn
84+
(goto-char (point-min))
85+
;; Skip the prelude, if any.
86+
(unless (outline-on-heading-p t) (outline-next-heading))
87+
(point)))
88+
(end (progn
89+
(goto-char (point-max))
90+
;; Keep empty last line, if available.
91+
(if (bolp) (1- (point)) (point)))))
92+
(if (< end beg)
93+
(setq beg (prog1 end (setq end beg))))
94+
;; First hide everything.
95+
(foldvis-outline--flag-region beg end t)
96+
;; Then unhide the top level headers.
97+
(outline-map-region
98+
(lambda ()
99+
(if (<= (funcall outline-level) levels)
100+
(foldvis-outline--show-heading)))
101+
beg end)
102+
;; Finally unhide any trailing newline.
103+
(goto-char (point-max))
104+
(if (and (bolp) (not (bobp)) (outline-invisible-p (1- (point))))
105+
(foldvis-outline--flag-region (1- (point)) (point) nil)))))
106+
107+
(defun foldvis-outline--nodes ()
108+
"Return a list of foldable nodes."
109+
(let (outline-view-change-hook
110+
foldvis-outline--nodes)
111+
(ignore-errors (foldvis-outline--hide-sublevels 1))
112+
foldvis-outline--nodes))
113+
114+
;;
115+
;;; Core
116+
117+
(defun foldvis-outline--create (node)
118+
"Create indicators using NODE."
119+
(when-let* ((beg (nth 0 node))
120+
(end (nth 1 node)))
121+
(let ((folded (not (nth 2 node))))
122+
(foldvis--create-overlays beg end folded))))
123+
124+
(defun foldvis-outline--within-window (node wend wstart)
125+
"Return nil if NODE is not within the current window display range.
126+
127+
Arguments WEND and WSTART are the range for caching."
128+
(when-let*
129+
((range (cl-case foldvis-render-method
130+
((or full partial) (cons (nth 0 node)
131+
(nth 1 node)))
132+
(t
133+
(user-error "Invalid render method: %s" foldvis-render-method))))
134+
(start (car range))
135+
(end (cdr range))
136+
((or (and (<= wstart start) (<= end wend)) ; with in range
137+
(and (<= wstart end) (<= start wstart)) ; just one above
138+
(and (<= wend end) (<= start wend))))) ; just one below
139+
node))
140+
141+
;;;###autoload
142+
(defun foldvis-outline--refresh (&rest _)
143+
"Refresh indicators for all folding range."
144+
(when outline-minor-mode
145+
(when-let* ((nodes-to-fold (foldvis-outline--nodes))
146+
(wend (window-end nil t))
147+
(wstart (window-start))
148+
(nodes-to-fold
149+
(cl-remove-if-not (lambda (node)
150+
(foldvis-outline--within-window node wend wstart))
151+
nodes-to-fold)))
152+
(foldvis--remove-ovs)
153+
(thread-last nodes-to-fold
154+
(mapc #'foldvis-outline--create)))
155+
(run-hooks 'foldvis-refresh-hook)))
156+
157+
(provide 'foldvis-outline)
158+
;;; foldvis-outline.el ends here

foldvis-treesit-fold.el

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
(advice-remove 'treesit-fold--after-command #'foldvis-refresh))
5656

5757
;;;###autoload
58-
(defun foldvis-treesit-fold--valid ()
58+
(defun foldvis-treesit-fold--valid-p ()
5959
"Return non-nil if the backend is valid."
6060
(and (featurep 'treesit-fold) treesit-fold-mode))
6161

foldvis-ts-fold.el

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@
6363
(advice-remove 'ts-fold--after-command #'foldvis-refresh))
6464

6565
;;;###autoload
66-
(defun foldvis-ts-fold--valid ()
66+
(defun foldvis-ts-fold--valid-p ()
6767
"Return non-nil if the backend is valid."
6868
(and (featurep 'ts-fold) ts-fold-mode))
6969

foldvis.el

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ See macro `with-selected-window' description for arguments WINDOW and BODY."
168168
(defun foldvis--choose-backend ()
169169
"Set the current possible backend."
170170
(let* ((backend (cl-some (lambda (x)
171-
(when (foldvis--call-backend "-valid" x)
171+
(when (foldvis--call-backend "-valid-p" x)
172172
x))
173173
foldvis-backends))
174174
(changed (and foldvis--backend backend

0 commit comments

Comments
 (0)