aboutsummaryrefslogtreecommitdiffstats
path: root/lisp/textmodes
diff options
context:
space:
mode:
authorStefan Monnier2001-11-29 02:15:03 +0000
committerStefan Monnier2001-11-29 02:15:03 +0000
commit7d7715b5f6977aa18295eb8aa210a20b0808a478 (patch)
tree00ad6deaab5abf64937639ec149d1434dc2b8cd5 /lisp/textmodes
parent66458f3247156a5eb48eb7558f6f211b6ef65695 (diff)
downloademacs-7d7715b5f6977aa18295eb8aa210a20b0808a478.tar.gz
emacs-7d7715b5f6977aa18295eb8aa210a20b0808a478.zip
(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.
Diffstat (limited to 'lisp/textmodes')
-rw-r--r--lisp/textmodes/outline.el256
1 files changed, 179 insertions, 77 deletions
diff --git a/lisp/textmodes/outline.el b/lisp/textmodes/outline.el
index 23d5c1520aa..dfd83a005c6 100644
--- a/lisp/textmodes/outline.el
+++ b/lisp/textmodes/outline.el
@@ -32,6 +32,8 @@
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
@@ -147,6 +149,7 @@ in the file it applies to."
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)
@@ -165,7 +168,7 @@ in the file it applies to."
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
@@ -253,10 +256,9 @@ See the command `outline-mode' for more information on this mode."
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.
@@ -318,7 +320,8 @@ Only visible heading lines are considered, unless INVISIBLE-OK is non-nil."
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
@@ -327,9 +330,104 @@ Only visible heading lines are considered, unless INVISIBLE-OK is non-nil."
327If INVISIBLE-OK is non-nil, an invisible heading line is ok too." 330If 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))))
332 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.
361If 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.
382If 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))))
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)))
@@ -347,13 +445,13 @@ A heading line is one that starts with a `*' (or that
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
@@ -380,63 +478,66 @@ This puts point at the start of the current subtree, and mark at the end."
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.
385If FLAG is nil then text is shown, while if FLAG is t the text is hidden." 485If FLAG is nil then text is shown, while if FLAG is t the text is hidden."
386 (save-excursion 486 (remove-overlays from to 'invisible 'outline)
387 (goto-char from) 487 (when flag
388 (end-of-line) 488 (let ((o (make-overlay from to)))
389 (outline-discard-overlays (point) to 'outline) 489 (overlay-put o 'invisible 'outline)
390 (if flag 490 (overlay-put o 'isearch-open-invisible 'outline-isearch-open-invisible)))
391 (let ((o (make-overlay (point) to))) 491 ;; Seems only used by lazy-lock. I.e. obsolete.
392 (overlay-put o 'invisible 'outline)
393 (overlay-put o 'isearch-open-invisible
394 'outline-isearch-open-invisible))))
395 (run-hooks 'outline-view-change-hook)) 492 (run-hooks 'outline-view-change-hook))
396 493
494(defun outline-reveal-toggle-invisible (o revealp)
495 (save-excursion
496 (goto-char (overlay-start o))
497 (if (null revealp)
498 ;; When hiding the area again, we could just clean it up and let
499 ;; reveal do the rest, by simply doing:
500 ;; (remove-overlays (overlay-start o) (overlay-end o)
501 ;; 'invisible 'outline)
502 ;;
503 ;; That works fine as long as everything is in sync, but if the
504 ;; structure of the document is changed while revealing parts of it,
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."
@@ -444,7 +545,7 @@ If FLAG is nil then text is shown, while if FLAG is t the text is hidden."
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.
@@ -517,6 +618,7 @@ Show the heading too, if it is currently invisible."
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)
@@ -540,6 +642,17 @@ Show the heading too, if it is currently invisible."
540 nil)))) 642 nil))))
541 (run-hooks 'outline-view-change-hook)) 643 (run-hooks 'outline-view-change-hook))
542 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)))
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)
@@ -607,28 +720,15 @@ Default is enough to cause the following heading to appear."
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)
612 "Move to the heading line of which the present line is a subheading.
613This function considers both visible and invisible heading lines.
614With argument, move up ARG levels."
615 (outline-back-to-heading t)
616 (if (eq (funcall outline-level) 1)
617 (error "Already at top level of the outline"))
618 (while (and (> (funcall outline-level) 1)
619 (> arg 0)
620 (not (bobp)))
621 (let ((present-level (funcall outline-level)))
622 (while (and (not (< (funcall outline-level) present-level))
623 (not (bobp)))
624 (outline-previous-heading))
625 (setq arg (- arg 1)))))
626 725
627(defun outline-up-heading (arg) 726(defun outline-up-heading (arg &optional invisible-ok)
628 "Move to the visible heading line of which the present line is a subheading. 727 "Move to the visible heading line of which the present line is a subheading.
629With argument, move up ARG levels." 728With argument, move up ARG levels.
729If INVISIBLE-OK is non-nil, also consider invisible lines."
630 (interactive "p") 730 (interactive "p")
631 (outline-back-to-heading) 731 (outline-back-to-heading invisible-ok)
632 (if (eq (funcall outline-level) 1) 732 (if (eq (funcall outline-level) 1)
633 (error "Already at top level of the outline")) 733 (error "Already at top level of the outline"))
634 (while (and (> (funcall outline-level) 1) 734 (while (and (> (funcall outline-level) 1)
@@ -637,7 +737,9 @@ With argument, move up ARG levels."
637 (let ((present-level (funcall outline-level))) 737 (let ((present-level (funcall outline-level)))
638 (while (and (not (< (funcall outline-level) present-level)) 738 (while (and (not (< (funcall outline-level) present-level))
639 (not (bobp))) 739 (not (bobp)))
640 (outline-previous-visible-heading 1)) 740 (if invisible-ok
741 (outline-previous-heading)
742 (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)
@@ -720,7 +822,7 @@ convenient way to make a table of contents of the buffer."
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