comparison lisp/textmodes/outline.el @ 41657:4f8e9cc04af5

(outline-up-heading): Add `invisible-ok' arg. (outline-up-heading-all): Remove. (hide-sublevels): Move to end-of-heading before calling flag-region. (outline-copy-overlay, outline-discard-overlays): Remove. (outline-flag-region): Use `remove-overlays'. Don't move to end-of-heading. (outline-next-visible-heading, outline-back-to-heading) (outline-on-heading-p): Use outline-invisible-p. (outline-font-lock-level): Use outline-up-heading's new arg. (outline-minor-mode): Simplify. (outline-map-tree, outline-reveal-toggle-invisible): New funs. (outline): Put a `reveal-toggle-invisible' property. (outline-level-heading): New var. (outline-insert-heading, outline-promote, outline-demote) (outline-toggle-children): New commands.
author Stefan Monnier <monnier@iro.umontreal.ca>
date Thu, 29 Nov 2001 02:15:03 +0000
parents fc133c103640
children 83b107455579
comparison
equal deleted inserted replaced
41656:ec668c07bca2 41657:4f8e9cc04af5
30 ;; with all stuff below hidden. See the Emacs manual for details. 30 ;; with all stuff below hidden. See the Emacs manual for details.
31 31
32 ;;; Todo: 32 ;;; Todo:
33 33
34 ;; - subtree-terminators 34 ;; - subtree-terminators
35 ;; - better handle comments before function bodies (i.e. heading)
36 ;; - don't bother hiding whitespace
35 37
36 ;;; Code: 38 ;;; Code:
37 39
38 (defgroup outlines nil 40 (defgroup outlines nil
39 "Support for hierarchical outlining" 41 "Support for hierarchical outlining"
145 (defvar outline-font-lock-keywords 147 (defvar outline-font-lock-keywords
146 '(;; 148 '(;;
147 ;; Highlight headings according to the level. 149 ;; Highlight headings according to the level.
148 (eval . (list (concat "^" outline-regexp ".+") 150 (eval . (list (concat "^" outline-regexp ".+")
149 0 '(or (cdr (assq (outline-font-lock-level) 151 0 '(or (cdr (assq (outline-font-lock-level)
152 ;; FIXME: this is silly!
150 '((1 . font-lock-function-name-face) 153 '((1 . font-lock-function-name-face)
151 (2 . font-lock-variable-name-face) 154 (2 . font-lock-variable-name-face)
152 (3 . font-lock-keyword-face) 155 (3 . font-lock-keyword-face)
153 (4 . font-lock-builtin-face) 156 (4 . font-lock-builtin-face)
154 (5 . font-lock-comment-face) 157 (5 . font-lock-comment-face)
163 (let ((count 1)) 166 (let ((count 1))
164 (save-excursion 167 (save-excursion
165 (outline-back-to-heading t) 168 (outline-back-to-heading t)
166 (while (and (not (bobp)) 169 (while (and (not (bobp))
167 (not (eq (funcall outline-level) 1))) 170 (not (eq (funcall outline-level) 1)))
168 (outline-up-heading-all 1) 171 (outline-up-heading 1 t)
169 (setq count (1+ count))) 172 (setq count (1+ count)))
170 count))) 173 count)))
171 174
172 (defvar outline-view-change-hook nil 175 (defvar outline-view-change-hook nil
173 "Normal hook to be run after outline visibility changes.") 176 "Normal hook to be run after outline visibility changes.")
251 (set (make-local-variable 'line-move-ignore-invisible) t) 254 (set (make-local-variable 'line-move-ignore-invisible) t)
252 ;; Cause use of ellipses for invisible text. 255 ;; Cause use of ellipses for invisible text.
253 (add-to-invisibility-spec '(outline . t))) 256 (add-to-invisibility-spec '(outline . t)))
254 (setq line-move-ignore-invisible nil) 257 (setq line-move-ignore-invisible nil)
255 ;; Cause use of ellipses for invisible text. 258 ;; Cause use of ellipses for invisible text.
256 (remove-from-invisibility-spec '(outline . t))) 259 (remove-from-invisibility-spec '(outline . t))
257 ;; When turning off outline mode, get rid of any outline hiding. 260 ;; When turning off outline mode, get rid of any outline hiding.
258 (or outline-minor-mode 261 (show-all)))
259 (show-all)))
260 262
261 (defcustom outline-level 'outline-level 263 (defcustom outline-level 'outline-level
262 "*Function of no args to compute a header's nesting level in an outline. 264 "*Function of no args to compute a header's nesting level in an outline.
263 It can assume point is at the beginning of a header line." 265 It can assume point is at the beginning of a header line."
264 :type 'function 266 :type 'function
316 (save-excursion 318 (save-excursion
317 (while (not found) 319 (while (not found)
318 (or (re-search-backward (concat "^\\(" outline-regexp "\\)") 320 (or (re-search-backward (concat "^\\(" outline-regexp "\\)")
319 nil t) 321 nil t)
320 (error "before first heading")) 322 (error "before first heading"))
321 (setq found (and (or invisible-ok (outline-visible)) (point))))) 323 (setq found (and (or invisible-ok (not (outline-invisible-p)))
324 (point)))))
322 (goto-char found) 325 (goto-char found)
323 found))) 326 found)))
324 327
325 (defun outline-on-heading-p (&optional invisible-ok) 328 (defun outline-on-heading-p (&optional invisible-ok)
326 "Return t if point is on a (visible) heading line. 329 "Return t if point is on a (visible) heading line.
327 If INVISIBLE-OK is non-nil, an invisible heading line is ok too." 330 If INVISIBLE-OK is non-nil, an invisible heading line is ok too."
328 (save-excursion 331 (save-excursion
329 (beginning-of-line) 332 (beginning-of-line)
330 (and (bolp) (or invisible-ok (outline-visible)) 333 (and (bolp) (or invisible-ok (not (outline-invisible-p)))
331 (looking-at outline-regexp)))) 334 (looking-at outline-regexp))))
335
336 (defvar outline-level-heading ()
337 "Alist associating a heading for every possible level.")
338 (make-variable-buffer-local 'outline-level-heading)
339
340 (defun outline-insert-heading ()
341 "Insert a new heading at same depth at point."
342 (interactive)
343 (let ((head (save-excursion
344 (condition-case nil
345 (outline-back-to-heading)
346 (error (outline-next-heading)))
347 (if (eobp)
348 (or (cdar outline-level-heading) "")
349 (match-string 0)))))
350 (unless (or (string-match "[ \t]\\'" head)
351 (not (string-match outline-regexp (concat head " "))))
352 (setq head (concat head " ")))
353 (unless (bolp) (end-of-line) (newline))
354 (insert head)
355 (unless (eolp)
356 (save-excursion (newline-and-indent)))
357 (run-hooks 'outline-insert-heading-hook)))
358
359 (defun outline-promote (&optional children)
360 "Promote the current heading higher up the tree.
361 If prefix argument CHILDREN is given, promote also all the children."
362 (interactive "P")
363 (outline-back-to-heading)
364 (let* ((head (match-string 0))
365 (level (save-match-data (funcall outline-level)))
366 (up-head (or (cdr (assoc head outline-level-headings))
367 (cdr (assoc (1- level) outline-level-headings))
368 (save-excursion
369 (save-match-data
370 (outline-up-heading 1 t)
371 (match-string 0))))))
372
373 (unless (assoc level outline-level-headings)
374 (push (cons level head) outline-level-headings))
375
376 (replace-match up-head nil t)
377 (when children
378 (outline-map-tree 'outline-promote level))))
379
380 (defun outline-demote (&optional children)
381 "Demote the current heading lower down the tree.
382 If prefix argument CHILDREN is given, demote also all the children."
383 (interactive "P")
384 (outline-back-to-heading)
385 (let* ((head (match-string 0))
386 (level (save-match-data (funcall outline-level)))
387 (down-head
388 (or (let ((x (car (rassoc head outline-level-headings))))
389 (if (stringp x) x))
390 (cdr (assoc (1+ level) outline-level-headings))
391 (save-excursion
392 (save-match-data
393 (while (and (not (eobp))
394 (progn
395 (outline-next-heading)
396 (<= (funcall outline-level) level))))
397 (when (eobp)
398 ;; Try again from the beginning of the buffer.
399 (goto-char (point-min))
400 (while (and (not (eobp))
401 (progn
402 (outline-next-heading)
403 (<= (funcall outline-level) level)))))
404 (unless (eobp) (match-string 0))))
405 (save-match-data
406 ;; Bummer!! There is no lower heading in the buffer.
407 ;; Let's try to invent one by repeating the first char.
408 (let ((new-head (concat (substring head 0 1) head)))
409 (if (string-match (concat "\\`" outline-regexp) new-head)
410 ;; Why bother checking that it is indeed of lower level ?
411 new-head
412 ;; Didn't work: keep it as is so it's still a heading.
413 head))))))
414
415 (unless (assoc level outline-level-headings)
416 (push (cons level head) outline-level-headings))
417
418 (replace-match down-head nil t)
419 (when children
420 (outline-map-tree 'outline-demote level))))
421
422 (defun outline-map-tree (fun level)
423 "Call FUN for every heading underneath the current one."
424 (save-excursion
425 (while (and (progn
426 (outline-next-heading)
427 (> (funcall outline-level) level))
428 (not (eobp)))
429 (funcall fun))))
332 430
333 (defun outline-end-of-heading () 431 (defun outline-end-of-heading ()
334 (if (re-search-forward outline-heading-end-regexp nil 'move) 432 (if (re-search-forward outline-heading-end-regexp nil 'move)
335 (forward-char -1))) 433 (forward-char -1)))
336 434
345 (end-of-line)) 443 (end-of-line))
346 (while (and (not (bobp)) (< arg 0)) 444 (while (and (not (bobp)) (< arg 0))
347 (while (and (not (bobp)) 445 (while (and (not (bobp))
348 (re-search-backward (concat "^\\(" outline-regexp "\\)") 446 (re-search-backward (concat "^\\(" outline-regexp "\\)")
349 nil 'move) 447 nil 'move)
350 (not (outline-visible)))) 448 (outline-invisible-p)))
351 (setq arg (1+ arg))) 449 (setq arg (1+ arg)))
352 (while (and (not (eobp)) (> arg 0)) 450 (while (and (not (eobp)) (> arg 0))
353 (while (and (not (eobp)) 451 (while (and (not (eobp))
354 (re-search-forward (concat "^\\(" outline-regexp "\\)") 452 (re-search-forward (concat "^\\(" outline-regexp "\\)")
355 nil 'move) 453 nil 'move)
356 (not (outline-visible)))) 454 (outline-invisible-p)))
357 (setq arg (1- arg))) 455 (setq arg (1- arg)))
358 (beginning-of-line)) 456 (beginning-of-line))
359 457
360 (defun outline-previous-visible-heading (arg) 458 (defun outline-previous-visible-heading (arg)
361 "Move to the previous heading line. 459 "Move to the previous heading line.
378 (setq beg (point)) 476 (setq beg (point))
379 (outline-end-of-subtree) 477 (outline-end-of-subtree)
380 (push-mark (point)) 478 (push-mark (point))
381 (goto-char beg))) 479 (goto-char beg)))
382 480
481
482 (put 'outline 'reveal-toggle-invisible 'outline-reveal-toggle-invisible)
383 (defun outline-flag-region (from to flag) 483 (defun outline-flag-region (from to flag)
384 "Hides or shows lines from FROM to TO, according to FLAG. 484 "Hide or show lines from FROM to TO, according to FLAG.
385 If FLAG is nil then text is shown, while if FLAG is t the text is hidden." 485 If FLAG is nil then text is shown, while if FLAG is t the text is hidden."
486 (remove-overlays from to 'invisible 'outline)
487 (when flag
488 (let ((o (make-overlay from to)))
489 (overlay-put o 'invisible 'outline)
490 (overlay-put o 'isearch-open-invisible 'outline-isearch-open-invisible)))
491 ;; Seems only used by lazy-lock. I.e. obsolete.
492 (run-hooks 'outline-view-change-hook))
493
494 (defun outline-reveal-toggle-invisible (o revealp)
386 (save-excursion 495 (save-excursion
387 (goto-char from) 496 (goto-char (overlay-start o))
388 (end-of-line) 497 (if (null revealp)
389 (outline-discard-overlays (point) to 'outline) 498 ;; When hiding the area again, we could just clean it up and let
390 (if flag 499 ;; reveal do the rest, by simply doing:
391 (let ((o (make-overlay (point) to))) 500 ;; (remove-overlays (overlay-start o) (overlay-end o)
392 (overlay-put o 'invisible 'outline) 501 ;; 'invisible 'outline)
393 (overlay-put o 'isearch-open-invisible 502 ;;
394 'outline-isearch-open-invisible)))) 503 ;; That works fine as long as everything is in sync, but if the
395 (run-hooks 'outline-view-change-hook)) 504 ;; structure of the document is changed while revealing parts of it,
396 505 ;; the resulting behavior can be ugly. I.e. we need to make
506 ;; sure that we hide exactly a subtree.
507 (progn
508 (let ((end (overlay-end o)))
509 (delete-overlay o)
510 (while (progn
511 (hide-subtree)
512 (outline-next-visible-heading 1)
513 (and (not (eobp)) (< (point) end))))))
514
515 ;; When revealing, we just need to reveal sublevels. If point is
516 ;; inside one of the sublevels, reveal will call us again.
517 ;; But we need to preserve the original overlay.
518 (let ((o1 (copy-overlay o)))
519 (overlay-put o1 'invisible 'outline) ;We rehide some of the text.
520 (while (progn
521 (show-entry)
522 (show-children)
523 ;; Normally just the above is needed.
524 ;; But in odd cases, the above might fail to show anything.
525 ;; To avoid an infinite loop, we have to make sure that
526 ;; *something* gets shown.
527 (and (equal (overlay-start o) (overlay-start o1))
528 (< (point) (overlay-end o))
529 (= 0 (forward-line 1)))))
530 ;; If still nothing was shown, just kill the damn thing.
531 (when (equal (overlay-start o) (overlay-start o1))
532 ;; I've seen it happen at the end of buffer.
533 (delete-overlay o1))))))
397 534
398 ;; Function to be set as an outline-isearch-open-invisible' property 535 ;; Function to be set as an outline-isearch-open-invisible' property
399 ;; to the overlay that makes the outline invisible (see 536 ;; to the overlay that makes the outline invisible (see
400 ;; `outline-flag-region'). 537 ;; `outline-flag-region').
401 (defun outline-isearch-open-invisible (overlay) 538 (defun outline-isearch-open-invisible (overlay)
402 ;; We rely on the fact that isearch places point one the matched text. 539 ;; We rely on the fact that isearch places point on the matched text.
403 (show-entry)) 540 (show-entry))
404
405
406 ;; Exclude from the region BEG ... END all overlays
407 ;; which have PROP as the value of the `invisible' property.
408 ;; Exclude them by shrinking them to exclude BEG ... END,
409 ;; or even by splitting them if necessary.
410 ;; Overlays without such an `invisible' property are not touched.
411 (defun outline-discard-overlays (beg end prop)
412 (if (< end beg)
413 (setq beg (prog1 end (setq end beg))))
414 (save-excursion
415 (dolist (o (overlays-in beg end))
416 (if (eq (overlay-get o 'invisible) prop)
417 ;; Either push this overlay outside beg...end
418 ;; or split it to exclude beg...end
419 ;; or delete it entirely (if it is contained in beg...end).
420 (if (< (overlay-start o) beg)
421 (if (> (overlay-end o) end)
422 (progn
423 (move-overlay (outline-copy-overlay o)
424 (overlay-start o) beg)
425 (move-overlay o end (overlay-end o)))
426 (move-overlay o (overlay-start o) beg))
427 (if (> (overlay-end o) end)
428 (move-overlay o end (overlay-end o))
429 (delete-overlay o)))))))
430
431 ;; Make a copy of overlay O, with the same beginning, end and properties.
432 (defun outline-copy-overlay (o)
433 (let ((o1 (make-overlay (overlay-start o) (overlay-end o)
434 (overlay-buffer o)))
435 (props (overlay-properties o)))
436 (while props
437 (overlay-put o1 (car props) (nth 1 props))
438 (setq props (cdr (cdr props))))
439 o1))
440 541
441 (defun hide-entry () 542 (defun hide-entry ()
442 "Hide the body directly following this heading." 543 "Hide the body directly following this heading."
443 (interactive) 544 (interactive)
444 (outline-back-to-heading) 545 (outline-back-to-heading)
445 (outline-end-of-heading) 546 (outline-end-of-heading)
446 (save-excursion 547 (save-excursion
447 (outline-flag-region (point) (progn (outline-next-preface) (point)) t))) 548 (outline-flag-region (point) (progn (outline-next-preface) (point)) t)))
448 549
449 (defun show-entry () 550 (defun show-entry ()
450 "Show the body directly following this heading. 551 "Show the body directly following this heading.
451 Show the heading too, if it is currently invisible." 552 Show the heading too, if it is currently invisible."
452 (interactive) 553 (interactive)
515 ;; Keep advancing to the next top-level heading. 616 ;; Keep advancing to the next top-level heading.
516 (while (or (and (bobp) (outline-on-heading-p)) 617 (while (or (and (bobp) (outline-on-heading-p))
517 (outline-next-heading)) 618 (outline-next-heading))
518 (let ((end (save-excursion (outline-end-of-subtree) (point)))) 619 (let ((end (save-excursion (outline-end-of-subtree) (point))))
519 ;; Hide everything under that. 620 ;; Hide everything under that.
621 (outline-end-of-heading)
520 (outline-flag-region (point) end t) 622 (outline-flag-region (point) end t)
521 ;; Show the first LEVELS levels under that. 623 ;; Show the first LEVELS levels under that.
522 (if (> levels 0) 624 (if (> levels 0)
523 (show-children levels)) 625 (show-children levels))
524 ;; Move to the next, since we already found it. 626 ;; Move to the next, since we already found it.
537 (error nil)) 639 (error nil))
538 (outline-flag-region (1- (point)) 640 (outline-flag-region (1- (point))
539 (save-excursion (forward-line 1) (point)) 641 (save-excursion (forward-line 1) (point))
540 nil)))) 642 nil))))
541 (run-hooks 'outline-view-change-hook)) 643 (run-hooks 'outline-view-change-hook))
644
645 (defun outline-toggle-children ()
646 "Show or hide the current subtree depending on its current state."
647 (interactive)
648 (outline-back-to-heading)
649 (if (save-excursion
650 (end-of-line)
651 (not (outline-invisible-p)))
652 (hide-subtree)
653 (show-children)
654 (show-entry)))
542 655
543 (defun outline-flag-subtree (flag) 656 (defun outline-flag-subtree (flag)
544 (save-excursion 657 (save-excursion
545 (outline-back-to-heading) 658 (outline-back-to-heading)
546 (outline-end-of-heading) 659 (outline-end-of-heading)
605 (forward-char -1)) 718 (forward-char -1))
606 (point)) 719 (point))
607 (progn (outline-end-of-heading) (point)) 720 (progn (outline-end-of-heading) (point))
608 nil))))))) 721 nil)))))))
609 (run-hooks 'outline-view-change-hook)) 722 (run-hooks 'outline-view-change-hook))
723
610 724
611 (defun outline-up-heading-all (arg) 725
612 "Move to the heading line of which the present line is a subheading. 726 (defun outline-up-heading (arg &optional invisible-ok)
613 This function considers both visible and invisible heading lines. 727 "Move to the visible heading line of which the present line is a subheading.
614 With argument, move up ARG levels." 728 With argument, move up ARG levels.
615 (outline-back-to-heading t) 729 If INVISIBLE-OK is non-nil, also consider invisible lines."
730 (interactive "p")
731 (outline-back-to-heading invisible-ok)
616 (if (eq (funcall outline-level) 1) 732 (if (eq (funcall outline-level) 1)
617 (error "Already at top level of the outline")) 733 (error "Already at top level of the outline"))
618 (while (and (> (funcall outline-level) 1) 734 (while (and (> (funcall outline-level) 1)
619 (> arg 0) 735 (> arg 0)
620 (not (bobp))) 736 (not (bobp)))
621 (let ((present-level (funcall outline-level))) 737 (let ((present-level (funcall outline-level)))
622 (while (and (not (< (funcall outline-level) present-level)) 738 (while (and (not (< (funcall outline-level) present-level))
623 (not (bobp))) 739 (not (bobp)))
624 (outline-previous-heading)) 740 (if invisible-ok
625 (setq arg (- arg 1))))) 741 (outline-previous-heading)
626 742 (outline-previous-visible-heading 1)))
627 (defun outline-up-heading (arg)
628 "Move to the visible heading line of which the present line is a subheading.
629 With argument, move up ARG levels."
630 (interactive "p")
631 (outline-back-to-heading)
632 (if (eq (funcall outline-level) 1)
633 (error "Already at top level of the outline"))
634 (while (and (> (funcall outline-level) 1)
635 (> arg 0)
636 (not (bobp)))
637 (let ((present-level (funcall outline-level)))
638 (while (and (not (< (funcall outline-level) present-level))
639 (not (bobp)))
640 (outline-previous-visible-heading 1))
641 (setq arg (- arg 1))))) 743 (setq arg (- arg 1)))))
642 744
643 (defun outline-forward-same-level (arg) 745 (defun outline-forward-same-level (arg)
644 "Move forward to the ARG'th subheading at same level as this one. 746 "Move forward to the ARG'th subheading at same level as this one.
645 Stop at the first and last subheadings of a superior heading." 747 Stop at the first and last subheadings of a superior heading."
718 (insert-buffer-substring buffer start end) 820 (insert-buffer-substring buffer start end)
719 (insert "\n\n"))) 821 (insert "\n\n")))
720 (let ((temp-buffer (current-buffer))) 822 (let ((temp-buffer (current-buffer)))
721 (with-current-buffer buffer 823 (with-current-buffer buffer
722 (while (outline-next-heading) 824 (while (outline-next-heading)
723 (when (outline-visible) 825 (unless (outline-invisible-p)
724 (setq start (point) 826 (setq start (point)
725 end (progn (outline-end-of-heading) (point))) 827 end (progn (outline-end-of-heading) (point)))
726 (with-current-buffer temp-buffer 828 (with-current-buffer temp-buffer
727 (insert-buffer-substring buffer start end) 829 (insert-buffer-substring buffer start end)
728 (insert "\n\n")))))) 830 (insert "\n\n"))))))