[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
- [elpa] master c74eeb5 125/433: # Updated email address, (continued)
- [elpa] master c74eeb5 125/433: # Updated email address, Dmitry Gutov, 2018/03/15
- [elpa] master ee185fa 127/433: # Removed a duplicate entry, Dmitry Gutov, 2018/03/15
- [elpa] master e0f50ca 121/433: Wrote File Variables submode class for the new syntax., Dmitry Gutov, 2018/03/15
- [elpa] master dbf0649 126/433: # Updated for SourceForge, Dmitry Gutov, 2018/03/15
- [elpa] master 644b5be 131/433: (here-doc): Updated to use new front-offset values., Dmitry Gutov, 2018/03/15
- [elpa] master 20ae2f5 139/433: # Updated docstring., Dmitry Gutov, 2018/03/15
- [elpa] master 3f20d0f 142/433: # Added CVS snapshot step., Dmitry Gutov, 2018/03/15
- [elpa] master 60d9e35 129/433: (mmm-make-region): Made sure overlays get the delimiter and sticky, Dmitry Gutov, 2018/03/15
- [elpa] master 447eb61 110/433: Wrote about changing key bindings and local variables., Dmitry Gutov, 2018/03/15
- [elpa] master d504cd7 134/433: (mmm-ify-by-class): Added completion on autoloaded classes. Excluded, Dmitry Gutov, 2018/03/15
- [elpa] master bb60f11 001/433: Initial revision,
Dmitry Gutov <=
- [elpa] master da57a75 151/433: # Updated., Dmitry Gutov, 2018/03/15
- [elpa] master b59c78e 086/433: (mmm-update-mode-info): Used `mmm-make-temp-buffer'. Put font-lock, Dmitry Gutov, 2018/03/15
- [elpa] master 3c3042d 093/433: # Updated., Dmitry Gutov, 2018/03/15
- [elpa] master d367aa7 098/433: Added `version.texi' to step 3., Dmitry Gutov, 2018/03/15
- [elpa] master 9a94c1f 097/433: # Updated., Dmitry Gutov, 2018/03/15
- [elpa] master a64bc70 104/433: Added comment about `mmm-global-mode'., Dmitry Gutov, 2018/03/15
- [elpa] master e947f88 107/433: # Updated., Dmitry Gutov, 2018/03/15
- [elpa] master 67fbc7f 114/433: (mmm-match-region, mmm-ify): Caught errors from :match-submode., Dmitry Gutov, 2018/03/15
- [elpa] master 699645a 112/433: Allowed here-documents in any mode with :match-submode., Dmitry Gutov, 2018/03/15
- [elpa] master a2d04ab 128/433: # Removed another duplicate entry, Dmitry Gutov, 2018/03/15