summaryrefslogtreecommitdiff
path: root/lisp
diff options
context:
space:
mode:
Diffstat (limited to 'lisp')
-rw-r--r--lisp/battery.el279
-rw-r--r--lisp/emacs-lisp/easy-mmode.el169
-rw-r--r--lisp/filecache.el666
-rw-r--r--lisp/progmodes/meta-mode.el1099
4 files changed, 2213 insertions, 0 deletions
diff --git a/lisp/battery.el b/lisp/battery.el
new file mode 100644
index 00000000000..b04b6af369f
--- /dev/null
+++ b/lisp/battery.el
@@ -0,0 +1,279 @@
+;;; battery.el --- display battery status information.
+
+;; Copyright (C) 1997 Ralph Schleicher
+
+;; Author: Ralph Schleicher <rs@purple.UL.BaWue.DE>
+;; Keywords: local hardware
+
+;; This file is not part of 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 this program; see the file COPYING. If not, write to
+;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+;;; Commentary:
+
+;; There is at present only a function interpreting the new `/proc/apm'
+;; file format of Linux version 1.3.58 or newer. That is, what a lucky
+;; coincidence, exactly the interface provided by the author's labtop.
+
+;;; Code:
+
+(require 'timer)
+
+
+(defvar battery-status-function
+ (cond ((and (eq system-type 'gnu/linux)
+ (file-readable-p "/proc/apm"))
+ 'battery-linux-proc-apm))
+ "*Function for getting battery status information.
+The function have to return an alist of conversion definitions.
+Cons cells are of the form
+
+ (CONVERSION . REPLACEMENT-TEXT)
+
+CONVERSION is the character code of a \"conversion specification\"
+introduced by a `%' character in a control string.")
+
+(defvar battery-echo-area-format
+ (cond ((eq battery-status-function 'battery-linux-proc-apm)
+ "Power %L, battery %B (%p%% load, remaining time %t)"))
+ "*Control string formatting the string to display in the echo area.
+Ordinary characters in the control string are printed as-is, while
+conversion specifications introduced by a `%' character in the control
+string are substituted as defined by the current value of the variable
+`battery-status-function'.")
+
+(defvar battery-mode-line-string nil
+ "String to display in the mode line.")
+
+(defvar battery-mode-line-format
+ (cond ((eq battery-status-function 'battery-linux-proc-apm)
+ " [%b%p%%]"))
+ "*Control string formatting the string to display in the mode line.
+Ordinary characters in the control string are printed as-is, while
+conversion specifications introduced by a `%' character in the control
+string are substituted as defined by the current value of the variable
+`battery-status-function'.")
+
+(defvar battery-update-interval 60
+ "*Seconds after which the battery status will be updated.")
+
+(defvar battery-update-timer nil
+ "Interval timer object.")
+
+;;;### autoload
+(defun battery ()
+ "Display battery status information in the echo area.
+The text beeing displayed in the echo area is controlled by the variables
+`battery-echo-area-format' and `battery-status-function'."
+ (interactive)
+ (message "%s" (if (and battery-echo-area-format battery-status-function)
+ (battery-format battery-echo-area-format
+ (funcall battery-status-function))
+ "Battery status not available")))
+
+;;;### autoload
+(defun display-battery ()
+ "Display battery status information in the mode line.
+The text beeing displayed in the mode line is controlled by the variables
+`battery-mode-line-format' and `battery-status-function'.
+The mode line will be updated automatically every `battery-update-interval'
+seconds."
+ (interactive)
+ (setq battery-mode-line-string "")
+ (or global-mode-string (setq global-mode-string '("")))
+ (or (memq 'battery-mode-line-string global-mode-string)
+ (setq global-mode-string (append global-mode-string
+ '(battery-mode-line-string))))
+ (and battery-update-timer (cancel-timer battery-update-timer))
+ (setq battery-update-timer (run-at-time nil battery-update-interval
+ 'battery-update-handler))
+ (battery-update))
+
+(defun battery-update-handler ()
+ (battery-update)
+ (sit-for 0))
+
+(defun battery-update ()
+ "Update battery status information in the mode line."
+ (setq battery-mode-line-string (if (and battery-mode-line-format
+ battery-status-function)
+ (battery-format
+ battery-mode-line-format
+ (funcall battery-status-function))
+ ""))
+ (force-mode-line-update))
+
+
+;;; `/proc/apm' interface for Linux.
+
+(defconst battery-linux-proc-apm-regexp
+ (concat "^\\([^ ]+\\)" ; Driver version.
+ " \\([^ ]+\\)" ; APM BIOS version.
+ " 0x\\([0-9a-f]+\\)" ; APM BIOS flags.
+ " 0x\\([0-9a-f]+\\)" ; AC line status.
+ " 0x\\([0-9a-f]+\\)" ; Battery status.
+ " 0x\\([0-9a-f]+\\)" ; Battery flags.
+ " \\([0-9]+\\)%" ; Load percentage.
+ " \\([0-9]+\\)" ; Remaining time.
+ " \\(.*\\)" ; Time unit.
+ "$")
+ "Regular expression matching contents of `/proc/apm'.")
+
+(defun battery-linux-proc-apm ()
+ "Get APM status information from Linux kernel.
+This function works only with the new `/proc/apm' format introduced
+in Linux version 1.3.58.
+
+The following %-sequences are provided:
+%v Linux driver version
+%V APM BIOS version
+%I APM BIOS status (verbose)
+%L AC line status (verbose)
+%B Battery status (verbose)
+%b Battery status, empty means high, `-' means low,
+ `!' means critical, and `+' means charging
+%p battery load percentage
+%s Remaining time in seconds
+%m Remaining time in minutes
+%h Remaining time in hours
+%t Remaining time in the form `h:min'"
+ (let (driver-version bios-version bios-interface line-status
+ battery-status battery-status-symbol load-percentage
+ seconds minutes hours remaining-time buffer tem)
+ (unwind-protect
+ (save-excursion
+ (setq buffer (generate-new-buffer " *battery*"))
+ (buffer-disable-undo buffer)
+ (set-buffer buffer)
+ (battery-insert-file-contents "/proc/apm")
+ (re-search-forward battery-linux-proc-apm-regexp)
+ (setq driver-version (match-string 1))
+ (setq bios-version (match-string 2))
+ (setq tem (battery-hex-to-int-2 (match-string 3)))
+ (if (not (logand tem 2))
+ (setq bios-interface "not supported")
+ (setq bios-interface "enabled")
+ (cond ((logand tem 16) (setq bios-interface "disabled"))
+ ((logand tem 32) (setq bios-interface "disengaged")))
+ (setq tem (battery-hex-to-int-2 (match-string 4)))
+ (cond ((= tem 0) (setq line-status "off-line"))
+ ((= tem 1) (setq line-status "on-line"))
+ ((= tem 2) (setq line-status "on backup")))
+ (setq tem (battery-hex-to-int-2 (match-string 6)))
+ (if (= tem 255)
+ (setq battery-status "N/A")
+ (setq tem (battery-hex-to-int-2 (match-string 5)))
+ (cond ((= tem 0) (setq battery-status "high"
+ battery-status-symbol ""))
+ ((= tem 1) (setq battery-status "low"
+ battery-status-symbol "-"))
+ ((= tem 2) (setq battery-status "critical"
+ battery-status-symbol "!"))
+ ((= tem 3) (setq battery-status "charging"
+ battery-status-symbol "+")))
+ (setq load-percentage (match-string 7))
+ (setq seconds (string-to-number (match-string 8)))
+ (and (string-equal (match-string 9) "min")
+ (setq seconds (* 60 seconds)))
+ (setq minutes (/ seconds 60)
+ hours (/ seconds 3600))
+ (setq remaining-time
+ (format "%d:%02d" hours (- minutes (* 60 hours)))))))
+ (and buffer (kill-buffer buffer)))
+ (list (cons ?v driver-version)
+ (cons ?V bios-version)
+ (cons ?I bios-interface)
+ (cons ?L line-status)
+ (cons ?B battery-status)
+ (cons ?b battery-status-symbol)
+ (cons ?p load-percentage)
+ (cons ?s (and seconds (number-to-string seconds)))
+ (cons ?m (and minutes (number-to-string minutes)))
+ (cons ?h (and hours (number-to-string hours)))
+ (cons ?t remaining-time))))
+
+
+;;; Private functions.
+
+(defun battery-format (format alist)
+ "Substitute %-sequences in FORMAT."
+ (let ((index 0)
+ (length (length format))
+ (result "")
+ char flag elem)
+ (while (< index length)
+ (setq char (aref format index))
+ (if (not flag)
+ (if (char-equal char ?%)
+ (setq flag t)
+ (setq result (concat result (char-to-string char))))
+ (cond ((char-equal char ?%)
+ (setq result (concat result "%")))
+ ((setq elem (assoc char alist))
+ (setq result (concat result (cdr elem)))))
+ (setq flag nil))
+ (setq index (1+ index)))
+ (or (null flag)
+ (setq result (concat result "%")))
+ result))
+
+(defun battery-insert-file-contents (file-name)
+ "Insert contents of file FILE-NAME after point.
+FILE-NAME can be a non-ordinary file, for example, a named pipe.
+Return t if file exists."
+ (let ((load-read-function 'battery-read-function)
+ (load-path '("."))
+ (load-history nil))
+ (save-excursion
+ (load file-name nil t t))))
+
+(defun battery-read-function (&optional stream)
+ "Function for reading expressions from STREAM.
+Value is always nil."
+ (let (char)
+ (while (not (< (setq char (get-file-char)) 0))
+ (insert char))))
+
+(defconst battery-hex-map '((?0 . 0) (?1 . 1) (?2 . 2) (?3 . 3)
+ (?4 . 4) (?5 . 5) (?6 . 6) (?7 . 7)
+ (?8 . 8) (?9 . 9) (?a . 10) (?b . 11)
+ (?c . 12) (?d . 13) (?e . 14) (?f . 15)))
+
+(defun battery-hex-to-int (string)
+ "Convert a hexadecimal number (a string) into a number."
+ (save-match-data
+ (and (string-match "^[ \t]+" string)
+ (setq string (substring string (match-end 0))))
+ (and (string-match "^0[xX]" string)
+ (setq string (substring string (match-end 0)))))
+ (battery-hex-to-int-2 string))
+
+(defun battery-hex-to-int-2 (string)
+ (let ((index 0)
+ (length (length string))
+ (value 0)
+ (elem nil))
+ (while (and (< index length)
+ (setq elem (assoc (downcase (aref string index))
+ battery-hex-map)))
+ (setq value (+ (* 16 value) (cdr elem))
+ index (1+ index)))
+ value))
+
+
+(provide 'battery)
+
+;;; battery.el ends here
diff --git a/lisp/emacs-lisp/easy-mmode.el b/lisp/emacs-lisp/easy-mmode.el
new file mode 100644
index 00000000000..866b32ccc8f
--- /dev/null
+++ b/lisp/emacs-lisp/easy-mmode.el
@@ -0,0 +1,169 @@
+;;; easy-mmode.el --- easy definition of minor modes.
+
+;; Copyright (C) 1997 Free Software Foundation, Inc.
+
+;; Author: Georges Brun-Cottan <Georges.Brun-Cottan@inria.fr>
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs 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.
+
+;; GNU Emacs 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, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+;;; Commentary:
+
+;; Minor modes are useful and common. This package makes defining a
+;; minor mode easy, by focusing on the writing of the minor mode
+;; functionalities themselves. Moreover, this package enforces a
+;; conventional naming of user interface primitives, making things
+;; natural for the minor-mode end-users.
+
+;; For each mode, easy-mmode defines the following:
+;; <mode> : The minor mode predicate. A buffer-local variable.
+;; <mode>-map : The keymap possibly associated to <mode>.
+;; <mode>-hook,<mode>-on-hook,<mode>-off-hook and <mode>-mode:
+;; see `easy-mmode-define-minor-mode' documentation
+;;
+;; eval
+;; (pp (macroexpand '(easy-mmode-define-minor-mode <your-mode> <doc>)))
+;; to check the result before using it.
+
+;; The order in which minor modes are installed is important. Keymap
+;; lookup proceeds down minor-mode-map-alist, and the order there
+;; tends to be the reverse of the order in which the modes were
+;; installed. Perhaps there should be a feature to let you specify
+;; orderings.
+
+;;; Code:
+
+(defun easy-mmode-define-keymap (keymap-alist &optional menu-name)
+ "Return a keymap builded from KEYMAP-ALIST.
+KEYMAP-ALIST must be a list of (KEYBINDING . BINDING) where
+KEYBINDING and BINDINGS are suited as for define-key.
+optional MENU-NAME is passed to `make-sparse-keymap'."
+ (let ((keymap (make-sparse-keymap menu-name)))
+ (mapcar
+ (function (lambda (bind)
+ (define-key keymap
+ (car bind) (cdr bind))))
+ keymap-alist)
+ keymap))
+
+(defmacro easy-mmode-define-toggle (mode &optional doc)
+ "Define a one arg toggle mode MODE function and associated hooks.
+MODE-mode is the so defined function that toggle the mode.
+optional DOC is its associated documentation.
+
+Hooks are checked for run, each time MODE-mode is called.
+They run under the followings conditions:
+MODE-hook: if the mode is toggled.
+MODE-on-hook: if the mode is on.
+MODE-off-hook: if the mode is off.
+
+When the mode is effectively toggled, two hooks may run.
+If so MODE-hook is guaranteed to be the first.
+
+\(defmacro easy-mmode-define-toggle (MODE &optional DOC)"
+ (let* ((mode-name
+ (if (string-match "-mode\\'" (symbol-name mode))
+ (symbol-name mode)
+ (concat (symbol-name mode) "-mode")))
+ (hook (intern (concat mode-name "-hook")))
+ (hook-on (intern (concat mode-name "-on-hook")))
+ (hook-off (intern (concat mode-name "-off-hook")))
+ (toggle (intern mode-name))
+ (mode toggle)
+ (toggle-doc (or doc
+ (format "With no argument, toggle %s mode.
+With arg turn mode on.
+With zero or negative arg turn mode off"
+ mode-name))))
+ `(progn
+ (defvar ,hook nil
+ ,(format "Hook called when %s mode is toggled" mode-name))
+
+ (defvar ,hook-on nil
+ ,(format "Hook called when %s mode is turned on" mode-name))
+
+ (defvar ,hook-off nil
+ ,(format "Hook called when %s mode is turned off" mode-name))
+
+ (defun ,toggle (&optional arg)
+ ,toggle-doc
+ (interactive "P")
+ (let ((old-mode ,mode))
+ (setq ,mode
+ (if arg
+ (or (listp arg);; C-u alone
+ (> (prefix-numeric-value arg) 0))
+ (not ,mode)))
+ (and ,hook
+ (not (equal old-mode ,mode))
+ (run-hooks ',hook))
+ (and ,hook-on
+ ,mode
+ (run-hooks ',hook-on))
+ (and ,hook-off
+ (not ,mode)
+ (run-hooks ',hook-off)))))))
+
+;;;###autoload
+(defmacro easy-mmode-define-minor-mode
+ (mode doc &optional init-value &optional lighter &optional keymap)
+ "Define a new minor mode MODE.
+This function defines the associated control variable, keymap,
+toggle command, and hooks (see `easy-mmode-define-toggle').
+
+DOC is the documentation for the mode toggle command.
+Optional LIGHTER is displayed in the mode-bar when the mode is on.
+Optional KEYMAP is the default (defvar) keymap bound to the mode keymap.
+If it is a list, it is passed to `easy-mmode-define-keymap'
+in order to build a valid keymap.
+
+\(defmacro easy-mmode-define-minor-mode
+ (MODE DOC &optional INIT-VALUE &optional LIGHTER &optional KEYMAP)...\)"
+ (let* ((mode-name (symbol-name mode))
+ (mode-doc (format "%s mode control switch." mode-name))
+ (keymap-name (concat mode-name "-map"))
+ (keymap-doc (format "Keymap activated when %s mode is on." mode-name)))
+ `(progn
+ ;; define the switch
+ (defvar ,mode ,init-value ,mode-doc)
+ (make-variable-buffer-local ',mode)
+
+ ;; define the minor-mode keymap
+ (defvar ,(intern keymap-name)
+ (cond ((and ,keymap (keymapp ,keymap))
+ ,keymap)
+ ((listp ,keymap)
+ (easy-mmode-define-keymap ,keymap))
+ (t (error "Invalid keymap %S" ,keymap)))
+ ,keymap-doc)
+
+ ;; define the toggle and the hooks
+ ,(macroexpand `(easy-mmode-define-toggle ,mode ,doc)) ; toggle and hooks
+
+ ;; update the mode-bar
+ (or (assq ',mode minor-mode-alist)
+ (setq minor-mode-alist
+ (cons (list ',mode ,lighter) minor-mode-alist)))
+
+ ;; update the minor-mode-map
+ (or (assq ',mode minor-mode-map-alist)
+ (setq minor-mode-map-alist
+ (cons (cons ',mode ,(intern keymap-name)) minor-mode-map-alist)))) ))
+
+(provide 'easy-mmode)
+
+;;; easy-mmode.el ends here
diff --git a/lisp/filecache.el b/lisp/filecache.el
new file mode 100644
index 00000000000..e8a55ac1645
--- /dev/null
+++ b/lisp/filecache.el
@@ -0,0 +1,666 @@
+;;; filecache.el --- Find files using a pre-loaded cache
+;;
+;; Author: Peter Breton
+;; Created: Sun Nov 10 1996
+;; Version: $Id: filecache.el,v 1.13 1997/02/07 22:27:51 pbreton Exp $
+;; Keywords:
+;; Time-stamp: <97/02/07 17:26:54 peter>
+;;
+;; Copyright (C) Peter Breton Thu Dec 12 1996
+;;
+;; This 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.
+;;
+;; filecache.el 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.
+;;
+;; LCD Archive Entry:
+;; filecache.el|Peter Breton|pbreton@i-kinetics.com|
+;; Find files using a pre-loaded cache|
+;; Thu Dec 12 1996|1.0|~/misc/filecache.el.gz|
+;;
+;; Purpose:
+;;
+;; Find files using a pre-loaded cache
+;;
+;;; Commentary:
+;;
+;; The file-cache package is an attempt to make it easy to locate files
+;; by name, without having to remember exactly where they are located.
+;; This is very handy when working with source trees. You can also add
+;; frequently used files to the cache to create a hotlist effect.
+;; The cache can be used with any interactive command which takes a
+;; filename as an argument.
+;;
+;; It is worth noting that this package works best when most of the files
+;; in the cache have unique names, or (if they have the same name) exist in
+;; only a few directories. The worst case is many files all with
+;; the same name and in different directories, for example a big source tree
+;; with a Makefile in each directory. In such a case, you should probably
+;; use an alternate strategy to find the files.
+;;
+;; ADDING FILES TO THE CACHE:
+;;
+;; Use the following functions to add items to the file cache:
+;;
+;; * `file-cache-add-file': Adds a single file to the cache
+;;
+;; * `file-cache-add-file-list': Adds a list of files to the cache
+;;
+;; The following functions use the regular expressions in
+;; `file-cache-delete-regexps' to eliminate unwanted files:
+;;
+;; * `file-cache-add-directory': Adds the files in a directory to the
+;; cache. You can also specify a regular expression to match the files
+;; which should be added.
+;;
+;; * `file-cache-add-directory-list': Same as above, but acts on a list
+;; of directories. You can use `load-path', `exec-path' and the like.
+;;
+;; * `file-cache-add-directory-using-find': Uses the `find' command to
+;; add a directory tree to the cache.
+;;
+;; * `file-cache-add-directory-using-locate': Uses the `locate' command to
+;; add files matching a pattern to the cache.
+;;
+;; Use the function `file-cache-clear-cache' to remove all items from the
+;; cache. There are a number of `file-cache-delete' functions provided
+;; as well, but in general it is probably better to not worry too much
+;; about extra files in the cache.
+;;
+;; The most convenient way to initialize the cache is with an
+;; `eval-after-load' function, as noted in the INSTALLATION section.
+;;
+;; FINDING FILES USING THE CACHE:
+;;
+;; You can use the file-cache with any function that expects a filename as
+;; an argument. For example:
+;;
+;; 1) Invoke a function which expects a filename as an argument:
+;; M-x find-file
+;;
+;; 2) Begin typing a file name.
+;;
+;; 3) Invoke `file-cache-minibuffer-complete' (bound by default to
+;; C-TAB) to complete on the filename using the cache.
+;;
+;; 4) When you have found a unique completion, the minibuffer contents
+;; will change to the full name of that file.
+;;
+;; If there are a number of directories which contain the completion,
+;; invoking `file-cache-minibuffer-complete' repeatedly will cycle through
+;; them.
+;;
+;; 5) You can then edit the minibuffer contents, or press RETURN.
+;;
+;; It is much easier to simply try it than trying to explain it :)
+;;
+;;; INSTALLATION
+;;
+;; Insert the following into your .emacs:
+;;
+;; (autoload 'file-cache-minibuffer-complete "filecache" nil t)
+;;
+;; For maximum utility, you should probably define an `eval-after-load'
+;; form which loads your favorite files:
+;;
+;; (eval-after-load
+;; "filecache"
+;; '(progn
+;; (message "Loading file cache...")
+;; (file-cache-add-directory-using-find "~/projects")
+;; (file-cache-add-directory-list load-path)
+;; (file-cache-add-directory "~/")
+;; (file-cache-add-file-list (list "~/foo/bar" "~/baz/bar"))
+;; ))
+;;
+;; If you clear and reload the cache frequently, it is probably easiest
+;; to put your initializations in a function:
+;;
+;; (eval-after-load
+;; "filecache"
+;; '(my-file-cache-initialize))
+;;
+;; (defun my-file-cache-initialize ()
+;; (interactive)
+;; (message "Loading file cache...")
+;; (file-cache-add-directory-using-find "~/projects")
+;; (file-cache-add-directory-list load-path)
+;; (file-cache-add-directory "~/")
+;; (file-cache-add-file-list (list "~/foo/bar" "~/baz/bar"))
+;; ))
+;;
+;; Of course, you can still add files to the cache afterwards, via
+;; Lisp functions.
+;;
+;; RELATED WORK:
+;;
+;; This package is a distant relative of Noah Friedman's fff utilities.
+;; Our goal is pretty similar, but the implementation strategies are
+;; different.
+;;
+;;; Change log:
+;; $Log: filecache.el,v $
+;; Revision 1.13 1997/02/07 22:27:51 pbreton
+;; Keybindings use autoload cookies instead of variable
+;;
+;; Revision 1.12 1997/02/07 22:02:29 pbreton
+;; Added small changes suggested by RMS:
+;; Revamped the doc strings
+;; Added keybindings (using `file-cache-default-minibuffer-key' variable)
+;;
+;; Revision 1.11 1997/02/01 16:44:47 pbreton
+;; Changed `file-cache-directory-name' function. Instead of using a
+;; completing-read, it cycles through the directory list.
+;;
+;; Eliminated bug where file-cache-file-name was called twice per completion.
+;;
+;; Revision 1.10 1997/01/26 05:44:24 pbreton
+;; Added file-cache-delete functions
+;; Added file-cache-completions-buffer variable
+;; Added file-cache-completions-keymap variable
+;; Changed file-cache-completion-setup-function to use
+;; file-cache-completions-keymap
+;; Added file-cache-choose-completion and file-cache-mouse-choose-completion.
+;; These rely on a patch to 'simple.el'
+;; Added file-cache-debug-read-from-minibuffer function
+;;
+;; Revision 1.9 1997/01/17 17:54:24 pbreton
+;; File names are no longer case-insensitive; this was tolerable on NT but
+;; not on Unix. Instead, file-cache-minibuffer-complete checks to see if the
+;; last command was itself, and if the same string is in the minibuffer. If so,
+;; this string is used for completion.
+;;
+;; Added some functions to delete from the file-cache
+;;
+;; Completing-read of directories requires temporary binding of
+;; enable-recursive-minibuffers variable.
+;;
+;; Revision 1.8 1997/01/17 14:01:08 pbreton
+;; Changed file-cache-minibuffer-complete so that it operates in the
+;; minibuffer instead of as a recursive minibuffer call.
+;;
+;; File-cache-alist now expects a filename and a list of directories (there
+;; should be at least one). If the list has only one element, that element
+;; is used; if it has multiple directories, the user is prompted to choose
+;; one.
+;;
+;; File names in the cache are now canonicalized to lowercase, to resolve a
+;; problem which occurs when the cache has files like README and readme.
+;;
+;; Removed a lot of the extra completion functions which weren't used.
+;;
+;; Revision 1.7 1996/12/29 15:48:28 pbreton
+;; Added functions:
+;; `file-cache-minibuffer-complete-using-suffix'
+;; `file-cache-minibuffer-complete-with-directory-filter'
+;; `file-cache-minibuffer-complete-with-filename-filter'
+;; Added documentation for these functions
+;;
+;; Revision 1.6 1996/12/24 20:27:56 pbreton
+;; Added predicate functions to `file-cache-minibuffer-complete'
+;;
+;; Revision 1.5 1996/12/14 18:05:11 pbreton
+;; Fixed uniquify bug by using `member' instead of `memq'
+;; Made file-cache-add-* prompts more descriptive
+;; More documentation
+;;
+;; Revision 1.4 1996/12/13 14:42:37 pbreton
+;; Removed `file-cache-top-directory' variable
+;; Changed file-cache-initialize to file-cache-add-from-file-cache-buffer
+;; Regexp to match files in file-cache-buffer is now a variable
+;;
+;; Revision 1.3 1996/12/12 06:01:27 peter
+;; Added `file-cache-add-file' and `file-cache-add-file-list' functions
+;;
+;; Revision 1.2 1996/12/12 05:47:49 peter
+;; Fixed uniquifying bug
+;; Added directory functions
+;; `file-cache-find-file' now uses file-cache-file-name
+;; `file-cache-minibuffer-complete' handles string completion correctly.
+;; It also prepends `file-cache-minibuffer-prompt' to the normal prompt
+;;
+;; Revision 1.1 1996/11/26 12:12:43 peter
+;; Initial revision
+;;
+;;; Code:
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Variables
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; User-modifiable variables
+(defvar file-cache-filter-regexps
+ (list "~$" "\\.o$" "\\.exe$" "\\.a$" "\\.elc$" ",v$" "\\.output$"
+ "\\.$" "#$")
+ "*List of regular expressions used as filters by the file cache.
+File names which match these expressions will not be added to the cache.
+Note that the functions `file-cache-add-file' and `file-cache-add-file-list'
+do not use this variable.")
+
+(defvar file-cache-find-command "find"
+ "*External program used by `file-cache-add-directory-using-find'.")
+
+(defvar file-cache-locate-command "locate"
+ "*External program used by `file-cache-add-directory-using-locate'.")
+
+;; Minibuffer messages
+(defvar file-cache-no-match-message " [File Cache: No match]"
+ "Message to display when there is no completion.")
+
+(defvar file-cache-sole-match-message " [File Cache: sole completion]"
+ "Message to display when there is only one completion.")
+
+(defvar file-cache-non-unique-message " [File Cache: complete but not unique]"
+ "Message to display when there is a non-unique completion.")
+
+(defvar file-cache-multiple-directory-message nil)
+
+;; Internal variables
+;; This should be named *Completions* because that's what the function
+;; switch-to-completions in simple.el expects
+(defvar file-cache-completions-buffer "*Completions*"
+ "Buffer to display completions when using the file cache.")
+
+(defvar file-cache-buffer "*File Cache*"
+ "Buffer to hold the cache of file names.")
+
+(defvar file-cache-buffer-default-regexp "^.+$"
+ "Regexp to match files in `file-cache-buffer'.")
+
+(defvar file-cache-last-completion nil)
+
+(defvar file-cache-alist nil
+ "Internal data structure to hold cache of file names.")
+
+(defvar file-cache-completions-keymap nil
+ "Keymap for file cache completions buffer.")
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Functions to add files to the cache
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun file-cache-add-directory (directory &optional regexp)
+ "Add DIRECTORY to the file cache.
+If the optional REGEXP argument is non-nil, only files which match it will
+be added to the cache."
+ (interactive "DAdd files from directory: ")
+ (let* ((dir (expand-file-name directory))
+ (dir-files (directory-files dir t regexp))
+ )
+ ;; Filter out files we don't want to see
+ (mapcar
+ '(lambda (file)
+ (mapcar
+ '(lambda (regexp)
+ (if (string-match regexp file)
+ (setq dir-files (delq file dir-files))))
+ file-cache-filter-regexps))
+ dir-files)
+ (file-cache-add-file-list dir-files)))
+
+(defun file-cache-add-directory-list (directory-list &optional regexp)
+ "Add DIRECTORY-LIST (a list of directory names) to the file cache.
+If the optional REGEXP argument is non-nil, only files which match it
+will be added to the cache. Note that the REGEXP is applied to the files
+in each directory, not to the directory list itself."
+ (interactive "XAdd files from directory list: ")
+ (mapcar
+ '(lambda (dir) (file-cache-add-directory dir regexp))
+ directory-list))
+
+(defun file-cache-add-file-list (file-list)
+ "Add FILE-LIST (a list of files names) to the file cache."
+ (interactive "XFile List: ")
+ (mapcar 'file-cache-add-file file-list))
+
+;; Workhorse function
+(defun file-cache-add-file (file)
+ "Add FILE to the file cache."
+ (interactive "fAdd File: ")
+ (let* ((file-name (file-name-nondirectory file))
+ (dir-name (file-name-directory file))
+ (the-entry (assoc file-name file-cache-alist))
+ )
+ ;; Does the entry exist already?
+ (if the-entry
+ (if (or (and (stringp (cdr the-entry))
+ (string= dir-name (cdr the-entry)))
+ (and (listp (cdr the-entry))
+ (member dir-name (cdr the-entry))))
+ nil
+ (setcdr the-entry (append (list dir-name) (cdr the-entry)))
+ )
+ ;; If not, add it to the cache
+ (setq file-cache-alist
+ (cons (cons file-name (list dir-name))
+ file-cache-alist)))
+ ))
+
+(defun file-cache-add-directory-using-find (directory)
+ "Use the `find' command to add files to the file cache.
+Find is run in DIRECTORY."
+ (interactive "DAdd files under directory: ")
+ (let ((dir (expand-file-name directory)))
+ (set-buffer (get-buffer-create file-cache-buffer))
+ (erase-buffer)
+ (call-process file-cache-find-command nil
+ (get-buffer file-cache-buffer) nil
+ dir "-name"
+ (if (memq system-type
+ (list 'windows-nt 'ms-dos)) "'*'" "*")
+ "-print")
+ (file-cache-add-from-file-cache-buffer)))
+
+(defun file-cache-add-directory-using-locate (string)
+ "Use the `locate' command to add files to the file cache.
+STRING is passed as an argument to the locate command."
+ (interactive "sAdd files using locate string: ")
+ (set-buffer (get-buffer-create file-cache-buffer))
+ (erase-buffer)
+ (call-process file-cache-locate-command nil
+ (get-buffer file-cache-buffer) nil
+ string)
+ (file-cache-add-from-file-cache-buffer))
+
+(defun file-cache-add-from-file-cache-buffer (&optional regexp)
+ "Add any entries found in the file cache buffer.
+Each entry matches the regular expression `file-cache-buffer-default-regexp'
+or the optional REGEXP argument."
+ (set-buffer file-cache-buffer)
+ (mapcar
+ (function (lambda (elt)
+ (goto-char (point-min))
+ (delete-matching-lines elt)))
+ file-cache-filter-regexps)
+ (goto-char (point-min))
+ (let ((full-filename))
+ (while (re-search-forward
+ (or regexp file-cache-buffer-default-regexp)
+ (point-max) t)
+ (setq full-filename (buffer-substring-no-properties
+ (match-beginning 0) (match-end 0)))
+ (file-cache-add-file full-filename))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Functions to delete from the cache
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun file-cache-clear-cache ()
+ "Clear the file cache."
+ (interactive)
+ (setq file-cache-alist nil))
+
+;; This clears *all* files with the given name
+(defun file-cache-delete-file (file)
+ "Delete FILE from the file cache."
+ (interactive
+ (list (completing-read "Delete file from cache: " file-cache-alist)))
+ (setq file-cache-alist
+ (delq (assoc file file-cache-alist) file-cache-alist)))
+
+(defun file-cache-delete-file-list (file-list)
+ "Delete FILE-LIST (a list of files) from the file cache."
+ (interactive "XFile List: ")
+ (mapcar 'file-cache-delete-file file-list))
+
+(defun file-cache-delete-file-regexp (regexp)
+ "Delete files matching REGEXP from the file cache."
+ (interactive "sRegexp: ")
+ (let ((delete-list))
+ (mapcar '(lambda (elt)
+ (and (string-match regexp (car elt))
+ (setq delete-list (cons (car elt) delete-list))))
+ file-cache-alist)
+ (file-cache-delete-file-list delete-list)
+ (message "Deleted %d files from file cache" (length delete-list))))
+
+(defun file-cache-delete-directory (directory)
+ "Delete DIRECTORY from the file cache."
+ (interactive "DDelete directory from file cache: ")
+ (let ((dir (expand-file-name directory))
+ (result 0))
+ (mapcar
+ '(lambda (entry)
+ (if (file-cache-do-delete-directory dir entry)
+ (setq result (1+ result))))
+ file-cache-alist)
+ (if (zerop result)
+ (error "No entries containing %s found in cache" directory)
+ (message "Deleted %d entries" result))))
+
+(defun file-cache-do-delete-directory (dir entry)
+ (let ((directory-list (cdr entry))
+ (directory (file-cache-canonical-directory dir))
+ )
+ (and (member directory directory-list)
+ (if (equal 1 (length directory-list))
+ (setq file-cache-alist
+ (delq entry file-cache-alist))
+ (setcdr entry (delete directory directory-list)))
+ )
+ ))
+
+(defun file-cache-delete-directory-list (directory-list)
+ "Delete DIRECTORY-LIST (a list of directories) from the file cache."
+ (interactive "XDirectory List: ")
+ (mapcar 'file-cache-delete-directory directory-list))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Utility functions
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;; Returns the name of a directory for a file in the cache
+(defun file-cache-directory-name (file)
+ (let* ((directory-list (cdr (assoc file file-cache-alist)))
+ (len (length directory-list))
+ (directory)
+ (num)
+ )
+ (if (not (listp directory-list))
+ (error "Unknown type in file-cache-alist for key %s" file))
+ (cond
+ ;; Single element
+ ((eq 1 len)
+ (setq directory (elt directory-list 0)))
+ ;; No elements
+ ((eq 0 len)
+ (error "No directory found for key %s" file))
+ ;; Multiple elements
+ (t
+ (let* ((minibuffer-dir (file-name-directory (buffer-string)))
+ (dir-list (member minibuffer-dir directory-list))
+ )
+ (setq directory
+ ;; If the directory is in the list, return the next element
+ ;; Otherwise, return the first element
+ (if dir-list
+ (or (elt directory-list
+ (setq num (1+ (- len (length dir-list)))))
+ (elt directory-list (setq num 0)))
+ (elt directory-list (setq num 0))))
+ )
+ )
+ )
+ ;; If there were multiple directories, set up a minibuffer message
+ (setq file-cache-multiple-directory-message
+ (and num (format " [%d of %d]" (1+ num) len)))
+ directory))
+
+;; Returns the name of a file in the cache
+(defun file-cache-file-name (file)
+ (let ((directory (file-cache-directory-name file)))
+ (concat directory file)))
+
+;; Return a canonical directory for comparison purposes.
+;; Such a directory ends with a forward slash.
+(defun file-cache-canonical-directory (dir)
+ (let ((directory dir))
+ (if (not (char-equal ?/ (string-to-char (substring directory -1))))
+ (concat directory "/")
+ directory)))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Minibuffer functions
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;;;###autoload
+(defun file-cache-minibuffer-complete ()
+ "Complete a filename in the minibuffer using a preloaded cache."
+ (interactive)
+ (let*
+ (
+ (completion-ignore-case nil)
+ (case-fold-search nil)
+ (string (file-name-nondirectory (buffer-string)))
+ (completion-string (try-completion string file-cache-alist))
+ (completion-list)
+ (len)
+ (file-cache-string)
+ )
+ (cond
+ ;; If it's the longest match, insert it
+ ((stringp completion-string)
+ ;; If we've already inserted a unique string, see if the user
+ ;; wants to use that one
+ (if (and (string= string completion-string)
+ (assoc string file-cache-alist))
+ (if (and (eq last-command this-command)
+ (string= file-cache-last-completion completion-string))
+ (progn
+ (erase-buffer)
+ (insert-string (file-cache-file-name completion-string))
+ (setq file-cache-last-completion nil)
+ )
+ (file-cache-temp-minibuffer-message file-cache-non-unique-message)
+ (setq file-cache-last-completion string)
+ )
+ (setq file-cache-last-completion string)
+ (setq completion-list (all-completions string file-cache-alist)
+ len (length completion-list))
+ (if (> len 1)
+ (progn
+ (goto-char (point-max))
+ (insert-string
+ (substring completion-string (length string)))
+ ;; Add our own setup function to the Completions Buffer
+ (let ((completion-setup-hook
+ (reverse
+ (append (list 'file-cache-completion-setup-function)
+ completion-setup-hook)))
+ )
+ (with-output-to-temp-buffer file-cache-completions-buffer
+ (display-completion-list completion-list))
+ )
+ )
+ (setq file-cache-string (file-cache-file-name completion-string))
+ (if (string= file-cache-string (buffer-string))
+ (file-cache-temp-minibuffer-message file-cache-sole-match-message)
+ (erase-buffer)
+ (insert-string file-cache-string)
+ (if file-cache-multiple-directory-message
+ (file-cache-temp-minibuffer-message
+ file-cache-multiple-directory-message)))
+ )))
+
+ ;; If it's the only match, replace the original contents
+ ((eq completion-string t)
+ (setq file-cache-string (file-cache-file-name string))
+ (if (string= file-cache-string (buffer-string))
+ (file-cache-temp-minibuffer-message file-cache-sole-match-message)
+ (erase-buffer)
+ (insert-string file-cache-string)
+ (if file-cache-multiple-directory-message
+ (file-cache-temp-minibuffer-message
+ file-cache-multiple-directory-message))
+ ))
+
+ ;; No match
+ ((eq completion-string nil)
+ (file-cache-temp-minibuffer-message file-cache-no-match-message))
+ )
+))
+
+;; Lifted from "complete.el"
+(defun file-cache-temp-minibuffer-message (msg)
+ "A Lisp version of `temp_minibuffer_message' from minibuf.c."
+ (let ((savemax (point-max)))
+ (save-excursion
+ (goto-char (point-max))
+ (insert msg))
+ (let ((inhibit-quit t))
+ (sit-for 2)
+ (delete-region savemax (point-max))
+ (if quit-flag
+ (setq quit-flag nil
+ unread-command-events (list 7))))))
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Completion functions
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun file-cache-completion-setup-function ()
+ (set-buffer file-cache-completions-buffer)
+
+ (if file-cache-completions-keymap
+ nil
+ (setq file-cache-completions-keymap
+ (copy-keymap completion-list-mode-map))
+ (define-key file-cache-completions-keymap [mouse-2]
+ 'file-cache-mouse-choose-completion)
+ (define-key file-cache-completions-keymap "\C-m"
+ 'file-cache-choose-completion))
+
+ (use-local-map file-cache-completions-keymap)
+ )
+
+(defun file-cache-choose-completion ()
+ "Choose a completion in the `*Completions*' buffer."
+ (interactive)
+ (let ((completion-no-auto-exit t))
+ (choose-completion)
+ (select-window (active-minibuffer-window))
+ (file-cache-minibuffer-complete)
+ )
+ )
+
+(defun file-cache-mouse-choose-completion (event)
+ "Choose a completion with the mouse."
+ (interactive "e")
+ (let ((completion-no-auto-exit t))
+ (mouse-choose-completion event)
+ (select-window (active-minibuffer-window))
+ (file-cache-minibuffer-complete)
+ )
+ )
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Debugging functions
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defun file-cache-debug-read-from-minibuffer (file)
+ "Debugging function."
+ (interactive
+ (list (completing-read "File Cache: " file-cache-alist)))
+ (message "%s" (assoc file file-cache-alist))
+ )
+
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;; Keybindings
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+;;;###autoload (define-key minibuffer-local-completion-map [C-tab] 'file-cache-minibuffer-complete)
+;;;###autoload (define-key minibuffer-local-map [C-tab] 'file-cache-minibuffer-complete)
+;;;###autoload (define-key minibuffer-local-must-match-map [C-tab] 'file-cache-minibuffer-complete)
+
+(provide 'filecache)
+
+;;; filecache.el ends here
diff --git a/lisp/progmodes/meta-mode.el b/lisp/progmodes/meta-mode.el
new file mode 100644
index 00000000000..b615862cc52
--- /dev/null
+++ b/lisp/progmodes/meta-mode.el
@@ -0,0 +1,1099 @@
+;;; meta-mode.el --- major mode for editing Metafont or MetaPost sources.
+
+;; Copyright (C) 1997 by Ulrik Vieth.
+
+;; Author: Ulrik Vieth <vieth@thphy.uni-duesseldorf.de>
+;; Version: 1.0
+;; Keywords: Metafont, MetaPost, tex, languages
+
+;;; This file is *not* part of 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, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+;;; Commentary:
+
+;; Description:
+;;
+;; This Emacs Lisp package provides a major mode for editing Metafont
+;; or MetaPost sources. It includes all the necessary code to set up
+;; a major mode including an approriate syntax table, keymap, and a
+;; mode-specific pull-down menu. It also provides a sophisticated set
+;; of font-lock patterns, a fancy indentation function adapted from
+;; AUC-TeX's latex.el, and some basic mode-specific editing functions
+;; such as functions to move to the beginning or end of the enclosing
+;; environment, or to mark, re-indent, or comment-out environments.
+;; On the other hand, it doesn't yet provide any functionality for
+;; running Metafont or MetaPost in a shell buffer form within Emacs,
+;; but such functionality might be added later, either as part of this
+;; package or as a separate Emacs Lisp package.
+
+;; Installation:
+;;
+;; Install this file (meta-mode.el) in your personal or system-wide
+;; Emacs Lisp directory and add these lines to your startup files:
+;;
+;; (autoload 'metafont-mode "meta-mode" "Metafont editing mode." t)
+;; (autoload 'metapost-mode "meta-mode" "MetaPost editing mode." t)
+;;
+;; (setq auto-mode-alist
+;; (append '(("\\.mf\\'" . metafont-mode)
+;; ("\\.mp\\'" . metapost-mode)) auto-mode-alist))
+;;
+;; An interface to running Metafont or MetaPost as a shell process
+;; from within Emacs is currently under development as a separate
+;; Emacs Lisp package (meta-buf.el). In order to have that package
+;; loaded automatically when first entering Metafont or MetaPost mode,
+;; you might use the load-hook provided in this package by adding
+;; these lines to your startup file:
+;;
+;; (add-hook 'meta-mode-load-hook
+;; '(lambda () (require 'meta-buf)))
+;;
+;; The add-on package loaded this way may in turn make use of the
+;; mode-hooks provided in this package to activate additional features
+;; when entering Metafont or MetaPost mode.
+
+;; Font Lock Support:
+;;
+;; If you are using global-font-lock-mode (introduced in Emacs 19.31),
+;; fontification in Metafont and/or MetaPost mode will be activated
+;; automatically. To speed up fontification for the rather complex
+;; patterns used in these modes, it may be a good idea to activate
+;; lazy-lock as a font-lock-support-mode (introduced in Emacs 19.32)
+;; by adding these lines to your startup file:
+;;
+;; (global-font-lock-mode t)
+;; (setq font-lock-support-mode 'lazy-lock-mode)
+;;
+;; If you are using an older version of Emacs, which doesn't provide
+;; global-font-lock-mode or font-lock-support-mode, you can also
+;; activate fontification in Metafont and/or MetaPost mode by adding
+;; the following lines to your startup file:
+;;
+;; (add-hook 'meta-common-mode-hook 'turn-on-font-lock)
+;; (add-hook 'meta-common-mode-hook 'turn-on-lazy-lock)
+
+;; Customization:
+;;
+;; Following the usual Emacs Lisp coding conventions, the major modes
+;; defined in this package provide several hook variables to allow for
+;; local customization when entering the modes. In particular, there
+;; is a `meta-common-mode-hook' which applies to both modes as well as
+;; `metafont-mode-hook' and `metapost-mode-hook' which apply to the
+;; individual modes. In addition, there are several variables and
+;; regexps controlling e.g. the behavior of the indentation function,
+;; which may be customized via `edit-options'. Please refer to the
+;; docstrings in the code below for details.
+
+;; Availability:
+;;
+;; This package is currently available via my "TeX Software" WWW page:
+;;
+;; http://www.thphy.uni-duesseldorf.de/~vieth/subjects/tex/software.html
+;;
+;; As of this version 1.0, this package will be uploaded to CTAN
+;; archives, where it shall find a permanent home, presumably in
+;; tex-archive/support/emacs-modes. It will also be submitted for
+;; integration into the GNU Emacs distribution at that time.
+;;
+;; History:
+;;
+;; v 0.0 -- 1997/02/01 UV Started writing meta-mode.el.
+;; v 0.1 -- 1997/02/02 UV Added preliminary set of font-lock patterns.
+;; v 0.2 -- 1997/02/03 UV Improved and debugged font-lock patterns.
+;; Added indent-line-function for TAB.
+;; v 0.3 -- 1997/02/17 UV Improved font-lock patterns and syntax table.
+;; Improved and debbuged indentation function.
+;; v 0.4 -- 1997/02/18 UV Added functions to indent regions for M-C-q,
+;; also added a preliminary mode-specific menu.
+;; v 0.5 -- 1997/02/19 UV Added functions to skip to next or previous
+;; defun and to re-indent or comment-out defuns.
+;; v 0.6 -- 1997/02/20 UV More debugging, testing and clean-up.
+;; v 0.7 -- 1997/02/22 UV Use easymenu to define mode-specific menu.
+;; v 0.8 -- 1997/02/24 UV Added completion function for M-TAB.
+;; v 0.9 -- 1997/03/08 UV Added fill-paragraph function for comments.
+;; Also fixed a few remaining font-lock problems.
+;; Added meta-mode-load-hook to load meta-buf.el.
+;; v 1.0 -- 1997/04/07 UV Cleanup for official public release.
+;;
+;; Historical Footnote:
+;;
+;; This package was begun on February 1, 1997, exactly 20 years after
+;; the genesis of TeX took place according to Don Knuth's own account
+;; (cf. ``The Errors of TeX'', reprinted in ``Literate Programming'',
+;; Chapter 10, p. 249). What better date could there be to choose?
+;;
+
+
+;;; Code:
+
+(require 'easymenu)
+
+;;; Fontification.
+
+(defvar meta-font-lock-keywords
+ (let ((input-keywords
+ "\\(input\\|generate\\)")
+ (begin-keywords
+ (concat "\\(begin\\(char\\|fig\\|graph\\|logochar\\)\\|"
+ "\\cmchar\\|dcchar\\|ecchar\\)"))
+ (end-keywords
+ "\\(end\\(char\\|fig\\|graph\\)\\)")
+ (macro-keywords-1
+ "\\(def\\|let\\|mode_def\\|vardef\\)")
+ (macro-keywords-2
+ "\\(primarydef\\|secondarydef\\|tertiarydef\\)")
+;(make-regexp
+; '("expr" "suffix" "text" "primary" "secondary" "tertiary") t)
+ (args-keywords
+ (concat "\\(expr\\|primary\\|s\\(econdary\\|uffix\\)\\|"
+ "te\\(rtiary\\|xt\\)\\)"))
+;(make-regexp
+; '("boolean" "color" "numeric" "pair" "path" "pen" "picture"
+; "string" "transform" "newinternal") t)
+ (type-keywords
+ (concat "\\(boolean\\|color\\|n\\(ewinternal\\|umeric\\)\\|"
+ "p\\(a\\(ir\\|th\\)\\|en\\|icture\\)\\|string\\|"
+ "transform\\)"))
+;(make-regexp
+; '("for" "forever" "forsuffixes" "endfor"
+; "step" "until" "upto" "downto" "thru" "within"
+; "iff" "if" "elseif" "else" "fi" "exitif" "exitunless"
+; "let" "def" "vardef" "enddef" "mode_def"
+; "true" "false" "known" "unknown" "and" "or" "not"
+; "save" "interim" "inner" "outer" "relax"
+; "begingroup" "endgroup" "expandafter" "scantokens"
+; "generate" "input" "endinput" "end" "bye"
+; "message" "errmessage" "errhelp" "special" "numspecial"
+; "readstring" "readfrom" "write") t)
+ (syntactic-keywords
+ (concat "\\(and\\|b\\(egingroup\\|ye\\)\\|"
+ "d\\(ef\\|ownto\\)\\|e\\(lse\\(\\|if\\)"
+ "\\|nd\\(\\|def\\|for\\|group\\|input\\)"
+ "\\|rr\\(help\\|message\\)"
+ "\\|x\\(it\\(if\\|unless\\)\\|pandafter\\)\\)\\|"
+ "f\\(alse\\|i\\|or\\(\\|ever\\|suffixes\\)\\)\\|"
+ "generate\\|i\\(ff?\\|n\\(ner\\|put\\|terim\\)\\)\\|"
+ "known\\|let\\|m\\(essage\\|ode_def\\)\\|"
+ "n\\(ot\\|umspecial\\)\\|o\\(r\\|uter\\)\\|"
+ "re\\(ad\\(from\\|string\\)\\|lax\\)\\|"
+ "s\\(ave\\|cantokens\\|pecial\\|tep\\)\\|"
+ "t\\(hru\\|rue\\)\\|"
+ "u\\(n\\(known\\|til\\)\\|pto\\)\\|"
+ "vardef\\|w\\(ithin\\|rite\\)\\)"))
+ )
+ (list
+ ;; embedded TeX code in btex ... etex
+ (cons (concat "\\(btex\\|verbatimtex\\)"
+ "[ \t]+\\(.*\\)[ \t]+"
+ "\\(etex\\)")
+ '((1 font-lock-keyword-face)
+ (2 font-lock-string-face)
+ (3 font-lock-keyword-face)))
+ ;; unary macro definitions: def, vardef, let
+ (cons (concat "\\<" macro-keywords-1 "\\>"
+ "[ \t]+\\(\\sw+\\|\\s_+\\|\\s.+\\)")
+ '((1 font-lock-keyword-face)
+ (2 font-lock-function-name-face)))
+ ;; binary macro defintions: <leveldef> x operator y
+ (cons (concat "\\<" macro-keywords-2 "\\>"
+ "[ \t]+\\(\\sw+\\)"
+ "[ \t]*\\(\\sw+\\|\\s.+\\)"
+ "[ \t]*\\(\\sw+\\)")
+ '((1 font-lock-keyword-face)
+ (2 font-lock-variable-name-face nil t)
+ (3 font-lock-function-name-face nil t)
+ (4 font-lock-variable-name-face nil t)))
+ ;; variable declarations: numeric, pair, color, ...
+ (cons (concat "\\<" type-keywords "\\>"
+ "\\([ \t]+\\(\\sw+\\)\\)*")
+ '((1 font-lock-type-face)
+ (font-lock-match-meta-declaration-item-and-skip-to-next
+ (goto-char (match-end 1)) nil
+ (1 font-lock-variable-name-face nil t))))
+ ;; argument declarations: expr, suffix, text, ...
+ (cons (concat "\\<" args-keywords "\\>"
+ "\\([ \t]+\\(\\sw+\\|\\s_+\\)\\)*")
+ '((1 font-lock-type-face)
+ (font-lock-match-meta-declaration-item-and-skip-to-next
+ (goto-char (match-end 1)) nil
+ (1 font-lock-variable-name-face nil t))))
+ ;; special case of arguments: expr x of y
+ (cons (concat "\\(expr\\)[ \t]+\\(\\sw+\\)"
+ "[ \t]+\\(of\\)[ \t]+\\(\\sw+\\)")
+ '((1 font-lock-type-face)
+ (2 font-lock-variable-name-face)
+ (3 font-lock-keyword-face nil t)
+ (4 font-lock-variable-name-face nil t)))
+ ;; syntactic keywords
+ (cons (concat "\\<" syntactic-keywords "\\>")
+ 'font-lock-keyword-face)
+ ;; beginchar, beginfig
+ (cons (concat "\\<" begin-keywords "\\>")
+ 'font-lock-keyword-face)
+ ;; endchar, endfig
+ (cons (concat "\\<" end-keywords "\\>")
+ 'font-lock-keyword-face)
+ ;; input, generate
+ (cons (concat "\\<" input-keywords "\\>"
+ "[ \t]+\\(\\sw+\\)")
+ '((1 font-lock-keyword-face)
+ (2 font-lock-reference-face)))
+ ;; embedded Metafont/MetaPost code in comments
+ (cons "|\\([^|]+\\)|"
+ '(1 font-lock-reference-face t))
+ ))
+ "Default expressions to highlight in Metafont or MetaPost mode.")
+
+
+(defun font-lock-match-meta-declaration-item-and-skip-to-next (limit)
+ ;; Match and move over Metafont/MetaPost declaration item after point.
+ ;;
+ ;; The expected syntax of an item is either "word" or "symbol",
+ ;; possibly ending with optional whitespace. Everything following
+ ;; the item (but belonging to it) is expected to by skipable by
+ ;; `forward-sexp'. The list of items is expected to be separated
+ ;; by commas and terminated by semicolons or equals signs.
+ ;;
+ (if (looking-at "[ \t]*\\(\\sw+\\|\\s_+\\)")
+ (save-match-data
+ (condition-case nil
+ (save-restriction
+ ;; Restrict to end of line, currently guaranteed to be LIMIT.
+ (narrow-to-region (point-min) limit)
+ (goto-char (match-end 1))
+ ;; Move over any item value, etc., to the next item.
+ (while (not (looking-at "[ \t]*\\(\\(,\\)\\|;\\|=\\|$\\)"))
+ (goto-char (or (scan-sexps (point) 1) (point-max))))
+ (goto-char (match-end 2)))
+ (error t)))))
+
+
+
+;;; Completion.
+
+;; The data used to prepare the following lists of primitives and
+;; standard macros available in Metafont or MetaPost was extracted
+;; from the original sources like this:
+;;
+;; grep '^primitive' texk-7.0/web2c/{mf,mp}.web |\
+;; sed 's/primitive(\("[a-zA-Z]*"\).*/\1/' > {mf,mp}_prim.list
+;;
+;; grep '\(let\|def\|vardef\|primarydef\|secondarydef\|tertiarydef\)'
+;; texmf/meta{font,post}/plain.{mf,mp} > {mf,mp}_plain.list
+
+(defconst meta-common-primitives-list
+ '("ASCII" "addto" "also" "and" "angle" "atleast" "batchmode"
+ "begingroup" "boolean" "boundarychar" "char" "charcode" "chardp"
+ "charexists" "charext" "charht" "charic" "charlist" "charwd"
+ "contour" "controls" "cosd" "curl" "cycle" "day" "decimal" "def"
+ "delimiters" "designsize" "directiontime" "doublepath" "dump" "else"
+ "elseif" "end" "enddef" "endfor" "endgroup" "endinput" "errhelp"
+ "errmessage" "errorstopmode" "everyjob" "exitif" "expandafter"
+ "expr" "extensible" "false" "fi" "floor" "fontdimen" "fontmaking"
+ "for" "forever" "forsuffixes" "headerbyte" "hex" "if" "inner"
+ "input" "interim" "intersectiontimes" "jobname" "kern" "known"
+ "length" "let" "ligtable" "makepath" "makepen" "message" "mexp"
+ "mlog" "month" "newinternal" "nonstopmode" "normaldeviate" "not"
+ "nullpen" "nullpicture" "numeric" "oct" "odd" "of" "or" "outer"
+ "pair" "path" "pausing" "pen" "pencircle" "penoffset" "picture"
+ "point" "postcontrol" "precontrol" "primary" "primarydef" "quote"
+ "randomseed" "readstring" "reverse" "rotated" "save" "scaled"
+ "scantokens" "scrollmode" "secondary" "secondarydef" "shifted"
+ "shipout" "show" "showdependencies" "showstats" "showstopping"
+ "showtoken" "showvariable" "sind" "skipto" "slanted" "special"
+ "sqrt" "step" "str" "string" "subpath" "substring" "suffix"
+ "tension" "tertiary" "tertiarydef" "text" "time" "to"
+ "tracingcapsules" "tracingchoices" "tracingcommands"
+ "tracingequations" "tracingmacros" "tracingonline" "tracingoutput"
+ "tracingrestores" "tracingspecs" "tracingstats" "tracingtitles"
+ "transform" "transformed" "true" "turningnumber" "uniformdeviate"
+ "unknown" "until" "vardef" "warningcheck" "withpen" "xpart"
+ "xscaled" "xxpart" "xypart" "year" "ypart" "yscaled" "yxpart"
+ "yypart" "zscaled")
+ "List of primitives common to Metafont and MetaPost.")
+
+(defconst metafont-primitives-list
+ '("at" "autorounding" "chardx" "chardy" "cull" "display"
+ "dropping" "fillin" "from" "granularity" "hppp" "inwindow"
+ "keeping" "numspecial" "openwindow" "proofing" "smoothing"
+ "totalweight" "tracingedges" "tracingpens" "turningcheck" "vppp"
+ "withweight" "xoffset" "yoffset")
+ "List of primitives only defined in Metafont.")
+
+(defconst metapost-primitives-list
+ '("arclength" "arctime" "bluepart" "bounded" "btex" "clip"
+ "clipped" "color" "dashed" "dashpart" "etex" "filled" "fontpart"
+ "fontsize" "greenpart" "infont" "linecap" "linejoin" "llcorner"
+ "lrcorner" "miterlimit" "mpxbreak" "pathpart" "penpart"
+ "prologues" "readfrom" "redpart" "setbounds" "stroked" "textpart"
+ "textual" "tracinglostchars" "truecorners" "ulcorner" "urcorner"
+ "verbatimtex" "withcolor" "within" "write")
+ "List of primitives only defined in MetaPost.")
+
+(defconst meta-common-plain-macros-list
+ '( "abs" "bot" "bye" "byte" "ceiling" "clear_pen_memory"
+ "clearit" "clearpen" "clearxy" "counterclockwise" "cutdraw" "decr"
+ "dir" "direction" "directionpoint" "div" "dotprod" "downto" "draw"
+ "drawdot" "erase" "exitunless" "fill" "filldraw" "flex" "gobble"
+ "hide" "incr" "interact" "interpath" "intersectionpoint" "inverse"
+ "label" "labels" "lft" "loggingall" "magstep" "makelabel" "max"
+ "min" "mod" "numtok" "penlabels" "penpos" "penstroke" "pickup"
+ "range" "reflectedabout" "relax" "rotatedabout" "rotatedaround"
+ "round" "rt" "savepen" "shipit" "softjoin" "solve" "stop"
+ "superellipse" "takepower" "tensepath" "thru" "top" "tracingall"
+ "tracingnone" "undraw" "undrawdot" "unfill" "unfilldraw"
+ "unitvector" "upto" "whatever")
+ "List of macros common to plain Metafont and MetaPost.")
+
+(defconst metafont-plain-macros-list
+ '("beginchar" "change_width" "culldraw" "cullit" "cutoff"
+ "define_blacker_pixels" "define_corrected_pixels"
+ "define_good_x_pixels" "define_good_y_pixels"
+ "define_horizontal_corrected_pixels" "define_pixels"
+ "define_whole_blacker_pixels" "define_whole_pixels"
+ "define_whole_vertical_blacker_pixels"
+ "define_whole_vertical_pixels" "endchar" "fix_units"
+ "font_coding_scheme" "font_extra_space" "font_identifier"
+ "font_normal_shrink" "font_normal_space" "font_normal_stretch"
+ "font_quad" "font_size" "font_slant" "font_x_height" "gfcorners"
+ "good.bot" "good.lft" "good.rt" "good.top" "good.x" "good.y"
+ "grayfont" "hround" "imagerules" "italcorr" "labelfont"
+ "lowres_fix" "makebox" "makegrid" "maketicks" "mode_lowres"
+ "mode_proof" "mode_setup" "mode_smoke" "nodisplays" "notransforms"
+ "openit" "penrazor" "pensquare" "proofoffset" "proofrule"
+ "proofrulethickness" "screenchars" "screenrule" "screenstrokes"
+ "showit" "slantfont" "smode" "titlefont" "vround")
+ "List of macros only defined in plain Metafont.")
+
+(defconst metapost-plain-macros-list
+ '("arrowhead" "bbox" "beginfig" "buildcycle" "center" "cutafter"
+ "cutbefore" "dashpattern" "dotlabel" "dotlabels" "drawarrow"
+ "drawdblarrow" "drawoptions" "endfig" "image" "label" "off" "on"
+ "thelabel")
+ "List of macros only defined in plain MetaPost.")
+
+(defconst metapost-graph-macros-list
+ '("augment" "auto.x" "auto.y" "autogrid" "begingraph" "endgraph"
+ "format" "frame" "gdata" "gdotlabel" "gdraw" "gdrawarrow"
+ "gdrawdblarrow" "gfill" "glabel" "grid" "itick" "otick" "plot"
+ "setcoords" "setrange")
+ "List of macros only defined in MetaPost \"graph\" package.")
+
+(defconst metapost-boxes-macros-list
+ '("boxit" "boxjoin" "bpath" "circleit" "drawboxed" "drawboxes"
+ "drawunboxed" "fixpos" "fixsize" "pic" "rboxit")
+ "List of macros only defined in MetaPost \"boxes\" package.")
+
+
+(defvar metafont-symbol-list
+ (append meta-common-primitives-list
+ metafont-primitives-list
+ meta-common-plain-macros-list
+ metafont-plain-macros-list)
+ "List of known symbols to complete in Metafont mode.")
+
+(defvar metapost-symbol-list
+ (append meta-common-primitives-list
+ metapost-primitives-list
+ meta-common-plain-macros-list
+ metapost-plain-macros-list
+ metapost-graph-macros-list
+ metapost-boxes-macros-list)
+ "List of known symbols to complete in MetaPost mode.")
+
+
+(defvar meta-symbol-list nil
+ "List of known symbols to complete in Metafont or MetaPost mode.")
+
+(defvar meta-symbol-changed nil
+ "Flag indicating whether `meta-symbol-list' has been initialized.")
+
+(defvar meta-complete-list nil
+; (list (list "\\<\\(\\sw+\\)" 1 'meta-symbol-list)
+; (list "" 'ispell-complete-word))
+ "List of ways to perform completion in Metafont or MetaPost mode.
+
+Each entry is a list with the following elements:
+1. Regexp matching the preceding text.
+2. A number indicating the subgroup in the regexp containing the text.
+3. A function returning an alist of possible completions.
+4. Text to append after a succesful completion (if any).
+
+Or alternatively:
+1. Regexp matching the preceding text.
+2. Function to do the actual completion.")
+
+
+(defun meta-add-symbols (&rest entries)
+ "Add entries to list of known symbols in Metafont or MetaPost mode."
+ (if meta-symbol-changed
+ (setq meta-symbol-list (cons entries meta-symbol-list))
+ (setq meta-symbol-changed t)
+ (setq meta-symbol-list (cons entries meta-symbol-list))))
+
+(defun meta-symbol-list ()
+ "Return value of list of known symbols in Metafont or MetaPost mode.
+If the list was changed, sort the list and remove duplicates first."
+ (if (not meta-symbol-changed)
+ ()
+ (setq meta-symbol-changed nil)
+ (message "Preparing completion list...")
+ ;; sort list of symbols
+ (setq meta-symbol-list
+ (sort (mapcar 'meta-listify (apply 'append meta-symbol-list))
+ 'meta-car-string-lessp))
+ ;; remove duplicates
+ (let ((entry meta-symbol-list))
+ (while (and entry (cdr entry))
+ (let ((this (car entry))
+ (next (car (cdr entry))))
+ (if (not (string-equal (car this) (car next)))
+ (setq entry (cdr entry))
+ (if (> (length next) (length this))
+ (setcdr this (cdr next)))
+ (setcdr entry (cdr (cdr entry)))))))
+ (message "Preparing completion list... done"))
+ meta-symbol-list)
+
+(defun meta-listify (a)
+ ;; utility function used in `meta-add-symbols'
+ (if (listp a) a (list a)))
+
+(defun meta-car-string-lessp (a b)
+ ;; utility function used in `meta-add-symbols'
+ (string-lessp (car a) (car b)))
+
+
+(defun meta-complete-symbol ()
+ "Perform completion on Metafont or MetaPost symbol preceding point."
+ (interactive "*")
+ (let ((list meta-complete-list)
+ entry)
+ (while list
+ (setq entry (car list)
+ list (cdr list))
+ (if (meta-looking-at-backward (car entry) 200)
+ (setq list nil)))
+ (if (numberp (nth 1 entry))
+ (let* ((sub (nth 1 entry))
+ (close (nth 3 entry))
+ (begin (match-beginning sub))
+ (end (match-end sub))
+ (pattern (meta-match-buffer 0))
+ (symbol (buffer-substring begin end))
+ (list (funcall (nth 2 entry)))
+ (completion (try-completion symbol list)))
+ (cond ((eq completion t)
+ (and close
+ (not (looking-at (regexp-quote close)))
+ (insert close)))
+ ((null completion)
+ (error "Can't find completion for \"%s\"" pattern))
+ ((not (string-equal symbol completion))
+ (delete-region begin end)
+ (insert completion)
+ (and close
+ (eq (try-completion completion list) t)
+ (not (looking-at (regexp-quote close)))
+ (insert close)))
+ (t
+ (message "Making completion list...")
+ (let ((list (all-completions symbol list nil)))
+ (with-output-to-temp-buffer "*Completions*"
+ (display-completion-list list)))
+ (message "Making completion list... done"))))
+ (funcall (nth 1 entry)))))
+
+
+(defun meta-looking-at-backward (regexp &optional limit)
+ ;; utility function used in `meta-complete-symbol'
+ (let ((pos (point)))
+ (save-excursion
+ (and (re-search-backward
+ regexp (if limit (max (point-min) (- (point) limit))) t)
+ (eq (match-end 0) pos)))))
+
+(defun meta-match-buffer (n)
+ ;; utility function used in `meta-complete-symbol'
+ (if (match-beginning n)
+ (let ((str (buffer-substring (match-beginning n) (match-end n))))
+ (set-text-properties 0 (length str) nil str)
+ (copy-sequence str))
+ ""))
+
+
+
+;;; Indentation.
+
+(defvar meta-indent-level 2
+ "*Indentation of begin-end blocks in Metafont or MetaPost mode.")
+
+
+(defvar meta-left-comment-regexp "%%+"
+ "*Regexp matching comments that should be placed on the left margin.")
+
+(defvar meta-right-comment-regexp nil
+ "*Regexp matching comments that should be placed to the right margin.")
+
+(defvar meta-ignore-comment-regexp "%[^%]"
+ "*Regexp matching comments that whose indentation should not be touched.")
+
+
+(defvar meta-begin-environment-regexp
+ (concat "\\(begin\\(char\\|fig\\|gr\\(aph\\|oup\\)\\|logochar\\)\\|"
+ "def\\|for\\(\\|ever\\|suffixes\\)\\|if\\|mode_def\\|"
+ "primarydef\\|secondarydef\\|tertiarydef\\|vardef\\)")
+ "*Regexp matching the beginning of environments to be indented.")
+
+(defvar meta-end-environment-regexp
+ (concat "\\(end\\(char\\|def\\|f\\(ig\\|or\\)\\|gr\\(aph\\|oup\\)\\)"
+ "\\|fi\\)")
+ "*Regexp matching the end of environments to be indented.")
+
+(defvar meta-within-environment-regexp
+; (concat "\\(e\\(lse\\(\\|if\\)\\|xit\\(if\\|unless\\)\\)\\)")
+ (concat "\\(else\\(\\|if\\)\\)")
+ "*Regexp matching keywords within environments not to be indented.")
+
+
+(defun meta-comment-indent ()
+ "Return the indentation for a comment in Metafont or MetaPost mode."
+ (if (and meta-left-comment-regexp
+ (looking-at meta-left-comment-regexp))
+ (current-column)
+ (skip-chars-backward "\t ")
+ (max (if (bolp) 0 (1+ (current-column)))
+ comment-column)))
+
+(defun meta-indent-line ()
+ "Indent the line containing point as Metafont or MetaPost source."
+ (interactive)
+ (let ((indent (meta-indent-calculate)))
+ (save-excursion
+ (if (/= (current-indentation) indent)
+ (let ((beg (progn (beginning-of-line) (point)))
+ (end (progn (back-to-indentation) (point))))
+ (delete-region beg end)
+ (indent-to indent))))
+ (if (< (current-column) indent)
+ (back-to-indentation))))
+
+(defun meta-indent-calculate ()
+ "Return the indentation of current line of Metafont or MetaPost source."
+ (save-excursion
+ (back-to-indentation)
+ (cond
+ ;; Comments to the left margin.
+ ((and meta-left-comment-regexp
+ (looking-at meta-left-comment-regexp))
+ 0)
+ ;; Comments to the right margin.
+ ((and meta-right-comment-regexp
+ (looking-at meta-right-comment-regexp))
+ comment-column)
+ ;; Comments best left alone.
+ ((and meta-ignore-comment-regexp
+ (looking-at meta-ignore-comment-regexp))
+ (current-indentation))
+ ;; Backindent at end of environments.
+ ((looking-at
+ (concat "\\<" meta-end-environment-regexp "\\>"))
+ (- (meta-indent-calculate-last) meta-indent-level))
+ ;; Backindent at keywords within environments.
+ ((looking-at
+ (concat "\\<" meta-within-environment-regexp "\\>"))
+ (- (meta-indent-calculate-last) meta-indent-level))
+ (t (meta-indent-calculate-last)))))
+
+(defun meta-indent-calculate-last ()
+ "Return the indentation of previous line of Metafont or MetaPost source."
+ (save-restriction
+ (widen)
+ (skip-chars-backward "\n\t ")
+ (move-to-column (current-indentation))
+ ;; Ignore comments.
+ (while (and (looking-at comment-start) (not (bobp)))
+ (skip-chars-backward "\n\t ")
+ (if (not (bobp))
+ (move-to-column (current-indentation))))
+ (cond
+ ((bobp) 0)
+ (t (+ (current-indentation)
+ (meta-indent-level-count)
+ (cond
+ ;; Compensate for backindent at end of environments.
+ ((looking-at
+ (concat "\\<"meta-end-environment-regexp "\\>"))
+ meta-indent-level)
+ ;; Compensate for backindent within environments.
+ ((looking-at
+ (concat "\\<" meta-within-environment-regexp "\\>"))
+ meta-indent-level)
+ (t 0)))))
+ ))
+
+(defun meta-indent-level-count ()
+ "Count indentation change for begin-end commands in the current line."
+ (save-excursion
+ (save-restriction
+ (let ((count 0))
+ (narrow-to-region
+ (point) (save-excursion
+ (re-search-forward "[^\\\\\"]%\\|\n\\|\\'" nil t)
+ (backward-char) (point)))
+ (while (re-search-forward "\\<\\sw+\\>\\|(\\|)" nil t)
+ (save-excursion
+ (goto-char (match-beginning 0))
+ (cond
+ ;; Count number of begin-end keywords within line.
+ ((looking-at
+ (concat "\\<" meta-begin-environment-regexp "\\>"))
+ (setq count (+ count meta-indent-level)))
+ ((looking-at
+ (concat "\\<" meta-end-environment-regexp "\\>"))
+ (setq count (- count meta-indent-level)))
+ ;; Count number of open-close parentheses within line.
+ ((looking-at "(")
+ (setq count (+ count meta-indent-level)))
+ ((looking-at ")")
+ (setq count (- count meta-indent-level)))
+ )))
+ count))))
+
+
+
+;;; Filling paragraphs.
+
+(defun meta-fill-paragraph (&optional justify)
+ "Like \\[fill-paragraph], but handle Metafont or MetaPost comments.
+If any part of the current line is a comment, fill the comment or the
+paragraph of it that point is in, preserving the comment's indentation
+and initial semicolons."
+ (interactive "P")
+ (let (has-comment ; Non-nil if line contains a comment.
+ has-code-and-comment ; Non-nil if line contains code and a comment.
+ comment-fill-prefix ; If has-comment, fill-prefix for the comment.
+ )
+ ;; Figure out what kind of comment we are looking at.
+ (save-excursion
+ (beginning-of-line)
+ (cond
+ ;; A line with nothing but a comment on it?
+ ((looking-at (concat "[ \t]*" comment-start-skip))
+ (setq has-comment t)
+ (setq comment-fill-prefix
+ (buffer-substring (match-beginning 0) (match-end 0))))
+ ;; A line with some code, followed by a comment?
+ ((condition-case nil
+ (save-restriction
+ (narrow-to-region (point-min)
+ (save-excursion (end-of-line) (point)))
+ (while (not (looking-at (concat comment-start "\\|$")))
+ (skip-chars-forward (concat "^" comment-start "\n\"\\\\"))
+ (cond
+ ((eq (char-after (point)) ?\\) (forward-char 2))
+ ((eq (char-after (point)) ?\") (forward-sexp 1))))
+ (looking-at comment-start-skip))
+ (error nil))
+ (setq has-comment t
+ has-code-and-comment t)
+ (setq comment-fill-prefix
+ (concat (make-string (/ (current-column) 8) ?\t)
+ (make-string (% (current-column) 8) ?\ )
+ (buffer-substring (match-beginning 0) (match-end 0)))))
+ ))
+ (if (not has-comment)
+ (fill-paragraph justify)
+ ;; Narrow to include only the comment, and then fill the region.
+ (save-excursion
+ (save-restriction
+ (beginning-of-line)
+ (narrow-to-region
+ ;; Find the first line we should include in the region to fill.
+ (save-excursion
+ (while (and (zerop (forward-line -1))
+ (looking-at (concat "^[ \t]*" comment-start))))
+ (or (looking-at (concat ".*" comment-start))
+ (forward-line 1))
+ (point))
+ ;; Find the beginning of the first line past the region to fill.
+ (save-excursion
+ (while (progn (forward-line 1)
+ (looking-at (concat "^[ \t]*" comment-start))))
+ (point)))
+ (let* ((paragraph-start
+ (concat paragraph-start "\\|[ \t%]*$"))
+ (paragraph-separate
+ (concat paragraph-start "\\|[ \t%]*$"))
+ (paragraph-ignore-fill-prefix nil)
+ (fill-prefix comment-fill-prefix)
+ (after-line (if has-code-and-comment
+ (save-excursion (forward-line 1) (point))))
+ (end (progn (forward-paragraph)
+ (or (bolp) (newline 1))
+ (point)))
+ (beg (progn (backward-paragraph)
+ (if (eq (point) after-line) (forward-line -1))
+ (point)))
+ (after-pos (save-excursion
+ (goto-char beg)
+ (if (not (looking-at fill-prefix))
+ (progn
+ (re-search-forward comment-start-skip)
+ (point)))))
+ )
+ (fill-region-as-paragraph beg end justify nil after-pos))
+ )))
+ t))
+
+
+
+;;; Editing commands.
+
+(defvar meta-begin-defun-regexp
+ (concat "\\(begin\\(char\\|fig\\|logochar\\)\\|def\\|mode_def\\|"
+ "primarydef\\|secondarydef\\|tertiarydef\\|vardef\\)")
+ "*Regexp matching beginning of defuns in Metafont or MetaPost mode.")
+
+(defvar meta-end-defun-regexp
+ (concat "\\(end\\(char\\|def\\|fig\\)\\)")
+ "*Regexp matching the end of defuns in Metafont or MetaPost mode.")
+
+
+(defun meta-beginning-of-defun (&optional arg)
+ "Move backward to beginnning of a defun in Metafont or MetaPost code.
+With numeric argument, do it that many times.
+Negative arg -N means move forward to Nth following beginning of defun.
+Returns t unless search stops due to beginning or end of buffer."
+ (interactive "p")
+ (if (or (null arg) (= 0 arg)) (setq arg 1))
+ (and arg (< arg 0) (not (eobp)) (forward-char 1))
+ (and (re-search-backward
+ (concat "\\<" meta-begin-defun-regexp "\\>") nil t arg)
+ (progn (goto-char (match-beginning 0))
+ (skip-chars-backward "%")
+ (skip-chars-backward " \t") t)))
+
+(defun meta-end-of-defun (&optional arg)
+ "Move forward to end of a defun in Metafont or MetaPost code.
+With numeric argument, do it that many times.
+Negative argument -N means move back to Nth preceding end of defun.
+Returns t unless search stops due to beginning or end of buffer."
+ (interactive "p")
+ (if (or (null arg) (= 0 arg)) (setq arg 1))
+ (and (< arg 0) (not (bobp)) (forward-line -1))
+ (and (re-search-forward
+ (concat "\\<" meta-end-defun-regexp "\\>") nil t arg)
+ (progn (goto-char (match-end 0))
+ (skip-chars-forward ";")
+ (skip-chars-forward " \t")
+ (if (looking-at "\n") (forward-line 1)) t)))
+
+
+(defun meta-comment-region (beg end &optional arg)
+ "Comment out active region as Metafont or MetaPost source."
+ (interactive "r")
+ (comment-region beg end arg))
+
+(defun meta-uncomment-region (beg end)
+ "Uncomment active region as Metafont or MetaPost source."
+ (interactive "r")
+ (comment-region beg end -1))
+
+(defun meta-comment-defun (&optional arg)
+ "Comment out current environment as Metafont or MetaPost source.
+With prefix argument, uncomment the environment.
+The environment used is the one that contains point or follows point."
+ (interactive "P")
+ (save-excursion
+ (let* ((end (if (meta-end-of-defun) (point) (point-max)))
+ (beg (if (meta-beginning-of-defun) (point) (point-min))))
+ (comment-region beg end arg))))
+
+(defun meta-uncomment-defun ()
+ "Uncomment current environment as Metafont or MetaPost source."
+ (interactive)
+ (meta-comment-defun -1))
+
+
+(defun meta-indent-region (beg end)
+ "Indent the active region as Metafont or MetaPost source."
+ (interactive "r")
+ (indent-region beg end nil))
+
+(defun meta-indent-buffer ()
+ "Indent the whole buffer contents as Metafont or MetaPost source."
+ (interactive)
+ (save-excursion
+ (indent-region (point-min) (point-max) nil)))
+
+(defun meta-indent-defun ()
+ "Indent the current environment as Metafont or MetaPost source.
+The environment indented is the one that contains point or follows point."
+ (interactive)
+ (save-excursion
+ (let* ((end (if (meta-end-of-defun) (point) (point-max)))
+ (beg (if (meta-beginning-of-defun) (point) (point-min))))
+ (indent-region beg end nil))))
+
+
+(defun meta-mark-defun ()
+ "Put mark at end of the environment, point at the beginning.
+The environment marked is the one that contains point or follows point."
+ (interactive)
+ (push-mark (point))
+ (meta-end-of-defun)
+ (push-mark (point) nil t)
+ (meta-beginning-of-defun))
+
+
+
+;;; Syntax table, keymap and menu.
+
+(defvar meta-mode-abbrev-table nil
+ "Abbrev table used in Metafont or MetaPost mode.")
+(define-abbrev-table 'meta-mode-abbrev-table ())
+
+(defvar meta-mode-syntax-table nil
+ "Syntax table used in Metafont or MetaPost mode.")
+(if meta-mode-syntax-table
+ ()
+ (setq meta-mode-syntax-table (make-syntax-table))
+ ;; underscores are word constituents
+ (modify-syntax-entry ?_ "w" meta-mode-syntax-table)
+ ;; miscellaneous non-word symbols
+ (modify-syntax-entry ?# "_" meta-mode-syntax-table)
+ (modify-syntax-entry ?@ "_" meta-mode-syntax-table)
+ (modify-syntax-entry ?$ "_" meta-mode-syntax-table)
+ (modify-syntax-entry ?? "_" meta-mode-syntax-table)
+ (modify-syntax-entry ?! "_" meta-mode-syntax-table)
+ ;; binary operators
+ (modify-syntax-entry ?& "." meta-mode-syntax-table)
+ (modify-syntax-entry ?+ "." meta-mode-syntax-table)
+ (modify-syntax-entry ?- "." meta-mode-syntax-table)
+ (modify-syntax-entry ?/ "." meta-mode-syntax-table)
+ (modify-syntax-entry ?* "." meta-mode-syntax-table)
+ (modify-syntax-entry ?. "." meta-mode-syntax-table)
+ (modify-syntax-entry ?: "." meta-mode-syntax-table)
+ (modify-syntax-entry ?= "." meta-mode-syntax-table)
+ (modify-syntax-entry ?< "." meta-mode-syntax-table)
+ (modify-syntax-entry ?> "." meta-mode-syntax-table)
+ (modify-syntax-entry ?| "." meta-mode-syntax-table)
+ ;; opening and closing delimiters
+ (modify-syntax-entry ?\( "()" meta-mode-syntax-table)
+ (modify-syntax-entry ?\) ")(" meta-mode-syntax-table)
+ (modify-syntax-entry ?\[ "(]" meta-mode-syntax-table)
+ (modify-syntax-entry ?\] ")[" meta-mode-syntax-table)
+ (modify-syntax-entry ?\{ "(}" meta-mode-syntax-table)
+ (modify-syntax-entry ?\} "){" meta-mode-syntax-table)
+ ;; comment character
+ (modify-syntax-entry ?% "<" meta-mode-syntax-table)
+ (modify-syntax-entry ?\n ">" meta-mode-syntax-table)
+ ;; escape character, needed for embedded TeX code
+ (modify-syntax-entry ?\\ "\\" meta-mode-syntax-table)
+ )
+
+(defvar meta-mode-map nil
+ "Keymap used in Metafont or MetaPost mode.")
+(if meta-mode-map
+ ()
+ (setq meta-mode-map (make-sparse-keymap))
+ (define-key meta-mode-map "\t" 'meta-indent-line)
+ (define-key meta-mode-map "\C-m" 'reindent-then-newline-and-indent)
+ ;; Comment Paragraphs:
+; (define-key meta-mode-map "\M-a" 'backward-sentence)
+; (define-key meta-mode-map "\M-e" 'forward-sentence)
+; (define-key meta-mode-map "\M-h" 'mark-paragraph)
+; (define-key meta-mode-map "\M-q" 'fill-paragraph)
+ ;; Navigation:
+ (define-key meta-mode-map "\M-\C-a" 'meta-beginning-of-defun)
+ (define-key meta-mode-map "\M-\C-e" 'meta-end-of-defun)
+ (define-key meta-mode-map "\M-\C-h" 'meta-mark-defun)
+ ;; Indentation:
+ (define-key meta-mode-map "\M-\C-q" 'meta-indent-defun)
+ (define-key meta-mode-map "\C-c\C-qe" 'meta-indent-defun)
+ (define-key meta-mode-map "\C-c\C-qr" 'meta-indent-region)
+ (define-key meta-mode-map "\C-c\C-qb" 'meta-indent-buffer)
+ ;; Commenting Out:
+ (define-key meta-mode-map "\C-c%" 'meta-comment-defun)
+; (define-key meta-mode-map "\C-uC-c%" 'meta-uncomment-defun)
+ (define-key meta-mode-map "\C-c;" 'meta-comment-region)
+ (define-key meta-mode-map "\C-c:" 'meta-uncomment-region)
+ ;; Symbol Completion:
+ (define-key meta-mode-map "\M-\t" 'meta-complete-symbol)
+ ;; Shell Commands:
+; (define-key meta-mode-map "\C-c\C-c" 'meta-command-file)
+; (define-key meta-mode-map "\C-c\C-k" 'meta-kill-job)
+; (define-key meta-mode-map "\C-c\C-l" 'meta-recenter-output)
+ )
+
+(easy-menu-define
+ meta-mode-menu meta-mode-map
+ "Menu used in Metafont or MetaPost mode."
+ (list "Meta"
+ ["Forward Environment" meta-beginning-of-defun t]
+ ["Backward Environment" meta-end-of-defun t]
+ "--"
+ ["Indent Line" meta-indent-line t]
+ ["Indent Environment" meta-indent-defun t]
+ ["Indent Region" meta-indent-region
+ :active (meta-mark-active)]
+ ["Indent Buffer" meta-indent-buffer t]
+ "--"
+ ["Comment Out Environment" meta-comment-defun t]
+ ["Uncomment Environment" meta-uncomment-defun t]
+ ["Comment Out Region" meta-comment-region
+ :active (meta-mark-active)]
+ ["Uncomment Region" meta-uncomment-region
+ :active (meta-mark-active)]
+ "--"
+ ["Complete Symbol" meta-complete-symbol t]
+; "--"
+; ["Command on Buffer" meta-command-file t]
+; ["Kill Job" meta-kill-job t]
+; ["Recenter Output Buffer" meta-recenter-output-buffer t]
+ ))
+
+;; Compatibility: XEmacs doesn't have the `mark-active' variable.
+(defun meta-mark-active ()
+ "Return whether the mark and region are currently active in this buffer."
+ (or (and (boundp 'mark-active) mark-active) (mark)))
+
+
+
+;;; Hook variables.
+
+(defvar meta-mode-load-hook nil
+ "*Hook evaluated when first loading Metafont or MetaPost mode.")
+
+(defvar meta-common-mode-hook nil
+ "*Hook evaluated by both `metafont-mode' and `metapost-mode'.")
+
+(defvar metafont-mode-hook nil
+ "*Hook evaluated by `metafont-mode' after `meta-common-mode-hook'.")
+(defvar metapost-mode-hook nil
+ "*Hook evaluated by `metapost-mode' after `meta-common-mode-hook'.")
+
+
+
+;;; Initialization.
+
+(defun meta-common-initialization ()
+ "Common initialization for Metafont or MetaPost mode."
+ (kill-all-local-variables)
+
+ (make-local-variable 'paragraph-start)
+ (make-local-variable 'paragraph-separate)
+ (setq paragraph-start
+ (concat page-delimiter "\\|$"))
+ (setq paragraph-separate
+ (concat page-delimiter "\\|$"))
+
+ (make-local-variable 'paragraph-ignore-fill-prefix)
+ (setq paragraph-ignore-fill-prefix t)
+
+ (make-local-variable 'comment-start-skip)
+ (make-local-variable 'comment-start)
+ (make-local-variable 'comment-end)
+ (make-local-variable 'comment-multi-line)
+ (setq comment-start-skip "%+[ \t]*")
+ (setq comment-start "%")
+ (setq comment-end "")
+ (setq comment-multi-line nil)
+
+ (make-local-variable 'parse-sexp-ignore-comments)
+ (setq parse-sexp-ignore-comments t)
+
+ (make-local-variable 'comment-indent-function)
+ (setq comment-indent-function 'meta-comment-indent)
+ (make-local-variable 'fill-paragraph-function)
+ (setq fill-paragraph-function 'meta-fill-paragraph)
+ (make-local-variable 'indent-line-function)
+ (setq indent-line-function 'meta-indent-line)
+ ;; No need to define a mode-specific 'indent-region-function.
+ ;; Simply use the generic 'indent-region and 'comment-region.
+
+ ;; Set defaults for font-lock mode.
+ (make-local-variable 'font-lock-defaults)
+ (setq font-lock-defaults
+ '(meta-font-lock-keywords
+ nil nil ((?_ . "w")) nil
+ (font-lock-comment-start-regexp . "%")))
+
+ ;; Activate syntax table, keymap and menu.
+ (setq local-abbrev-table meta-mode-abbrev-table)
+ (set-syntax-table meta-mode-syntax-table)
+ (use-local-map meta-mode-map)
+ (easy-menu-add meta-mode-menu)
+ )
+
+
+(defun metafont-mode ()
+ "Major mode for editing Metafont sources.
+Special commands:
+\\{meta-mode-map}
+
+Turning on Metafont mode calls the value of the variables
+`meta-common-mode-hook' and `metafont-mode-hook'."
+ (interactive)
+ (meta-common-initialization)
+ (setq mode-name "Metafont")
+ (setq major-mode 'metafont-mode)
+
+ ;; Set defaults for completion function.
+ (make-local-variable 'meta-symbol-list)
+ (make-local-variable 'meta-symbol-changed)
+ (make-local-variable 'meta-complete-list)
+ (setq meta-symbol-list nil)
+ (setq meta-symbol-changed nil)
+ (apply 'meta-add-symbols metafont-symbol-list)
+ (setq meta-complete-list
+ (list (list "\\<\\(\\sw+\\)" 1 'meta-symbol-list)
+ (list "" 'ispell-complete-word)))
+ (run-hooks 'meta-common-mode-hook 'metafont-mode-hook))
+
+(defun metapost-mode ()
+ "Major mode for editing MetaPost sources.
+Special commands:
+\\{meta-mode-map}
+
+Turning on MetaPost mode calls the value of the variable
+`meta-common-mode-hook' and `metafont-mode-hook'."
+ (interactive)
+ (meta-common-initialization)
+ (setq mode-name "MetaPost")
+ (setq major-mode 'metapost-mode)
+
+ ;; Set defaults for completion function.
+ (make-local-variable 'meta-symbol-list)
+ (make-local-variable 'meta-symbol-changed)
+ (make-local-variable 'meta-complete-list)
+ (setq meta-symbol-list nil)
+ (setq meta-symbol-changed nil)
+ (apply 'meta-add-symbols metapost-symbol-list)
+ (setq meta-complete-list
+ (list (list "\\<\\(\\sw+\\)" 1 'meta-symbol-list)
+ (list "" 'ispell-complete-word)))
+ (run-hooks 'meta-common-mode-hook 'metapost-mode-hook))
+
+
+;;; Just in case ...
+
+(provide 'meta-mode)
+(run-hooks 'meta-mode-load-hook)
+
+;;; meta-mode.el ends here