home *** CD-ROM | disk | FTP | other *** search
Text File | 1993-07-07 | 60.9 KB | 1,775 lines |
- ;; A folding-editor-like minor mode.
-
- ;; Copyright (C) 1992, 1993, Jamie Lokier. All rights reserved.
-
- ;; This file is intended to be used with GNU Emacs.
-
- ;; This program is free software; you can redistribute it and/or modify
- ;; it under the terms of the GNU General Public License as published by
- ;; the Free Software Foundation; either version 2, or (at your option)
- ;; any later version.
-
- ;; This program is distributed in the hope that it will be useful,
- ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
- ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ;; GNU General Public License for more details.
-
- ;; You should have received a copy of the GNU General Public License
- ;; along with GNU Emacs; see the file COPYING. If not, write to
- ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
-
- ;; ---------------------------------------------------------------------
-
- ;; This is version 1.6.2 of Folding mode, under development.
-
- ;; This file has been edited with a folding editor (itself! :-).
-
- ;; Send suggestions and/or bug fixes to "u90jl@ecs.ox.ac.uk".
-
- ;; If you can, please check the most recent version of Folding mode
- ;; before reporting bugs. If you can't, don't be afraid of reporting
- ;; bugs anyway.
-
- ;;{{{ Information
-
- ;; ----------------------- Archive information --------------------------
-
- ;; LCD Archive Entry:
- ;; folding|Jamie Lokier|u90jl@ecs.ox.ac.uk|
- ;; A folding-editor-like minor mode|
- ;; 06-Jul-1993|1.6.2|~/modes/folding.el.Z|
-
- ;; -------------------------- Installation ------------------------------
-
- ;; To install Folding mode, put this file (folding.el) on you Emacs-Lisp
- ;; load path, and put the following in your .emacs:
- ;;
- ;; (autoload 'folding-mode "folding"
- ;; "Minor mode that simulates a folding editor" t)
- ;;
- ;; To have Folding mode start automatically when opening folded files,
- ;; add the following to your .emacs as well:
- ;;
- ;; (defun folding-mode-find-file-hook ()
- ;; "One of the hooks called whenever a `find-file' is successful."
- ;; (and (assq 'folded-file (buffer-local-variables))
- ;; folded-file
- ;; (folding-mode 1)
- ;; (kill-local-variable 'folded-file)))
- ;;
- ;; (or (memq 'folding-mode-find-file-hook find-file-hooks)
- ;; (setq find-file-hooks (append find-file-hooks
- ;; '(folding-mode-find-file-hook))))
- ;;
- ;; If you load folding.el all the time during startup, none of the above
- ;; is necessary; it can be replaced with this after loading folding.el:
- ;;
- ;; (folding-mode-add-find-file-hook)
- ;;
- ;; Brief documentation for Folding mode (what it is, how you use it) is
- ;; provided with the definition of the function `folding-mode'.
- ;;
- ;; The best way to learn how to use Folding mode after installing it is
- ;; to find-file the source, M-x eval-current-buffer, M-x folding-mode,
- ;; and move in and out of the folds. Keys are documented under the
- ;; function `folding-mode', though you might want to customize them.
- ;; Keys in folding mode are bound in the keymap `folding-mode-map'.
-
- ;; --------------------------- And the rest -----------------------------
-
- ;; There are is no real documentation yet; I haven't had time. I intend
- ;; to write some one day, but I will refrain from predicting when. Read
- ;; the documentation for the function `folding-mode' for the most useful
- ;; tips.
-
- ;; Emacs 18:
- ;; Folding mode has been tested with versions 18.55 and 18.58 of Emacs.
-
- ;; Epoch:
- ;; Folding mode has been tested on Epoch 4.0p2.
-
- ;; Lucid Emacs:
- ;; There is code in here to handle some aspects of Lucid Emacs.
- ;; However, up to version 19.6, there appears to be no way to display
- ;; folds. Selective-display does not work, and neither do invisible
- ;; extents, so Folding mode has no chance of working. This is likely to
- ;; change in future versions of Lucid Emacs.
-
- ;; Emacs 19:
- ;; Tested on version 19.8, appears to be fine.
- ;; Minor bug: display the buffer in several different frames, then move
- ;; in and out of folds in the buffer. The frames are automatically
- ;; moved to the top of the stacking order.
-
- ;; Some of the code is quite horrible, generally in order to avoid some
- ;; Emacs display "features". Some of it is specific to certain versions
- ;; of Emacs. By the time Emacs 19 is around and everyone is using it,
- ;; hopefully most of it won't be necessary.
-
- ;; ------------------------ More known bugs -----------------------------
-
- ;; *** Needs fold-fold-region to be more intelligent about
- ;; finding a good region. Check folding a whole current fold.
-
- ;; *** Now works with 19! But check out what happens when you exit a
- ;; fold with the file displayed in two frames. Both windows get
- ;; fronted. Better fix that sometime.
-
- ;; ------------------------- Future features ----------------------------
-
- ;; *** I will add a `fold-next-error' sometime. It will only work with
- ;; Emacs versions later than 18.58, because compile.el in earlier
- ;; versions does not count line-numbers in the right way, when selective
- ;; display is active.
-
- ;; *** Fold titles should be optionally allowed on the closing fold
- ;; marks, and `fold-tidy-inside' should check that the opening title
- ;; matches the closing title.
-
- ;; *** `folded-file' set in the local variables at the end of a file
- ;; could encode the type of fold marks used in that file, and other
- ;; things, like the margins inside folds.
-
- ;; *** I can see a lot of use for the newer features of Emacs 19:
- ;;
- ;; Using invisible text-properties (I hope they are intended to
- ;; make text invisible; it isn't implemented like that yet), it
- ;; will be possible to hide folded text without affecting the
- ;; text of the buffer. At the moment, Folding mode uses
- ;; selective display to hide text, which involves substituting
- ;; carriage-returns for line-feeds in the buffer. This isn't
- ;; such a good way. It may also be possible to display
- ;; different folds in different windows in Emacs 19.
- ;;
- ;; Using even more text-properties, it may be possible to track
- ;; pointer movements in and out of folds, and have Folding mode
- ;; automatically enter or exit folds as necessary to maintain a
- ;; sensible display. Because the text itself is not modified
- ;; (if overlays are used to hide text), this is quite safe. It
- ;; would make it unnecessary to provide functions like
- ;; `fold-forward-char', `fold-goto-line' or `fold-next-error',
- ;; and things like I-search would automatically move in and out
- ;; of folds as necessary.
- ;;
- ;; Yet more text-properties/overlays might make it possible to
- ;; avoid using narrowing. This might allow some major modes to
- ;; indent text properly, e.g., C++ mode.
-
- ;;}}}
- ;;{{{ Declare `folding' as a feature
-
- (provide 'folding)
-
- ;;}}}
- ;;{{{ Check Emacs version and set some constants.
-
- ;; Sets `fold-emacs-version' to `epoch, `lucid, or the numbers 18 or 19,
- ;; as appropriate, and sets a few related variables.
-
- (setq fold-epoch-screens-p nil
- fold-lucid-screens-p nil
- fold-lucid-keymaps-p nil
- fold-emacs-frames-p nil)
-
- (let ((case-fold-search t))
- (cond ((boundp 'epoch::version) ;; Epoch
- (setq fold-epoch-screens-p t))
- ((string-match "lucid" emacs-version) ;; Lucid Emacs
- (setq fold-lucid-screens-p t
- fold-lucid-keymaps-p t))
- ((string< emacs-version "19")) ;; Emacs 18.x (or less)
- (t ;; Emacs 19+
- (setq fold-emacs-frames-p t))))
-
- ;;}}}
- ;;{{{ Start Folding mode, and related items. Documentation is here
-
- ;;{{{ folding-mode the variable
-
- (defvar folding-mode nil
- "Non-nil means Folding mode is active in the current buffer.")
-
- (make-variable-buffer-local 'folding-mode)
- (set-default 'folding-mode nil)
-
- ;;}}}
- ;;{{{ folding-mode the function
-
- (defun folding-mode (&optional arg inter)
- "Turns Folding mode (a minor mode) on and off.
-
- These are the basic commands that Folding mode provides:
- \\<folding-mode-map>
- fold-enter: `\\[fold-enter]'
- Enters the fold that the point is on.
-
- fold-exit: `\\[fold-exit]'
- Exits the current fold.
-
- fold-fold-region: `\\[fold-fold-region]'
- Surrounds the region with a new fold.
-
- fold-top-level: `\\[fold-top-level]'
- Exits all folds.
-
- fold-show: `\\[fold-show]'
- Opens the fold that the point is on, but does not enter it.
-
- fold-hide: `\\[fold-hide]'
- Closes the fold that the point is in, exiting it if necessary.
-
- fold-whole-buffer: `\\[fold-whole-buffer]'
- Folds the whole buffer.
-
- fold-open-buffer: `\\[fold-open-buffer]'
- Unfolds the whole buffer; good to do just before a search.
-
- fold-remove-folds: `\\[fold-remove-folds]'
- Makes a ready-to-print, formatted, unfolded copy in another buffer.
-
- Read the documentation for the above functions for more information.
-
- Folds are a way of hierarchically organising the text in a file, so that
- the text can be viewed and edited at different levels. It is similar to
- Outline mode in that parts of the text can be hidden from view. A fold
- is a region of text, surrounded by special \"fold marks\", which act
- like brackets, grouping the text. Fold mark pairs can be nested, and
- they can have titles. When a fold is folded, the text is hidden from
- view, except for the first line, which acts like a title for the fold.
-
- Folding mode is a minor mode, designed to cooperate with many other
- major modes, so that many types of text can be folded while they are
- being edited (eg., plain text, program source code, Texinfo, etc.).
-
- For most types of folded file, lines representing folds have \"{{{\"
- near the beginning. To enter a fold, move the point to the folded line
- and type `\\[fold-enter]'. You should no longer be able to see the rest
- of the file, just the contents of the fold, which you couldn't see
- before. You can use `\\[fold-exit]' to leave a fold, and you can enter
- and exit folds to move around the structure of the file.
-
- All of the text is present in a folded file all of the time. It is just
- hidden. Folded text shows up as a line (the top fold mark) with \"...\"
- at the end. If you are in a fold, the mode line displays \"inside n
- folds Narrow\", and because the buffer is narrowed you can't see outside
- of the current fold's text.
-
- By arranging sections of a large file in folds, and maybe subsections in
- sub-folds, you can move around a file quickly and easily, and only have
- to scroll through a couple of pages at a time. If you pick the titles
- for the folds carefully, they can be a useful form of documentation, and
- make moving though the file a lot easier. In general, searching through
- a folded file for a particular item is much easier than without folds.
-
- To make a new fold, set the mark at one end of the text you want in the
- new fold, and move the point to the other end. Then type
- `\\[fold-fold-region]'. The text you selected will be made into a fold,
- and the fold will be entered. If you just want a new, empty fold, set
- the mark where you want the fold, and then create a new fold there
- without moving the point. Don't worry if the point is in the middle of
- a line of text, `fold-fold-region' will not break text in the middle of
- a line. After making a fold, the fold is entered and the point is
- positioned ready to enter a title for the fold. Do not delete the fold
- marks, which are usually something like \"{{{\" and \"}}}\". There may
- also be a bit of fold mark which goes after the fold title.
-
- If the fold markers get messed up, or you just want to see the whole
- unfolded file, use `\\[fold-open-buffer]' to unfolded the whole file, so
- you can see all the text and all the marks. This is useful for
- checking/correcting unbalanced fold markers, and for searching for
- things. Use `\\[fold-whole-file]' to fold the buffer again.
-
- `fold-exit' will attempt to tidy the current fold just before exiting
- it. It will remove any extra blank lines at the top and bottom,
- \(outside the fold marks). It will then ensure that fold marks exists,
- and if they are not, will add them (after asking). Finally, the number
- of blank lines between the fold marks and the contents of the fold is
- set to 1 (by default).
-
- You can make folded files start Folding mode automatically when they are
- visited by setting `folded-file' to t in the file's local variables.
- For example, having the following at the end of an Emacs-Lisp file
- causes it to be folded when visited:
-
- ;; Local variables:
- ;; folded-file: t
- ;; end:
-
- This only works if you have the appropriate hook set up. Look up the
- function `folding-mode-add-find-file-hook' for details.
-
- If the fold marks are not set on entry to Folding mode, they are set to
- a default for current major mode, as defined by `fold-mode-marks-alist'
- or to \"{{{ \" and \"}}}\" if none are specified.
-
- To bind different commands to keys in Folding mode, set the bindings in
- the keymap `folding-mode-map'.
-
- The hooks `folding-mode-hook' and `<major-mode-name>-folding-hook' are
- called before folding the buffer and applying the key bindings in
- `folding-mode-map'. This is a good hook to set extra or different key
- bindings in `folding-mode-map'. Note that key bindings in
- `folding-mode-map' are only examined just after calling these hooks; new
- bindings in those maps only take effect when Folding mode is being
- started.
-
- If Folding mode is not called interactively (`(interactive-p)' is nil),
- and it is called with two or less arguments, all of which are nil, then
- the point will not be altered if `fold-fold-on-startup' is set and
- `fold-whole-buffer' is called. This is generally not a good thing, as
- it can leave the point inside a hidden region of a fold, but it is
- required if the local variables set \"mode: folding\" when the file is
- first read (see `hack-local-variables').
-
- Not that you should ever want to, but to call Folding mode from a
- program with the default behaviour (toggling the mode), call it with
- something like `(folding-mode nil t)'.
-
- Here is the full list of keys bound in Folding mode:
- \\{folding-mode-map}"
- (interactive)
- (let ((new-folding-mode
- (if (not arg) (not folding-mode)
- (> (prefix-numeric-value arg) 0))))
- (or (eq new-folding-mode
- folding-mode)
- (if folding-mode
- (progn
- (setq selective-display nil)
- (fold-clear-stack)
- (widen)
- (fold-subst-regions (list 1 (point-max)) ?\r ?\n)
- (and (boundp 'fold-saved-local-keymap)
- (progn
- (use-local-map fold-saved-local-keymap)
- (kill-local-variable 'fold-saved-local-keymap)
- (makunbound 'fold-saved-local-keymap))))
- (make-local-variable 'fold-saved-local-keymap)
- (setq fold-saved-local-keymap (current-local-map))
- (setq selective-display t)
- (setq selective-display-ellipses t)
- (widen)
- (set (make-local-variable 'fold-stack) nil)
- (make-local-variable 'fold-top-mark)
- (make-local-variable 'fold-secondary-top-mark)
- (make-local-variable 'fold-top-regexp)
- (make-local-variable 'fold-bottom-mark)
- (make-local-variable 'fold-bottom-regexp)
- (make-local-variable 'fold-regexp)
- (or (and (boundp 'fold-top-regexp)
- fold-top-regexp
- (boundp 'fold-bottom-regexp)
- fold-bottom-regexp)
- (let ((fold-marks (assq major-mode
- fold-mode-marks-alist)))
- (if fold-marks
- (setq fold-marks (cdr fold-marks))
- (setq fold-marks '("{{{ " "}}}")))
- (apply 'fold-set-marks fold-marks)))
- (unwind-protect
- (let ((hook-symbol (intern-soft
- (concat
- (symbol-name major-mode)
- "-folding-hook"))))
- (run-hooks 'folding-mode-hook)
- (and hook-symbol
- (run-hooks hook-symbol)))
- (fold-set-mode-line)
- (use-local-map
- (fold-merge-keymaps (current-local-map) folding-mode-map)))
- (and fold-fold-on-startup
- (if (or (interactive-p)
- arg
- inter)
- (fold-whole-buffer)
- (save-excursion
- (fold-whole-buffer))))
- (fold-narrow-to-region nil nil t)))
- (setq folding-mode new-folding-mode)))
-
- ;;}}}
- ;;{{{ folding-mode-map
-
- (defvar folding-mode-map nil
- "Keymap used in Folding mode (a minor mode).")
-
- (and fold-lucid-keymaps-p
- (set-keymap-name folding-mode-map 'folding-mode-map))
-
- (if folding-mode-map
- nil
- (setq folding-mode-map (make-sparse-keymap))
- (define-key folding-mode-map "\M-g" 'fold-goto-line)
- (define-key folding-mode-map "\C-c>" 'fold-enter)
- (define-key folding-mode-map "\C-c<" 'fold-exit)
- (define-key folding-mode-map "\C-c\C-t" 'fold-top-level)
- (define-key folding-mode-map "\C-c\C-f" 'fold-fold-region)
- (define-key folding-mode-map "\C-c\C-s" 'fold-show)
- (define-key folding-mode-map "\C-c\C-h" 'fold-hide)
- (define-key folding-mode-map "\C-c\C-o" 'fold-open-buffer)
- (define-key folding-mode-map "\C-c\C-w" 'fold-whole-buffer)
- (define-key folding-mode-map "\C-c\C-r" 'fold-remove-folds)
- (define-key folding-mode-map "\C-f" 'fold-forward-char)
- (define-key folding-mode-map "\C-b" 'fold-backward-char)
- (define-key folding-mode-map "\C-e" 'fold-end-of-line))
-
- ;;}}}
- ;;{{{ fold-stack
-
- ;; This is a list of structures which keep track of folds being entered
- ;; and exited. It is a list of (MARKER . MARKER) pairs, followed by the
- ;; symbol `folded'. The first of these represents the fold containing
- ;; the current one. If the view is currently outside all folds, this
- ;; variable has value nil.
-
- (defvar fold-stack nil
- "A list of marker pairs representing folds entered so far.")
-
- ;;}}}
- ;;{{{ fold-clear-stack
-
- ;; Clear the fold stack, and release all the markers it refers to.
-
- (defun fold-clear-stack ()
- (let ((stack fold-stack))
- (setq fold-stack nil)
- (while (and stack (not (eq 'folded (car stack))))
- (set-marker (car (car stack)) nil)
- (set-marker (cdr (car stack)) nil)
- (setq stack (cdr stack)))))
-
- ;;}}}
- ;;{{{ fold-mode-string
-
- (defvar fold-mode-string nil
- "Buffer-local variable that holds the fold depth description.")
-
- (set-default 'fold-mode-string " Folding")
-
- ;;}}}
- ;;{{{ fold-set-mode-line
-
- ;; Sets `fold-mode-string' appropriately. This allows the Folding mode
- ;; description in the mode line to reflect the current fold depth."
-
- (defun fold-set-mode-line ()
- (if (null fold-stack)
- (kill-local-variable 'fold-mode-string)
- (make-local-variable 'fold-mode-string)
- (setq fold-mode-string (if (eq 'folded (car fold-stack))
- " inside 1 fold"
- (concat " inside "
- (length fold-stack)
- " folds")))))
-
- ;;}}}
- ;;{{{ Update minor-mode-alist
-
- (or (assq 'folding-mode minor-mode-alist)
- (setq minor-mode-alist
- (cons '(folding-mode fold-mode-string)
- minor-mode-alist)))
-
- ;;}}}
-
- ;;}}}
- ;;{{{ Hooks and variables
-
- ;;{{{ folding-mode-hook
-
- (defvar folding-mode-hook nil
- "Hook called when Folding mode is entered.
-
- A hook named `<major-mode>-folding-hook' is also called, if it
- exists. Eg., `c-mode-folding-hook' is called whenever Folding mode is
- started in C mode.")
-
- ;;}}}
- ;;{{{ fold-fold-on-startup
-
- (defvar fold-fold-on-startup t
- "*If non-nil, buffers are folded when starting Folding mode.")
-
- ;;}}}
- ;;{{{ fold-internal-margins
-
- (defvar fold-internal-margins 1
- "*Number of blank lines left next to fold marks when tidying folds.
-
- This variable is local to each buffer. To set the default value for all
- buffers, use `set-default'.
-
- When exiting a fold, and at other times, `fold-tidy-inside' is invoked
- to ensure that the fold is in the correct form before leaving it. This
- variable specifies the number of blank lines to leave between the
- enclosing fold marks and the enclosed text.
-
- If this value is nil or negative, no blank lines are added or removed
- inside the fold marks. A value of 0 (zero) is valid, meaning leave no
- blank lines.
-
- See also `fold-tidy-inside'.")
-
- (make-variable-buffer-local 'fold-internal-margins)
-
- ;;}}}
- ;;{{{ fold-mode-marks-alist
-
- (defvar fold-mode-marks-alist nil
- "List of (major-mode . fold marks) default combinations to use.
- When Folding mode is started, the major mode is checked, and if there
- are fold marks for that major mode stored in `fold-mode-marks-alist',
- those marks are used by default. If none are found, the default values
- of \"{{{ \" and \"}}}\" are used.")
-
- ;;}}}
-
- ;;}}}
- ;;{{{ Regular expressions for matching fold marks
-
- ;;{{{ fold-set-marks
-
- ;; You think those "\\(\\)" pairs are peculiar? Me too. Emacs regexp
- ;; stuff has a bug; sometimes "\\(.*\\)" fails when ".*" succeeds, but
- ;; only in a folded file! Strange bug! Must check it out sometime.
-
- (defun fold-set-marks (top bottom &optional secondary)
- "Sets the folding top and bottom marks for the current buffer.
-
- The fold top mark is set to TOP, and the fold bottom mark is set to
- BOTTOM. And optional SECONDARY top mark can also be specified -- this
- is inserted by `fold-fold-region' after the fold top mark, and is
- presumed to be put after the title of the fold. This is not necessary
- with the bottom mark because it has no title.
-
- Various regular expressions are set with this function, so don't set the
- mark variables directly."
- (set (make-local-variable 'fold-top-mark)
- top)
- (set (make-local-variable 'fold-bottom-mark)
- bottom)
- (set (make-local-variable 'fold-secondary-top-mark)
- secondary)
- (set (make-local-variable 'fold-top-regexp)
- (concat "\\(^\\|\r+\\)[ \t]*"
- (regexp-quote fold-top-mark)))
- (set (make-local-variable 'fold-bottom-regexp)
- (concat "\\(^\\|\r+\\)[ \t]*"
- (regexp-quote fold-bottom-mark)))
- (set (make-local-variable 'fold-regexp)
- (concat "\\(^\\|\r\\)\\([ \t]*\\)\\(\\("
- (regexp-quote fold-top-mark)
- "\\)\\|\\("
- (regexp-quote fold-bottom-mark)
- "[ \t]*\\(\\)\\($\\|\r\\)\\)\\)")))
-
- ;;}}}
-
- ;;}}}
- ;;{{{ Cursor movement that skips folded regions
-
- ;;{{{ fold-forward-char
-
- (defun fold-forward-char (&optional arg)
- "Move point right ARG characters, skipping hidden folded regions.
- Moves left if ARG is negative. On reaching end of buffer, stop and
- signal error."
- (interactive "p")
- (if (eq arg 1)
- ;; Do it a faster way for arg = 1.
- (if (eq (following-char) ?\r)
- (let ((saved (point))
- (inhibit-quit t))
- (end-of-line)
- (if (not (eobp))
- (forward-char)
- (goto-char saved)
- (error "End of buffer")))
- ;; `forward-char' here will do its own error if (eobp).
- (forward-char))
- (if (> 0 (or arg (setq arg 1)))
- (fold-backward-char (- arg))
- (let (goal saved)
- (while (< 0 arg)
- (skip-chars-forward "^\r" (setq goal (+ (point) arg)))
- (if (eq goal (point))
- (setq arg 0)
- (if (eobp)
- (error "End of buffer")
- (setq arg (- goal 1 (point))
- saved (point))
- (let ((inhibit-quit t))
- (end-of-line)
- (if (not (eobp))
- (forward-char)
- (goto-char saved)
- (error "End of buffer"))))))))))
-
- ;;}}}
- ;;{{{ fold-backward-char
-
- (defun fold-backward-char (&optional arg)
- "Move point left ARG characters, skipping hidden folded regions.
- Moves right if ARG is negative. On reaching beginning of buffer, stop
- and signal error."
- (interactive "p")
- (if (eq arg 1)
- ;; Do it a faster way for arg = 1.
- ;; Catch the case where we are in a hidden region, and bump into a \r.
- (if (or (eq (preceding-char) ?\n)
- (eq (preceding-char) ?\r))
- (let ((pos (1- (point)))
- (inhibit-quit t))
- (forward-char -1)
- (beginning-of-line)
- (skip-chars-forward "^\r" pos))
- (forward-char -1))
- (if (> 0 (or arg (setq arg 1)))
- (fold-forward-char (- arg))
- (let (goal)
- (while (< 0 arg)
- (skip-chars-backward "^\r\n" (max (point-min)
- (setq goal (- (point) arg))))
- (if (eq goal (point))
- (setq arg 0)
- (if (bobp)
- (error "Beginning of buffer")
- (setq arg (- (point) 1 goal)
- goal (point))
- (let ((inhibit-quit t))
- (forward-char -1)
- (beginning-of-line)
- (skip-chars-forward "^\r" goal)))))))))
-
- ;;}}}
- ;;{{{ fold-end-of-line
-
- (defun fold-end-of-line (&optional arg)
- "Move point to end of current line, but before hidden folded region.
-
- Has the same behavior as `end-of-line', except that if the current line
- ends with some hidden folded text (represented by an ellipsis), the
- point is positioned just before it. This prevents the point from being
- placed inside the folded text, which is not normally useful."
- (interactive "p")
- (if (or (eq arg 1)
- (not arg))
- (beginning-of-line)
- ;; `forward-line' also moves point to beginning of line.
- (forward-line (1- arg)))
- (skip-chars-forward "^\r\n"))
-
- ;;}}}
- ;;{{{ fold-skip-ellipsis-backward
-
- (defun fold-skip-ellipsis-backward ()
- "Moves the point backwards out of folded text.
-
- If the point is inside a folded region, the cursor is displayed at the
- end of the ellipsis representing the folded part. This function checks
- to see if this is the case, and if so, moves the point backwards until
- it is just outside the hidden region, and just before the ellipsis.
-
- Returns t if the point was moved, nil otherwise."
- (interactive)
- (let ((pos (point))
- result)
- (save-excursion
- (beginning-of-line)
- (skip-chars-forward "^\r" pos)
- (or (eq pos (point))
- (setq pos (point)
- result t)))
- (goto-char pos)
- result))
-
- ;;}}}
-
- ;;}}}
- ;;{{{ Moving in and out of folds
-
- ;;{{{ fold-enter
-
- (defun fold-enter (&optional noerror)
- "Open and enter the fold at or around the point.
-
- Enters the fold that the point is inside, wherever the point is inside
- the fold, provided it is a valid fold with balanced top and bottom
- marks. Returns nil if the fold entered contains no sub-folds, t
- otherwise. If an optional argument NOERROR is non-nil, returns nil if
- there are no folds to enter, instead of causing an error.
-
- If the point is inside a folded, hidden region (as represented by an
- ellipsis), the position of the point in the buffer is preserved, and as
- many folds as necessary are entered to make the surrounding text
- visible. This is useful after some commands eg., search commands."
- (interactive)
- (let ((goal (point)))
- (if (fold-skip-ellipsis-backward)
- (while (prog2 (beginning-of-line)
- (fold-enter t)
- (goto-char goal)))
- (let ((data (fold-show noerror t)))
- (and data
- (progn
- (setq fold-stack
- (if fold-stack
- (cons (cons (point-min-marker) (point-max-marker))
- fold-stack)
- '(folded)))
- (fold-set-mode-line)
- (fold-narrow-to-region (car data) (nth 1 data))
- (nth 2 data)))))))
-
- ;;}}}
- ;;{{{ fold-exit
-
- (defun fold-exit ()
- "Exits the current fold."
- (interactive)
- (if fold-stack
- (progn
- (fold-tidy-inside)
- (fold-subst-regions (list (point-min) (point-max)) ?\n ?\r)
- (goto-char (point-min)) ;; So point is correct in other windows.
- (if (eq (car fold-stack) 'folded)
- (fold-narrow-to-region nil nil t)
- (fold-narrow-to-region (marker-position (car (car fold-stack)))
- (marker-position (cdr (car fold-stack))) t))
- (and (consp (car fold-stack))
- (set-marker (car (car fold-stack)) nil)
- (set-marker (cdr (car fold-stack)) nil))
- (setq fold-stack (cdr fold-stack)))
- (error "Outside all folds"))
- (fold-set-mode-line))
-
- ;;}}}
- ;;{{{ fold-show
-
- (defun fold-show (&optional noerror noskip)
- "Opens the fold that the point is on, but does not enter it.
- Optional arg NOERROR means don't signal an error if there is no fold,
- just return nil. NOSKIP means don't jump out of a hidden region first.
-
- Returns ((START END SUBFOLDS-P). START and END indicate the extents of
- the fold that was shown. If SUBFOLDS-P is non-nil, the fold contains
- subfolds."
- (interactive "p")
- (or noskip
- (fold-skip-ellipsis-backward))
- (let ((point (point))
- backward forward start end subfolds-not-p)
- (unwind-protect
- (or (and (integerp (car-safe (setq backward (fold-skip-folds t))))
- (integerp (car-safe (setq forward (fold-skip-folds nil))))
- (progn
- (goto-char (car forward))
- (skip-chars-forward "^\r\n")
- (setq end (point))
- (skip-chars-forward "\r\n")
- (not (and fold-stack (eobp))))
- (progn
- (goto-char (car backward))
- (skip-chars-backward "^\r\n")
- (setq start (point))
- (skip-chars-backward "\r\n")
- (not (and fold-stack (bobp))))
- (progn
- (setq point start)
- (setq subfolds-not-p ; Avoid holding the list through a GC.
- (not (or (cdr backward) (cdr forward))))
- (fold-subst-regions (append backward (nreverse forward))
- ?\r ?\n)
- (list start end (not subfolds-not-p))))
- (if noerror
- nil
- (error "Not on a fold")))
- (goto-char point))))
-
- ;;}}}
- ;;{{{ fold-hide
-
- (defun fold-hide ()
- "Close the fold around the point, undoes effect of `fold-show'."
- (interactive)
- (fold-skip-ellipsis-backward)
- (if (and (integerp (setq start (car-safe (fold-skip-folds t))))
- (integerp (setq end (car-safe (fold-skip-folds nil)))))
- (if (and fold-stack
- (or (eq start (point-min))
- (eq end (point-max))))
- (error "Cannot hide current fold")
- (goto-char start)
- (skip-chars-backward "^\r\n")
- (fold-subst-regions (list start end) ?\n ?\r))
- (error "Not on a fold")))
-
- ;;}}}
- ;;{{{ fold-top-level
-
- (defun fold-top-level ()
- "Exits all folds, to the top level."
- (interactive)
- (while fold-stack
- (fold-exit)))
-
- ;;}}}
- ;;{{{ fold-goto-line
-
- (defun fold-goto-line (line)
- "Go to line ARG, entering as many folds as possible."
- (interactive "nGoto line: ")
- (fold-top-level)
- (goto-char 1)
- (and (< 1 line)
- (re-search-forward "[\n\C-m]" nil 0 (1- line)))
- (let ((goal (point)))
- (while (prog2 (beginning-of-line)
- (fold-enter t)
- (goto-char goal))))
- (fold-narrow-to-region (point-min) (point-max) t))
-
- ;;}}}
-
- ;;}}}
- ;;{{{ Searching for fold boundaries
-
- ;;{{{ fold-skip-folds
-
- ;; Skips forward through the buffer (backward if BACKWARD is non-nil)
- ;; until it finds a closing fold mark or the end of the buffer. The
- ;; point is not moved. Jumps over balanced fold-mark pairs on the way.
- ;; Returns t if the end of buffer was found in an unmatched fold-mark
- ;; pair, otherwise a list.
-
- ;; If the point is actually on an fold start mark, the mark is ignored;
- ;; if it is on an end mark, the mark is noted. This decision is
- ;; reversed if BACKWARD is non-nil. If optional OUTSIDE is non-nil and
- ;; BACKWARD is nil, either mark is noted.
-
- ;; The first element of the list is a position in the end of the closing
- ;; fold mark if one was found, or nil. It is followed by (END START)
- ;; pairs (flattened, not a list of pairs). The pairs indicating the
- ;; positions of folds skipped over; they are positions in the fold
- ;; marks, not necessarily at the ends of the fold marks. They are in
- ;; the opposite order to that in which they were skipped. The point is
- ;; left in a meaningless place. If going backwards, the pairs are
- ;; (START END) pairs, as the fold marks are scanned in the opposite
- ;; order.
-
- ;; Works by maintaining the position of the top and bottom marks found
- ;; so far. They are found separately using a normal string search for
- ;; the fixed part of a fold mark (because it is faster than a regexp
- ;; search if the string does not occur often outside of fold marks),
- ;; checking that it really is a proper fold mark, then considering the
- ;; earliest one found. The position of the other (if found) is
- ;; maintained to avoid an unnecessary search at the next iteration.
-
- (defun fold-skip-folds (backward &optional outside)
- (save-excursion
- (let ((depth 0) pairs point temp start first last
- (first-mark (if backward fold-bottom-mark fold-top-mark))
- (last-mark (if backward fold-top-mark fold-bottom-mark))
- (search (if backward 'search-backward 'search-forward)))
- (skip-chars-backward "^\r\n")
- (if outside
- nil
- (and (eq (preceding-char) ?\r)
- (forward-char -1))
- (if (looking-at fold-top-regexp)
- (if backward
- (setq last (match-end 1))
- (skip-chars-forward "^\r\n"))))
- (while (progn
- ;; Find last first, prevents unnecessary searching for first.
- (setq point (point))
- (or last
- (while (and (funcall search last-mark first t)
- (progn
- (setq temp (point))
- (goto-char (match-beginning 0))
- (skip-chars-backward " \t")
- (and (not (setq last
- (if (eq (preceding-char) ?\r)
- temp
- (and (bolp) temp))))
- (goto-char temp)))))
- (goto-char point))
- (or first
- (while (and (funcall search first-mark last t)
- (progn
- (setq temp (point))
- (goto-char (match-beginning 0))
- (skip-chars-backward " \t")
- (and (not (setq first
- (if (eq (preceding-char) ?\r)
- temp
- (and (bolp) temp))))
- (goto-char temp))))))
- ;; Return value of conditional says whether to iterate again.
- (if (not last)
- ;; Return from this with the result.
- (not (setq pairs (if first t (cons nil pairs))))
- (if (and first (if backward (> first last) (< first last)))
- (progn
- (goto-char first)
- (if (eq 0 depth)
- (setq start first
- first nil
- depth 1) ;; non-nil value, loop again.
- (setq first nil
- depth (1+ depth)))) ;; non-nil value, loop again
- (goto-char last)
- (if (eq 0 depth)
- (not (setq pairs (cons last pairs)))
- (or (< 0 (setq depth (1- depth)))
- (setq pairs (cons last (cons start pairs))))
- (setq last nil)
- t)))))
- pairs)))
-
- ;;}}}
-
- ;;}}}
- ;;{{{ Functions that actually modify the buffer
-
- ;;{{{ fold-fold-region
-
- (defun fold-fold-region (start end)
- "Places fold marks at the beginning and end of a specified region.
- The region is specified by two arguments START and END. The point is
- left at a suitable place ready to insert the title of the fold."
- (interactive "r")
- (and (< end start)
- (setq start (prog1 end
- (setq end start))))
- (setq end (set-marker (make-marker) end))
- (goto-char start)
- (beginning-of-line)
- (setq start (point))
- (insert-before-markers fold-top-mark)
- (let ((saved-point (point)))
- (and fold-secondary-top-mark
- (insert-before-markers fold-secondary-top-mark))
- (insert-before-markers ?\n)
- (goto-char (marker-position end))
- (set-marker end nil)
- (and (not (bolp))
- (eq 0 (forward-line))
- (eobp)
- (insert ?\n))
- (insert fold-bottom-mark)
- (insert ?\n)
- (setq fold-stack (if fold-stack
- (cons (cons (point-min-marker)
- (point-max-marker))
- fold-stack)
- '(folded)))
- (fold-narrow-to-region start (1- (point)))
- (goto-char saved-point)
- (fold-set-mode-line))
- (save-excursion (fold-tidy-inside)))
-
- ;;}}}
- ;;{{{ fold-tidy-inside
-
- ;; Note to self: The long looking code for checking and modifying those
- ;; blank lines is to make sure the text isn't modified unnecessarily.
- ;; Don't remove it again!
-
- (defun fold-tidy-inside ()
- "Adds or removes blank lines at the top and bottom of the current fold.
- Also adds fold marks at the top and bottom (after asking), if they are not
- there already. The amount of space left depends on the variable
- `fold-internal-margins', which is one by default."
- (interactive)
- (if buffer-read-only nil
- (goto-char (point-min))
- (and (eolp)
- (progn (skip-chars-forward "\n\t ")
- (delete-region (point-min) (point))))
- (and (if (looking-at fold-top-regexp)
- (progn (forward-line 1)
- (and (eobp) (insert ?\n))
- t)
- (and (y-or-n-p "Insert missing fold-top-mark? ")
- (progn (insert (concat fold-top-mark
- "<Replaced missing fold top mark>"
- (or fold-secondary-top-mark "")
- "\n"))
- t)))
- fold-internal-margins
- (<= 0 fold-internal-margins)
- (let* ((p1 (point))
- (p2 (progn (skip-chars-forward "\n") (point)))
- (p3 (progn (skip-chars-forward "\n\t ")
- (skip-chars-backward "\t " p2) (point))))
- (if (eq p2 p3)
- (or (eq p2 (setq p3 (+ p1 fold-internal-margins)))
- (if (< p2 p3)
- (newline (- p3 p2))
- (delete-region p3 p2)))
- (delete-region p1 p3)
- (or (eq 0 fold-internal-margins)
- (newline fold-internal-margins)))))
- (goto-char (point-max))
- (and (bolp)
- (progn (skip-chars-backward "\n")
- (delete-region (point) (point-max))))
- (beginning-of-line)
- (and (or (looking-at fold-bottom-regexp)
- (progn (goto-char (point-max)) nil)
- (and (y-or-n-p "Insert missing fold-bottom-mark? ")
- (progn
- (insert (concat "\n" fold-bottom-mark))
- (beginning-of-line)
- t)))
- fold-internal-margins
- (<= 0 fold-internal-margins)
- (let* ((p1 (point))
- (p2 (progn (skip-chars-backward "\n") (point)))
- (p3 (progn (skip-chars-backward "\n\t ")
- (skip-chars-forward "\t " p2) (point))))
- (if (eq p2 p3)
- (or (eq p2 (setq p3 (- p1 1 fold-internal-margins)))
- (if (> p2 p3)
- (newline (- p2 p3))
- (delete-region p2 p3)))
- (delete-region p3 p1)
- (newline (1+ fold-internal-margins)))))))
-
- ;;}}}
-
- ;;}}}
- ;;{{{ Operations on the whole buffer
-
- ;;{{{ fold-whole-buffer
-
- (defun fold-whole-buffer ()
- "Folds every fold in the current buffer.
- Fails if the fold markers are not balanced correctly.
-
- If the buffer is being viewed in a fold, folds are repeatedly exited to
- get to the top level first (this allows the folds to be tidied on the
- way out). The buffer modification flag is not affected, and this
- function will work on read-only buffers."
-
- (interactive)
- (message "Folding buffer...")
- (let ((narrow-min (point-min))
- (narrow-max (point-max))
- fold-list fold)
- (save-excursion
- (widen)
- (goto-char 1)
- (setq fold-list (fold-skip-folds nil t))
- (narrow-to-region narrow-min narrow-max)
- (and (eq t fold-list)
- (error "Cannot fold whole buffer -- unmatched begin-fold mark"))
- (and (integerp (car fold-list))
- (error "Cannot fold whole buffer -- extraneous end-fold mark"))
- (fold-top-level)
- (widen)
- (goto-char 1)
- ;; Do the modifications forwards.
- (fold-subst-regions (nreverse (cdr fold-list)) ?\n ?\r))
- (beginning-of-line)
- (fold-narrow-to-region nil nil t)
- (message "Folding buffer... done")))
-
- ;;}}}
- ;;{{{ fold-open-buffer
-
- (defun fold-open-buffer ()
- "Unfolds the entire buffer, leaving the point where it is.
- Does not affect the buffer-modified flag, and can be used on read-only
- buffers."
- (interactive)
- (message "Unfolding buffer...")
- (fold-clear-stack)
- (fold-set-mode-line)
- (unwind-protect
- (progn
- (widen)
- (fold-subst-regions (list 1 (point-max)) ?\r ?\n))
- (fold-narrow-to-region nil nil t))
- (message "Unfolding buffer... done"))
-
- ;;}}}
- ;;{{{ fold-remove-folds
-
- (defun fold-remove-folds (&optional buffer pre-title post-title pad)
- "Removes folds from a buffer, for printing.
-
- It copies the contents of the (hopefully) folded buffer BUFFER into a
- buffer called `*Unfolded: <Original-name>*', removing all of the fold
- marks. It keeps the titles of the folds, however, and numbers them.
- Subfolds are numbered in the form 5.1, 5.2, 5.3 etc., and the titles are
- indented to eleven characters.
-
- It accepts four arguments. BUFFER is the name of the buffer to be
- operated on, or a buffer. nil means use the current buffer. PRE-TITLE
- is the text to go before the replacement fold titles, POST-TITLE is the
- text to go afterwards. Finally, if PAD is non-nil, the titles are all
- indented to the same column, which is eleven plus the length of
- PRE-TITLE. Otherwise just one space is placed between the number and
- the title."
- (interactive (list (read-buffer "Remove folds from buffer: "
- (buffer-name)
- t)
- (read-string "String to go before enumerated titles: ")
- (read-string "String to go after enumerated titles: ")
- (y-or-n-p "Pad section numbers with spaces? ")))
- (set-buffer (setq buffer (get-buffer buffer)))
- (setq pre-title (or pre-title "")
- post-title (or post-title ""))
- (or folding-mode
- (error "Must be in Folding mode before removing folds"))
- (let ((new-buffer (get-buffer-create (concat "*Unfolded: "
- (buffer-name buffer)
- "*")))
- (section-list '(1))
- (section-prefix-list '(""))
- title
- (secondary-mark-length (length fold-secondary-top-mark))
- (regexp fold-regexp)
- (secondary-mark fold-secondary-top-mark)
- prefix
- (mode major-mode))
- (buffer-flush-undo new-buffer)
- (save-excursion
- (set-buffer new-buffer)
- (delete-region (point-min)
- (point-max)))
- (save-restriction
- (widen)
- (copy-to-buffer new-buffer (point-min) (point-max)))
- (display-buffer new-buffer t)
- (set-buffer new-buffer)
- (subst-char-in-region (point-min) (point-max) ?\r ?\n)
- (funcall mode)
- (while (re-search-forward regexp nil t)
- (if (match-beginning 4)
- (progn
- (goto-char (match-end 4))
- (setq title
- (buffer-substring (point)
- (progn (end-of-line)
- (point))))
- (delete-region (save-excursion
- (goto-char (match-beginning 4))
- (skip-chars-backward "\n\r")
- (point))
- (progn
- (skip-chars-forward "\n\r")
- (point)))
- (and (<= secondary-mark-length
- (length title))
- (string-equal secondary-mark
- (substring title
- (- secondary-mark-length)))
- (setq title (substring title
- 0
- (- secondary-mark-length))))
- (setq section-prefix-list
- (cons (setq prefix (concat (car section-prefix-list)
- (int-to-string (car section-list))
- "."))
- section-prefix-list))
- (or (cdr section-list)
- (insert ?\n))
- (setq section-list
- (cons 1
- (cons (1+ (car section-list))
- (cdr section-list))))
- (setq title (concat prefix
- (if pad
- (make-string
- (max 2 (- 8 (length prefix))) ? )
- " ")
- title))
- (message "Reformatting: %s%s%s"
- pre-title
- title
- post-title)
- (insert "\n\n"
- pre-title
- title
- post-title
- "\n\n"))
- (goto-char (match-beginning 5))
- (or (setq section-list (cdr section-list))
- (error "Too many bottom-of-fold marks"))
- (setq section-prefix-list (cdr section-prefix-list))
- (delete-region (point)
- (progn
- (forward-line 1)
- (point)))))
- (and (cdr section-list)
- (error
- "Too many top-of-fold marks -- reached end of file prematurely"))
- (goto-char (point-min))
- (buffer-enable-undo)
- (set-buffer-modified-p nil)
- (message "All folds reformatted.")))
-
- ;;}}}
- ;;}}}
- ;;{{{ Standard fold marks for various major modes
-
- ;;{{{ A function to set default marks, `fold-add-to-marks-list'
-
- (defun fold-add-to-marks-list (mode top bottom
- &optional secondary noforce message)
- "Add/set fold marks for a particular major mode.
- When called interactively, asks for a major-mode name, and for
- fold marks to be used in that mode. It adds the new set to
- `fold-mode-marks-alist', and if the mode name is the same as the current
- major mode for the current buffer, the marks in use are also changed.
-
- If called non-interactively, arguments are MODE, TOP, BOTTOM and
- SECONDARY. MODE is the symbol for the major mode for which marks are
- being set. TOP, BOTTOM and SECONDARY are strings, the three fold marks
- to be used. SECONDARY may be nil (as opposed to the empty string), but
- the other two must be non-empty strings, and is an optional argument.
-
- Two other optional arguments are NOFORCE, meaning do not change the
- marks if marks are already set for the specified mode if non-nil, and
- MESSAGE, which causes a message to be displayed if it is non-nil. This
- is also the message displayed if the function is called interactively.
-
- To set default fold marks for a particular mode, put something like the
- following in your .emacs:
-
- \(fold-add-to-marks-list 'major-mode \"(** {{{ \" \"(** }}} **)\" \" **)\")
-
- Look at the variable `fold-mode-marks-alist' to see what default settings
- already apply.
-
- `fold-set-marks' can be used to set the fold marks in use in the current
- buffer without affecting the default value for a particular mode."
- (interactive
- (let* ((mode (completing-read
- (concat "Add fold marks for major mode ("
- (symbol-name major-mode)
- "): ")
- obarray
- (function
- (lambda (arg)
- (and (commandp arg)
- (string-match "-mode\\'"
- (symbol-name arg)))))
- t))
- (mode (if (equal mode "")
- major-mode
- (intern mode)))
- (object (assq mode fold-mode-marks-alist))
- (old-top (and object
- (nth 1 object)))
- top
- (old-bottom (and object
- (nth 2 object)))
- bottom
- (secondary (and object
- (nth 3 object)))
- (prompt "Top fold marker: "))
- (and (equal secondary "")
- (setq secondary nil))
- (while (not top)
- (setq top (read-string prompt (or old-top "{{{ ")))
- (and (equal top "")
- (setq top nil)))
- (setq prompt (concat prompt
- top
- ", Bottom marker: "))
- (while (not bottom)
- (setq bottom (read-string prompt (or old-bottom "}}}")))
- (and (equal bottom "")
- (setq bottom nil)))
- (setq prompt (concat prompt
- bottom
- (if secondary
- ", Secondary marker: "
- ", Secondary marker (none): "))
- secondary (read-string prompt secondary))
- (and (equal secondary "")
- (setq secondary nil))
- (list mode top bottom secondary nil t)))
- (let ((object (assq mode fold-mode-marks-alist)))
- (if (and object
- noforce
- message)
- (message "Fold markers for `%s' are already set."
- (symbol-name mode))
- (if object
- (or noforce
- (setcdr object (if secondary
- (list top bottom secondary)
- (list top bottom))))
- (setq fold-mode-marks-alist
- (cons (if secondary
- (list mode top bottom secondary)
- (list mode top bottom))
- fold-mode-marks-alist)))
- (and message
- (message "Set fold marks for `%s' to \"%s\" and \"%s\"."
- (symbol-name mode)
- (if secondary
- (concat top "name" secondary)
- (concat top "name"))
- bottom)
- (and (eq major-mode mode)
- (fold-set-marks top bottom secondary))))))
-
- ;;}}}
- ;;{{{ Set some useful default fold marks
-
- (fold-add-to-marks-list 'c-mode "/* {{{ " "/* }}} */" " */" t)
- (fold-add-to-marks-list 'emacs-lisp-mode ";;{{{ " ";;}}}" nil t)
- (fold-add-to-marks-list 'lisp-interaction-mode ";;{{{ " ";;}}}" nil t)
- (fold-add-to-marks-list 'plain-tex-mode "%{{{ " "%}}}" nil t)
- (fold-add-to-marks-list 'plain-TeX-mode "%{{{ " "%}}}" nil t)
- (fold-add-to-marks-list 'latex-mode "%{{{ " "%}}}" nil t)
- (fold-add-to-marks-list 'LaTeX-mode "%{{{ " "%}}}" nil t)
- (fold-add-to-marks-list 'orwell-mode "{{{ " "}}}" nil t)
- (fold-add-to-marks-list 'fundamental-mode "{{{ " "}}}" nil t)
- (fold-add-to-marks-list 'modula-2-mode "(* {{{ " "(* }}} *)" " *)" t)
- (fold-add-to-marks-list 'shellscript-mode "# {{{ " "# }}}" nil t)
- (fold-add-to-marks-list 'perl-mode "# {{{ " "# }}}" nil t)
- (fold-add-to-marks-list 'texinfo-mode "@c {{{ " "@c {{{endfold}}}" " }}}" t)
- (fold-add-to-marks-list 'occam-mode "-- {{{ " "-- }}}" nil t)
- (fold-add-to-marks-list 'lisp-mode ";;{{{ " ";;}}}" nil t)
- (fold-add-to-marks-list 'tex-mode "%{{{ " "%}}}" nil t)
- (fold-add-to-marks-list 'TeX-mode "%{{{ " "%}}}" nil t)
- (fold-add-to-marks-list 'c++-mode "// {{{ " "// }}}" nil t)
- (fold-add-to-marks-list 'bison-mode "/* {{{ " "/* }}} */" " */" t)
- (fold-add-to-marks-list 'Bison-mode "/* {{{ " "/* }}} */" " */" t)
- (fold-add-to-marks-list 'gofer-mode "-- {{{ " "-- }}}" nil t)
- (fold-add-to-marks-list 'ml-mode "(* {{{ " "(* }}} *)" " *)" t)
- (fold-add-to-marks-list 'sml-mode "(* {{{ " "(* }}} *)" " *)" t)
-
- ;;}}}
-
- ;;}}}
- ;;{{{ Start Folding mode automatically for folded files
-
- ;;{{{ folding-mode-find-file-hook
-
- (defun folding-mode-find-file-hook ()
- "One of the hooks called whenever a `find-file' is successful.
- It checks to see if `folded-file' has been set as a buffer-local
- variable, and automatically starts Folding mode if it has.
-
- This allows folded files to be automatically folded when opened.
-
- To make this hook effective, the symbol `folding-mode-find-file-hook'
- should be placed at the end of `find-file-hooks'. If you have
- some other hook in the list, for example a hook to automatically
- uncompress or decrypt a buffer, it should go earlier on in the list.
-
- See also `folding-mode-add-find-file-hook'."
- (and (assq 'folded-file (buffer-local-variables))
- folded-file
- (folding-mode 1)
- (kill-local-variable 'folded-file)))
-
- ;;}}}
- ;;{{{ folding-mode-add-find-file-hook
-
- (defun folding-mode-add-find-file-hook ()
- "Appends `folding-mode-find-file-hook' to the list `find-file-hooks'.
-
- This has the effect that afterwards, when a folded file is visited, if
- appropriate Emacs local variable entries are recognised at the end of
- the file, Folding mode is started automatically.
-
- If `inhibit-local-variables' is non-nil, this will not happen regardless
- of the setting of `find-file-hooks'.
-
- To declare a file to be folded, put `folded-file: t' in the file's
- local variables. eg., at the end of a C source file, put:
-
- /*
- Local variables:
- folded-file: t
- */
-
- The local variables can be inside a fold."
- (interactive)
- (or (memq 'folding-mode-find-file-hook find-file-hooks)
- (setq find-file-hooks (append find-file-hooks
- '(folding-mode-find-file-hook)))))
-
- ;;}}}
-
- ;;}}}
- ;;{{{ Gross, crufty hacks that seem necessary
-
- ;; The functions here have been tested with Emacs 18.55, Emacs 18.58,
- ;; Epoch 4.0p2 (based on Emacs 18.58) and Lucid Emacs 19.6.
-
- ;; Note that Lucid Emacs 19.6 can't do selective-display, and its
- ;; "invisible extents" don't work either, so Folding mode just won't
- ;; work with that version.
-
- ;; They shouldn't do the wrong thing with later versions of Emacs, but
- ;; they might not have the special effects either. They may appear to
- ;; be excessive; that is not the case. All of the peculiar things these
- ;; functions do is done to avoid some side-effect of Emacs' internal
- ;; logic that I have met. Some of them work around bugs or unfortunate
- ;; (lack of) features in Emacs. In most cases, it would be better to
- ;; move this into the Emacs C code.
-
- ;; Folding mode is designed to be simple to cooperate with as many
- ;; things as possible. These functions go against that principle at the
- ;; coding level, but make life for the user bearable.
-
- ;;{{{ fold-merge-keymaps
-
- ;; Merge keymaps, because miner-mode keymaps aren't available in Emacs
- ;; 18. In Lucid Emacs, keymaps can have parent keymaps, so that
- ;; mechanism is used instead and MAP isn't copied.
-
- ;; Takes two keymaps, MAP and EXTRA. Merges each binding in EXTRA into
- ;; a copy of MAP, and returns the new keymap (bindings in EXTRA override
- ;; those in MAP). MAP or EXTRA may be nil, indicating an empty keymap.
- ;; If they are both nil, nil is returned. Sub-keymaps and even cons
- ;; cells containing bindings are not copied unnecessarily (well,
- ;; sometimes they are). This means that if you modify the local map
- ;; when Folding mode is active, the effects are unpredictable: you may
- ;; also affect the keymap that was active before Folding mdoe was
- ;; started, and you may affect folding-mode-map.
-
- (defun fold-merge-keymaps (map extra)
- (or map (setq map extra extra nil))
- (if (null extra)
- (and map (copy-keymap map))
- (if fold-lucid-keymaps-p
- (let ((new (copy-keymap extra)))
- (set-keymap-parent new map)
- new)
- (or (keymapp extra)
- (signal 'wrong-type-argument (list 'keymapp extra)))
- (or (keymapp map)
- (signal 'wrong-type-argument (list 'keymapp map)))
- (and (vectorp extra)
- (let ((key (length extra))
- (oldextra extra))
- (setq extra nil)
- (while (<= 0 (setq key (1- key)))
- (and (aref oldextra key)
- (setq extra (cons (cons key (aref oldextra key)) extra))))
- (setq extra (cons 'keymap extra))))
- (and (cdr extra)
- (let (key keycode cons-binding realdef def submap)
-
- ;; Note that this copy-sequence will copy the spine of the
- ;; sparse keymap, but it will not copy the cons cell used
- ;; for each binding. This is important; define-key does a
- ;; setcdr to rebind a key, if that key was bound already,
- ;; so define-key can't be used to change a binding. Using
- ;; copy-keymap instead would be excessive and slow, because
- ;; it would be repeatedly invoked, as this function is
- ;; called recursively.
-
- (setq map (copy-sequence map))
- (while (setq extra (cdr extra))
- (setq keycode (car (car extra))
- key (char-to-string keycode)
- def (cdr (car extra))
- realdef def)
- (while (and def (if (symbolp def)
- (setq def (symbol-function def))
- (and (consp def)
- (integerp (cdr def))
- (keymapp (car def))
- (setq def (lookup-key (car def)
- (char-to-string
- (cdr def))))))))
- (if (and (keymapp def)
- (setq submap (lookup-key map key)))
- (progn
- (while (and submap
- (if (symbolp submap)
- (setq submap (symbol-function submap))
- (and (consp submap)
- (integerp (cdr submap))
- (keymapp (car submap))
- (setq submap (lookup-key
- (car submap)
- (char-to-string
- (cdr submap))))))))
- (if (keymapp submap)
- (if (vectorp map)
- (aset map keycode
- (fold-merge-keymaps submap def))
- (setcdr (setq map (delq (assq keycode map) map))
- (cons (cons keycode
- (fold-merge-keymaps submap def))
- (cdr map))))
- (if (vectorp map)
- (aset map keycode realdef)
- (setcdr (setq map (delq (assq keycode map) map))
- (cons (cons keycode realdef) (cdr map))))))
- (and def
- (if (vectorp map)
- (aset map keycode realdef)
- (and (setq cons-binding (assq keycode map))
- (setq map (delq cons-binding map)))
- (setcdr map (cons (cons keycode realdef)
- (cdr map)))))))))
- map)))
-
- ;;}}}
- ;;{{{ fold-subst-regions
-
- ;; Substitute newlines for carriage returns or vice versa.
- ;; Avoid excessive file locking.
-
- ;; Substitutes characters in the buffer, even in a read-only buffer.
- ;; Takes LIST, a list of regions specified as sequence in the form
- ;; (START1 END1 START2 END2 ...). In every region specified by each
- ;; pair, substitutes each occurence of character FIND by REPLACE.
-
- ;; The buffer-modified flag is not affected, undo information is not
- ;; kept for the change, and the function works on read-only files. This
- ;; function is much more efficient called with a long sequence than
- ;; called for each region in the sequence.
-
- ;; If the buffer is not modified when the function is called, the
- ;; modified-flag is set before performing all the substitutions, and
- ;; locking is temporarily disabled. This prevents Emacs from trying to
- ;; make then delete a lock file for *every* substitution, which slows
- ;; folding considerably, especially on a slow networked filesystem.
- ;; Without this, on my system, folding files on startup (and reading
- ;; other peoples' folded files) takes about five times longer. Emacs
- ;; still locks the file once for this call under those circumstances; I
- ;; can't think of a way around that, but it isn't really a problem.
-
- ;; I consider these problems to be a bug in `subst-char-in-region'.
-
- (defun fold-subst-regions (list find replace)
- (let ((buffer-read-only buffer-read-only) ;; Protect read-only flag.
- (modified (buffer-modified-p))
- (ask1 (symbol-function 'ask-user-about-supersession-threat))
- (ask2 (symbol-function 'ask-user-about-lock)))
- (unwind-protect
- (progn
- (setq buffer-read-only nil)
- (or modified
- (progn
- (fset 'ask-user-about-supersession-threat
- '(lambda (&rest x) nil))
- (fset 'ask-user-about-lock
- '(lambda (&rest x) nil))
- (set-buffer-modified-p t))) ; Prevent file locking in the loop
- (while list
- (subst-char-in-region (car list) (nth 1 list) find replace t)
- (setq list (cdr (cdr list)))))
- ;; buffer-read-only is restored by the let.
- ;; Don't want to change MODIFF time if it was modified before.
- (or modified
- (unwind-protect
- (set-buffer-modified-p nil)
- (fset 'ask-user-about-supersession-threat ask1)
- (fset 'ask-user-about-lock ask2))))))
-
- ;;}}}
- ;;{{{ fold-narrow-to-region
-
- ;; Narrow to region, without surprising displays.
-
- ;; Similar to `narrow-to-region', but also adjusts window-start to be
- ;; the start of the narrowed region. If an optional argument CENTRE is
- ;; non-nil, the window-start is positioned to leave the point at the
- ;; centre of the window, like `recenter'. START may be nil, in which
- ;; case the function acts more like `widen'.
-
- ;; Actually, all the window-starts for every window displaying the
- ;; buffer, as well as the last_window_start for the buffer are set. The
- ;; points in every window are set to the point in the current buffer.
- ;; All this logic is necessary to prevent the display getting really
- ;; weird occasionally, even if there is only one window. Try making
- ;; this function like normal `narrow-to-region' with a touch of
- ;; `recenter', then moving around lots of folds in a buffer displayed in
- ;; several windows. You'll see what I mean.
-
- ;; last_window_start is set by making sure that the selected window is
- ;; displaying the current buffer, then setting the window-start, then
- ;; making the selected window display another buffer (which sets
- ;; last_window_start), then setting the selected window to redisplay the
- ;; buffer it displayed originally.
-
- ;; Note that whenever window-start is set, the point cannot be moved
- ;; outside the displayed area until after a proper redisplay. If this
- ;; is possible, centre the display on the point.
-
- ;; In Emacs 19; Epoch or Lucid Emacs, searches all screens for all
- ;; windows. In Emacs 19, they are called "frames".
-
- (defun fold-narrow-to-region (&optional start end centre)
- (let* ((the-window (selected-window))
- (the-screen (and fold-epoch-screens-p (epoch::current-screen)))
- (screens (and fold-epoch-screens-p (epoch::screens-of-buffer)))
- (selected-buffer (window-buffer the-window))
- (window-ring the-window)
- (window the-window)
- (point (point))
- (buffer (current-buffer))
- temp)
- (unwind-protect
- (progn
- (unwind-protect
- (progn
- (if start
- (narrow-to-region start end)
- (widen))
- (setq point (point))
- (set-window-buffer window buffer)
- (while (progn
- (and (eq buffer (window-buffer window))
- (if centre
- (progn
- (select-window window)
- (goto-char point)
- (vertical-motion
- (- (lsh (window-height window) -1)))
- (set-window-start window (point))
- (set-window-point window point))
- (set-window-start window (or start 1))
- (set-window-point window point)))
- (or (not (eq (setq window
- (if fold-emacs-frames-p
- (next-window window nil t)
- (if fold-lucid-screens-p
- (next-window window nil t t)
- (next-window window))))
- window-ring))
- (and (setq screens (cdr screens))
- (setq window (epoch::first-window (car screens))
- window-ring window))))))
- (and the-screen (epoch::select-screen the-screen))
- (select-window the-window))
- ;; Set last_window_start.
- (unwind-protect
- (if (not (eq buffer selected-buffer))
- (set-window-buffer the-window selected-buffer)
- (if (get-buffer "*scratch*")
- (set-window-buffer the-window (get-buffer "*scratch*"))
- (set-window-buffer
- the-window (setq temp (generate-new-buffer " *temp*"))))
- (set-window-buffer the-window buffer))
- (and temp
- (kill-buffer temp))))
- ;; Undo this side-effect of set-window-buffer.
- (set-buffer buffer)
- (goto-char (point)))))
-
- ;;}}}
-
- ;;}}}
- ;;{{{ Miscellaneous
-
- ;;{{{ kill-all-local-variables-hooks
-
- ;; This does not normally have any effect in Emacs. In my setup,
- ;; this hook is called when the major mode changes, and it gives
- ;; Folding mode a chance to clear up first.
-
- (and (boundp 'kill-all-local-variables-hooks)
- (or (memq 'fold-end-mode-quickly
- kill-all-local-variables-hooks)
- (setq kill-all-local-variables-hooks
- (cons 'fold-end-mode-quickly
- kill-all-local-variables-hooks))))
-
- ;;}}}
- ;;{{{ list-buffers-mode-alist
-
- ;; Also has no effect in standard Emacs. With this variable set,
- ;; my setup shows "Folding" in the mode name part of the buffer list,
- ;; which looks nice :-).
-
- (and (boundp 'list-buffers-mode-alist)
- (or (assq 'folding-mode list-buffers-mode-alist)
- (setq list-buffers-mode-alist
- (cons '(folding-mode "Folding")
- list-buffers-mode-alist))))
-
- ;;}}}
- ;;{{{ fold-end-mode-quickly
-
- (defun fold-end-mode-quickly ()
- "Replaces all ^M's with linefeeds and widen a folded buffer.
- Only has any effect if Folding mode is active.
-
- This should not in general be used for anything. It is used when changing
- major modes, by being placed in kill-mode-tidy-alist, to tidy the buffer
- slightly. It is similar to `(folding-mode 0)', except that it does not
- restore saved keymaps etc. Repeat: Do not use this function. Its
- behaviour is liable to change."
- (and (boundp 'folding-mode)
- (assq 'folding-mode
- (buffer-local-variables))
- folding-mode
- (progn
- (widen)
- (fold-clear-stack)
- (fold-subst-regions (list 1 (point-max)) ?\r ?\n))))
-
- ;;}}}
- ;;{{{ eval-current-buffer-open-folds
-
- (defun eval-current-buffer-open-folds (&optional printflag)
- "Evaluate all of a folded buffer as Lisp code.
- Unlike `eval-current-buffer', this function will evaluate all of a
- buffer, even if it is folded. It will also work correctly on non-folded
- buffers, so is a good candidate for being bound to a key if you program
- in Emacs-Lisp.
-
- It works by making a copy of the current buffer in another buffer,
- unfolding it and evaluating it. It then deletes the copy.
-
- Programs can pass argument PRINTFLAG which controls printing of output:
- nil means discard it; anything else is stream for print."
- (interactive)
- (if (or (and (boundp 'folding-mode-flag)
- folding-mode-flag)
- (and (boundp 'folding-mode)
- folding-mode))
- (let ((temp-buffer
- (generate-new-buffer (buffer-name))))
- (message "Evaluating unfolded buffer...")
- (save-restriction
- (widen)
- (copy-to-buffer temp-buffer 1 (point-max)))
- (set-buffer temp-buffer)
- (subst-char-in-region 1 (point-max) ?\r ?\n)
- (let ((real-message-def (symbol-function 'message))
- (suppress-eval-message))
- (fset 'message
- (function
- (lambda (&rest args)
- (setq suppress-eval-message t)
- (fset 'message real-message-def)
- (apply 'message args))))
- (unwind-protect
- (eval-current-buffer printflag)
- (fset 'message real-message-def)
- (kill-buffer temp-buffer))
- (or suppress-eval-message
- (message "Evaluating unfolded buffer... Done"))))
- (eval-current-buffer printflag)))
-
- ;;}}}
-
- ;;}}}
-
- ;;{{{ Emacs local variables
-
- ;; Local variables:
- ;; folded-file: t
- ;; end:
-
- ;;}}}
-