emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[elpa] master bb60f11 001/433: Initial revision


From: Dmitry Gutov
Subject: [elpa] master bb60f11 001/433: Initial revision
Date: Thu, 15 Mar 2018 19:43:23 -0400 (EDT)

branch: master
commit bb60f11590eb631ce11d42d6a44701db3632c91a
Author: mas <mas>
Commit: mas <mas>

    Initial revision
---
 mmm-auto.el   | 154 ++++++++++++++++++
 mmm-class.el  | 210 +++++++++++++++++++++++++
 mmm-cmds.el   | 365 +++++++++++++++++++++++++++++++++++++++++++
 mmm-compat.el | 159 +++++++++++++++++++
 mmm-mason.el  | 181 ++++++++++++++++++++++
 mmm-mode.el   | 457 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 mmm-region.el | 453 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 mmm-sample.el | 160 +++++++++++++++++++
 mmm-utils.el  | 132 ++++++++++++++++
 mmm-vars.el   | 488 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 10 files changed, 2759 insertions(+)

diff --git a/mmm-auto.el b/mmm-auto.el
new file mode 100644
index 0000000..ff2ee74
--- /dev/null
+++ b/mmm-auto.el
@@ -0,0 +1,154 @@
+;;; mmm-auto.el --- loading and enabling MMM Mode automatically
+
+;; Copyright (C) 2000 by Michael Abraham Shulman
+
+;; Author: Michael Abraham Shulman <address@hidden>
+;; Version: $Id: mmm-auto.el,v 1.1 2000/04/27 10:32:25 mas Exp $
+
+;;{{{ GPL
+
+;; This file 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 file 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:
+
+;; This file contains functions and hooks to load and enable MMM Mode
+;; automatically. It sets up autoloads for the main MMM Mode functions
+;; and interactive commands, and also sets up MMM Global Mode.
+
+;;{{{ Comments on MMM Global Mode
+
+;; This is a kludge partially borrowed from `global-font-lock-mode'.
+;; The idea is the same: we have a function (here `mmm-mode-on-maybe')
+;; that we want to be run whenever a major mode starts. Unfortunately,
+;; there is no hook (like, say `major-mode-hook') that all major modes
+;; run when they are finished. They just run their own, specific hook.
+;; So what we're going to do is find a way to insinuate our function
+;; into *all* those hooks. (This is a bit different from what global
+;; font-lock mode does--it uses `post-command-hook').
+
+;; In order to do this magic, we rely on the fact that there *is* a
+;; hook that all major modes run when *beginning* their work. They
+;; must call `kill-all-local-variables', which in turn runs
+;; `change-major-mode-hook'. So we add a function to *that* hook which
+;; inspects the call stack to find the mode function which is calling
+;; it (mode functions are recognizable by ending in "-mode"), and add
+;; our function to that mode's hook.
+
+;; Actually, in the interests of generality, what it adds to that
+;; mode's hook is the function `mmm-run-major-mode-hook', which in
+;; turn runs the hook `mmm-major-mode-hook'. Our desired function
+;; `mmm-mode-on-maybe' is then added to that hook. This way, if the
+;; user wants to run something else on every major mode, they can just
+;; add it to `mmm-major-mode-hook' and take advantage of this hack.
+
+;; In out-of-the box Emacs, almost all major modes will be four frames
+;; back. The frames are:
+;; 1. mmm-major-mode-change
+;; 2. run-hooks(change-major-mode-hook)
+;; 3. kill-all-local-variables
+;; 4. DESIRED-MAJOR-mode
+;; When gnuserv is loaded, it adds an extra layer (a function called
+;; `server-kill-all-local-variables'), making five. I can imagine
+;; other packages doing the same thing, so for safety's sake, if we
+;; don't find a function whose name ends in `-mode', we keep looking
+;; until we run out of frames. I'm 99% sure that there will always be
+;; at least four frames, though.
+
+;;}}}
+
+;;; Code:
+
+(require 'cl)
+(require 'mmm-vars)
+
+;;{{{ Autoloads
+
+;; To shut up the byte compiler.
+(eval-and-compile
+  (autoload 'mmm-mode-on "mmm-mode" "Turn on MMM Mode. See `mmm-mode'.")
+  (autoload 'mmm-mode "mmm-mode"
+    "Minor mode to allow multiple major modes in one buffer.
+Without ARG, toggle MMM Mode. With ARG, turn MMM Mode on iff ARG is
+positive and off otherwise." t))
+
+(autoload 'mmm-ify-by-class "mmm-cmds" "" t)
+(autoload 'mmm-ify-by-regexp "mmm-cmds" "" t)
+(autoload 'mmm-ify-region "mmm-cmds" "" t)
+(autoload 'mmm-parse-buffer "mmm-cmds" "" t)
+(autoload 'mmm-parse-region "mmm-cmds" "" t)
+(autoload 'mmm-parse-block "mmm-cmds" "" t)
+(autoload 'mmm-clear-current-region "mmm-cmds" "" t)
+(autoload 'mmm-reparse-current-region "mmm-cmds" "" t)
+(autoload 'mmm-end-current-region "mmm-cmds" "" t)
+(autoload 'mmm-insertion-help "mmm-cmds" "" t)
+(autoload 'mmm-insert-region "mmm-cmds" "" t)
+
+;;}}}
+;;{{{ Automatic Hook Adding
+
+(defun mmm-major-mode-change ()
+  "Add mode hooks to turn MMM Mode on where appropriate.
+Actually adds `mmm-run-major-mode-hook' to all major mode hooks."
+  (unless (window-minibuffer-p (selected-window))
+    (loop for lookback from 4
+          for frame = (backtrace-frame lookback)
+          while frame
+          if (mmm-get-mode-hook (cadr frame))
+          do (add-hook it 'mmm-run-major-mode-hook)
+          and return t)))
+(add-hook 'change-major-mode-hook 'mmm-major-mode-change)
+
+(defun mmm-get-mode-hook (function)
+  "If FUNCTION is a mode function, get its hook variable.
+Otherwise, return nil."
+  (when (symbolp function)
+    (let ((name (symbol-name function)))
+      (and (> (length name) 5)
+           (string= (substring name -5) "-mode")
+           (intern (format "%s-hook" name))))))
+
+;;}}}
+;;{{{ MMM Global Mode
+
+(defun mmm-mode-on-maybe ()
+  "Conditionally turn on MMM Mode.
+Turn on MMM Mode if `global-mmm-mode' is non-nil and there are classes
+to apply, or always if `global-mmm-mode' is t."
+  (cond ((eq mmm-global-mode t) (mmm-mode-on))
+        ((not mmm-global-mode))
+        ((mmm-get-all-classes) (mmm-mode-on))))
+
+;; Add our function to our hook.
+(add-hook 'mmm-major-mode-hook 'mmm-mode-on-maybe)
+
+;; File Local variables don't get set by the time the major mode is
+;; starting up, apparently. So we need to add the hook here too.
+(add-hook 'find-file-hooks 'mmm-mode-on-maybe)
+
+(defalias 'mmm-add-find-file-hooks 'mmm-add-find-file-hook)
+
+(defun mmm-add-find-file-hook ()
+  "Equivalent to \(setq mmm-global-mode 'maybe).
+This function is deprecated and may be removed in future."
+  (setq mmm-global-mode 'maybe))
+
+;;}}}
+
+(provide 'mmm-auto)
+
+;;; mmm-auto.el ends here
\ No newline at end of file
diff --git a/mmm-class.el b/mmm-class.el
new file mode 100644
index 0000000..a4b0fff
--- /dev/null
+++ b/mmm-class.el
@@ -0,0 +1,210 @@
+;;; mmm-class.el --- MMM submode class variables and functions
+
+;; Copyright (C) 2000 by Michael Abraham Shulman
+
+;; Author: Michael Abraham Shulman <address@hidden>
+;; Version: $Id: mmm-class.el,v 1.1 2000/04/27 10:33:27 mas Exp $
+
+;;{{{ GPL
+
+;; This file 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 file 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:
+
+;; This file contains variable and function definitions for
+;; manipulating and applying MMM submode classes. See `mmm-vars.el'
+;; for variables that list classes.
+
+;;; Code:
+
+(require 'font-lock)
+(require 'cl)
+(require 'mmm-vars)
+(require 'mmm-region)
+
+;;{{{ Get Class Specifications
+
+(defun mmm-get-class-spec (class)
+  "Get the class specification for CLASS.
+CLASS can be either a symbol to look up in `mmm-classes-alist' or a
+class specifier itself."
+  (cond ((symbolp class)        ; A symbol must be looked up
+         (or (cdr (assq class mmm-classes-alist))
+             (signal 'mmm-invalid-submode-class (list class))))
+        ((listp class)          ; A list must be a class spec
+         class)
+        (t (signal 'mmm-invalid-submode-class (list class)))))
+
+;;}}}
+;;{{{ Apply Classes
+
+(defun* mmm-apply-class
+    (class &optional (start (point-min)) (stop (point-max)) face)
+  "Apply the submode class CLASS from START to STOP in FACE."
+  ;; The "special" class t means do nothing. It is used to turn on
+  ;; MMM Mode without applying any classes.
+  (unless (eq class t)
+    (apply #'mmm-ify :start start :stop stop :face face
+           (mmm-get-class-spec class))))
+
+(defun* mmm-apply-classes
+    (classes &key (start (point-min)) (stop (point-max)) face)
+  "Apply all submode classes in CLASSES, in order.
+All classes are applied regardless of any errors that may occur in
+other classes. If any errors occur, `mmm-apply-classes' exits with an
+error once all classes have been applied."
+  (let (invalid-classes)
+    (dolist (class classes)
+      (condition-case err
+          (mmm-apply-class class start stop face)
+        (mmm-invalid-submode-class
+         ;; Save the name of the invalid class, so we can report them
+         ;; all together at the end.
+         (add-to-list 'invalid-classes (second err)))))
+    (when invalid-classes
+      (signal 'mmm-invalid-submode-class invalid-classes))))
+
+;;}}}
+;;{{{ Apply All Classes
+
+(defun* mmm-apply-all (&key (start (point-min)) (stop (point-max)))
+  "MMM-ify START to STOP by mode/ext, `mmm-classes', and history."
+  (mmm-clear-overlays start stop 'strict)
+  (mmm-apply-classes (mmm-get-all-classes) :start start :stop stop)
+  (mmm-update-current-submode)
+  (mmm-refontify-maybe start stop))
+
+(defun mmm-refontify-maybe (start stop)
+  "Re-fontify from START to STOP."
+  (and (featurep 'font-lock)
+       font-lock-mode
+       (if (or start stop)
+           (font-lock-fontify-region (or start (point-min))
+                                     (or stop (point-max)))
+         (font-lock-fontify-buffer))))
+
+;;}}}
+;;{{{ Scan for Regions
+
+(defun* mmm-ify
+    (&rest all &key classes handler submode face
+           (start (point-min)) (stop (point-max))
+           front back save-matches (case-fold-search t)
+           (beg-sticky (not (number-or-marker-p front)))
+           (end-sticky (not (number-or-marker-p back)))
+           (front-offset 0) (back-offset 0) front-verify back-verify
+           front-form back-form
+           &allow-other-keys)
+  "Create submode regions from START to STOP according to arguments.
+If CLASSES is supplied, it must be a list of valid CLASSes. Otherwise,
+the rest of the arguments are for an actual class being applied. See
+`mmm-classes-alist' for information on what they all mean."
+  (cond
+   ;; If we have a class list, apply them all.
+   (classes
+    (mmm-apply-classes classes :start start :stop stop :face face))
+   ;; Otherwise, apply this class.
+   ;; If we have a handler, call it.
+   (handler
+    (apply handler all))
+   ;; Otherwise, we search from START to STOP for submode regions,
+   ;; continuining over errors, until we don't find any more. If FRONT
+   ;; and BACK are number-or-markers, this should only execute once.
+   (t
+    (mmm-save-all
+     (goto-char start)
+     (loop for (beg end front-form back-form) =
+           (apply #'mmm-match-region :start (point)
+                  (mmm-save-keywords front back stop
+                    save-matches front-offset back-offset front-verify
+                    back-verify front-form back-form))
+           while beg
+           while (/= beg end)   ; Sanity check
+           do
+           (condition-case nil
+               (mmm-make-region submode beg end :face face
+                 :front front-form :back back-form
+                 :beg-sticky beg-sticky :end-sticky end-sticky)
+             ;; If our region is invalid, go back to the end of the front
+             ;; match and continue on.
+             (mmm-invalid-parent (goto-char (- beg front-offset)))))))))
+
+;;}}}
+;;{{{ Match Regions
+
+(defun* mmm-match-region
+    (&key front back start stop front-verify back-verify front-offset
+          back-offset save-matches front-form back-form)
+  "Find the first valid region between point and STOP.
+Return \(BEG END FRONT-FORM BACK-FORM) specifying the region. See
+`mmm-match-and-verify' for the valid values of FRONT and BACK
+\(markers, regexps, or functions)."
+  (when (mmm-match-and-verify front start stop front-verify)
+    (let ((beg (+ (match-end 0) front-offset))
+          (front-form (mmm-get-form front-form)))
+      (when (mmm-match-and-verify (mmm-format-matches back save-matches)
+                                  beg stop back-verify)
+        (let ((end (+ (match-beginning 0) back-offset))
+              (back-form (mmm-get-form back-form)))
+          (values beg end front-form back-form))))))
+
+(defun mmm-match-and-verify (pos start stop &optional verify)
+  "Find first match for POS between point and STOP satisfying VERIFY.
+Return non-nil if a match was found, and set match data. POS can be a
+number-or-marker, a regexp, or a function.
+
+If POS is a number-or-marker, it is used as-is. If it is a string, it
+is searched for as a regexp until VERIFY returns non-nil. If it is a
+function, it is called with argument STOP and must return non-nil iff
+a match is found, and set the match data. Note that VERIFY is ignored
+unless POS is a regexp."
+  (cond
+   ;; A marker can be used as-is, but only if it's in bounds.
+   ((and (number-or-marker-p pos) (>= pos start) (<= pos stop))
+    (goto-char pos)
+    (looking-at ""))            ; Set the match data
+   ;; Strings are searched for as regexps.
+   ((stringp pos)
+    (loop always (re-search-forward pos stop 'limit)
+          until (or (not verify) (mmm-save-all (funcall verify)))))
+   ;; Otherwise it must be a function.
+   ((functionp pos)
+    (funcall pos stop))))
+
+;;}}}
+;;{{{ Get Delimiter Forms
+
+(defun mmm-get-form (form)
+  "Return the delimiter form specified by FORM.
+If FORM is nil, call `mmm-default-get-form'. If FORM is a string,
+return it. If FORM is a function, call it. If FORM is a list, return
+its `car' \(usually in this case, FORM is a one-element list
+containing a function to be used as the delimiter form."
+  (cond ((stringp form) form)
+        ((not form) (mmm-default-get-form))
+        ((functionp form) (funcall form))
+        ((listp form) (car form))))
+
+(defun mmm-default-get-form ()
+  (regexp-quote (match-string 0)))
+
+;;}}}
+
+(provide 'mmm-class)
+
+;;; mmm-class.el ends here
\ No newline at end of file
diff --git a/mmm-cmds.el b/mmm-cmds.el
new file mode 100644
index 0000000..e7fe448
--- /dev/null
+++ b/mmm-cmds.el
@@ -0,0 +1,365 @@
+;;; mmm-cmds.el --- MMM Mode interactive commands and keymap
+
+;; Copyright (C) 2000 by Michael Abraham Shulman
+
+;; Author: Michael Abraham Shulman <address@hidden>
+;; Version: $Id: mmm-cmds.el,v 1.1 2000/04/27 10:34:15 mas Exp $
+
+;;{{{ GPL
+
+;; This file 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 file 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:
+
+;; This file contains the interactive commands for MMM Mode.
+
+;;; Code:
+
+(require 'font-lock)
+(require 'mmm-compat)
+(require 'mmm-vars)
+(require 'mmm-class)
+
+;;{{{ Applying Predefined Classes
+
+(defun mmm-ify-by-class (class)
+  "Add submode regions according to an existing submode class."
+  (interactive "SSubmode Class: ")
+  (mmm-apply-class class)
+  (mmm-add-to-history class)
+  (when (and (featurep 'font-lock) font-lock-mode)
+    (font-lock-fontify-buffer)))
+
+;;}}}
+;;{{{ Applying by the Region
+
+(defun mmm-ify-region (submode front back)
+  "Add a submode region for SUBMODE coinciding with current region."
+  (interactive "aSubmode: \nr")
+  (mmm-ify :submode submode :front front :back back)
+  (setq front (mmm-make-marker front t nil)
+        back (mmm-make-marker back nil nil))
+  (mmm-add-to-history `(:submode ,submode :front ,front :back ,back))
+  (when (and (featurep 'font-lock) font-lock-mode)
+    (font-lock-fontify-buffer)))
+
+;;}}}
+;;{{{ Applying simple Regexps
+
+(defun mmm-ify-by-regexp
+  (submode front front-offset back back-offset save-matches)
+  "Add SUBMODE regions to the buffer delimited by FRONT and BACK.
+With prefix argument, prompts for all additional keywords arguments.
+See `mmm-classes-alist'."
+  (interactive "aSubmode: 
+sFront Regexp: 
+nOffset from Front Regexp: 
+sBack Regexp: 
+nOffset from Back Regexp: 
+nNumber of matched substrings to save: ")
+  (let ((args (mmm-save-keywords submode front back
+                front-offset back-offset save-matches)))
+    (apply #'mmm-ify args)
+    (mmm-add-to-history args))
+  (when (and (featurep 'font-lock) font-lock-mode)
+    (font-lock-fontify-buffer)))
+
+;;}}}
+;;{{{ Re-parsing Areas
+
+(defun mmm-parse-buffer ()
+  "Re-apply all applicable submode classes to current buffer.
+Clears all current submode regions, reapplies all past interactive
+mmm-ification, and applies `mmm-classes' and mode-extension classes."
+  (interactive)
+  (message "MMM-ifying buffer...")
+  (mmm-apply-all)
+  (message "MMM-ifying buffer...done"))
+
+(defun mmm-parse-region (start stop)
+  "Re-apply all applicable submode classes between START and STOP.
+Clears all current submode regions, reapplies all past interactive
+mmm-ification, and applies `mmm-classes' and mode-extension classes."
+  (interactive "r")
+  (message "MMM-ifying region...")
+  (mmm-apply-all :start start :stop stop)
+  (message "MMM-ifying region...done"))
+
+(defun mmm-parse-block (&optional lines)
+  "Re-parse LINES lines before and after point \(default 1).
+Clears all current submode regions, reapplies all past interactive
+mmm-ification, and applies `mmm-classes' and mode-extension classes.
+
+This command is intended for use when you have just typed what should
+be the delimiters of a submode region and you want to create the
+region. However, you may want to look into the various types of
+delimiter auto-insertion that MMM Mode provides. See, for example,
+`mmm-insert-region'."
+  (interactive "p")
+  (message "MMM-ifying block...")
+  (destructuring-bind (start stop) (mmm-get-block lines)
+    (when (< start stop)
+      (mmm-apply-all :start start :stop stop)))
+  (message "MMM-ifying block...done"))
+
+(defun mmm-get-block (lines)
+  (let ((inhibit-point-motion-hooks t))
+    (list (save-excursion
+            (forward-line (- lines))
+            (beginning-of-line)
+            (point))
+          (save-excursion
+            (forward-line lines)
+            (end-of-line)
+            (point)))))
+
+;;}}}
+;;{{{ Clear Submode Regions
+
+;; See also `mmm-clear-history' which is interactive.
+
+(defun mmm-clear-current-region ()
+  "Deletes the submode region point is currently in, if any."
+  (interactive)
+  (delete-overlay (mmm-overlay-at (point) 'all)))
+
+(defun mmm-clear-regions (start stop)
+  "Deletes all submode regions from START to STOP."
+  (interactive "r")
+  (mmm-clear-overlays start stop))
+
+(defun mmm-clear-all-regions ()
+  "Deletes all submode regions in the current buffer."
+  (interactive)
+  (mmm-clear-overlays))
+
+;;}}}
+;;{{{ Reparse Current Region
+
+(defun mmm-reparse-current-region ()
+  "Clear and reparse the area of the current submode region.
+Use this command if a submode region's boundaries have become wrong."
+  (interactive)
+  (let ((ovl (mmm-overlay-at (point) 'all)))
+    (when ovl
+      (let ((beg (save-excursion
+                   (goto-char (mmm-front-start ovl))
+                   (forward-line -1)
+                   (point)))
+            (end (save-excursion
+                   (goto-char (mmm-back-end ovl))
+                   (forward-line 1)
+                   (point))))
+        (mmm-parse-region beg end)))))
+
+;;}}}
+;;{{{ End Current Region
+
+(defun* mmm-end-current-region (&optional arg)
+  "End current submode region.
+If ARG is nil, end it at the most appropriate place, usually its
+current back boundary. If ARG is non-nil, end it at point. If the
+current region is correctly bounded, the first does nothing, but the
+second deletes that delimiter as well.
+
+If the region's BACK property is a string, it is inserted as above and
+the overlay moved if necessary. If it is a function, it is called with
+two arguments--the overlay, and \(if ARG 'middle t)--and must do the
+entire job of this function."
+  (interactive "P")
+  (let ((ovl (mmm-overlay-at)))
+    (when ovl
+      (combine-after-change-calls
+        (save-match-data
+          (save-excursion
+            (when (mmm-match-back ovl)
+              (if arg
+                  (replace-match "")
+                (return-from mmm-end-current-region)))))
+        (let ((back (overlay-get ovl 'back)))
+          (cond ((stringp back)
+                 (save-excursion
+                   (unless arg (goto-char (overlay-end ovl)))
+                   (save-excursion (insert back))
+                   (move-overlay ovl (overlay-start ovl) (point))))
+                ((functionp back)
+                 (funcall back ovl (if arg 'middle t))))))
+      (mmm-refontify-maybe (save-excursion (forward-line -1) (point))
+                           (save-excursion (forward-line 1) (point))))))
+
+;;}}}
+;;{{{ Insert regions by keystroke
+
+;; This is the "default" binding in the MMM Mode keymap. Keys defined
+;; by classes should be control keys, to avoid conflicts with MMM
+;; commands.
+(defun mmm-insert-region (arg)
+  "Insert a submode region based on last character in invoking keys.
+Keystrokes after `mmm-mode-prefix-key' which are not bound to an MMM
+Mode command \(see `mmm-command-modifiers') are passed on to this
+function. If they have the modifiers `mmm-insert-modifiers', then they
+are looked up, sans those modifiers, in all current submode classes to
+find an insert skeleton. For example, in Mason, `p' \(with appropriate
+prefix and modifiers) will insert a <%perl>...</%perl> region."
+  (interactive "P")
+  (let* ((seq (this-command-keys))
+         (event (aref seq (1- (length seq))))
+         (mods (event-modifiers event))
+         (key (mmm-event-key event)))
+    (if (subsetp mmm-insert-modifiers mods)
+        (mmm-insert-by-key
+         (append (set-difference mods mmm-insert-modifiers)
+                 key)
+         arg))))
+
+(defun mmm-insert-by-key (key &optional arg)
+  "Insert a submode region based on event KEY.
+Inspects all the classes of the current buffer to find a matching
+:insert key sequence. See `mmm-classes-alist'. ARG, if present, is
+passed on to `skeleton-proxy-new' to control wrapping.
+
+KEY must be a list \(MODIFIERS... . BASIC-KEY) where MODIFIERS are
+symbols such as shift, control, etc. and BASIC-KEY is a character code
+or a symbol such as tab, return, etc. Note that if there are no
+MODIFIERS, the dotted list becomes simply BASIC-KEY."
+  (multiple-value-bind (class skel str) (mmm-get-insertion-spec key)
+    (when skel
+      (let ((after-change-functions nil))
+        ;; XEmacs' skeleton doesn't manage positions by itself, so we
+        ;; have to do it.
+        (if mmm-xemacs (setq skeleton-positions nil))
+        (skeleton-proxy-new skel str arg)
+        (destructuring-bind (back end beg front) skeleton-positions
+          ;; TODO: Find a way to trap invalid-parent signals from
+          ;; make-region and undo the skeleton insertion.
+          (mmm-make-region (plist-get class :submode) beg end
+                           :front (buffer-substring front beg)
+                           :back (buffer-substring end back)
+                           :face (plist-get class :face)
+                           :beg-sticky (plist-get class :beg-sticky)
+                           :end-sticky (plist-get class :end-sticky))
+          (when (and (featurep 'font-lock) font-lock-mode)
+            (font-lock-fontify-region front back)))))))
+
+(defun mmm-get-insertion-spec (key &optional classlist)
+  "Get the insertion info for KEY from all classes in CLASSLIST.
+Return \(CLASS SKEL STR) where CLASS is the class spec a match was
+found in, SKEL is the skeleton to insert, and STR is the argument.
+CLASSLIST defaults to the return value of `mmm-get-all-classes'."
+  (loop for classname in (or classlist (mmm-get-all-classes))
+        for class = (mmm-get-class-spec classname)
+        for inserts = (plist-get class :insert)
+        for skel = (cddr (assoc key inserts))
+        with str
+        ;; If SKEL is a dotted pair, it means call another key's
+        ;; insertion spec with an argument.
+        unless (consp (cdr skel))
+        do (setq str (cdr skel)
+                 skel (cdr (assoc (car skel) inserts)))
+        if skel return (list class skel str)
+        ;; If we have a group class, recurse.
+        if (plist-get class :classes)
+           if (mmm-get-insertion-spec key it)
+              return it
+           else
+              return nil))
+
+;;}}}
+;;{{{ Help on Insertion
+
+(defun mmm-insertion-help ()
+  "Display help on currently available MMM insertion commands."
+  (interactive)
+  (with-output-to-temp-buffer "*Help*"
+    (princ "Available MMM Mode Insertion Commands:\n")
+    (princ "Key             Inserts\n")
+    (princ "---             -------\n\n")
+    (mapcar #'mmm-display-insertion-key
+            (mmm-get-all-insertion-keys))))
+
+(defun mmm-display-insertion-key (spec)
+  "Print an insertion binding to standard output.
+SPEC should be \(KEY NAME ...) where KEY is an insertion key and NAME
+is a symbol naming the insertion."
+  (let* ((str (make-string 16 ?\ ))
+         ;; This gets us a dotted list, because of the way insertion
+         ;; keys are specified.
+         (key (append mmm-insert-modifiers (car spec)))
+         (lastkey (nthcdr (1- (safe-length key)) key)))
+    ;; Now we make it a true list
+    (if (consp key)
+        (setcdr lastkey (list (cdr lastkey)))
+      (setq key (list key)))
+    ;; Get the spacing right
+    (store-substring str 0
+      (key-description
+       (apply #'vector (append mmm-mode-prefix-key (list key)))))
+    (princ str)
+    ;; Now print the binding symbol
+    (princ (cadr spec))
+    (princ "\n")))
+
+(defun mmm-get-all-insertion-keys (&optional classlist)
+  "Return an alist of all currently available insertion keys.
+Elements look like \(KEY NAME ...) where KEY is an insertion key and
+NAME is a symbol naming the insertion."
+  (remove-duplicates
+   (loop for classname in (or classlist (mmm-get-all-classes))
+         for class = (mmm-get-class-spec classname)
+         append (plist-get class :insert) into keys
+         ;; If we have a group class, recurse.
+         if (plist-get class :classes)
+         do (setq keys (append keys (mmm-get-all-insertion-keys it)))
+         finally return keys)
+   :test #'equal
+   :key #'(lambda (x) (cons (car x) (cadr x)))
+   :from-end t))
+
+;;}}}
+
+;;{{{ Auto Insertion (copied from interactive session);-COM-
+;-COM-
+;-COM-;; Don't use `mmm-ify-region' of course. And rather than having
+;-COM-;; classes define their own functions, we should have them pass a
+;-COM-;; skeleton as an attribute. Then our insert function can turn off
+;-COM-;; after-change hooks and add the submode region afterward.
+;-COM-
+;-COM-(define-skeleton mmm-see-inline
+;-COM-  "" nil
+;-COM-  -1 @ " " _ " " @ "%>"
+;-COM-  '(apply #'mmm-ify-region 'cperl-mode (reverse skeleton-positions)))
+;-COM-
+;-COM-(define-skeleton mmm-see-other
+;-COM-  "" nil
+;-COM-  @ ";\n" _ "\n" @ "<%/" str ">"
+;-COM-  '(apply #'mmm-ify-region 'cperl-mode (reverse skeleton-positions)))
+;-COM-
+;-COM-(make-local-hook 'after-change-functions)
+;-COM-(add-hook 'after-change-functions 'mmm-detect t)
+;-COM-
+;-COM-(defun mmm-detect (beg end length)
+;-COM-  (when (mmm-looking-back-at "<% ")
+;-COM-    (mmm-see-inline))
+;-COM-  (when (mmm-looking-back-at "<%\\(\\w+\\)>")
+;-COM-    (mmm-see-other (match-string 1))))
+;-COM-
+;;}}}
+
+(provide 'mmm-cmds)
+
+;;; mmm-cmds.el ends here
\ No newline at end of file
diff --git a/mmm-compat.el b/mmm-compat.el
new file mode 100644
index 0000000..56e329e
--- /dev/null
+++ b/mmm-compat.el
@@ -0,0 +1,159 @@
+;;; mmm-compat.el --- MMM Hacks for compatibility with other Emacsen
+
+;; Copyright (C) 2000 by Michael Abraham Shulman
+
+;; Author: Michael Abraham Shulman <address@hidden>
+;; Version: $Id: mmm-compat.el,v 1.1 2000/04/27 10:34:37 mas Exp $
+
+;;{{{ GPL
+
+;; This file 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 file 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:
+
+;; This file provides a number of hacks that are necessary for MMM
+;; Mode to function in different Emacsen. MMM Mode is designed for FSF
+;; Emacs 20, but these hacks usually enable it to work almost
+;; perfectly in FSF Emacs 19 or XEmacs.
+
+;;; Code:
+
+(require 'cl)
+
+;;{{{ Emacsen Detection
+
+(defvar mmm-xemacs (featurep 'xemacs)
+  "Whether we are running XEmacs.")
+
+;;}}}
+;;{{{ Keywords (Emacs 19)
+
+;; Emacs 19 doesn't automatically set keyword variables to themselves.
+;; We shouldn't have to do any more than these, since CL automatically
+;; defines all keywords used for function arguments.
+(defvar mmm-keywords-used
+  '(:group :regexp :region :function :insert)
+  "List of extra keywords used by MMM Mode.")
+
+(dolist (keyword mmm-keywords-used)
+  (set keyword keyword))
+
+;;}}}
+;;{{{ Customization (Emacs 19)
+
+(condition-case ()
+    (require 'custom)
+  (error nil))
+
+(unless (and (featurep 'custom)
+             (fboundp 'custom-declare-variable))
+  (defmacro defgroup (&rest args)
+    nil)
+  (defmacro defface (var values doc &rest args)
+    (` (make-face (quote (, var)))))
+  (defmacro defcustom (var value doc &rest args) 
+    (` (defvar (, var) (, value) (, doc)))))
+
+;;}}}
+;;{{{ Regexp-Opt (Emacs 19)
+
+(condition-case ()
+    (require 'regexp-opt)
+  (error nil))
+
+(unless (and (featurep 'regexp-opt)
+             (fboundp 'regexp-opt))
+  ;; No regexp-opt; create one
+  (defun regexp-opt (strings &optional paren)
+    (concat (if paren "\\(" "")
+            (mapconcat 'regexp-quote strings "\\|")
+            (if paren "\\)" ""))))
+
+;;}}}
+;;{{{ Regexp-Opt (XEmacs)
+
+(defmacro mmm-regexp-opt (strings paren)
+  "Act like FSF Emacs' `regexp-opt', whichever Emacs we're in.
+XEmacs' `regexp-opt' requires an extra parameter to do grouping."
+  (if (featurep 'xemacs)
+      `(regexp-opt ,strings ,paren t)
+    `(regexp-opt ,strings ,paren)))
+
+;;}}}
+;;{{{ Overlays (XEmacs)
+
+;; The main thing we use from FSF Emacs that XEmacs doesn't support
+;; are overlays. XEmacs uses extents instead, but comes with a package
+;; to emulate overlays.
+(when mmm-xemacs
+  ;; This does almost everything we need.
+  (require 'overlay))
+
+;; We also use a couple "special" overlay properties which have
+;; different names for XEmacs extents.
+(defvar mmm-evaporate-property
+  (if (featurep 'xemacs) 'detachable 'evaporate)
+  "The name of the overlay property controlling evaporation.")
+
+;; We don't use this any more, since its behavior is different in FSF
+;; and XEmacs: in the one it replaces the buffer's local map, but in
+;; the other it gets stacked on top of it. Instead we just set the
+;; buffer's local map temporarily.
+;;;(defvar mmm-keymap-property
+;;;  (if (featurep 'xemacs) 'keymap 'local-map)
+;;;  "The name of the overlay property controlling keymaps.")
+
+;;}}}
+;;{{{ Keymaps and Events (XEmacs)
+
+;; In XEmacs, keymaps are a primitive type, while in FSF Emacs, they
+;; are a list whose car is the symbol `keymap'. Among other things,
+;; this means that they handle default bindings differently.
+(defmacro mmm-set-keymap-default (keymap binding)
+  (if (featurep 'xemacs)
+      `(set-keymap-default-binding ,keymap ,binding)
+    `(define-key ,keymap [t] ,binding)))
+
+;; In XEmacs, events are a primitive type, while in FSF Emacs, they
+;; are represented by characters or vectors. We treat them as vectors.
+;; We can use `event-modifiers' in both Emacsen to extract the
+;; modifiers, but the function to extract the basic key is different.
+(defmacro mmm-event-key (event)
+  (if (featurep 'xemacs)
+      `(event-key ,event)
+    `(event-basic-type ,event)))
+
+;;}}}
+;;{{{ Skeleton (XEmacs)
+
+;; XEmacs' `skeleton' package doesn't provide `@' to record positions.
+(defvar skeleton-positions ())
+(defun mmm-fixup-skeleton ()
+  "Add `@' to `skeleton-further-elements' if XEmacs and not there.
+This makes `@' in skeletons act approximately like it does in FSF."
+  (and (featurep 'xemacs)
+       (defvar skeleton-further-elements ())
+       (not (assoc '@ skeleton-further-elements))
+       (add-to-list 'skeleton-further-elements
+                    '(@ ''(push (point) skeleton-positions)))))
+
+;;}}}
+
+(provide 'mmm-compat)
+
+;;; mmm-compat.el ends here
\ No newline at end of file
diff --git a/mmm-mason.el b/mmm-mason.el
new file mode 100644
index 0000000..1f76851
--- /dev/null
+++ b/mmm-mason.el
@@ -0,0 +1,181 @@
+;;; mmm-mason.el --- MMM submode class for Mason components
+
+;; Copyright (C) 2000 by Michael Abraham Shulman
+
+;; Author: Michael Abraham Shulman <address@hidden>
+;; Version: $Id: mmm-mason.el,v 1.1 2000/04/27 10:36:27 mas Exp $
+
+;;{{{ GPL
+
+;; This file 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 file 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:
+
+;; This file contains the definition of an MMM Mode submode class for
+;; editing Mason components.
+
+;;{{{ CPerl Workaround
+
+;; Note that CPerl mode tends to think it starts in the middle of an
+;; expression at the beginning of a submode region, and indents the
+;; first statement a couple extra columns. A workaround I've found is
+;; to begin all multiline Perl sections with an empty statement:
+
+;; <%perl>;
+;; print $var;
+;; </%perl>
+
+;; It isn't pretty, but it isn't ugly either, and it works better than
+;; anything else I can think of.
+
+;;}}}
+;;{{{ PSGML Workaround
+
+;; Michael Alan Dorman has reported problems with psgml-mode and
+;; Mason. He suggests adding these lines to .emacs to turn it off.
+
+;; (delete '("\\.html$" . sgml-html-mode) auto-mode-alist)
+;; (delete '("\\.shtml$" . sgml-html-mode) auto-mode-alist)
+
+;; Maybe these problems will be fixed in the future.
+
+;;}}}
+;;{{{ autohandlers and dhandlers
+
+;; Another useful thing to do with Mason is putting autohandlers and
+;; dhandlers in html-mode (and thus, assuming mmm-mode-ext-classes is
+;; set up correctly, mmm-mode/mason). One way to do this is a line
+;; such as
+
+;; %# -*- mode: html; mmm-classes: mason -*-
+
+;; at the top of each such file. Another option is to a line like this
+;; to .emacs, which will put all files named "autohandler" or
+;; "dhandler" into html-mode:
+
+;; (add-to-list 'auto-mode-alist '("\\(auto\\|d\\)handler\\'" . html-mode))
+
+;;}}}
+
+;;; Code:
+
+(require 'mmm-auto)
+
+;;{{{ Perl Tags
+
+(defvar mmm-mason-perl-mode
+  (if (fboundp 'cperl-mode) 'cperl-mode 'perl-mode)
+  "What mode to use for Perl sections in Mason files.
+Usually either `perl-mode' or `cperl-mode'. The default is
+`cperl-mode' if that is available, otherwise `perl-mode'.")
+
+(defvar mmm-mason-perl-tags
+  '("perl" "init" "cleanup" "args" "once" "filter" "perl_init"
+    "perl_cleanup" "perl_args" "perl_once" "perl_filter"
+    "attr" "flags" "shared"))
+
+(defvar mmm-mason-non-perl-tags
+  '("doc" "perl_doc" "text" "perl_text" "def" "perl_def" "method"))
+
+(defvar mmm-mason-perl-tags-regexp
+  (concat "<%" (mmm-regexp-opt mmm-mason-perl-tags t) ">")
+  "Matches tags beginning Mason sections containing Perl code.
+Saves the name of the tag matched.")
+
+(defvar mmm-mason-tag-names-regexp
+  (regexp-opt (append mmm-mason-perl-tags mmm-mason-non-perl-tags) t)
+  "Matches any Mason tag name after the \"<%\". Used to verify that a
+\"<%\" sequence starts an inline section.")
+
+(defun mmm-mason-verify-inline ()
+  (not (looking-at mmm-mason-tag-names-regexp)))
+
+;;}}}
+;;{{{ Add Classes
+
+(mmm-add-group
+ 'mason
+ `((mason-text
+    :submode nil
+    :front "<%text>"
+    :back "</%text>"
+    :insert ((?t mason-<%text> nil @ "<%text>" @ "\n"
+                 _ "\n" @ "</%text>" @)))
+   (mason-doc
+    :submode text-mode
+    :front "<%doc>"
+    :back "</%doc>"
+    :face nil
+    :insert ((?# mason-<%doc> nil @ "<%doc>" @ "\n"
+                 _ "\n" @ "</%doc>" @)
+             (?3 mason-<%doc> ?# . nil)))
+   (mason-perl
+    :submode ,mmm-mason-perl-mode
+    :front ,mmm-mason-perl-tags-regexp
+    :back "</%~1>"
+    :save-matches 1
+    :insert ((?, mason-<%TAG> "Perl section: " @ "<%" str ">" @
+                    ";\n" _ "\n" @ "</%" str ">" @)
+             (?< mason-<%TAG> ?, . nil)
+             (?p mason-<%perl> ?, . "perl")
+             (?i mason-<%init> ?, . "init")
+             (?a mason-<%args> ?, . "args")
+             (?f mason-<%flags> ?, . "flags")
+             (?r mason-<%attr> ?, . "attr")
+             (?c mason-<%cleanup> ?, . "cleanup")
+             (?o mason-<%once> ?, . "once")
+             (?l mason-<%filter> ?, . "filter")
+             (?s mason-<%shared> ?, . "shared")))
+   (mason-inline
+    :submode ,mmm-mason-perl-mode
+    :front "<%"
+    :front-verify mmm-mason-verify-inline
+    :back "%>"
+    :insert ((?% mason-<%-%> nil @ "<%" @ " " _ " " @ "%>" @)
+             (?5 mason-<%-%> ?% . nil)))
+   (mason-call
+    :submode ,mmm-mason-perl-mode
+    :front "<&"
+    :back "&>"
+    :insert ((?& mason-<&-&> nil @ "<&" @ " " _ " " @ "&>" @)
+             (?7 mason-<&-&> ?% . nil)))
+   (mason-one-line
+    :submode ,mmm-mason-perl-mode
+    :front "^%"
+    :back "\n"
+    :insert ((return mason-%-line nil (mmm-mason-start-line)
+                     @ "%" @ " " _ @ '(mmm-mason-end-line) "\n" @)))))
+
+;;}}}
+;;{{{ One-line Sections
+
+(defun mmm-mason-start-line ()
+  (if (= (point)
+         (save-excursion (beginning-of-line) (point)))
+      ""
+    "\n"))
+
+(defun mmm-mason-end-line ()
+  (if (= (char-after (point)) (string-to-char "\n"))
+      (delete-char 1)))
+
+;;}}}
+
+(provide 'mmm-mason)
+
+;;; mmm-mason.el ends here
\ No newline at end of file
diff --git a/mmm-mode.el b/mmm-mode.el
new file mode 100644
index 0000000..7f42f95
--- /dev/null
+++ b/mmm-mode.el
@@ -0,0 +1,457 @@
+;;; mmm-mode.el --- Allow Multiple Major Modes in a buffer
+
+;; Copyright (C) 1999 by Michael Abraham Shulman
+
+;; Author: Michael Abraham Shulman <address@hidden>
+;; Version: $Id: mmm-mode.el,v 1.1 2000/04/27 10:31:44 mas Exp $
+;; Keywords: convenience faces languages tools
+
+;;{{{ GPL
+
+;; This file 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 file 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:
+
+;;; MMM Mode is a minor mode that allows multiple major modes to
+;;; coexist in a single buffer. Refer to the documentation of the
+;;; function `mmm-mode' for more detailed information. This file
+;;; contains mode on/off functions and the mode keymap, but mostly
+;;; just loads all the subsidiary files.
+
+;;{{{ Parameter Naming
+
+;;; Since version 0.3.7, I've tried to use a uniform scheme for naming
+;;; parameters. Here's a brief summary.
+
+;;; BEG and END refer to the beginning and end of a region.
+;;; FRONT and BACK refer to the respective delimiters of a region.
+;;; FRONT- and BACK-OFFSET are the offsets from delimiter matches.
+;;; FRONT-BEG through BACK-END are the endings of the delimiters.
+;;; START and STOP bound actions, like searching, fontification, etc.
+
+;;}}}
+;;{{{ CL and Parameters
+
+;;; Keyword parameters can be nice because it makes it easier to see
+;;; what's getting passed as what. But I try not to use them in user
+;;; functions, because CL doesn't make good documentation strings.
+;;; Similarly, any hook or callback function can't take keywords,
+;;; since Emacs as a whole doesn't use them. And for small parameter
+;;; lists, they are overkill. So I use them only for a large number of
+;;; optional parameters, such as `mmm-make-region'.
+
+;;; An exception is the various submode class application functions,
+;;; which all take all their arguments as keywords, for consistency
+;;; and so the classes alist looks nice.
+
+;;; When using keyword arguments, defaults should *always* be supplied
+;;; in all arglists. (This pertains mostly to :start and :stop
+;;; arguments, usually defaulting to (point-min) and (point-max)
+;;; respectively.) `mmm-save-keywords' should only be used for lists
+;;; with more than four arguments, such as in `mmm-ify-by-regexp'.
+
+;;; In general, while I have no qualms about using things from CL like
+;;; `mapl', `loop' and `destructuring-bind', I try not to use `defun*'
+;;; more than I have to. For one, it sometimes makes bad documentation
+;;; strings. Furthermore, to a `defun'ned function, a nil argument is
+;;; the same as no argument, so it will use its (manual) default, but
+;;; to a `defun*'ned function, a nil argument *is* the argument, so
+;;; any default specified in the arglist will be ignored. Confusion of
+;;; this type should be avoided when at all possible.
+
+;;; By the way, in Elisp CL, there is no reason to use `mapc' over
+;;; `mapcar' unless you need keyword parameters, in which case you
+;;; might as well use `mapcar*'. `mapcar' is an Elisp primitive, so
+;;; it's fast, and `mapc' uses it internally anyway.
+
+;;}}}
+
+;;; Code:
+
+;;{{{ Load Everything Else
+
+(require 'cl)
+;; If we don't load font-lock now, but it is loaded later, the
+;; necessary mmm-font-lock- properties may not be there.
+(require 'font-lock)
+(require 'mmm-compat)
+(require 'mmm-utils)
+(require 'mmm-vars)
+(require 'mmm-auto)
+(require 'mmm-region)
+(require 'mmm-class)
+;;; This file is set up to autoload by `mmm-auto.el'.
+;;(require 'mmm-cmds)
+
+;;}}}
+;;{{{ Toggle Function
+
+(defvar mmm-mode nil
+  "Non-nil means MMM Mode is turned on in this buffer.
+Do not set this variable directly; use the function `mmm-mode'.")
+(make-variable-buffer-local 'mmm-mode)
+
+(defun mmm-mode (&optional arg)
+  "Minor mode to allow multiple major modes in one buffer.
+Without ARG, toggle MMM Mode. With ARG, turn MMM Mode on iff ARG is
+positive and off otherwise.
+
+Commands Available:
+\\<mmm-mode-map>
+\\{mmm-mode-map}
+
+BASIC CONCEPTS
+
+The idea of MMM Mode is to allow multiple major modes to coexist in
+the same buffer. There is one \"dominant\" or \"default\" major mode
+that controls most of the buffer, and a number of \"submodes\" that
+each hold sway over certain regions. While the point is in a submode
+region, the following changes occur:
+
+1. The local keymap is that of the submode.
+2. The mode line changes to show what submode region is active.
+3. The major mode menu and popup are that of the submode.
+4. Some local variables of the submode shadow the default mode's.
+5. The syntax table and indentation are those of the submode.
+6. Font-lock fontifies correctly for the submode.
+7. The submode regions are highlighted by a background color.
+
+These changes are accomplished by adding Emacs Lisp objects called
+\"overlays\" to the buffer to mark the submode regions, and adding a
+`post-command-hook' to update the submode changes that Emacs won't do
+automatically. There are two ways to create the submode regions:
+interactively and automatically. Creating submode regions is referred
+to as \"mmm-ification.\"
+
+
+THE MMM MINOR MODE
+
+The MMM Minor Mode must be on in a buffer for submode regions to be
+effective. Fortunately, it is automagically turned on by any
+mmm-ification, interactive or automatic. When activated, it is denoted
+by \"MMM\" in the mode line. You can also turn it on manually with the
+function `mmm-mode', in which case it mmm-ifies the buffer
+automatically. Do not set the variable `mmm-mode' directly. Turning
+MMM Mode off automatically removes all submode regions from the
+buffer.
+
+MMM Mode has its own keymap, which is bound by default to the prefix
+key \"\\C-c%\". This is a good mnemonic for me since I use MMM Mode to
+edit HTML files with embedded languages such as HTML::Mason, which
+uses the character \"%\" to introduce server-side code. You can
+customize this with the variable `mmm-prefix-key'. When MMM Mode is
+activated, many of the functions discussed below have keyboard
+equivalents, given in parentheses after their name.
+
+
+GETTING STARTED
+
+There are six sample submode classes that come with MMM Mode: Embedded
+CSS in HTML \(requires `css-mode'), Embedded Javascript in HTML
+\(requires `javascript-mode'), HTML in Perl here-documents, the
+HTML::Mason syntax for server-side Perl in HTML, Emacs Lisp in
+\"eval\" file variables, and HTML in PL/SQL \(helpful to have some
+PL/SQL mode).
+
+If one of these is what you need, then all that's necessary is to put
+a line containing \"-*- mmm-classes: CLASS -*-\" at the top of each
+file you want to use MMM Mode in, where CLASS is one of embedded-css,
+javascript, html-here, mason, eval-elisp, or htp-p. After this edit
+you can type M-x normal-mode \(in order to re-parse the file
+variables) and then M-x mmm-mode to activate the appropriate submode
+regions \(assuming MMM Mode is loaded).
+
+I suggest reading my comments on whatever classes you are using. These
+can be found in the file \"mmm-mode\" at the bottom in the appropriate
+section. Hopefully in the future, these will become doc-strings.
+
+If you want to use more than one class in a file, simply set
+`mmm-classes' to a list of symbols rather than a single symbol. If you
+want MMM Mode to be activated automatically whenever you find a file
+with `mmm-classes' set, call `mmm-add-find-file-hook' in your Emacs
+initialization file. \(See \"Loading MMM Mode \", below)
+
+If you want to use one of these submode classes in all buffers with a
+certain major mode or file extension, call `mmm-add-mode-ext-class' in
+your Emacs initialization file. For example, if you want all files
+with the extension .mason to be in html-mode with the MMM class mason
+activated, try this:
+
+\(add-to-list 'auto-mode-alist '(\"\\\\.mason\\\\'\" . html-mode))
+\(mmm-add-mode-ext-class 'html-mode \"\\\\.mason\\\\'\" 'mason)
+
+If none of the supplied classes is what you need, you'll have to write
+your own. Reading through the documentation and looking at the
+supplied classes should help you. You may want to try interactive
+mmm-ification until your regexps or functions are perfected. If your
+class works well and you think others might find it useful, send it to
+me and maybe I'll include it in the next release.
+
+
+INTERACTIVE MMM-IFICATION
+
+There are four functions that create regions interactively:
+`mmm-ify-region' \(\\[mmm-ify-region]), `mmm-ify-by-regexp' 
\(\\[mmm-ify-by-regexp]),
+`mmm-ify-by-function' \(\\[mmm-ify-by-function]), and `mmm-ify-by-class' 
\(\\[mmm-ify-by-class]).
+The first adds a region between point and mark. The second adds
+regions throughout the file delimited by regexps. The third adds
+regions as computed by a user-defined function. The fourth adds
+regions as appropriate for a submode class. For more info, see the
+documentation for these functions.
+
+
+AUTOMATIC MMM-IFICATION
+
+Automatic mmm-ification is done by means of \"submode classes.\" A
+submode class is a set of submodes along with methods of adding
+regions for them. These methods can be either a set of regexps
+analogous to the arguments of `mmm-ify-by-regexp', a function which
+could be passed to `mmm-ify-by-function', or another submode class to
+invoke. Whenever automatic mmm-ification takes place \(see below for
+when this occurs), three things happen:
+
+1. All existing submode regions are removed.
+2. All recent interactive mmm-ification is reapplied.
+3. The buffer-local variables `mmm-classes' and `mmm-mode-ext-classes'
+   are inspected for classes to mmm-ify the buffer with.
+
+Each class found in the third step is looked up in `mmm-classes-alist'
+to find its associated submode(s), method(s), and face(s), and
+appropriate submode regions are added. To create a class, simply add
+an element to `mmm-classes-alist'. See the documentation for that
+variable for the correct format of elements. The variable
+`mmm-classes' is suitable for setting in a file variables list.
+
+Automatic mmm-ification is done by the functions `mmm-parse-buffer'
+\(\\[mmm-parse-buffer]) and `mmm-parse-region'. These functions can be called
+interactively, and the first has a default key binding. The function
+`mmm-ify-by-all' sets `mmm-mode-ext-classes' appropriately for the
+current buffer by looking in `mmm-mode-ext-classes-alist'. The
+function `mmm-add-find-file-hook' adds `mmm-ify-by-all' to
+`find-file-hooks' for which it is well suited.
+
+
+LOADING MMM MODE
+
+Suggested lines for a .emacs file are:
+
+\(require 'mmm-mode)
+\(mmm-add-find-file-hook)
+
+Autoloading MMM Mode is not particularly useful if you want Automatic
+MMM-ification by classes to occur whenever you find a file which has
+the local variable `mmm-classes' set or a mode/extension in
+`mmm-mode-ext-classes-alist', since MMM Mode would have to be loaded
+as soon as you find a file. But if you only activate MMM Mode
+interactively, you can autoload it as follows:
+
+\(autoload 'mmm-mode \"mmm-mode\" \"Multiple Major Modes\" t)
+\(autoload 'mmm-parse-buffer \"mmm-mode\" \"Automatic MMM-ification\" t)
+
+and similar lines for any other functions you want to call directly.
+
+
+MISCELLANY
+
+After you type a new region that should be a submode, you can run the
+function `mmm-parse-block' \(\\[mmm-parse-block]) to detect it with automatic
+mmm-ification.
+
+The function `mmm-clear-overlays' \(\\[mmm-clear-overlays]) removes all 
submode regions
+in the current buffer, without turning off MMM Mode. It clears the
+history of interactive mmm-ification, but does not change the value of
+`mmm-classes'.
+
+
+CUSTOMIZATION
+
+Besides those already discussed, there are a number of variables that
+can be used to customize MMM Mode. The appearance can be customized
+with the variables `mmm-default-submode-face', `mmm-mode-string', and
+`mmm-submode-mode-line-format', which see for further information.
+
+The variable `mmm-save-local-variables' controls what buffer-local
+variables are saved for submodes. This is how comments are handled,
+for instance. You can add variable names to this list. Often something
+that seems like a problem with MMM Mode can be solved by simply saving
+an extra variable.
+
+When entering MMM Mode, the hook `mmm-mode-hook' is run. A hook named
+<major-mode>-mmm-hook is also run, if it exists. For example,
+`html-mode-mmm-hook' is run whenever MMM Mode is entered in HTML mode.
+
+Furhermore, a hook named <submode>-submode-hook is run whenever a
+submode region of a given mode is created. For example,
+`cperl-mode-submode-hook' is run whenever a CPerl mode submode region
+is created, in any buffer. When submode hooks are run, point is
+guaranteed to be at the start of the newly created submode region.
+
+All these, and some others, can be reached through M-x customize under
+Programming | Tools | Mmm, except the major mode and submode hooks
+\(obviously)."
+  (interactive "P")
+  (if (if arg (> (prefix-numeric-value arg) 0) (not mmm-mode))
+      (mmm-mode-on)
+    (mmm-mode-off)))
+
+;;}}}
+;;{{{ Mode On
+
+(defun mmm-mode-on ()
+  "Turn on MMM Mode. See `mmm-mode'."
+  (interactive)
+  ;; This function is called from mode hooks, so we need to make sure
+  ;; we're not in a temporary buffer. We don't need to worry about
+  ;; recursively ending up in ourself, however, since by that time the
+  ;; variable `mmm-mode' will already be set.
+  (mmm-valid-buffer
+   (unless mmm-mode
+     (mmm-update-mode-info major-mode)
+     (mmm-add-hooks)
+     (mmm-fixup-skeleton)
+     (when (featurep 'font-lock)
+       (make-local-variable 'font-lock-fontify-region-function)
+       (make-local-variable 'font-lock-beginning-of-syntax-function)
+       (setq font-lock-fontify-region-function 'mmm-fontify-region
+             font-lock-beginning-of-syntax-function 'mmm-beginning-of-syntax))
+     (setq mmm-mode t)
+     (condition-case err
+         (mmm-apply-all)
+       (mmm-invalid-submode-class
+        ;; Complain, but don't die, since we want files to go ahead
+        ;; and be opened anyway, and the mode to go ahead and be
+        ;; turned on. Should we delete all previously made submode
+        ;; regions when we find an invalid one?
+        (message "%s" (error-message-string err))))
+     (run-hooks 'mmm-mode-hook)
+     (mmm-run-major-hook))))
+
+;;}}}
+;;{{{ Mode Off
+
+(defun mmm-mode-off ()
+  "Turn off MMM Mode. See `mmm-mode'."
+  (interactive)
+  (when mmm-mode
+    (mmm-remove-hooks)
+    (mmm-clear-overlays)
+    (mmm-clear-history)
+    (mmm-clear-mode-ext-classes)
+    (mmm-update-submode-region)
+    (when (boundp 'font-lock-mode)
+      (setq font-lock-fontify-region-function
+            (get major-mode 'mmm-fontify-region-function)
+            font-lock-beginning-of-syntax-function
+            (get major-mode 'mmm-beginning-of-syntax-function))
+      (if font-lock-mode (font-lock-fontify-buffer)))
+    (setq mmm-mode nil)))
+
+(add-to-list 'minor-mode-alist (list 'mmm-mode mmm-mode-string))
+
+;;}}}
+;;{{{ Mode Keymap
+
+(defvar mmm-mode-map (make-sparse-keymap)
+  "Keymap for MMM Minor Mode.")
+
+(defvar mmm-mode-prefix-map (make-sparse-keymap)
+  "Keymap for MMM Minor Mode after `mmm-mode-prefix-key'.")
+
+(defvar mmm-mode-menu-map (make-sparse-keymap "MMM")
+  "Keymap for MMM Minor Mode menu.")
+
+(defun mmm-define-key (key binding)
+  (define-key mmm-mode-prefix-map
+    (vector (append mmm-command-modifiers (list key)))
+    binding))
+
+(when mmm-use-old-command-keys
+  (mmm-use-old-command-keys))
+
+(mmm-define-key ?c 'mmm-ify-by-class)
+(mmm-define-key ?x 'mmm-ify-by-regexp)
+(mmm-define-key ?r 'mmm-ify-region)
+
+(mmm-define-key ?b 'mmm-parse-buffer)
+(mmm-define-key ?g 'mmm-parse-region)
+(mmm-define-key ?% 'mmm-parse-block)
+(mmm-define-key ?5 'mmm-parse-block)
+
+(mmm-define-key ?k 'mmm-clear-current-region)
+(mmm-define-key ?\  'mmm-reparse-current-region)
+(mmm-define-key ?e 'mmm-end-current-region)
+
+;; This one is exact, since C-h is (usually) already used for help.
+(define-key mmm-mode-prefix-map [?h] 'mmm-insertion-help)
+
+;; Default bindings to do insertion (dynamic)
+(mmm-set-keymap-default mmm-mode-prefix-map 'mmm-insert-region)
+
+;; Set up the prefix help command, since otherwise the default binding
+;; overrides it.
+(define-key mmm-mode-prefix-map (vector help-char) prefix-help-command)
+
+;; And put it all onto the prefix key
+(define-key mmm-mode-map mmm-mode-prefix-key mmm-mode-prefix-map)
+
+;; Order matters for the menu bar.
+(define-key mmm-mode-menu-map [off]
+  '("MMM Mode Off" . mmm-mode-off))
+(define-key mmm-mode-menu-map [sep0] '(menu-item "----"))
+
+(define-key mmm-mode-menu-map [clhist]
+  '("Clear History" . mmm-clear-history))
+(define-key mmm-mode-menu-map [end]
+  '("End Current" . mmm-end-current-region))
+(define-key mmm-mode-menu-map [clear]
+  '("Clear Current" . mmm-clear-current-region))
+(define-key mmm-mode-menu-map [reparse]
+  '("Reparse Current" . mmm-reparse-current-region))
+
+(define-key mmm-mode-menu-map [sep10] '(menu-item "----"))
+
+(define-key mmm-mode-menu-map [ins-help]
+  '("List Insertion Keys" . mmm-insertion-help))
+
+(define-key mmm-mode-menu-map [sep20] '(menu-item "----"))
+
+(define-key mmm-mode-menu-map [region]
+  '(menu-item "MMM-ify Region" mmm-ify-region :enable mark-active))
+(define-key mmm-mode-menu-map [regexp]
+  '("MMM-ify by Regexp" . mmm-ify-by-regexp))
+(define-key mmm-mode-menu-map [class]
+  '("Apply Submode Class" . mmm-ify-by-class))
+
+(define-key mmm-mode-menu-map [sep30] '(menu-item "----"))
+
+(define-key mmm-mode-menu-map [parse-region]
+  '(menu-item "Parse Region" mmm-parse-region :enable mark-active))
+(define-key mmm-mode-menu-map [parse-buffer]
+  '("Parse Buffer" . mmm-parse-buffer))
+(define-key mmm-mode-menu-map [parse-block]
+  '("Parse Block" . mmm-parse-block))
+
+(define-key mmm-mode-map [menu-bar mmm] (cons "MMM" mmm-mode-menu-map))
+
+(add-to-list 'minor-mode-map-alist (cons 'mmm-mode mmm-mode-map))
+
+;;}}}
+
+(provide 'mmm-mode)
+
+;;; mmm-mode.el ends here
\ No newline at end of file
diff --git a/mmm-region.el b/mmm-region.el
new file mode 100644
index 0000000..4a7bfe4
--- /dev/null
+++ b/mmm-region.el
@@ -0,0 +1,453 @@
+;;; mmm-region.el --- Manipulating and behavior of MMM submode regions
+
+;; Copyright (C) 2000 by Michael Abraham Shulman
+
+;; Author: Michael Abraham Shulman <address@hidden>
+;; Version: $Id: mmm-region.el,v 1.1 2000/04/27 10:35:40 mas Exp $
+
+;;{{{ GPL
+
+;; This file 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 file 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:
+
+;; This file provides the functions and variables to create, delete,
+;; and inspect submode regions, as well as functions that make them
+;; behave like the submode with respect to syntax tables, local maps,
+;; font lock, etc.
+
+;;; Code:
+
+(require 'cl)
+(require 'mmm-compat)
+(require 'mmm-utils)
+(require 'mmm-vars)
+
+;; CREATION & DELETION
+;;{{{ Markers
+
+(defun mmm-make-marker (pos beg-p sticky-p)
+  "Make a marker at POS that is or isn't sticky.
+BEG-P represents whether the marker delimits the beginning of a
+region \(or the end of it). STICKY-P is whether it should be sticky,
+i.e. whether text inserted at the marker should be inside the region."
+  (let ((mkr (set-marker (make-marker) pos)))
+    (set-marker-insertion-type mkr (if beg-p (not sticky-p) sticky-p))
+    mkr))
+
+;;}}}
+;;{{{ Make Submode Regions
+
+(defun* mmm-make-region
+    (submode beg end &rest rest &key (front "") (back "")
+             (beg-sticky t) (end-sticky t) face
+             &allow-other-keys)
+  "Make a submode region from BEG to END of SUBMODE in FACE.
+FACE defaults to `mmm-default-submode-face'. FRONT and BACK are
+regexps or functions to match the correct delimiters--see
+`mmm-match-front' and `mmm-match-back'. BEG-STICKY and END-STICKY
+determine whether the front and back of the region, respectively, are
+sticky with respect to new insertion. All other keyword arguments are
+stored as properties of the overlay, un-keyword-ified."
+  (mmm-mode-on)
+  ;; For now, complain about overlapping regions. Most callers should
+  ;; trap this and continue on. In future, submode regions will be
+  ;; allowed to sit inside others.
+  (when (mmm-overlays-in beg end)
+    (signal 'mmm-invalid-parent nil))
+  (when submode
+    (mmm-update-mode-info submode))
+  ;; Conditionally sticky overlays are by default sticky. Then the
+  ;; insert-in-front and -behind functions fix them.
+  (let ((ovl (make-overlay beg end nil (not beg-sticky) end-sticky)))
+    ;; This loop covers front, back, beg-sticky, end-sticky, and
+    ;; anything else the caller wants to put on the overlay. It also
+    ;; does face, but we re-do that later because we want to
+    ;; defaultify it.
+    (loop for (var val) on rest by #'cddr
+          do (overlay-put ovl (intern (substring (symbol-name var) 1)) val))
+    (mapcar #'(lambda (pair) (overlay-put ovl (car pair) (cadr pair)))
+            `((mmm t)           ; Mark our overlays
+              (mmm-mode ,submode)
+              ;; These have special meaning to Emacs
+              (,mmm-evaporate-property t)
+              (face ,(or face (if submode 'mmm-default-submode-face)))
+              ))
+    (when submode
+      (save-excursion
+        (goto-char (overlay-start ovl))
+        (mmm-run-submode-hook submode)))
+    (mmm-update-current-submode)
+    ovl))
+
+;;}}}
+;;{{{ Clear Overlays
+
+;; See also `mmm-clear-current-region'.
+
+(defun mmm-clear-overlays (&optional start stop strict)
+  "Clears all MMM overlays between START and STOP.
+If STRICT, only clear those strictly included, rather than partially."
+  (mapcar #'delete-overlay
+        (mmm-overlays-in (or start (point-min))
+                         (or stop (point-max))
+                         strict))
+  (mmm-update-current-submode))
+
+;;}}}
+
+;; INSPECTION
+;;{{{ Current Overlays
+
+;; Emacs counts an overlay starting at POS as "at" POS, but not an
+;; overlay ending at POS. XEmacs is more sensible and uses beg- and
+;; end-stickiness to determine whether an endpoint is within an
+;; extent. Here we want to act like XEmacs does.
+
+(defun mmm-overlay-at (&optional pos type)
+  "Return the highest-priority MMM Mode overlay at POS.
+TYPE is passed on to `mmm-overlays-at', which see."
+  (car (mmm-overlays-at (or pos (point)) type)))
+
+(defun mmm-overlays-at (&optional pos type)
+  "Return a list of the MMM overlays at POS, in decreasing priority.
+TYPE should be nil, `beg', `end', `none', or `all'. If `none', return
+only overlays strictly including POS. If nil, return overlays starting
+at POS only if they are beg-sticky, and those ending at POS only if
+they are end-sticky. If `beg', return all overlays starting at POS but
+none ending at POS, if `end', return all overlays ending at POS
+but none starting at POS, and if `all', return both."
+  (or pos (setq pos (point)))
+  (remove-if-not #'(lambda (ovl)
+                     (mmm-included-p ovl pos type))
+                 (mmm-overlays-in (1- pos) (1+ pos))))
+
+(defun mmm-included-p (ovl pos type)
+  (cond ((eql (overlay-start ovl) pos)
+         (case type
+           ((none end) nil)
+           ((nil) (overlay-get ovl 'beg-sticky))
+           ((beg all) t)))
+        ((eql (overlay-end ovl) pos)
+         (case type
+           ((none beg) nil)
+           ((nil) (overlay-get ovl 'end-sticky))
+           ((end all) t)))
+        (t t)))
+
+(defun mmm-overlays-in (start stop &optional strict)
+  "Return the MMM overlays in START to STOP, in decreasing priority.
+If STRICT is non-nil, include only those overlays which are entirely
+contained in the region, including their delimiters \(if any)."
+  (mmm-sort-overlays
+   (remove-if-not #'(lambda (ovl)
+                      (and (overlay-get ovl 'mmm)
+                           (or (not strict)
+                               (>= stop (mmm-back-end ovl))
+                               (<= start (mmm-front-start ovl)))))
+                  (overlays-in start stop))))
+
+(defun mmm-sort-overlays (overlays)
+  "Sort OVERLAYS in order of decreasing priority."
+  (sort (copy-list overlays)
+        #'(lambda (x y) (> (or (overlay-get x 'priority) 0)
+                           (or (overlay-get y 'priority) 0)))))
+
+;;}}}
+;;{{{ Current Submode
+
+(defvar mmm-current-submode nil
+  "What submode we think we are currently in.
+May be out of date; call `mmm-update-current-submode' to correct it.")
+(make-variable-buffer-local 'mmm-current-submode)
+
+(defun mmm-update-current-submode (&optional pos)
+  "Set the `mmm-current-submode' to the `mmm-submode-at' POS. 
+Return non-nil iff the value changed."
+  (not (eq (prog1 mmm-current-submode
+            (setq mmm-current-submode
+                   (mmm-submode-at (or pos (point)))))
+          mmm-current-submode)))
+
+(defun mmm-submode-at (&optional pos type)
+  "Return the submode at POS \(or point), or NIL if none.
+TYPE is passed on to `mmm-overlays-at', which see."
+  (let ((ovl (mmm-overlay-at (or pos (point)) type)))
+    (if ovl (overlay-get ovl 'mmm-mode))))
+
+;;}}}
+;;{{{ Match Front & Back
+
+(defun mmm-match-front (ovl)
+  "Return non-nil if the front delimiter of OVL matches as it should.
+Sets the match data to the front delimiter, if it is a regexp,
+otherwise calls it as a function with point at the beginning of the
+overlay and one argument being the overlay. The function should return
+non-nil if the front delimiter matches correctly, and set the match
+data appropriately."
+  (let ((front (overlay-get ovl 'front)))
+    (save-excursion
+      (goto-char (overlay-start ovl))
+      (if (stringp front)
+          ;; It's a regexp
+          (mmm-looking-back-at front)
+        ;; It's a function
+        (funcall front ovl)))))
+
+(defun mmm-match-back (ovl)
+  "Return non-nil if the back delimiter of OVL matches as it should.
+Sets the match data to the back delimiter, if it is a regexp,
+otherwise calls it as a function with point at the end of the overlay
+and one argument being the overlay. The function should return non-nil
+if the back delimiter matches correctly, and set the match data
+appropriately."
+  (let ((back (overlay-get ovl 'back)))
+    (save-excursion
+      (goto-char (overlay-end ovl))
+      (if (stringp back)
+          ;; It's a regexp
+          (looking-at back)
+        (funcall back ovl)))))
+
+;;}}}
+;;{{{ Delimiter Boundaries
+
+(defun mmm-front-start (ovl)
+  "Return the position at which the front delimiter of OVL starts.
+If OVL is not front-bounded correctly, return its start position."
+  (save-match-data
+    (if (mmm-match-front ovl)
+        (match-beginning 0)
+      (overlay-start ovl))))
+
+(defun mmm-back-end (ovl)
+  "Return the position at which the back delimiter of OVL ends.
+If OVL is not back-bounded correctly, return its end position."
+  (save-match-data
+    (if (mmm-match-back ovl)
+        (match-end 0)
+      (overlay-end ovl))))
+
+;;}}}
+
+;; BASIC UPDATING
+;;{{{ Submode Info
+
+(defun mmm-update-mode-info (mode)
+  "Make sure the `mmm-*' properties of MODE are present.
+These properties are used to store the required information about the
+mode for it to be a submode or a major mode with submodes."
+  (unless (get mode 'mmm-mode-name)
+    (save-excursion
+      (set-buffer (get-buffer-create "*mmm-temp*"))
+      (funcall mode)
+      (when (featurep 'font-lock)
+        ;; XEmacs doesn't have global-font-lock-mode (or rather, it
+        ;; has nothing but global-font-lock-mode).
+        (unless mmm-xemacs (turn-on-font-lock-if-enabled))
+        ;; Ensure font-lock-variables are present, and get them.
+        (font-lock-set-defaults)
+        (loop for (prop value) in (mmm-get-font-lock-properties)
+              do (put mode prop value)))
+      ;; Get non-font-lock information
+      (loop for (prop value) in (mmm-get-mode-properties)
+            do (put mode prop value))
+      (kill-buffer (current-buffer)))))
+
+(defun mmm-get-mode-properties ()
+  `((mmm-syntax-table ,(syntax-table))
+    (mmm-local-map ,(current-local-map))
+    (mmm-local-variables ,(mmm-get-local-variables))
+    (mmm-mode-name ,mode-name)))
+(defun mmm-get-font-lock-properties ()
+  `((mmm-fontify-region-function ,font-lock-fontify-region-function)
+    (mmm-beginning-of-syntax-function ,font-lock-beginning-of-syntax-function)
+    (mmm-font-lock-mode ,font-lock-mode)))
+
+;;}}}
+;;{{{ Updating Hooks
+
+(defun mmm-update-submode-region ()
+  "Update all MMM properties correctly for the current position.
+This function does the actual work of setting the different local
+maps, syntax tables, etc. for submodes."
+  ;; This next line is necessary because some derived modes can fool
+  ;; MMM Mode into thinking they're really the parent mode. For
+  ;; example, texinfo-mode looks like text-mode to the major mode
+  ;; hook, and hence doesn't get its properties updated.
+  (mmm-update-mode-info major-mode)
+  (when (mmm-update-current-submode)
+    (if mmm-current-submode
+       (setq mode-name
+             (mmm-format-string mmm-submode-mode-line-format
+                `(("~M" . ,(get major-mode 'mmm-mode-name))
+                  ("~m" . ,(get mmm-current-submode 'mmm-mode-name)))))
+      (setq mode-name (get major-mode 'mmm-mode-name)))
+    (mmm-update-for-mode (or mmm-current-submode major-mode) t)))
+
+(defun mmm-update-for-mode (mode &optional fontify)
+  (mmm-update-mode-info mode)
+  (set-syntax-table (get mode 'mmm-syntax-table))
+  (mmm-real-use-local-map (or (cdr (assoc mode mmm-local-maps-alist))
+                              (get mode 'mmm-local-map)))
+  (mmm-set-local-variables mode)
+  (and (featurep 'font-lock)
+       fontify
+       (get mode 'mmm-font-lock-mode)
+       (font-lock-mode 1)))
+
+(defun mmm-add-hooks ()
+  (make-local-hook 'change-major-mode-hook)
+  (add-hook 'change-major-mode-hook 'mmm-mode-off nil 'local)
+  (make-local-hook 'post-command-hook)
+  (add-hook 'post-command-hook 'mmm-update-submode-region nil 'local))
+
+(defun mmm-remove-hooks ()
+  (remove-hook 'change-major-mode-hook 'mmm-mode-off 'local)
+  (remove-hook 'post-command-hook 'mmm-update-submode-region 'local))
+
+;;}}}
+;;{{{ Local Variables
+
+(defun mmm-set-local-variables (mode)
+  "Set the local variables saved for MODE."
+  (mapcar #'(lambda (var)
+              (make-local-variable (car var))
+              (set (car var) (cadr var)))
+          (get mode 'mmm-local-variables)))
+
+(defun mmm-get-local-variables ()
+  "Get the local variables to save from this buffer."
+  (mapcar #'(lambda (var)
+             (list var (and (boundp var)
+                            (symbol-value var))))
+         mmm-save-local-variables))
+
+;;}}}
+;;{{{ Local Maps
+
+;; This is for the benefit of commands such as `vm-mail', which calls
+;; `mail-mode' but then changes the local map afterwards. It's kludgy,
+;; I know, but at the moment I don't have time to think of a neater
+;; solution.
+
+(defvar mmm-local-maps-alist ()
+  "Which local maps have been changed in this buffer.")
+(make-variable-buffer-local 'mmm-local-maps-alist)
+
+;; Save the real function away for our use.
+(fset 'mmm-real-use-local-map (symbol-function 'use-local-map))
+
+(defadvice use-local-map (after mmm-keep-record activate compile)
+  "Keep track of which local maps have been changed in which buffers."
+  (mmm-valid-buffer
+   (mmm-update-current-submode)
+   (let* ((mode (or mmm-current-submode major-mode))
+          (map (assq mode mmm-local-maps-alist)))
+     (if map
+         (setcdr map (current-local-map))
+       (push (cons mode (current-local-map)) mmm-local-maps-alist)))))
+
+;;}}}
+
+;; FONT LOCK
+;;{{{ Get Submode Regions
+
+(defun mmm-submode-changes-in (start stop)
+  "Return a list of all submode-change positions from START to STOP.
+The list is sorted in order of increasing buffer position."
+  (sort (remove-duplicates
+         (list* start stop
+                (mapcan #'(lambda (ovl)
+                            `(,(overlay-start ovl)
+                              ,(overlay-end ovl)))
+                        (mmm-overlays-in start stop))))
+        #'<))
+
+(defun mmm-regions-in (start stop)
+  "Return a list of regions of the form (MODE BEG END) whose disjoint
+union covers the region from START to STOP."
+  (let ((regions 
+         (maplist #'(lambda (pos-list)
+                      (if (cdr pos-list)
+                          (list (or (mmm-submode-at (car pos-list) 'beg)
+                                    major-mode)
+                                (car pos-list) (cadr pos-list))))
+                  (mmm-submode-changes-in start stop))))
+    (setcdr (last regions 2) nil)
+    regions))
+
+
+(defun mmm-regions-alist (start stop)
+  "Return a list of lists of the form \(MODE . REGIONS) where REGIONS
+is a list of elements of the form \(BEG END). The disjoint union all
+of the REGIONS covers START to STOP."
+  (let ((regions (mmm-regions-in start stop)))
+    (mapcar #'(lambda (mode)
+                (cons mode
+                      (mapcan #'(lambda (region)
+                                  (if (eq mode (car region))
+                                      (list (cdr region))))
+                              regions)))
+            ;; All the modes
+            (remove-duplicates (mapcar #'car regions)))))
+
+;;}}}
+;;{{{ Fontify Regions
+
+(defun mmm-fontify-region (start stop &optional loudly)
+  "Fontify from START to STOP keeping track of submodes correctly."
+  (when loudly
+    (message "Fontifying %s with submode regions..." (buffer-name)))
+  ;; For some reason `font-lock-fontify-block' binds this to nil, thus
+  ;; preventing `mmm-beginning-of-syntax' from doing The Right Thing.
+  ;; I don't know why it does this, but let's undo it here.
+  (let ((font-lock-beginning-of-syntax-function 'mmm-beginning-of-syntax))
+    (mapcar #'(lambda (elt)
+                (when (get (car elt) 'mmm-font-lock-mode)
+                  (mmm-fontify-region-list (car elt) (cdr elt))))
+            (mmm-regions-alist start stop)))
+  (mmm-update-for-mode (or mmm-current-submode major-mode))
+  (when loudly (message nil)))
+
+(defun mmm-fontify-region-list (mode regions)
+  "Fontify REGIONS, each like \(BEG END), in mode MODE."
+  (save-excursion
+    (let ((major-mode mode)
+          (func (get mode 'mmm-fontify-region-function)))
+      (mmm-update-for-mode major-mode)
+      (mapcar #'(lambda (reg)
+                  (funcall func (car reg) (cadr reg) nil))
+              regions))))
+;;}}}
+;;{{{ Beginning of Syntax
+
+(defun mmm-beginning-of-syntax ()
+  (goto-char
+   (let ((ovl (mmm-overlay-at (point)))
+         (func (get (or mmm-current-submode major-mode)
+                    'mmm-beginning-of-syntax-function)))
+     (max (if ovl (overlay-start ovl) (point-min))
+          (if func (progn (funcall func) (point)) (point-min))
+          (point-min)))))
+
+;;}}}
+
+(provide 'mmm-region)
+
+;;; mmm-region.el ends here
\ No newline at end of file
diff --git a/mmm-sample.el b/mmm-sample.el
new file mode 100644
index 0000000..c793eb5
--- /dev/null
+++ b/mmm-sample.el
@@ -0,0 +1,160 @@
+;;; mmm-sample.el --- Sample MMM submode classes
+
+;; Copyright (C) 2000 by Michael Abraham Shulman
+
+;; Author: Michael Abraham Shulman <address@hidden>
+;; Version: $Id: mmm-sample.el,v 1.1 2000/04/27 10:36:48 mas Exp $
+
+;;{{{ GPL
+
+;; This file 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 file 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:
+
+;; This file contains several sample submode classes for use with MMM
+;; Mode. For a more detailed and useful example, see `mmm-mason.el'.
+
+;;; Code:
+
+(require 'mmm-auto)
+
+;;{{{ CSS embedded in HTML
+
+;; This is the simplest example. Many applications will need no more
+;; than a simple regexp.
+(mmm-add-classes
+ '((embedded-css
+    :submode css-mode
+    :front "<style[^>]*>"
+    :back "</style>")))
+
+;;}}}
+;;{{{ HTML Here-documents
+
+;; A little more complicated regexp to match all Perl here-documents
+;; whose identifiers include the string "HTML". We use :save-matches
+;; to match the end of the named here-document. This class has not
+;; been extensively tested.
+
+(mmm-add-classes
+ '((perl-html
+    :submode html-mode
+    :front "<<\\([a-zA-Z0-9_-]*HTML[a-zA-Z0-9_-]*\\).*\n"
+    :back "^~1$"
+    :save-matches 1)))
+
+;;}}}
+;;{{{ Javascript in HTML
+
+(defvar mmm-javascript-mode
+  (if (fboundp 'javascript-mode) 'javascript-mode 'c++-mode)
+  "What mode to use for Javascript regions.
+The default is `javascript-mode' if there is a function by that name,
+otherwise `c++-mode'.  Some people prefer `c++-mode' regardless.")
+
+;; We use two classes here, one for code in a <script> tag and another
+;; for code embedded as a property of an HTML tag, then another class
+;; to group them together.
+(mmm-add-group
+ 'html-js
+ `((js-tag
+    :submode ,mmm-javascript-mode
+    :front "<script\[^>\]*>"
+    :back"</script>")
+   (js-inline
+    :submode ,mmm-javascript-mode
+    :front "on\w+=\""
+    :back "\"")))
+
+;;}}}
+
+;; NOT YET UPDATED
+;;{{{ ELisp in File Variables;-COM-
+;-COM-
+;-COM-;; This example is more complicated. It uses a function rather than a
+;-COM-;; regexp to find the regions. The idea is that the sexp after an
+;-COM-;; 'eval:' file variable should be in Emacs Lisp Mode, since it's
+;-COM-;; getting executed as Emacs Lisp.
+;-COM-
+;-COM-(defun* mmm-elisp-in-file-vars (start end)
+;-COM-  (save-match-data
+;-COM-    (goto-char (point-max))
+;-COM-    (backward-page)
+;-COM-    ;; Fool the compiler so it doesn't think this is a local
+;-COM-    ;; variables list.
+;-COM-    (when (search-forward "Local\ Variables:" end t)
+;-COM-      (goto-char (max (point) start))
+;-COM-      (mmm-find-evals (min (or (save-excursion (search-forward "End:" 
nil t))
+;-COM-                               (return-from mmm-elisp-in-file-vars))
+;-COM-                           end)))))
+;-COM-
+;-COM-(defun mmm-find-evals (bound)
+;-COM-  (if (search-forward "eval: " bound t)
+;-COM-      (cons (cons (prog1 (point) (forward-sexp)) (point))
+;-COM-            (mmm-find-evals bound))
+;-COM-    nil))
+;-COM-
+;-COM-(add-to-list 'mmm-classes-alist
+;-COM-  '(eval-elisp (:function emacs-lisp-mode mmm-elisp-in-file-vars)))
+;-COM-
+;;}}}
+;;{{{ HTML in PL/SQL;-COM-
+;-COM-
+;-COM-;; This one is the most complex example. In PL/SQL, HTML is generally
+;-COM-;; output as a (single quote delimited) string inside a call to htp.p or
+;-COM-;; its brethren. The problem is that there may be strings outside of
+;-COM-;; htp.p calls that should not be HTML, so we need to only look inside
+;-COM-;; these calls. The situation is complicated by PL/SQL's rule that two
+;-COM-;; sequential single quotes in a string mean to put a single quote
+;-COM-;; inside the string.
+;-COM-
+;-COM-;; These functions have not been thoroughly tested, and always search
+;-COM-;; the entire buffer, ignoring START and END.
+;-COM-
+;-COM-(defun mmm-html-in-plsql (start end)
+;-COM-  (save-match-data
+;-COM-    (let ((case-fold-search t))
+;-COM-      (and (re-search-forward "htp.p\\(\\|rn\\|rint\\)1?(" nil t)
+;-COM-           (mmm-html-in-plsql-in-htp
+;-COM-            ;; Find the end of the procedure call
+;-COM-            (save-excursion (forward-char -1) (forward-sexp) (point))
+;-COM-            start end)))))
+;-COM-
+;-COM-(defun mmm-html-in-plsql-in-htp (htp-end start end)
+;-COM-  (let (beg end)
+;-COM-    (or (and (re-search-forward "'" htp-end 'limit)
+;-COM-      (setf beg (match-end 0))
+;-COM-      ;; Find an odd number of 's to end the string.
+;-COM-      (do ((lgth 0 (length (match-string 0))))
+;-COM-          ((oddp lgth) t)
+;-COM-        (re-search-forward "'+" nil t))
+;-COM-      (setf end (1- (match-end 0)))
+;-COM-      (cons (cons beg end)
+;-COM-            (mmm-html-in-plsql-in-htp htp-end start end)))
+;-COM- ;; No more strings in the procedure call; look for another.
+;-COM- (and (eql (point) htp-end)
+;-COM-      (mmm-html-in-plsql start end)))))
+;-COM-
+;-COM-(add-to-list 'mmm-classes-alist
+;-COM-  '(htp-p (:function html-mode mmm-html-in-plsql)))
+;-COM-
+;;}}}
+
+(provide 'mmm-sample)
+
+;;; mmm-sample.el ends here
\ No newline at end of file
diff --git a/mmm-utils.el b/mmm-utils.el
new file mode 100644
index 0000000..1141889
--- /dev/null
+++ b/mmm-utils.el
@@ -0,0 +1,132 @@
+;;; mmm-utils.el --- Coding Utilities for MMM Mode
+
+;; Copyright (C) 2000 by Michael Abraham Shulman
+
+;; Author: Michael Abraham Shulman <address@hidden>
+;; Version: $Id: mmm-utils.el,v 1.1 2000/04/27 10:35:19 mas Exp $
+
+;;{{{ GPL
+
+;; This file 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 file 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:
+
+;; This file provides a number of macros and other coding utilities
+;; for MMM Mode.
+
+;;; Code:
+
+;;{{{ Valid Buffer
+
+;; We used to wrap almost everything in this, but I realized that
+;; only `mmm-mode-on' really needs it. Kept it as a macro, though,
+;; for modularity and in case we need it somewhere else.
+(defmacro mmm-valid-buffer (&rest body)
+  "Execute BODY if in a valid buffer for MMM Mode to be enabled.
+This means if not currently in one of the temporary buffers that MMM
+Mode creates, and not in one of `mmm-never-modes'."
+  `(unless (or (equal (buffer-name) "*mmm-temp*")
+               (memq major-mode mmm-never-modes))
+     ,@body))
+
+;;;(def-edebug-spec mmm-valid-buffer t)
+
+;;}}}
+;;{{{ Save Everything
+
+;; Never trust callback functions to preserve anything.
+(defmacro mmm-save-all (&rest body)
+  "Execute BODY forms, then restoring point, mark, current buffer,
+restrictions, and match data."
+  `(save-excursion
+     (save-restriction
+       (save-match-data
+         ,@body))))
+
+;;;(def-edebug-spec mmm-save-all t)
+
+;;}}}
+;;{{{ String Formatting
+
+(defun mmm-format-string (string arg-pairs)
+  "Format STRING by replacing arguments as specified by ARG-PAIRS.
+Each element of ARG-PAIRS is \(REGEXP . STR) where each STR is to be
+substituted for the corresponding REGEXP wherever it matches."
+  (let ((case-fold-search nil))
+    (save-match-data
+      (dolist (pair arg-pairs)
+        (while (string-match (car pair) string)
+          (setq string (replace-match (cdr pair) t t string))))))
+  string)
+
+(defun mmm-make-matches-list (count)
+  "Make a list of the most recent subexpression matches by number.
+Returns \(\(\"~0\" . \"whole-match\") \(\"~1\" . \"first-subexp\") ...)
+up until COUNT."
+  (loop for n from 0 to count
+        collect (cons (format "~%s" n) (or (match-string n) ""))))
+
+(defun mmm-format-matches (string count)
+  "Format STRING by the last COUNT matches.
+Does nothing if COUNT is nil or STRING is not a string."
+  (if (and count (stringp string))
+      (mmm-format-string string (mmm-make-matches-list count))
+    string))
+
+;;}}}
+;;{{{ Save Keywords
+
+(defmacro mmm-save-keyword (param)
+  "If the value of PARAM as a variable is non-nil, return the list
+\(:PARAM (symbol-value PARAM)), otherwise NIL. Best used only when it
+is important that nil valuess disappear."
+  `(if (and (boundp ',param) ,param)
+       (list (intern (concat ":" (symbol-name ',param))) ,param)
+     nil))
+
+(defmacro mmm-save-keywords (&rest params)
+  "Return a list saving the non-nil elements of PARAMS. E.g.
+\(let \(\(a 1) \(c 2)) \(mmm-save-keywords a b c))  ==>  \(:a 1 :c 2)
+Use of this macro can make code more readable when there are a lot of
+PARAMS, but less readable when there are only a few. Also best used
+only when it is important that nil valuess disappear."
+  `(append ,@(mapcar #'(lambda (param)
+                         (macroexpand `(mmm-save-keyword ,param)))
+                     params)))
+
+;;}}}
+;;{{{ Looking Back At
+
+(defun mmm-looking-back-at (regexp &optional bound)
+  "Return t if text before point matches REGEXP.
+Modifies the match data. If supplied, BOUND means not to look farther
+back that that many characters before point. Otherwise, it defaults to
+\(length REGEXP), which is good enough when REGEXP is a simple
+string."
+  (eq (point)
+      (save-excursion
+        (and (re-search-backward regexp
+               (- (point) (or bound (length regexp)))
+               t)
+             (match-end 0)))))
+
+;;}}}
+
+(provide 'mmm-utils)
+
+;;; mmm-utils.el ends here
\ No newline at end of file
diff --git a/mmm-vars.el b/mmm-vars.el
new file mode 100644
index 0000000..9f1e1f0
--- /dev/null
+++ b/mmm-vars.el
@@ -0,0 +1,488 @@
+;;; mmm-vars.el --- Variables for MMM Mode
+
+;; Copyright (C) 2000 by Michael Abraham Shulman
+
+;; Author: Michael Abraham Shulman <address@hidden>
+;; Version: $Id: mmm-vars.el,v 1.1 2000/04/27 10:33:49 mas Exp $
+
+;;{{{ GPL
+
+;; This file 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 file 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:
+
+;; This file provides the definitions for the variables used by MMM
+;; Mode, as well as several functions to manipulate them. It also
+;; defines the errors that MMM Mode can signal.
+
+;;; Code:
+
+(require 'mmm-compat)
+
+;; MISCELLANEOUS
+;;{{{ Shut up the Byte Compiler
+
+;; Otherwise it complains about undefined variables.
+(eval-when-compile
+  (defvar mmm-save-local-variables)
+  (defvar mmm-mode-string)
+  (defvar mmm-submode-mode-line-format)
+  (defvar mmm-mode-ext-classes-alist)
+  (defvar mmm-mode-prefix-key)
+  (defvar mmm-global-mode)
+  (defvar mmm-classes-alist))
+
+;;}}}
+;;{{{ Error Conditions
+
+;; Signalled when we try to put a submode region inside one where it
+;; isn't meant to go.
+(put 'mmm-invalid-parent
+     'error-conditions
+     '(mmm-invalid-parent mmm-error error))
+(put 'mmm-invalid-parent
+     'error-message
+     "Invalid submode region parent")
+
+;; Signalled when we try to apply a submode class that doesn't exist.
+(put 'mmm-invalid-submode-class
+     'error-conditions
+     '(mmm-invalid-submode-class mmm-error error))
+(put 'mmm-invalid-submode-class
+     'error-message
+     "Invalid or undefined submode class")
+
+;;}}}
+
+;; USER VARIABLES
+;;{{{ Customization Group
+
+(defgroup mmm nil
+  "Multiple Major Modes in one buffer."
+  :group 'tools)
+
+;;}}}
+;;{{{ Save Local Variables
+
+(defcustom mmm-save-local-variables 
+  '(comment-start 
+    comment-end
+    comment-start-skip
+    comment-column
+    comment-indent-function
+    comment-line-break-function
+    sentence-end
+    font-lock-keywords
+    font-lock-keywords-only
+    font-lock-keywords-case-fold-search
+    font-lock-syntax-table
+    font-lock-mark-block-function       ; Replace this?
+    font-lock-syntactic-keywords
+    indent-line-function
+    skeleton-transformation)
+  "Which local variables to save for secondary major modes.
+Changing the value of this variable after MMM Mode has been activated
+in some buffer may produce unpredictable results."
+  :group 'mmm
+  :type '(repeat (symbol :tag "Variable")))
+
+;;}}}
+;;{{{ Default Submode Face
+
+(defface mmm-default-submode-face 
+  '(
+    (t (:background "gray85"))
+    )
+  "Face used to indicate submode overlays by default.
+This can be overridden for specific submodes created by any method;
+see the documentation for that method. It is recommended that only the
+background color be set for this face, in order not to mess with
+font-lock too much."
+  :group 'mmm)
+
+
+;;}}}
+;;{{{ Mode Line Format
+
+(defcustom mmm-mode-string " MMM"
+  "*String to display in mode line as MMM minor mode indicator."
+  :group 'mmm
+  :type 'string)
+
+(defcustom mmm-submode-mode-line-format "~M[~m]"
+  "*Format of the Major Mode Mode-line display when point is in a
+submode region. ~M means the name of the default major mode, ~m means
+the name of the submode."
+  :group 'mmm
+  :type 'string)
+
+;;}}}
+;;{{{ Submode Classes
+
+(defvar mmm-classes nil
+  "*List of classes of submodes that apply to a file.
+Generally set in a local variables list. Can either be one symbol, or
+a list of symbols. Automatically buffer-local.")
+(make-variable-buffer-local 'mmm-classes)
+
+;;}}}
+;;{{{ Modes and Extensions
+
+(defcustom mmm-mode-ext-classes-alist nil
+  "Alist of submode classes for major modes and/or file extensions.
+This variable can now be directly modified.
+
+Elements look like \(MODE EXT CLASS), where MODE is a major mode, EXT
+is a regexp to match a filename such as in `auto-mode-alist', and
+CLASS is a submode class. CLASS is activated in all buffers in mode
+MODE \(if non-nil) and whose filenames match EXT \(if non-nil). If
+both MODE and EXT are nil, CLASS is activated in all buffers. If CLASS
+is the symbol t, MMM Mode is turned on in all buffers matching MODE
+and EXT, but no classes are activated.
+
+See `mmm-add-find-file-hook' and `mmm-global-mode'."
+  :group 'mmm
+  :type '(repeat (list (symbol :tag "Major Mode")
+                       (string :tag "Filename Regexp")
+                       (symbol :tag "Class")))
+  :require 'mmm-mode)
+
+(defun mmm-add-mode-ext-class (mode ext class)
+  "Add an element to `mmm-mode-ext-classes-alist', which see.
+That variable can now be directly modified, so this function is
+unnecessary. It probably won't go away, though."
+  (if (assq class mmm-classes-alist)
+      (add-to-list 'mmm-mode-ext-classes-alist (list mode ext class))
+    (signal 'mmm-invalid-submode-class (list class))))
+
+;;}}}
+;;{{{ Key Bindings
+
+(defcustom mmm-mode-prefix-key [(control ?c) ?%]
+  "Prefix key for the MMM Minor Mode Keymap."
+  :group 'mmm
+  :type 'vector)
+
+(defcustom mmm-command-modifiers '(control)
+  "List of key modifiers for MMM command keys.
+The MMM commands in the MMM Mode map, after `mmm-mode-prefix-key',
+are bound to default keys with these modifiers added. This variable
+must be set before MMM Mode is loaded to have an effect.
+
+It is suggested that the value of this variable be either nil or
+\(control), as the default keys are either plain keys or have only a
+meta modifier. The shift modifier is not particularly portable between
+Emacsen. The values of this variable and `mmm-insert-modifiers' should
+be disjoint."
+  :group 'mmm
+  :type '(repeat (symbol :tag "Modifier")))
+
+(defcustom mmm-insert-modifiers '()
+  "List of key modifiers for MMM submode insertion keys.
+When a key pressed after `mmm-mode-prefix-key' has no MMM Mode command
+binding, and its modifiers include these, then its basic type, plus any
+modifiers in addition to these, is looked up in classes' :insert
+specifications.
+
+It is suggested that the value of this variable be either nil or
+\(control), allowing submode classes to specify the presence or
+absence of the meta modifier. The shift modifier is not particularly
+portable between Emacsen. The values of `mmm-command-modifiers' and
+this variable should be disjoint."
+  :group 'mmm
+  :type '(repeat (symbol :tag "Modifier")))
+
+(defcustom mmm-use-old-command-keys nil
+  "Non-nil means to Use the old command keys for MMM Mode.
+MMM Mode commands then have no modifier while insertion commands have
+a control modifier, i.e. `mmm-command-modifiers' is set to nil and
+`mmm-insert-modifiers' is set to \(control). If nil, the values of
+these variables are as the default, or whatever the user has set them
+to. This variable must be set before MMM Mode is loaded."
+  :group 'mmm
+  :type 'boolean)
+
+(defun mmm-use-old-command-keys ()
+  "Use the old command keys \(no control modifer) in MMM Mode."
+  (setq mmm-command-modifiers '()
+        mmm-insert-modifiers '(control)))
+
+;;}}}
+;;{{{ MMM Hooks
+
+(defcustom mmm-mode-hook ()
+  "Hook run when MMM Mode is enabled in a buffer.
+
+A hook named mmm-<major-mode>-hook is also run, if it exists. For
+example, `mmm-html-mode-hook' is run whenever MMM Mode is entered with
+HTML mode the dominant mode.
+
+A hook named mmm-<submode>-submode-hook is run when a submode region
+of a given mode is created. For example, `mmm-cperl-mode-submode-hook'
+is run whenever a CPerl mode submode region is created, in any buffer.
+When submode hooks are run, point is guaranteed to be at the start of
+the newly created submode region.
+
+Finally, a hook named mmm-<class>-class-hook is run whenever a buffer
+is first mmm-ified with a given submode class. For example,
+`mmm-mason-class-hook' is run whenever the `mason' class is first
+applied in a buffer."
+  :group 'mmm
+  :type 'hook)
+
+(defun mmm-run-constructed-hook (body &optional suffix)
+  "Run the hook named `mmm-<BODY>-<SUFFIX>-hook', if it exists.
+If SUFFIX is nil or unsupplied, run `mmm-<BODY>-hook' instead."
+  (let ((hook (intern-soft (if suffix
+                               (format "mmm-%s-%s-hook" body suffix)
+                             (format "mmm-%s-hook" body)))))
+    (if hook (run-hooks hook))))
+
+(defun mmm-run-major-hook ()
+  (mmm-run-constructed-hook major-mode))
+
+(defun mmm-run-submode-hook (submode)
+  (mmm-run-constructed-hook submode "submode"))
+
+(defvar mmm-class-hooks-run ()
+  "List of submode classes for which hooks have already been run in
+the current buffer.")
+(make-variable-buffer-local 'mmm-class-hooks-run)
+
+(defun mmm-run-class-hook (class)
+  (unless (member class mmm-class-hooks-run)
+    (mmm-run-constructed-hook class "class")
+    (add-to-list 'mmm-class-hooks-run class)))
+
+;;}}}
+;;{{{ Major Mode Hook
+
+(defcustom mmm-major-mode-hook ()
+  "Hook run whenever a new major mode is finished starting up.
+MMM Mode implements this with a hack \(see comments in the source) so
+that `mmm-global-mode' will function correctly, but makes this hook
+available so that others can take advantage of the hack as well.
+
+Note that file local variables have *not* been processed by the time
+this hook is run. If a function needs to inspect them, it should also
+be added to `find-file-hooks'. However, `find-file-hooks' is not run
+when creating a non-file-based buffer, or when changing major modes in
+an existing buffer."
+  :group 'mmm
+  :type 'hook)
+
+(defun mmm-run-major-mode-hook ()
+  (run-hooks 'mmm-major-mode-hook))
+
+;;}}}
+;;{{{ MMM Global Mode
+
+(defcustom mmm-global-mode nil
+  "*Specify in which buffers to turn on MMM Mode automatically.
+* If nil, MMM Mode is never enabled automatically.
+* If t, MMM Mode is enabled automatically in all buffers.
+* If any other symbol, MMM mode is enabled only in those buffers that
+  have submode classes associated with them. See `mmm-classes' and
+  `mmm-mode-ext-classes' for more information."
+  :group 'mmm
+  :type '(choice (const :tag "Always" t)
+                 (const :tag "Never" nil)
+                 (other :tag "Maybe" maybe))
+  :require 'mmm-mode)
+
+;;}}}
+;;{{{ "Never" Modes
+
+(defcustom mmm-never-modes
+  '(help-mode
+    Info-mode
+    dired-mode
+    comint-mode
+    shell-mode)
+  "List of modes in which MMM Mode is *never* activated."
+  :group 'mmm
+  :type '(repeat (symbol :tag "Mode")))
+
+;;}}}
+
+;; NON-USER VARIABLES
+;;{{{ Classes Alist
+
+;; :parent could be an all-class argument. Same with :keymap.
+(defvar mmm-classes-alist nil
+  "*Alist containing all defined mmm submode classes.
+Each element looks like \(CLASS . ARGS) where CLASS is a symbol
+representing the submode class and ARGS is a list of keyword
+arguments, called a \"class specifier\". There are a large number of
+accepted keyword arguments.
+
+The argument CLASSES, if supplied, must be a list of other submode
+classes \(or class specifiers), representing other classes to call.
+FACE, if supplied, overrides FACE arguments to these classes, but all
+other arguments to this class are ignored.
+
+The argument HANDLER, if supplied, overrides any other processing. It
+must be a function, and all the arguments are passed to it as
+keywords, and it must do everything. See `mmm-ify' for what sorts of
+things it must do. This back-door interface should be cleaned up.
+
+The argument FACE, if supplied, overrides `mmm-default-submode-face'
+in specifying the display face of the submode regions. It must be a
+valid display face.
+
+If CLASSES and HANDLER are not supplied, SUBMODE must be. It specifies
+the submode to use for the submode regions, as a symbol such as
+`cperl-mode' or `emacs-lisp-mode'.
+
+FRONT and BACK are the means to find the submode regions, and can be
+either buffer positions \(number-or-markers), regular expressions, or
+functions. If they are absolute buffer positions, only one submode
+region is created, from FRONT to BACK. This is generally not used in
+named classes. \(Unnamed classes are created by interactive commands
+in `mmm-interactive-history').
+
+If FRONT is a regexp, then that regexp is searched for, and the end of
+its match, plus FRONT-OFFSET, becomes the beginning of the submode
+region. If FRONT is a function, that function is called instead, and
+must act somewhat like a search, in that it should start at point,
+take one argument as a search bound, and set the match data. A similar
+pattern is followed for BACK, save that the end of the submode region
+becomes the beginning of its match, plus BACK-OFFSET. FRONT- and
+BACK-OFFSET default to 0.
+
+FRONT-VERIFY and BACK-VERIFY, if supplied, must be functions that
+inspect the match data to see if a match found by FRONT or BACK
+respectively is valid.
+
+If SAVE-MATCHES is supplied, it must be a number, and means to format
+BACK, if it is a regexp, by replacing strings of the form \"~N\" by
+the corresponding value of \(match-string n) after matching FRONT,
+where N is between 0 and SAVE-MATCHES.
+
+FRONT-FORM and BACK-FORM, if given, must supply a regexp used to match
+the *actual* delimiter. If they are strings, they are used as-is. If
+they are functions, they are called and must inspect the match data.
+If they are lists, their `car' is taken as the delimiter. The default
+for both is \(regexp-quote \(match-string 0)).
+
+The last case is usually used for functions. Such a function must take
+1-2 arguments, the first being the overlay in question, and the second
+meaning to insert the delimiter and adjust the overlay rather than
+just matching the delimiter. See `mmm-match-front', `mmm-match-back',
+and `mmm-end-current-region'.
+
+CASE-FOLD-SEARCH, if specified, controls whether the search is
+case-insensitive. See `case-fold-search'. It defaults to `t'.
+
+INSERT specifies the keypress insertion spec for such submode regions.
+INSERT's value should be list of elements of the form \(KEY NAME .
+SPEC). Each KEY should be either a character, a function key symbol,
+or a dotted list \(MOD . KEY) where MOD is a symbol for a modifier
+key. The use of any other modifier than meta is discouraged, as
+`mmm-insert-modifiers' defaults to \(control), and other modifiers are
+not very portable. Each NAME should be a symbol representing the
+insertion for that key. Each SPEC can be either a skeleton, suitable
+for passing to `skeleton-insert' to create a submode region, or a
+dotted pair \(OTHER-KEY . ARG) meaning to use the skeleton defined for
+OTHER-KEY but pass it the argument ARG as the `str' variable, possible
+replacing a prompt string. Skeletons for insertion should have the
+symbol `_' where point \(or wrapped text) should go, and the symbol
+`@' in four different places: at the beginning of the front delimiter,
+the beginning of the submode region, the end of the submode region,
+and the end of the back delimiter.")
+
+(defun mmm-add-classes (classes)
+  "Add the submode classes CLASSES to `mmm-classes-alist'."
+  (dolist (class classes)
+    (add-to-list 'mmm-classes-alist class)))
+
+(defun mmm-add-group (group classes)
+  "Add CLASSES and a group named GROUP containing them all."
+  (mmm-add-classes classes)
+  (add-to-list 'mmm-classes-alist
+               (list group :classes (mapcar #'first classes))))
+
+;;}}}
+;;{{{ Version Number
+
+(defconst mmm-version "0.3.8-alpha"
+  "Current version of MMM Mode.")
+
+(defun mmm-version ()
+  (interactive)
+  (message "MMM Mode version %s by Michael Abraham Shulman" mmm-version))
+
+;;}}}
+;;{{{ Interactive History
+
+(defvar mmm-interactive-history nil
+  "History of interactive mmm-ification in the current buffer.
+Elements are either submode class symbols or class specifications. See
+`mmm-classes-alist' for more information.")
+(make-variable-buffer-local 'mmm-interactive-history)
+
+(defun mmm-add-to-history (class)
+  (add-to-list 'mmm-interactive-history class))
+
+(defun mmm-clear-history ()
+  "Clears history of interactive mmm-ification in current buffer."
+  (interactive)
+  (setq mmm-interactive-history nil))
+
+;;}}}
+;;{{{ Mode/Ext Manipulation
+
+(defvar mmm-mode-ext-classes ()
+  "List of classes associated with current buffer by mode and filename.
+Set automatically from `mmm-mode-ext-classes-alist'.")
+(make-variable-buffer-local 'mmm-mode-ext-classes)
+
+(defun mmm-get-mode-ext-classes ()
+  "Return classes for current buffer from major mode and filename.
+Uses `mmm-mode-ext-classes-alist' to find submode classes."
+  (or mmm-mode-ext-classes
+      (setq mmm-mode-ext-classes
+            (mapcar #'third
+                    (remove-if-not #'mmm-mode-ext-applies
+                                   mmm-mode-ext-classes-alist)))))
+
+(defun mmm-clear-mode-ext-classes ()
+  "Clear classes added by major mode and filename."
+  (setq mmm-mode-ext-classes nil))
+
+(defun mmm-mode-ext-applies (element)
+  (destructuring-bind (mode ext class) element
+    (and (if mode (eq mode major-mode) t)
+         (if ext (and (buffer-file-name)
+                      (save-match-data
+                        (string-match ext (buffer-file-name))))
+           t))))
+
+(defun mmm-get-all-classes ()
+  "Return a list of all classes applicable to the current buffer.
+These come from mode/ext associations, `mmm-classes', and interactive
+history."
+  (append mmm-interactive-history
+          (if (listp mmm-classes) mmm-classes (list mmm-classes))
+          (mmm-get-mode-ext-classes)))
+
+;;}}}
+
+(provide 'mmm-vars)
+
+;;; mmm-vars.el ends here
\ No newline at end of file



reply via email to

[Prev in Thread] Current Thread [Next in Thread]