changeset 104410:203567d53c98

Initial version.
author Chong Yidong <cyd@stupidchicken.com>
date Tue, 25 Aug 2009 04:19:35 +0000
parents 560b69982442
children 11f4ef827ca4
files lisp/cedet/mode-local.el
diffstat 1 files changed, 889 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/cedet/mode-local.el	Tue Aug 25 04:19:35 2009 +0000
@@ -0,0 +1,889 @@
+;;; mode-local.el --- Support for mode local facilities
+;;
+;; Copyright (C) 2004, 2005, 2007, 2008, 2009 Free Software Foundation, Inc.
+;;
+;; Author: David Ponce <david@dponce.com>
+;; Maintainer: David Ponce <david@dponce.com>
+;; Created: 27 Apr 2004
+;; Keywords: syntax
+
+;; 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 3 of the License, 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.  If not, see <http://www.gnu.org/licenses/>.
+
+;;; Commentary:
+;;
+;; Each major mode will want to support a specific set of behaviors.
+;; Usually generic behaviors that need just a little bit of local
+;; specifics.
+;;
+;; This library permits the setting of override functions for tasks of
+;; that nature, and also provides reasonable defaults.
+;;
+;; There are buffer local variables, and frame local variables.
+;; This library give the illusion of mode specific variables.
+;;
+;; You should use a mode-local variable or override to allow extension
+;; only if you expect a mode author to provide that extension.  If a
+;; user might wish to customize a give variable or function then
+;; the existing customization mechanism should be used.
+
+;; To Do:
+;; Allow customization of a variable for a specific mode?
+;;
+;; Add mecro for defining the '-default' functionality.
+
+;;; History:
+;;
+
+;;; Code:
+(eval-when-compile (require 'cl))
+
+;;; Compatibility
+;;
+(defun mode-local-define-derived-mode-needed-p ()
+  "Return non-nil if mode local has to fix `define-derived-mode'.
+That is, if `define-derived-mode' does not set `derived-mode-parent'."
+  (let ((body (cdr (macroexpand '(define-derived-mode c p ""))))
+        (bad t))
+    (while (and body bad)
+      (if (equal (car body) '(put 'c 'derived-mode-parent 'p))
+          (setq bad nil)
+        (setq body (cdr body))))
+    bad))
+
+(when (mode-local-define-derived-mode-needed-p)
+  ;; Workaround a bug in some (XEmacs) versions of
+  ;; `define-derived-mode' that don't set the `derived-mode-parent'
+  ;; property, and break mode-local.
+  (defadvice define-derived-mode
+    (after mode-local-define-derived-mode activate)
+    "Fix missing `derived-mode-parent' property on child."
+    (unless (eq 'fundamental-mode (ad-get-arg 1))
+      (let ((form (cdr ad-return-value)))
+        (setq ad-return-value nil)
+        (while form
+          (and (eq 'defun (car-safe (car form)))
+               (eq (ad-get-arg 0) (car (cdr-safe (car form))))
+               (push `(or (get ',(ad-get-arg 0) 'derived-mode-parent)
+                          (put ',(ad-get-arg 0) 'derived-mode-parent
+                               ',(ad-get-arg 1)))
+                     ad-return-value))
+          (push (car form) ad-return-value)
+          (setq form (cdr form)))
+        (setq ad-return-value `(progn ,@(nreverse ad-return-value)))
+        )))
+  )
+
+;;; Misc utilities
+;;
+(defun mode-local-map-file-buffers (function &optional predicate buffers)
+  "Run FUNCTION on every file buffer found.
+FUNCTION does not have arguments; when it is entered `current-buffer'
+is the currently selected file buffer.
+If optional argument PREDICATE is non nil, only select file buffers
+for which the function PREDICATE return non-nil.
+If optional argument BUFFERS is non-nil, it is a list of buffers to
+walk through.  It defaults to `buffer-list'."
+  (dolist (b (or buffers (buffer-list)))
+    (and (buffer-live-p b) (buffer-file-name b)
+         (with-current-buffer b
+           (when (or (not predicate) (funcall predicate))
+             (funcall function))))))
+
+(defsubst get-mode-local-parent (mode)
+  "Return the mode parent of the major mode MODE.
+Return nil if MODE has no parent."
+  (or (get mode 'mode-local-parent)
+      (get mode 'derived-mode-parent)))
+
+(defun mode-local-equivalent-mode-p (mode)
+  "Is the major-mode in the current buffer equivalent to a mode in MODES."
+  (let ((modes nil))
+    (while mode
+      (setq modes (cons mode modes)
+	    mode  (get-mode-local-parent mode)))
+    modes))
+
+(defun mode-local-map-mode-buffers (function modes)
+  "Run FUNCTION on every file buffer with major mode in MODES.
+MODES can be a symbol or a list of symbols.
+FUNCTION does not have arguments."
+  (or (listp modes) (setq modes (list modes)))
+  (mode-local-map-file-buffers
+   function #'(lambda ()
+		(let ((mm (mode-local-equivalent-mode-p major-mode))
+		      (ans nil))
+		  (while (and (not ans) mm)
+		    (setq ans (memq (car mm) modes)
+			  mm (cdr mm)) )
+		  ans))))
+
+;;; Hook machinery
+;;
+(defvar mode-local-init-hook nil
+  "Hook run after a new file buffer is created.
+The current buffer is the newly created file buffer.")
+
+(defvar mode-local-changed-mode-buffers nil
+  "List of buffers whose `major-mode' has changed recently.")
+
+(defvar mode-local--init-mode nil)
+
+(defsubst mode-local-initialized-p ()
+  "Return non-nil if mode local is initialized in current buffer.
+That is, if the current `major-mode' is equal to the major mode for
+which mode local bindings have been activated."
+  (eq mode-local--init-mode major-mode))
+
+(defun mode-local-post-major-mode-change ()
+  "`post-command-hook' run when there is a `major-mode' change.
+This makes sure mode local init type stuff can occur."
+  (remove-hook 'post-command-hook 'mode-local-post-major-mode-change nil)
+  (let ((buffers mode-local-changed-mode-buffers))
+    (setq mode-local-changed-mode-buffers nil)
+    (mode-local-map-file-buffers
+     #'(lambda ()
+         ;; Make sure variables are set up for this mode.
+         (activate-mode-local-bindings)
+         (run-hooks 'mode-local-init-hook))
+     #'(lambda ()
+         (not (mode-local-initialized-p)))
+     buffers)))
+
+(defun mode-local-on-major-mode-change ()
+  "Function called in `change-major-mode-hook'."
+  (add-to-list 'mode-local-changed-mode-buffers (current-buffer))
+  (add-hook 'post-command-hook 'mode-local-post-major-mode-change t nil))
+
+(add-hook 'find-file-hooks 'mode-local-post-major-mode-change)
+(add-hook 'change-major-mode-hook 'mode-local-on-major-mode-change)
+
+;;; Mode lineage
+;;
+(defsubst set-mode-local-parent (mode parent)
+  "Set parent of major mode MODE to PARENT mode.
+To work properly, this function should be called after PARENT mode
+local variables have been defined."
+  (put mode 'mode-local-parent parent)
+  ;; Refresh mode bindings to get mode local variables inherited from
+  ;; PARENT. To work properly, the following should be called after
+  ;; PARENT mode local variables have been defined.
+  (mode-local-map-mode-buffers #'activate-mode-local-bindings mode))
+
+(defmacro define-child-mode (mode parent &optional docstring)
+  "Make major mode MODE inherits behavior from PARENT mode.
+DOCSTRING is optional and not used.
+To work properly, this should be put after PARENT mode local variables
+definition."
+  `(set-mode-local-parent ',mode ',parent))
+
+(defun mode-local-use-bindings-p (this-mode desired-mode)
+  "Return non-nil if THIS-MODE can use bindings of DESIRED-MODE."
+  (let ((ans nil))
+    (while (and (not ans) this-mode)
+      (setq ans (eq this-mode desired-mode))
+      (setq this-mode (get-mode-local-parent this-mode)))
+    ans))
+
+
+;;; Core bindings API
+;;
+(defvar mode-local-symbol-table nil
+  "Buffer local mode bindings.
+These symbols provide a hook for a `major-mode' to specify specific
+behaviors.  Use the function `mode-local-bind' to define new bindings.")
+(make-variable-buffer-local 'mode-local-symbol-table)
+
+(defvar mode-local-active-mode nil
+  "Major mode in which bindings are active.")
+
+(defsubst new-mode-local-bindings ()
+  "Return a new empty mode bindings symbol table."
+  (make-vector 13 0))
+
+(defun mode-local-bind (bindings &optional plist mode)
+  "Define BINDINGS in the specified environment.
+BINDINGS is a list of (VARIABLE . VALUE).
+Optional argument PLIST is a property list each VARIABLE symbol will
+be set to.  The following properties have special meaning:
+
+- `constant-flag' if non-nil, prevent to rebind variables.
+- `mode-variable-flag' if non-nil, define mode variables.
+- `override-flag' if non-nil, define override functions.
+
+The `override-flag' and `mode-variable-flag' properties are mutually
+exclusive.
+
+If optional argument MODE is non-nil, it must be a major mode symbol.
+BINDINGS will be defined globally for this major mode.  If MODE is
+nil, BINDINGS will be defined locally in the current buffer, in
+variable `mode-local-symbol-table'.  The later should be done in MODE
+hook."
+  ;; Check plist consistency
+  (and (plist-get plist 'mode-variable-flag)
+       (plist-get plist 'override-flag)
+       (error "Bindings can't be both overrides and mode variables"))
+  (let (table variable varname value binding)
+    (if mode
+        (progn
+          ;; Install in given MODE symbol table.  Create a new one if
+          ;; needed.
+          (setq table (or (get mode 'mode-local-symbol-table)
+                          (new-mode-local-bindings)))
+          (put mode 'mode-local-symbol-table table))
+      ;; Fail if trying to bind mode variables in local context!
+      (if (plist-get plist 'mode-variable-flag)
+          (error "Mode required to bind mode variables"))
+      ;; Install in buffer local symbol table.  Create a new one if
+      ;; needed.
+      (setq table (or mode-local-symbol-table
+                      (setq mode-local-symbol-table
+                            (new-mode-local-bindings)))))
+    (while bindings
+      (setq binding  (car bindings)
+            bindings (cdr bindings)
+            varname  (symbol-name (car binding))
+            value    (cdr binding))
+      (if (setq variable (intern-soft varname table))
+          ;; Binding already exists
+          ;; Check rebind consistency
+          (cond
+           ((equal (symbol-value variable) value)
+            ;; Just ignore rebind with the same value.
+            )
+           ((get variable 'constant-flag)
+            (error "Can't change the value of constant `%s'"
+                   variable))
+           ((and (get variable 'mode-variable-flag)
+                 (plist-get plist 'override-flag))
+            (error "Can't rebind override `%s' as a mode variable"
+                   variable))
+           ((and (get variable 'override-flag)
+                 (plist-get plist 'mode-variable-flag))
+            (error "Can't rebind mode variable `%s' as an override"
+                   variable))
+           (t
+            ;; Merge plist and assign new value
+            (setplist variable (append plist (symbol-plist variable)))
+            (set variable value)))
+        ;; New binding
+        (setq variable (intern varname table))
+        ;; Set new plist and assign initial value
+        (setplist variable plist)
+        (set variable value)))
+    ;; Return the symbol table used
+    table))
+
+(defsubst mode-local-symbol (symbol &optional mode)
+  "Return the mode local symbol bound with SYMBOL's name.
+Return nil if the  mode local symbol doesn't exist.
+If optional argument MODE is nil, lookup first into locally bound
+symbols, then in those bound in current `major-mode' and its parents.
+If MODE is non-nil, lookup into symbols bound in that major mode and
+its parents."
+  (let ((name (symbol-name symbol)) bind)
+    (or mode
+        (setq mode mode-local-active-mode)
+        (setq mode major-mode
+              bind (and mode-local-symbol-table
+                        (intern-soft name mode-local-symbol-table))))
+    (while (and mode (not bind))
+      (or (and (get mode 'mode-local-symbol-table)
+               (setq bind (intern-soft
+                           name (get mode 'mode-local-symbol-table))))
+          (setq mode (get-mode-local-parent mode))))
+    bind))
+
+(defsubst mode-local-symbol-value (symbol &optional mode property)
+  "Return the value of the mode local symbol bound with SYMBOL's name.
+If optional argument MODE is non-nil, restrict lookup to that mode and
+its parents (see the function `mode-local-symbol' for more details).
+If optional argument PROPERTY is non-nil the mode local symbol must
+have that property set.  Return nil if the symbol doesn't exist, or
+doesn't have PROPERTY set."
+  (and (setq symbol (mode-local-symbol symbol mode))
+       (or (not property) (get symbol property))
+       (symbol-value symbol)))
+
+;;; Mode local variables
+;;
+(defun activate-mode-local-bindings (&optional mode)
+  "Activate variables defined locally in MODE and its parents.
+That is, copy mode local bindings into corresponding buffer local
+variables.
+If MODE is not specified it defaults to current `major-mode'.
+Return the alist of buffer-local variables that have been changed.
+Elements are (SYMBOL . PREVIOUS-VALUE), describing one variable."
+  ;; Hack -
+  ;; do not do this if we are inside set-auto-mode as we may be in
+  ;; an initialization race condition.
+  (if (or  (and (featurep 'emacs) (boundp 'keep-mode-if-same))
+	   (and (featurep 'xemacs) (boundp 'just-from-file-name)))
+      ;; We are inside set-auto-mode, as this is an argument that is
+      ;; vaguely unique.
+
+      ;; This will make sure that when everything is over, this will get
+      ;; called and we won't be under set-auto-mode anymore.
+      (mode-local-on-major-mode-change)
+
+    ;; Do the normal thing.
+    (let (modes table old-locals)
+      (unless mode
+	(set (make-local-variable 'mode-local--init-mode) major-mode)
+	(setq mode major-mode))
+      ;; Get MODE's parents & MODE in the right order.
+      (while mode
+	(setq modes (cons mode modes)
+	      mode  (get-mode-local-parent mode)))
+      ;; Activate mode bindings following parent modes order.
+      (dolist (mode modes)
+	(when (setq table (get mode 'mode-local-symbol-table))
+	  (mapatoms
+	   #'(lambda (var)
+	       (when (get var 'mode-variable-flag)
+		 (let ((v (intern (symbol-name var))))
+		   ;; Save the current buffer-local value of the
+		   ;; mode-local variable.
+		   (and (local-variable-p v (current-buffer))
+			(push (cons v (symbol-value v)) old-locals))
+		   (set (make-local-variable v) (symbol-value var)))))
+	   table)))
+      old-locals)))
+
+(defun deactivate-mode-local-bindings (&optional mode)
+  "Deactivate variables defined locally in MODE and its parents.
+That is, kill buffer local variables set from the corresponding mode
+local bindings.
+If MODE is not specified it defaults to current `major-mode'."
+  (unless mode
+    (kill-local-variable 'mode-local--init-mode)
+    (setq mode major-mode))
+  (let (table)
+    (while mode
+      (when (setq table (get mode 'mode-local-symbol-table))
+        (mapatoms
+         #'(lambda (var)
+             (when (get var 'mode-variable-flag)
+               (kill-local-variable (intern (symbol-name var)))))
+         table))
+      (setq mode (get-mode-local-parent mode)))))
+
+(defmacro with-mode-local-symbol (mode &rest body)
+   "With the local bindings of MODE symbol, evaluate BODY.
+The current mode bindings are saved, BODY is evaluated, and the saved
+bindings are restored, even in case of an abnormal exit.
+Value is what BODY returns.
+This is like `with-mode-local', except that MODE's value is used.
+To use the symbol MODE (quoted), use `with-mode-local'."
+   (let ((old-mode  (make-symbol "mode"))
+         (old-locals (make-symbol "old-locals"))
+	 (new-mode (make-symbol "new-mode"))
+         (local (make-symbol "local")))
+     `(let ((,old-mode mode-local-active-mode)
+            (,old-locals nil)
+	    (,new-mode ,mode)
+	    )
+        (unwind-protect
+            (progn
+              (deactivate-mode-local-bindings ,old-mode)
+              (setq mode-local-active-mode ,new-mode)
+              ;; Save the previous value of buffer-local variables
+              ;; changed by `activate-mode-local-bindings'.
+              (setq ,old-locals (activate-mode-local-bindings ,new-mode))
+              ,@body)
+          (deactivate-mode-local-bindings ,new-mode)
+          ;; Restore the previous value of buffer-local variables.
+          (dolist (,local ,old-locals)
+            (set (car ,local) (cdr ,local)))
+          ;; Restore the mode local variables.
+          (setq mode-local-active-mode ,old-mode)
+          (activate-mode-local-bindings ,old-mode)))))
+(put 'with-mode-local-symbol 'lisp-indent-function 1)
+
+(defmacro with-mode-local (mode &rest body)
+   "With the local bindings of MODE, evaluate BODY.
+The current mode bindings are saved, BODY is evaluated, and the saved
+bindings are restored, even in case of an abnormal exit.
+Value is what BODY returns.
+This lis like `with-mode-local-symbol', except that MODE is quoted
+and is note evaluated."
+   `(with-mode-local-symbol ',mode ,@body))
+(put 'with-mode-local 'lisp-indent-function 1)
+
+
+(defsubst mode-local-value (mode sym)
+  "Return the value of the MODE local variable SYM."
+  (or mode (error "Missing major mode symbol"))
+  (mode-local-symbol-value sym mode 'mode-variable-flag))
+
+(defmacro setq-mode-local (mode &rest args)
+  "Assign new values to variables local in MODE.
+MODE must be a major mode symbol.
+ARGS is a list (SYM VAL SYM VAL ...).
+The symbols SYM are variables; they are literal (not evaluated).
+The values VAL are expressions; they are evaluated.
+Set each SYM to the value of its VAL, locally in buffers already in
+MODE, or in buffers switched to that mode.
+Return the value of the last VAL."
+  (when args
+    (let (i ll bl sl tmp sym val)
+      (setq i 0)
+      (while args
+        (setq tmp  (make-symbol (format "tmp%d" i))
+              i    (1+ i)
+              sym  (car args)
+              val  (cadr args)
+              ll   (cons (list tmp val) ll)
+              bl   (cons `(cons ',sym ,tmp) bl)
+              sl   (cons `(set (make-local-variable ',sym) ,tmp) sl)
+              args (cddr args)))
+      `(let* ,(nreverse ll)
+         ;; Save mode bindings
+         (mode-local-bind (list ,@bl) '(mode-variable-flag t) ',mode)
+         ;; Assign to local variables in all existing buffers in MODE
+         (mode-local-map-mode-buffers #'(lambda () ,@sl) ',mode)
+         ;; Return the last value
+         ,tmp)
+      )))
+
+(defmacro defvar-mode-local (mode sym val &optional docstring)
+  "Define MODE local variable SYM with value VAL.
+DOCSTRING is optional."
+  `(progn
+     (setq-mode-local ,mode ,sym ,val)
+     (put (mode-local-symbol ',sym ',mode)
+          'variable-documentation ,docstring)
+     ',sym))
+(put 'defvar-mode-local 'lisp-indent-function 'defun)
+
+(defmacro defconst-mode-local (mode sym val &optional docstring)
+  "Define MODE local constant SYM with value VAL.
+DOCSTRING is optional."
+  (let ((tmp (make-symbol "tmp")))
+    `(let (,tmp)
+       (setq-mode-local ,mode ,sym ,val)
+       (setq ,tmp (mode-local-symbol ',sym ',mode))
+       (put ,tmp 'constant-flag t)
+       (put ,tmp 'variable-documentation ,docstring)
+       ',sym)))
+(put 'defconst-mode-local 'lisp-indent-function 'defun)
+
+;;; Function overloading
+;;
+(defun make-obsolete-overload (old new)
+  "Mark OLD overload as obsoleted by NEW overload."
+  (put old 'overload-obsoleted-by new)
+  (put old 'mode-local-overload t)
+  (put new 'overload-obsolete old))
+
+(defsubst overload-obsoleted-by (overload)
+  "Get the overload symbol obsoleted by OVERLOAD.
+Return the obsolete symbol or nil if not found."
+  (get overload 'overload-obsolete))
+
+(defsubst overload-that-obsolete (overload)
+  "Return the overload symbol that obsoletes OVERLOAD.
+Return the symbol found or nil if OVERLOAD is not obsolete."
+  (get overload 'overload-obsoleted-by))
+
+(defsubst fetch-overload (overload)
+  "Return the current OVERLOAD function, or nil if not found.
+First, lookup for OVERLOAD into locally bound mode local symbols, then
+in those bound in current `major-mode' and its parents."
+  (or (mode-local-symbol-value overload nil 'override-flag)
+      ;; If an obsolete overload symbol exists, try it.
+      (and (overload-obsoleted-by overload)
+           (mode-local-symbol-value
+            (overload-obsoleted-by overload) nil 'override-flag))))
+
+(defun mode-local--override (name args body)
+  "Return the form that handles overloading of function NAME.
+ARGS are the arguments to the function.
+BODY is code that would be run when there is no override defined.  The
+default is to call the function `NAME-default' with the appropriate
+arguments.
+See also the function `define-overload'."
+  (let* ((default (intern (format "%s-default" name)))
+         (overargs (delq '&rest (delq '&optional (copy-sequence args))))
+         (override (make-symbol "override")))
+    `(let ((,override (fetch-overload ',name)))
+       (if ,override
+           (funcall ,override ,@overargs)
+         ,@(or body `((,default ,@overargs)))))
+    ))
+
+(defun mode-local--expand-overrides (name args body)
+  "Expand override forms that overload function NAME.
+ARGS are the arguments to the function NAME.
+BODY is code where override forms are searched for expansion.
+Return result of expansion, or BODY if no expansion occurred.
+See also the function `define-overload'."
+  (let ((forms body)
+        (ditto t)
+        form xbody)
+    (while forms
+      (setq form (car forms))
+      (cond
+       ((atom form))
+       ((eq (car form) :override)
+        (setq form (mode-local--override name args (cdr form))))
+       ((eq (car form) :override-with-args)
+        (setq form (mode-local--override name (cadr form) (cddr form))))
+       ((setq form (mode-local--expand-overrides name args form))))
+      (setq ditto (and ditto (eq (car forms) form))
+            xbody (cons form xbody)
+            forms (cdr forms)))
+    (if ditto body (nreverse xbody))))
+
+(defun mode-local--overload-body (name args body)
+  "Return the code that implements overloading of function NAME.
+ARGS are the arguments to the function NAME.
+BODY specifies the overload code.
+See also the function `define-overload'."
+  (let ((result (mode-local--expand-overrides name args body)))
+    (if (eq body result)
+        (list (mode-local--override name args body))
+      result)))
+
+(defmacro define-overloadable-function (name args docstring &rest body)
+  "Define a new function, as with `defun' which can be overloaded.
+NAME is the name of the function to create.
+ARGS are the arguments to the function.
+DOCSTRING is a documentation string to describe the function.  The
+docstring will automatically had details about its overload symbol
+appended to the end.
+BODY is code that would be run when there is no override defined.  The
+default is to call the function `NAME-default' with the appropriate
+arguments.
+
+BODY can also include an override form that specifies which part of
+BODY is specifically overridden.  This permits to specify common code
+run for both default and overridden implementations.
+An override form is one of:
+
+  1. (:override [OVERBODY])
+  2. (:override-with-args OVERARGS [OVERBODY])
+
+OVERBODY is the code that would be run when there is no override
+defined.  The default is to call the function `NAME-default' with the
+appropriate arguments deduced from ARGS.
+OVERARGS is a list of arguments passed to the override and
+`NAME-default' function, in place of those deduced from ARGS."
+  `(eval-and-compile
+     (defun ,name ,args
+       ,docstring
+       ,@(mode-local--overload-body name args body))
+     (put ',name 'mode-local-overload t)))
+(put :override-with-args 'lisp-indent-function 1)
+
+(defalias 'define-overload 'define-overloadable-function)
+
+(defsubst function-overload-p (symbol)
+  "Return non-nil if SYMBOL is a function which can be overloaded."
+  (and symbol (symbolp symbol) (get symbol 'mode-local-overload)))
+
+(defmacro define-mode-local-override
+  (name mode args docstring &rest body)
+  "Define a mode specific override of the function overload NAME.
+Has meaning only if NAME has been created with `define-overload'.
+MODE is the major mode this override is being defined for.
+ARGS are the function arguments, which should match those of the same
+named function created with `define-overload'.
+DOCSTRING is the documentation string.
+BODY is the implementation of this function."
+  (let ((newname (intern (format "%s-%s" name mode))))
+    `(progn
+       (eval-and-compile
+	 (defun ,newname ,args
+	   ,(format "%s\n\nOverride %s in `%s' buffers."
+		    docstring name mode)
+	   ;; The body for this implementation
+	   ,@body)
+         ;; For find-func to locate the definition of NEWNAME.
+         (put ',newname 'definition-name ',name))
+       (mode-local-bind '((,name . ,newname))
+                        '(override-flag t)
+                        ',mode))
+    ))
+
+;;; Read/Query Support
+(defun mode-local-read-function (prompt &optional initial hist default)
+  "Interactively read in the name of a mode-local function.
+PROMPT, INITIAL, HIST, and DEFAULT are the same as for `completing-read'."
+  (completing-read prompt obarray 'function-overload-p t initial hist default))
+
+;;; Help support
+;;
+(defun overload-docstring-extension (overload)
+  "Return the doc string that augments the description of OVERLOAD."
+  (let ((doc "\n\This function can be overloaded\
+ (see `define-mode-local-override' for details).")
+        (sym (overload-obsoleted-by overload)))
+    (when sym
+      (setq doc (format "%s\nIt makes the overload `%s' obsolete."
+                        doc sym)))
+    (setq sym (overload-that-obsolete overload))
+    (when sym
+      (setq doc (format "%s\nThis overload is obsoletes;\nUse `%s' instead."
+                        doc sym)))
+    doc))
+
+(defun mode-local-augment-function-help (symbol)
+  "Augment the *Help* buffer for SYMBOL.
+SYMBOL is a function that can be overridden."
+  (with-current-buffer "*Help*"
+    (pop-to-buffer (current-buffer))
+    (unwind-protect
+	(progn
+	  (toggle-read-only -1)
+          (goto-char (point-min))
+          (unless (re-search-forward "^$" nil t)
+            (goto-char (point-max))
+            (beginning-of-line)
+            (forward-line -1))
+          (insert (overload-docstring-extension symbol) "\n")
+	  ;; NOTE TO SELF:
+	  ;; LIST ALL LOADED OVERRIDES FOR SYMBOL HERE
+	  )
+      (toggle-read-only 1))))
+
+;; Help for Overload functions.  Need to advise help.
+(defadvice describe-function (around mode-local-help activate)
+  "Display the full documentation of FUNCTION (a symbol).
+Returns the documentation as a string, also."
+  (prog1
+      ad-do-it
+    (if (function-overload-p (ad-get-arg 0))
+	(mode-local-augment-function-help (ad-get-arg 0)))))
+
+;; Help for mode-local bindings.
+(defun mode-local-print-binding (symbol)
+  "Print the SYMBOL binding."
+  (let ((value (symbol-value symbol)))
+    (princ (format "\n     `%s' value is\n       " symbol))
+    (if (and value (symbolp value))
+        (princ (format "`%s'" value))
+      (let ((pt (point)))
+        (pp value)
+        (save-excursion
+          (goto-char pt)
+          (indent-sexp))))
+    (or (bolp) (princ "\n"))))
+
+(defun mode-local-print-bindings (table)
+  "Print bindings in TABLE."
+  (let (us ;; List of unpecified symbols
+        mc ;; List of mode local constants
+        mv ;; List of mode local variables
+        ov ;; List of overloaded functions
+        fo ;; List of final overloaded functions
+        )
+    ;; Order symbols by type
+    (mapatoms
+     #'(lambda (s)
+         (add-to-list (cond
+                       ((get s 'mode-variable-flag)
+                        (if (get s 'constant-flag) 'mc 'mv))
+                       ((get s 'override-flag)
+                        (if (get s 'constant-flag) 'fo 'ov))
+                       ('us))
+                      s))
+     table)
+    ;; Print symbols by type
+    (when us
+      (princ "\n  !! Unpecified symbols\n")
+      (mapc 'mode-local-print-binding us))
+    (when mc
+      (princ "\n  ** Mode local constants\n")
+      (mapc 'mode-local-print-binding mc))
+    (when mv
+      (princ "\n  ** Mode local variables\n")
+      (mapc 'mode-local-print-binding mv))
+    (when fo
+      (princ "\n  ** Final overloaded functions\n")
+      (mapc 'mode-local-print-binding fo))
+    (when ov
+      (princ "\n  ** Overloaded functions\n")
+      (mapc 'mode-local-print-binding ov))
+    ))
+
+(defun mode-local-describe-bindings-2 (buffer-or-mode)
+  "Display mode local bindings active in BUFFER-OR-MODE."
+  (let (table mode)
+    (princ "Mode local bindings active in ")
+    (cond
+     ((bufferp buffer-or-mode)
+      (with-current-buffer buffer-or-mode
+        (setq table mode-local-symbol-table
+              mode major-mode))
+      (princ (format "%S\n" buffer-or-mode))
+      )
+     ((symbolp buffer-or-mode)
+      (setq mode buffer-or-mode)
+      (princ (format "`%s'\n" buffer-or-mode))
+      )
+     ((signal 'wrong-type-argument
+              (list 'buffer-or-mode buffer-or-mode))))
+    (when table
+      (princ "\n- Buffer local\n")
+      (mode-local-print-bindings table))
+    (while mode
+      (setq table (get mode 'mode-local-symbol-table))
+      (when table
+        (princ (format "\n- From `%s'\n" mode))
+        (mode-local-print-bindings table))
+      (setq mode (get-mode-local-parent mode)))))
+
+(defun mode-local-describe-bindings-1 (buffer-or-mode &optional interactive-p)
+  "Display mode local bindings active in BUFFER-OR-MODE.
+Optional argument INTERACTIVE-P is non-nil if the calling command was
+invoked interactively."
+  (if (fboundp 'with-displaying-help-buffer)
+      ;; XEmacs
+      (with-displaying-help-buffer
+       #'(lambda ()
+           (with-current-buffer standard-output
+             (mode-local-describe-bindings-2 buffer-or-mode)
+             (when (fboundp 'frob-help-extents)
+               (goto-char (point-min))
+               (frob-help-extents standard-output)))))
+    ;; GNU Emacs
+    (when (fboundp 'help-setup-xref)
+      (help-setup-xref
+       (list 'mode-local-describe-bindings-1 buffer-or-mode)
+       interactive-p))
+    (with-output-to-temp-buffer (help-buffer) ; "*Help*"
+      (with-current-buffer standard-output
+        (mode-local-describe-bindings-2 buffer-or-mode)))))
+
+(defun describe-mode-local-bindings (buffer)
+  "Display mode local bindings active in BUFFER."
+  (interactive "b")
+  (when (setq buffer (get-buffer buffer))
+    (mode-local-describe-bindings-1 buffer (interactive-p))))
+
+(defun describe-mode-local-bindings-in-mode (mode)
+  "Display mode local bindings active in MODE hierarchy."
+  (interactive
+   (list (completing-read
+          "Mode: " obarray
+          #'(lambda (s) (get s 'mode-local-symbol-table))
+          t (symbol-name major-mode))))
+  (when (setq mode (intern-soft mode))
+    (mode-local-describe-bindings-1 mode (interactive-p))))
+
+;;; Font-lock support
+;;
+(defconst mode-local-font-lock-keywords
+  (eval-when-compile
+    (let* (
+           ;; Variable declarations
+           (kv (regexp-opt
+                '(
+                  "defconst-mode-local"
+                  "defvar-mode-local"
+                  ) t))
+           ;; Function declarations
+           (kf (regexp-opt
+                '(
+                  "define-mode-local-override"
+                  "define-child-mode"
+                  "define-overload"
+                  "define-overloadable-function"
+                  ;;"make-obsolete-overload"
+                  "with-mode-local"
+                  ) t))
+           ;; Regexp depths
+           (kv-depth (regexp-opt-depth kv))
+           (kf-depth (regexp-opt-depth kf))
+           )
+      `((,(concat
+           ;; Declarative things
+           "(\\(" kv "\\|" kf "\\)"
+           ;; Whitespaces & names
+           "\\>[ \t]*\\(\\sw+\\)?[ \t]*\\(\\sw+\\)?"
+           )
+         (1 font-lock-keyword-face)
+         (,(+ 1 kv-depth kf-depth 1)
+          (cond ((match-beginning 2)
+                 font-lock-type-face)
+                ((match-beginning ,(+ 1 kv-depth 1))
+                 font-lock-function-name-face)
+                )
+          nil t)
+         (,(+ 1 kv-depth kf-depth 1 1)
+          (cond ((match-beginning 2)
+                 font-lock-variable-name-face)
+                )
+          nil t)))
+      ))
+  "Highlighted keywords.")
+
+
+;;; find-func support (Emacs 21.4, or perhaps 22.1)
+;;
+(condition-case nil
+    ;; Try to get find-func so we can modify it.
+    (require 'find-func)
+  (error nil))
+
+(when (boundp 'find-function-regexp)
+  (unless (string-match "ine-overload" find-function-regexp)
+    (if (string-match "(def\\\\(" find-function-regexp)
+	(let ((end (match-end 0))
+	      )
+	  (setq find-function-regexp
+		(concat (substring find-function-regexp 0 end)
+			"ine-overload\\|ine-mode-local-override\\|"
+			"ine-child-mode\\|"
+			(substring find-function-regexp end)))))
+    )
+  ;; The regexp for variables is a little more kind.
+  )
+
+;; TODO: Add XEmacs support
+;; (when (fboundp 'font-lock-add-keywords)
+;;   (font-lock-add-keywords 'emacs-lisp-mode
+;;                           mode-local-font-lock-keywords))
+
+;;; edebug support
+;;
+(defun mode-local-setup-edebug-specs ()
+  "Define edebug specification for mode local macros."
+  (def-edebug-spec setq-mode-local
+    (symbolp &rest symbolp form)
+    )
+  (def-edebug-spec defvar-mode-local
+    (&define symbolp name def-form [ &optional stringp ] )
+    )
+  (def-edebug-spec defconst-mode-local
+    defvar-mode-local
+    )
+  (def-edebug-spec define-overload
+    (&define name lambda-list stringp def-body)
+    )
+  (def-edebug-spec define-overloadable-function
+    (&define name lambda-list stringp def-body)
+    )
+  (def-edebug-spec define-mode-local-override
+    (&define name symbolp lambda-list stringp def-body)
+    )
+  )
+
+(add-hook 'edebug-setup-hook 'mode-local-setup-edebug-specs)
+
+(provide 'mode-local)
+
+;;; mode-local.el ends here