diff options
| author | Yuan Fu | 2020-10-13 05:14:21 +0200 |
|---|---|---|
| committer | Lars Ingebrigtsen | 2020-10-13 05:14:21 +0200 |
| commit | 1b45079ffa2d0b8f66f77cdcf1af2d3d08a5515b (patch) | |
| tree | 80defa6ea620b738d19e9e4f4e6c8a70d22c0963 | |
| parent | b31e48d4efb030b59a9058796c2da53357c379a3 (diff) | |
| download | emacs-1b45079ffa2d0b8f66f77cdcf1af2d3d08a5515b.tar.gz emacs-1b45079ffa2d0b8f66f77cdcf1af2d3d08a5515b.zip | |
Add cycling commands to outline
* lisp/outline.el (outline--cycle-state, outline-has-subheading-p)
(outline-cycle, outline-cycle-buffer): New functions.
(outline-mode-map): Add key bindings for the two new commands.
(outline--cycle-buffer-state): New variable.
* doc/emacs/text.text (Outline Visibility): Add 'outline-cycle' and
'outline-cycle-buffer'.
* etc/NEWS (Outline): Record the change (bug#41130).
| -rw-r--r-- | doc/emacs/text.texi | 10 | ||||
| -rw-r--r-- | etc/NEWS | 9 | ||||
| -rw-r--r-- | lisp/outline.el | 83 |
3 files changed, 102 insertions, 0 deletions
diff --git a/doc/emacs/text.texi b/doc/emacs/text.texi index 281e24421c2..9c2822ce15b 100644 --- a/doc/emacs/text.texi +++ b/doc/emacs/text.texi | |||
| @@ -1207,6 +1207,16 @@ everything except the top @var{n} levels of heading lines. Note that | |||
| 1207 | it completely reveals all the @var{n} top levels and the body lines | 1207 | it completely reveals all the @var{n} top levels and the body lines |
| 1208 | before the first heading. | 1208 | before the first heading. |
| 1209 | 1209 | ||
| 1210 | @findex outline-cycle | ||
| 1211 | @findex outline-cycle-buffer | ||
| 1212 | Outline also provides two convenience commands to cycle the | ||
| 1213 | visibility of each section and the whole buffer. Typing @kbd{TAB} on | ||
| 1214 | a heading invokes @code{outline-cycle}, which cycles the current | ||
| 1215 | section between "hide all", "subheadings", and "show all" state. | ||
| 1216 | Typing @kbd{S-TAB} invokes @code{outline-cycle-buffer}, which cycles | ||
| 1217 | the whole buffer between "only top-level headings", "all headings and | ||
| 1218 | subheadings", and "show all" states. | ||
| 1219 | |||
| 1210 | @anchor{Outline Search} | 1220 | @anchor{Outline Search} |
| 1211 | @findex reveal-mode | 1221 | @findex reveal-mode |
| 1212 | @vindex search-invisible | 1222 | @vindex search-invisible |
| @@ -233,6 +233,15 @@ preserving markers, properties and overlays. The new variable | |||
| 233 | number of seconds that 'revert-buffer-with-fine-grain' should spend | 233 | number of seconds that 'revert-buffer-with-fine-grain' should spend |
| 234 | trying to be non-destructive. | 234 | trying to be non-destructive. |
| 235 | 235 | ||
| 236 | ** Outline | ||
| 237 | |||
| 238 | +++ | ||
| 239 | *** New commands to cycle heading visibility. | ||
| 240 | Typing 'TAB' on a heading cycles the current section between "hide | ||
| 241 | all", "subheadings", and "show all" state. Typing 'S-TAB' anywhere in | ||
| 242 | the buffer cycles the whole buffer between "only top-level headings", | ||
| 243 | "all headings and subheadings", and "show all" states. | ||
| 244 | |||
| 236 | 245 | ||
| 237 | * Changes in Specialized Modes and Packages in Emacs 28.1 | 246 | * Changes in Specialized Modes and Packages in Emacs 28.1 |
| 238 | 247 | ||
diff --git a/lisp/outline.el b/lisp/outline.el index 6158ed594e9..95670e04936 100644 --- a/lisp/outline.el +++ b/lisp/outline.el | |||
| @@ -179,6 +179,12 @@ in the file it applies to.") | |||
| 179 | (let ((map (make-sparse-keymap))) | 179 | (let ((map (make-sparse-keymap))) |
| 180 | (define-key map "\C-c" outline-mode-prefix-map) | 180 | (define-key map "\C-c" outline-mode-prefix-map) |
| 181 | (define-key map [menu-bar] outline-mode-menu-bar-map) | 181 | (define-key map [menu-bar] outline-mode-menu-bar-map) |
| 182 | ;; Only takes effect if the point is on a heading. | ||
| 183 | (define-key map (kbd "TAB") | ||
| 184 | `(menu-item "" outline-cycle | ||
| 185 | :filter ,(lambda (cmd) | ||
| 186 | (when (outline-on-heading-p) cmd)))) | ||
| 187 | (define-key map (kbd "<backtab>") #'outline-cycle-buffer) | ||
| 182 | map)) | 188 | map)) |
| 183 | 189 | ||
| 184 | (defvar outline-font-lock-keywords | 190 | (defvar outline-font-lock-keywords |
| @@ -1125,6 +1131,83 @@ convenient way to make a table of contents of the buffer." | |||
| 1125 | (insert "\n\n")))))) | 1131 | (insert "\n\n")))))) |
| 1126 | (kill-new (buffer-string))))))) | 1132 | (kill-new (buffer-string))))))) |
| 1127 | 1133 | ||
| 1134 | (defun outline--cycle-state () | ||
| 1135 | "Return the cycle state of current heading. | ||
| 1136 | Return either 'hide-all, 'headings-only, or 'show-all." | ||
| 1137 | (save-excursion | ||
| 1138 | (let (start end ov-list heading-end) | ||
| 1139 | (outline-back-to-heading) | ||
| 1140 | (setq start (point)) | ||
| 1141 | (outline-end-of-heading) | ||
| 1142 | (setq heading-end (point)) | ||
| 1143 | (outline-end-of-subtree) | ||
| 1144 | (setq end (point)) | ||
| 1145 | (setq ov-list (cl-remove-if-not | ||
| 1146 | (lambda (o) (eq (overlay-get o 'invisible) 'outline)) | ||
| 1147 | (overlays-in start end))) | ||
| 1148 | (cond ((eq ov-list nil) 'show-all) | ||
| 1149 | ;; (eq (length ov-list) 1) wouldn’t work: what if there is | ||
| 1150 | ;; one folded subheading? | ||
| 1151 | ((and (eq (overlay-end (car ov-list)) end) | ||
| 1152 | (eq (overlay-start (car ov-list)) heading-end)) | ||
| 1153 | 'hide-all) | ||
| 1154 | (t 'headings-only))))) | ||
| 1155 | |||
| 1156 | (defun outline-has-subheading-p () | ||
| 1157 | "Return t if this heading has subheadings, nil otherwise." | ||
| 1158 | (save-excursion | ||
| 1159 | (outline-back-to-heading) | ||
| 1160 | (< (save-excursion (outline-next-heading) (point)) | ||
| 1161 | (save-excursion (outline-end-of-subtree) (point))))) | ||
| 1162 | |||
| 1163 | (defun outline-cycle () | ||
| 1164 | "Cycle between `hide all', `headings only' and `show all'. | ||
| 1165 | |||
| 1166 | `Hide all' means hide all subheadings and their bodies. | ||
| 1167 | `Headings only' means show sub headings but not their bodies. | ||
| 1168 | `Show all' means show all subheadings and their bodies." | ||
| 1169 | (interactive) | ||
| 1170 | (pcase (outline--cycle-state) | ||
| 1171 | ('hide-all | ||
| 1172 | (if (outline-has-subheading-p) | ||
| 1173 | (progn (outline-show-children) | ||
| 1174 | (message "Only headings")) | ||
| 1175 | (outline-show-subtree) | ||
| 1176 | (message "Show all"))) | ||
| 1177 | ('headings-only | ||
| 1178 | (outline-show-subtree) | ||
| 1179 | (message "Show all")) | ||
| 1180 | ('show-all | ||
| 1181 | (outline-hide-subtree) | ||
| 1182 | (message "Hide all")))) | ||
| 1183 | |||
| 1184 | (defvar-local outline--cycle-buffer-state 'show-all | ||
| 1185 | "Internal variable used for tracking buffer cycle state.") | ||
| 1186 | |||
| 1187 | (defun outline-cycle-buffer () | ||
| 1188 | "Cycle the whole buffer like in `outline-cycle'." | ||
| 1189 | (interactive) | ||
| 1190 | (pcase outline--cycle-buffer-state | ||
| 1191 | ('show-all | ||
| 1192 | (save-excursion | ||
| 1193 | (let ((start-point (point))) | ||
| 1194 | (while (not (eq (point) start-point)) | ||
| 1195 | (outline-up-heading 1)) | ||
| 1196 | (outline-hide-sublevels | ||
| 1197 | (progn (outline-back-to-heading) | ||
| 1198 | (funcall 'outline-level))))) | ||
| 1199 | (setq outline--cycle-buffer-state 'top-level) | ||
| 1200 | (message "Top level headings")) | ||
| 1201 | ('top-level | ||
| 1202 | (outline-show-all) | ||
| 1203 | (outline-hide-region-body (point-min) (point-max)) | ||
| 1204 | (setq outline--cycle-buffer-state 'all-heading) | ||
| 1205 | (message "All headings")) | ||
| 1206 | ('all-heading | ||
| 1207 | (outline-show-all) | ||
| 1208 | (setq outline--cycle-buffer-state 'show-all) | ||
| 1209 | (message "Show all")))) | ||
| 1210 | |||
| 1128 | (provide 'outline) | 1211 | (provide 'outline) |
| 1129 | (provide 'noutline) | 1212 | (provide 'noutline) |
| 1130 | 1213 | ||