changeset 27327:c7597f93c50d

*** empty log message ***
author Gerd Moellmann <gerd@gnu.org>
date Fri, 14 Jan 2000 14:02:21 +0000
parents 25ebadb8e258
children 7748234c70d7
files lisp/ChangeLog lisp/align.el lisp/cus-load.el lisp/loaddefs.el
diffstat 4 files changed, 1625 insertions(+), 1 deletions(-) [+]
line wrap: on
line diff
--- a/lisp/ChangeLog	Fri Jan 14 12:05:52 2000 +0000
+++ b/lisp/ChangeLog	Fri Jan 14 14:02:21 2000 +0000
@@ -1,5 +1,7 @@
 2000-01-14  Gerd Moellmann  <gerd@gnu.org>
 
+	* align.el: New file.
+
 	* menu-bar.el (menu-bar-tools-menu): Add EUDC submenu.
 
 	* net/eudc.el (toplevel): Define EUDC menu for Emacs.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/lisp/align.el	Fri Jan 14 14:02:21 2000 +0000
@@ -0,0 +1,1547 @@
+;;; align --- align text to a specific column, by regexp
+
+;; Copyright (C) 1999, 2000 Free Sofware Foundation
+
+;; Author: John Wiegley <johnw@gnu.org>
+;; Keywords: convenience languages lisp
+;; X-URL: http://www.emacs.org/~johnw/emacs.html
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation; either version 2, or (at your option)
+;; any later version.
+
+;; GNU Emacs is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with GNU Emacs; see the file COPYING.  If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
+
+;;; Commentary:
+
+;; This mode allows you to align regions in a context-sensitive fashion.
+;; The classic use is to align assignments:
+;;
+;;    int a = 1;
+;;    short foo = 2;
+;;    double blah = 4;
+;;
+;; becomes
+;;
+;;    int    a    = 1;
+;;    short  foo  = 2;
+;;    double blah = 4;
+
+;;; Usage:
+
+;; There are several variables which define how certain "categories"
+;; of syntax are to be treated.  These variables go by the name
+;; `align-CATEGORY-modes'.  For example, "c++" is such a category.
+;; There are several rules which apply to c++, but since several other
+;; languages have a syntax similar to c++ (e.g., c, java, etc), these
+;; modes are treated as belonging to the same category.
+;;
+;; If you want to add a new mode under a certain category, just
+;; customize that list, or add the new mode manually.  For example, to
+;; make jde-mode a c++ category mode, use this code in your .emacs
+;; file:
+;;
+;;    (setq align-c++-modes (cons 'jde-mode align-c++-modes))
+
+;; In some programming modes, it's useful to have the aligner run only
+;; after indentation is performed.  To achieve this, customize or set
+;; the variable `align-indent-before-aligning' to t.
+
+;;; Module Authors:
+
+;; In order to incorporate align's functionality into your own
+;; modules, there are only a few steps you have to follow.
+
+;;  1. Require or load in the align.el library.
+;;
+;;  2. Define your alignment and exclusion rules lists, either
+;;     customizable or not.
+;;
+;;  3. In your mode function, set the variables
+;;     `align-mode-rules-list' and `align-mode-exclude-rules-list'
+;;     to your own rules lists.
+
+;; If there is any need to add your mode name to one of the
+;; align-?-modes variables (for example, `align-dq-string-modes'), use
+;; `add-to-list', or some similar function which checks first to see
+;; if the value is already there.  Since the user may customize that
+;; mode list, and then write your mode name into their .emacs file,
+;; causing the symbol already to be present the next time they load
+;; your package.
+
+;; Example:
+;;
+;;   (require 'align)
+;;
+;;   (defcustom my-align-rules-list
+;;     '((my-rule
+;;        (regexp . "Sample")))
+;;     :type align-rules-list-type
+;;     :group 'my-package)
+;;
+;;   (put 'my-align-rules-list 'risky-local-variable t)
+;;
+;;   (add-to-list 'align-dq-string-modes 'my-package-mode)
+;;   (add-to-list 'align-open-comment-modes 'my-package-mode)
+;;
+;;   (defun my-mode ()
+;;      ...
+;;      (setq align-mode-rules-list my-align-rules-list))
+;;
+;; Note that if you need to install your own exclusion rules, then you
+;; will also need to reproduce any double-quoted string, or open
+;; comment exclusion rules that are defined in the standard
+;; `align-exclude-rules-list'.  At the moment there is no convenient
+;; way to mix both mode-local and global rules lists.
+
+;;; History:
+
+;; Version 1.0 was created in the earlier part of 1996, using a very
+;; simple algorithm that understand only basic regular expressions.
+;; Parts of the code were broken up and included in vhdl-mode.el
+;; around this time.  After several comments from users, and a need to
+;; find a more robust, performant algorithm, 2.0 was born in late
+;; 1998.  Many different approaches were taken (mostly due to the
+;; complexity of TeX tables), but finally a scheme was discovered
+;; which worked fairly well for most common usage cases.  Development
+;; beyond version 2.8 is not planned, except for problems that users
+;; might encounter.
+
+;;; Code:
+
+(defgroup align nil
+  "Align text to a specific column, by regexp."
+  :group 'fill)
+
+;;; User Variables:
+
+(defcustom align-load-hook nil
+  "*Hook that gets run after the aligner has been loaded."
+  :type 'hook
+  :group 'align)
+
+(defcustom align-indent-before-aligning nil
+  "*If non-nil, indent the marked region before aligning it."
+  :type 'boolean
+  :group 'align)
+
+(defcustom align-default-spacing 1
+  "*An integer that represents the default amount of padding to use.
+If `align-to-tab-stop' is non-nil, this will represent the number of
+tab stops to use for alignment, rather than the number of spaces.
+Each alignment rule can optionally override both this variable.  See
+`align-mode-alist'."
+  :type 'integer
+  :group 'align)
+
+(defcustom align-to-tab-stop 'indent-tabs-mode
+  "*If non-nil, alignments will always fall on a tab boundary.
+It may also be a symbol, whose value will be taken."
+  :type '(choice (const nil) symbol)
+  :group 'align)
+
+(defcustom align-region-heuristic 500
+  "*If non-nil, used as a heuristic by `align-current'.
+Since each alignment rule can possibly have its own set of alignment
+sections (whenever `align-region-separate' is non-nil, and not a
+string), this heuristic is used to determine how far before and after
+point we should search in looking for a region separator.  Larger
+values can mean slower perform in large files, although smaller values
+may cause unexpected behavior at times."
+  :type 'integer
+  :group 'align)
+
+(defcustom align-highlight-change-face 'highlight
+  "*The face to highlight with if changes are necessary."
+  :type 'face
+  :group 'align)
+
+(defcustom align-highlight-nochange-face 'secondary-selection
+  "*The face to highlight with if no changes are necessary."
+  :type 'face
+  :group 'align)
+
+(defcustom align-large-region 10000
+  "*If an integer, defines what constitutes a \"large\" region.
+If nil,then no messages will ever be printed to the minibuffer."
+  :type 'integer
+  :group 'align)
+
+(defcustom align-c++-modes '(c++-mode c-mode java-mode)
+  "*A list of modes whose syntax resembles C/C++."
+  :type '(repeat symbol)
+  :group 'align)
+
+(defcustom align-perl-modes '(perl-mode cperl-mode)
+  "*A list of modes where perl syntax is to be seen."
+  :type '(repeat symbol)
+  :group 'align)
+
+(defcustom align-lisp-modes
+  '(emacs-lisp-mode lisp-interaction-mode lisp-mode scheme-mode)
+  "*A list of modes whose syntax resembles Lisp."
+  :type '(repeat symbol)
+  :group 'align)
+
+(defcustom align-tex-modes
+  '(tex-mode plain-tex-mode latex-mode slitex-mode)
+  "*A list of modes whose syntax resembles TeX (and family)."
+  :type '(repeat symbol)
+  :group 'align)
+
+(defcustom align-text-modes '(text-mode outline-mode)
+  "*A list of modes whose content is plain text."
+  :type '(repeat symbol)
+  :group 'align)
+
+(defcustom align-dq-string-modes (append align-lisp-modes
+					 align-c++-modes
+					 align-perl-modes)
+  "*A list of modes where double quoted strings should be excluded."
+  :type '(repeat symbol)
+  :group 'align)
+
+(defcustom align-sq-string-modes align-perl-modes
+  "*A list of modes where single quoted strings should be excluded."
+  :type '(repeat symbol)
+  :group 'align)
+
+(defcustom align-open-comment-modes (append align-lisp-modes
+					    align-c++-modes
+					    align-perl-modes
+					    '(makefile-mode))
+  "*A list of modes with a single-line comment syntax.
+These are comments as in Lisp, which have a beginning but, end with
+the line (i.e., `comment-end' is an empty string)."
+  :type '(repeat symbol)
+  :group 'align)
+
+(defcustom align-region-separate "^\\s-*[{}]?\\s-*$"
+  "*Select the method by which alignment sections will be separated.
+If this is a symbol, that symbol's value will be used.
+
+For the sake of clarification, consider the following example, which
+will be referred to in the descriptions below.
+
+    int alpha = 1; /* one */
+    double beta = 2.0;
+    long gamma; /* ten */
+
+    unsigned int delta = 1; /* one */
+    long double epsilon = 3.0;
+    long long omega; /* ten */
+
+The possible settings for `align-region-separate' are:
+
+ `entire'  The entire region being aligned will be considered as a
+	   single alignment section.  Assuming that comments were not
+	   being aligned to a particular column, the example would
+	   become:
+
+	     int          alpha    = 1;   /* one */
+	     double       beta     = 2.0;
+	     long         gamma;          /* ten */
+
+	     unsigned int delta    = 1;   /* one */
+	     long double  epsilon;
+	     long long    chi      = 10;  /* ten */
+
+ `group'   Each contiguous set of lines where a specific alignment
+	   occurs is considered a section for that alignment rule.
+	   Note that each rule will may have any entirely different
+	   set of section divisions than another.
+
+	     int    alpha = 1; /* one */
+	     double beta  = 2.0;
+	     long   gamma; /* ten */
+
+	     unsigned int delta = 1; /* one */
+	     long double  epsilon;
+	     long long    chi = 10; /* ten */
+
+ `largest' When contiguous rule sets overlap, the largest section
+	   described will be taken as the alignment section for each
+	   rule touched by that section.
+
+	     int    alpha = 1;   /* one */
+	     double beta  = 2.0;
+	     long   gamma;       /* ten */
+
+	     unsigned int delta    = 1;  /* one */
+	     long double  epsilon;
+	     long long    chi      = 10; /* ten */
+
+	   NOTE: This option is not supported yet, due to algorithmic
+	   issues which haven't been satisfactorily resolved.  There
+	   are ways to do it, but they're both ugly and resource
+	   consumptive.
+
+ regexp    A regular expression string which defines the section
+	   divider.  If the mode you're in has a consistent divider
+	   between sections, the behavior will be very similar to
+	   `largest', and faster.  But if the mode does not use clear
+	   separators (for example, if you collapse your braces onto
+	   the preceding statement in C or perl), `largest' is
+	   probably the better alternative.
+
+ function  A function that will be passed the beginning and ending
+	   locations of the region in which to look for the section
+	   separator.  At the very beginning of the attempt to align,
+	   both of these parameters will be nil, in which case the
+	   function should return non-nil if it wants each rule to
+	   define its own section, or nil if it wants the largest
+	   section found to be used as the common section for all rules
+	   that occur there.
+
+ list      A list of markers within the buffer that represent where
+	   the section dividers lie.  Be certain to use markers!  For
+	   when the aligning begins, the ensuing contract/expanding of
+	   whitespace will throw off any non-marker positions.
+
+	   This method is intended for use in Lisp programs, and not
+	   by the user."
+  :type '(choice
+	  (const :tag "Entire region is one section" entire)
+	  (const :tag "Align by contiguous groups" group)
+;         (const largest)
+	  (regexp :tag "Regexp defines section boundaries")
+	  (function :tag "Function defines section boundaries"))
+  :group 'align)
+
+(put 'align-region-separate 'risky-local-variable t)
+
+(defvar align-rules-list-type
+  '(repeat
+    (cons
+     :tag "Alignment rule"
+     (symbol :tag "Title")
+     (cons :tag "Required attributes"
+	   (cons :tag "Regexp"
+		 (const :tag "(Regular expression to match)" regexp)
+		 (choice :value "\\(\\s-+\\)" regexp function))
+	   (repeat
+	    :tag "Optional attributes"
+	    (choice
+	     (cons :tag "Repeat"
+		   (const :tag "(Repeat this rule throughout line)"
+			  repeat)
+		   (boolean :value t))
+	     (cons :tag "Paren group"
+		   (const :tag "(Parenthesis group to use)" group)
+		   (choice :value 2
+			   integer (repeat integer)))
+	     (cons :tag "Modes"
+		   (const :tag "(Modes where this rule applies)" modes)
+		   (sexp :value (text-mode)))
+	     (cons :tag "Case-fold"
+		   (const :tag "(Should case be ignored for this rule)"
+			  case-fold)
+		   (boolean :value t))
+	     (cons :tag "To Tab Stop"
+		   (const :tag "(Should rule align to tab stops)"
+			  tab-stop)
+		   (boolean :value nil))
+	     (cons :tag "Valid"
+		   (const :tag "(Return non-nil if rule is valid)"
+			  valid)
+		   (function :value t))
+	     (cons :tag "Run If"
+		   (const :tag "(Return non-nil if rule should run)"
+			  run-if)
+		   (function :value t))
+	     (cons :tag "Column"
+		   (const :tag "(Column to fix alignment at)" column)
+		   (choice :value comment-column
+			   integer symbol))
+	     (cons :tag "Spacing"
+		   (const :tag "(Amount of spacing to use)" spacing)
+		   (integer :value 1))
+	     (cons :tag "Justify"
+		   (const :tag "(Should text be right justified)"
+			  justify)
+		   (boolean :value t))
+	     ;; make sure this stays up-to-date with any changes
+	     ;; in `align-region-separate'
+	     (cons :tag "Separate"
+		   (const :tag "(Separation to use for this rule)"
+			  separate)
+		   (choice :value "^\\s-*$"
+			   (const entire)
+			   (const group)
+;                          (const largest)
+			   regexp function)))))))
+  "The `type' form for any `align-rules-list' variable.")
+
+(unless (functionp 'c-guess-basic-syntax)
+  (autoload 'c-guess-basic-syntax "cc-engine"))
+
+(defcustom align-rules-list
+  `((lisp-second-arg
+     (regexp   . "\\(^\\s-+[^( \t\n]\\|(\\(\\S-+\\)\\s-+\\)\\S-+\\(\\s-+\\)")
+     (group    . 3)
+     (modes    . align-lisp-modes)
+     (run-if   . ,(function (lambda () current-prefix-arg))))
+
+    (lisp-alist-dot
+     (regexp   . "\\(\\s-*\\)\\.\\(\\s-*\\)")
+     (group    . (1 2))
+     (modes    . align-lisp-modes))
+
+    (open-comment
+     (regexp   . ,(function
+		   (lambda (end reverse)
+		     (funcall (if reverse 're-search-backward
+				're-search-forward)
+			      (concat "[^ \t\n\\\\]"
+				      (regexp-quote comment-start)
+				      "\\(.+\\)$") end t))))
+     (modes    . align-open-comment-modes))
+
+    (c-macro-definition
+     (regexp   . "^\\s-*#\\s-*define\\s-+\\S-+\\(\\s-+\\)")
+     (modes    . align-c++-modes))
+
+    (c-variable-declaration
+     (regexp   . ,(concat "[*&0-9A-Za-z_]>?[&*]*\\(\\s-+[*&]*\\)"
+			  "[A-Za-z_][0-9A-Za-z:_]*\\s-*\\(\\()\\|"
+			  "=[^=\n].*\\|(.*)\\|\\(\\[.*\\]\\)*\\)?"
+			  "\\s-*[;,]\\|)\\s-*$\\)"))
+     (group    . 1)
+     (modes    . align-c++-modes)
+     (justify  . t)
+     (valid
+      . ,(function
+	  (lambda ()
+	    (not (or (save-excursion
+		       (goto-char (match-beginning 1))
+		       (backward-word 1)
+		       (looking-at
+			"\\(goto\\|return\\|new\\|delete\\|throw\\)"))
+		     (if (and (boundp 'font-lock-mode) font-lock-mode)
+			 (eq (cadr (memq 'face (text-properties-at (point))))
+			     'font-lock-comment-face)
+		       (eq (caar (c-guess-basic-syntax)) 'c))))))))
+
+    (c-assignment
+     (regexp   . ,(concat "[^-=!^&*+<>/| \t\n]\\(\\s-*[-=!^&*+<>/|]*\\)"
+			  "=\\(\\s-*\\)\\([^= \t\n]\\|$\\)"))
+     (group    . (1 2))
+     (modes    . align-c++-modes)
+     (justify  . t)
+     (tab-stop . nil))
+
+    (perl-assignment
+     (regexp   . ,(concat "[^=!^&*-+<>/| \t\n]\\(\\s-*\\)=[~>]?"
+			  "\\(\\s-*\\)\\([^>= \t\n]\\|$\\)"))
+     (group    . (1 2))
+     (modes    . align-perl-modes)
+     (tab-stop . nil))
+
+    (make-assignment
+     (regexp   . "^\\s-*\\w+\\(\\s-*\\):?=\\(\\s-*\\)\\([^\t\n \\\\]\\|$\\)")
+     (group    . (1 2))
+     (modes    . '(makefile-mode))
+     (tab-stop . nil))
+
+    (c-comma-delimiter
+     (regexp   . ",\\(\\s-*\\)[^/ \t\n]")
+     (repeat   . t)
+     (modes    . align-c++-modes)
+     (run-if   . ,(function (lambda () current-prefix-arg))))
+;      (valid
+;       . ,(function
+;	  (lambda ()
+;	    (memq (caar (c-guess-basic-syntax))
+;		  '(brace-list-intro
+;		    brace-list-entry
+;		    brace-entry-open))))))
+
+    ;; With a prefix argument, comma delimiter will be aligned.  Since
+    ;; perl-mode doesn't give us enough syntactic information (and we
+    ;; don't do our own parsing yet), this rule is too destructive to
+    ;; run normally.
+    (perl-comma-delimiter
+     (regexp   . ",\\(\\s-*\\)[^# \t\n]")
+     (repeat   . t)
+     (modes    . align-perl-modes)
+     (run-if   . ,(function (lambda () current-prefix-arg))))
+
+    (c++-comment
+     (regexp   . "\\(\\s-*\\)\\(//.*\\|/\\*.*\\*/\\s-*\\)$")
+     (modes    . align-c++-modes)
+     (column   . comment-column)
+     (valid    . ,(function
+		   (lambda ()
+		     (save-excursion
+		       (goto-char (match-beginning 1))
+		       (not (bolp)))))))
+
+    (c-macro-line-continuation
+     (regexp   . "\\(\\s-*\\)\\\\$")
+     (modes    . (append align-c++-modes '(makefile-mode)))
+     (column   . c-backslash-column))
+;      (valid
+;       . ,(function
+;	  (lambda ()
+;	    (memq (caar (c-guess-basic-syntax))
+;		  '(cpp-macro cpp-macro-cont))))))
+
+    (c-chain-logic
+     (regexp   . "\\(\\s-*\\)\\(&&\\|||\\|\\<and\\>\\|\\<or\\>\\)")
+     (modes    . align-c++-modes)
+     (valid    . ,(function
+		   (lambda ()
+		     (save-excursion
+		       (goto-char (match-end 2))
+		       (looking-at "\\s-*\\(/[*/]\\|$\\)"))))))
+
+    (perl-chain-logic
+     (regexp   . "\\(\\s-*\\)\\(&&\\|||\\|\\<and\\>\\|\\<or\\>\\)")
+     (modes    . align-perl-modes)
+     (valid    . ,(function
+		   (lambda ()
+		     (save-excursion
+		       (goto-char (match-end 2))
+		       (looking-at "\\s-*\\(#\\|$\\)"))))))
+
+    (tex-record-separator
+     (regexp . ,(function
+		 (lambda (end reverse)
+		   (align-match-tex-pattern "&" end reverse))))
+     (group    . (1 2))
+     (modes    . align-tex-modes)
+     (repeat   . t))
+
+    (tex-tabbing-separator
+     (regexp   . ,(function
+		   (lambda (end reverse)
+		     (align-match-tex-pattern "\\\\[=>]" end reverse))))
+     (group    . (1 2))
+     (modes    . align-tex-modes)
+     (repeat   . t)
+     (run-if   . ,(function
+		   (lambda ()
+		     (eq major-mode 'latex-mode)))))
+
+    (tex-record-break
+     (regexp   . "\\(\\s-*\\)\\\\\\\\")
+     (modes    . align-tex-modes))
+
+    ;; With a numeric prefix argument, or C-u, space delimited text
+    ;; tables will be aligned.
+    (text-column
+     (regexp   . "\\(^\\|\\S-\\)\\(\\s-+\\)\\(\\S-\\|$\\)")
+     (group    . 2)
+     (modes    . align-text-modes)
+     (repeat   . t)
+     (run-if   . ,(function
+		   (lambda ()
+		     (and current-prefix-arg
+			  (not (eq '- current-prefix-arg)))))))
+
+    ;; With a negative prefix argument, lists of dollar figures will
+    ;; be aligned.
+    (text-dollar-figure
+     (regexp   . "\\$?\\(\\s-+[0-9]+\\)\\.")
+     (modes    . align-text-modes)
+     (justify  . t)
+     (run-if   . ,(function
+		   (lambda ()
+		     (eq '- current-prefix-arg))))))
+  "*An list describing all of the available alignment rules.
+The format is:
+
+   ((TITLE
+     (ATTRIBUTE . VALUE) ...)
+    ...)
+
+The following attributes are meaningful:
+
+`regexp'    This required attribute must be either a string describing
+	    a regular expression, or a function (described below).
+	    For every line within the section that this regular
+	    expression matches, the given rule will be applied to that
+	    line.  The exclusion rules denote which part(s) of the
+	    line should not be modified; the alignment rules cause the
+	    identified whitespace group to be contracted/expanded such
+	    that the \"alignment character\" (the character
+	    immediately following the identified parenthesis group),
+	    occurs in the same column for every line within the
+	    alignment section (see `align-region-separate' for a
+	    description of how the region is broken up into alignment
+	    sections).
+
+	    The `regexp' attribute describes how the text should be
+	    treated.  Within this regexp, there must be at least one
+	    group of characters (typically whitespace) identified by
+	    the special opening and closing parens used in regexp
+	    expressions (`\\\\(' and `\\\\)') (see the Emacs manual on
+	    the syntax of regular expressions for more info).
+
+	    If `regexp' is a function, it will be called as a
+	    replacement for `re-search-forward'.  This means that it
+	    should return nil if nothing is found to match the rule,
+	    or it should set the match data appropriately, move point
+	    to the end of the match, and return the value of point.
+
+`group'     For exclusion rules, the group identifies the range of
+	    characters that should be ignored.  For alignment rules,
+	    these are the characters that will be deleted/expanded for
+	    the purposes of alignment.  The \"alignment character\" is
+	    always the first character immediately following this
+	    parenthesis group.  This attribute may also be a list of
+	    integer, in which case multiple alignment characters will
+	    be aligned, with the list of integer identifying the
+	    whitespace groups which precede them.  The default for
+	    this attribute is 1.
+
+`modes'     The `modes' attribute, if set, should name a list of
+	    major modes -- or evaluate to such a value -- in which the
+	    rule is valid.  If not set, the rule will apply to all
+	    modes.
+
+`case-fold' If `regexp' is an ordinary regular expression string
+	    containing alphabetic character, sometimes you may want
+	    the search to proceed case-insensitively (for languages
+	    that ignore case, such as pascal for example).  In that
+	    case, set `case-fold' to nil, and the regular expression
+	    search will ignore case.  If `regexp' is set to a
+	    function, that function must handle the job of ignoring
+	    case by itself.
+
+`tab-stop'  If the `tab-stop' attribute is set, and non-nil, the
+	    alignment character will always fall on a tab stop
+	    (whether it uses tabs to get there or not depends on the
+	    value of `indent-tabs-mode').  If the `tab-stop' attribute
+	    is set to nil, tab stops will never be used.  Otherwise,
+	    the value of `align-to-tab-stop' determines whether or not
+	    to align to a tab stop.  The `tab-stop' attribute may also
+	    be a list of t or nil values, corresponding to the number
+	    of parenthesis groups specified by the `group' attribute.
+
+`repeat'    If the `repeat' attribute is present, and non-nil, the
+	    rule will be applied to the line continuously until no
+	    further matches are found.
+
+`valid'     If the `valid' attribute is set, it will be used to
+	    determine whether the rule should be invoked.  This form
+	    is evaluated after the regular expression match has been
+	    performed, so that it is possible to use the results of
+	    that match to determine whether the alignment should be
+	    performed.  The buffer should not be modified during the
+	    evaluation of this form.
+
+`run-if'    Like `valid', the `run-if' attribute tests whether the
+	    rule should be run at all -- even before any searches are
+	    done to determine if the rule applies to the alignment
+	    region.  This can save time, since `run-if' will only be
+	    run once for each rule.  If it returns nil, the rule will
+	    not be attempted.
+
+`column'    For alignment rules, if the `column' attribute is set --
+	    which must be an integer, or a symbol whose value is an
+	    integer -- it will be used as the column in which to align
+	    the alignment character.  If the text on a particular line
+	    happens to overrun that column, a single space character,
+	    or tab stop (see `align-to-tab-stop') will be added
+	    between the last text character and the alignment
+	    character.
+
+`spacing'   Alignment rules may also override the amount of spacing
+	    that would normally be used by providing a `spacing'
+	    attribute.  This must be an integer, or a list of integers
+	    corresponding to the number of parenthesis groups matched
+	    by the `group' attribute.  If a list of value is used, and
+	    any of those values is nil, `align-default-spacing' will
+	    be used for that subgroup.  See `align-default-spacing'
+	    for more details on spacing, tab stops, and how to
+	    indicate how much spacing should be used.  If TAB-STOP is
+	    present, it will override the value of `align-to-tab-stop'
+	    for that rule.
+
+`justify'   It is possible with `regexp' and `group' to identify a
+	    character group that contains more than just whitespace
+	    characters.  By default, any non-whitespace characters in
+	    that group will also be deleted while aligning the
+	    alignment character.  However, if the `justify' attribute
+	    is set to a non-nil value, only the initial whitespace
+	    characters within that group will be deleted.  This has
+	    the effect of right-justifying the characters that remain,
+	    and can be used for outdenting or just plain old right-
+	    justification.
+
+`separate'  Each rule can define its own section separator, which
+	    describes how to identify the separation of \"sections\"
+	    within the region to be aligned.  Setting the `separate'
+	    attribute overrides the value of `align-region-separate'
+	    (see the documentation of that variable for possible
+	    values), and any separation argument passed to `align'."
+  :type align-rules-list-type
+  :group 'align)
+
+(put 'align-rules-list 'risky-local-variable t)
+
+(defvar align-exclude-rules-list-type
+  '(repeat
+    (cons
+     :tag "Exclusion rule"
+     (symbol :tag "Title")
+     (cons :tag "Required attributes"
+	   (cons :tag "Regexp"
+		 (const :tag "(Regular expression to match)" regexp)
+		 (choice :value "\\(\\s-+\\)" regexp function))
+	   (repeat
+	    :tag "Optional attributes"
+	    (choice
+	     (cons :tag "Repeat"
+		   (const :tag "(Repeat this rule throughout line)"
+			  repeat)
+		   (boolean :value t))
+	     (cons :tag "Paren group"
+		   (const :tag "(Parenthesis group to use)" group)
+		   (choice :value 2
+			   integer (repeat integer)))
+	     (cons :tag "Modes"
+		   (const :tag "(Modes where this rule applies)" modes)
+		   (sexp :value (text-mode)))
+	     (cons :tag "Case-fold"
+		   (const :tag "(Should case be ignored for this rule)"
+			  case-fold)
+		   (boolean :value t)))))))
+  "The `type' form for any `align-exclude-rules-list' variable.")
+
+(defcustom align-exclude-rules-list
+  `((exc-dq-string
+     (regexp . "\"\\([^\"\n]+\\)\"")
+     (repeat . t)
+     (modes  . align-dq-string-modes))
+
+    (exc-sq-string
+     (regexp . "'\\([^'\n]+\\)'")
+     (repeat . t)
+     (modes  . align-sq-string-modes))
+
+    (exc-open-comment
+     (regexp
+      . ,(function
+	  (lambda (end reverse)
+	    (funcall (if reverse 're-search-backward
+		       're-search-forward)
+		     (concat "[^ \t\n\\\\]"
+			     (regexp-quote comment-start)
+			     "\\(.+\\)$") end t))))
+     (modes  . align-open-comment-modes))
+
+    (exc-c-comment
+     (regexp . "/\\*\\(.+\\)\\*/")
+     (repeat . t)
+     (modes  . align-c++-modes))
+
+    (exc-c-func-params
+     (regexp . "(\\([^)\n]+\\))")
+     (repeat . t)
+     (modes  . align-c++-modes))
+
+    (exc-c-macro
+     (regexp . "^\\s-*#\\s-*\\(if\\w*\\|endif\\)\\(.*\\)$")
+     (group  . 2)
+     (modes  . align-c++-modes)))
+  "*An list describing text that should be excluded from alignment.
+See the documentation for `align-rules-list' for more info."
+  :type align-exclude-rules-list-type
+  :group 'align)
+
+(put 'align-exclude-rules-list 'risky-local-variable t)
+
+;;; Internal Variables:
+
+(defvar align-mode-rules-list nil
+  "Alignment rules specific to the current major mode.
+See the variable `align-rules-list' for more details.")
+
+(make-variable-buffer-local 'align-mode-rules-list)
+
+(defvar align-mode-exclude-rules-list nil
+  "Alignment exclusion rules specific to the current major mode.
+See the variable `align-exclude-rules-list' for more details.")
+
+(make-variable-buffer-local 'align-mode-exclude-rules-list)
+
+(defvar align-highlight-overlays nil
+  "The current overlays highlighting the text matched by a rule.")
+
+;; Sample extension rule set, for vhdl-mode.  This should properly be
+;; in vhdl-mode.el itself.
+
+(defcustom align-vhdl-rules-list
+  `((vhdl-declaration
+     (regexp   . "\\(signal\\|variable\\|constant\\)\\(\\s-+\\)\\S-")
+     (group    . 2))
+
+    (vhdl-case
+     (regexp   . "\\(others\\|[^ \t\n=<]\\)\\(\\s-*\\)=>\\(\\s-*\\)\\S-")
+     (group    . (2 3))
+     (valid
+      . ,(function
+	  (lambda ()
+	    (not (string= (downcase (match-string 1))
+			  "others"))))))
+
+    (vhdl-colon
+     (regexp   . "[^ \t\n:]\\(\\s-*\\):\\(\\s-*\\)[^=\n]")
+     (group    . (1 2)))
+
+    (direction
+     (regexp   . ":\\s-*\\(in\\|out\\|inout\\|buffer\\)\\(\\s-*\\)")
+     (group    . 2))
+
+    (sig-assign
+     (regexp   . "[^ \t\n=<]\\(\\s-*\\)<=\\(\\s-*\\)\\S-")
+     (group    . (1 2)))
+
+    (var-assign
+     (regexp   . "[^ \t\n:]\\(\\s-*\\):="))
+
+    (use-entity
+     (regexp   . "\\(\\s-+\\)use\\s-+entity")))
+  "*Alignment rules for `vhdl-mode'.  See `align-rules-list' for more info."
+  :type align-rules-list-type
+  :group 'align)
+
+(put 'align-vhdl-rules-list 'risky-local-variable t)
+
+(defun align-set-vhdl-rules ()
+  "Setup the `align-mode-rules-list' variable for `vhdl-mode'."
+  (setq align-mode-rules-list align-vhdl-rules-list))
+
+(add-hook 'vhdl-mode-hook 'align-set-vhdl-rules)
+
+(add-to-list 'align-dq-string-modes 'vhdl-mode)
+(add-to-list 'align-open-comment-modes 'vhdl-mode)
+
+;;; User Functions:
+
+;;;###autoload
+(defun align (beg end &optional separate rules exclude-rules)
+  "Attempt to align a region based on a set of alignment rules.
+BEG and END mark the region.  If BEG and END are specifically set to
+nil (this can only be done programmatically), the beginning and end of
+the current alignment section will be calculated based on the location
+of point, and the value of `align-region-separate' (or possibly each
+rule's `separate' attribute).
+
+If SEPARATE is non-nil, it overrides the value of
+`align-region-separate' for all rules, except those that have their
+`separate' attribute set.
+
+RULES and EXCLUDE-RULES, if either is non-nil, will replace the
+default rule lists defined in `align-rules-list' and
+`align-exclude-rules-list'.  See `align-rules-list' for more details
+on the format of these lists."
+  (interactive "r")
+  (let ((separator
+	 (or separate
+	     (if (symbolp align-region-separate)
+		 (symbol-value align-region-separate)
+	       align-region-separate)
+	     'entire)))
+    (if (not (or ;(eq separator 'largest)
+		 (and (functionp separator)
+		      (not (funcall separator nil nil)))))
+	(align-region beg end separator
+		      (or rules align-mode-rules-list align-rules-list)
+		      (or exclude-rules align-mode-exclude-rules-list
+			  align-exclude-rules-list))
+      (let ((sec-first end)
+	    (sec-last beg))
+	(align-region beg end
+		      (or exclude-rules
+			  align-mode-exclude-rules-list
+			  align-exclude-rules-list) nil
+		      separator
+		      (function
+		       (lambda (b e mode)
+			 (when (and mode (listp mode))
+			   (setq sec-first (min sec-first b)
+				 sec-last  (max sec-last e))))))
+	(if (< sec-first sec-last)
+	    (align-region sec-first sec-last 'entire
+			  (or rules align-mode-rules-list align-rules-list)
+			  (or exclude-rules align-mode-exclude-rules-list
+			      align-exclude-rules-list)))))))
+
+;;;###autoload
+(defun align-regexp (beg end regexp &optional group spacing repeat)
+  "Align the current region using an ad-hoc rule read from the minibuffer.
+BEG and END mark the limits of the region.  This function will prompt
+for the REGEXP to align with.  If no prefix arg was specified, you
+only need to supply the characters to be lined up and any preceding
+whitespace is replaced.  If a prefix arg was specified, the full
+regexp with parenthesized whitespace should be supplied; it will also
+prompt for which parenthesis GROUP within REGEXP to modify, the amount
+of SPACING to use, and whether or not to REPEAT the rule throughout
+the line.  See `align-rules-list' for more information about these
+options.
+
+For example, let's say you had a list of phone numbers, and wanted to
+align them so that the opening parentheses would line up:
+
+    Fred (123) 456-7890
+    Alice (123) 456-7890
+    Mary-Anne (123) 456-7890
+    Joe (123) 456-7890
+
+There is no predefined rule to handle this, but you could easily do it
+using a REGEXP like \"(\". All you would have to do is to mark the
+region, call `align-regexp' and type in that regular expression."
+  (interactive
+   (append
+    (list (min (point) (mark))
+	  (max (point) (mark)))
+    (if current-prefix-arg
+	(list (read-string "Complex align using regexp: "
+			   "\\(\\s-*\\)")
+	      (string-to-int
+	       (read-string
+		"Parenthesis group to modify (justify if negative): " "1"))
+	      (string-to-int
+	       (read-string "Amount of spacing (or column if negative): "
+			    (number-to-string align-default-spacing)))
+	      (y-or-n-p "Repeat throughout line? "))
+      (list (concat "\\(\\s-*\\)"
+		    (read-string "Align regexp: "))
+	    1 align-default-spacing nil))))
+  (let ((rule
+	 (list (list nil (cons 'regexp regexp)
+		     (cons 'group (abs group))
+		     (if (< group 0)
+			 (cons 'justify t)
+		       (cons 'bogus nil))
+		     (if (>= spacing 0)
+			 (cons 'spacing spacing)
+		       (cons 'column (abs spacing)))
+		     (cons 'repeat repeat)))))
+    (align-region beg end 'entire rule nil nil)))
+
+;;;###autoload
+(defun align-entire (beg end &optional rules exclude-rules)
+  "Align the selected region as if it were one alignment section.
+BEG and END mark the extent of the region.  If RULES or EXCLUDE-RULES
+is set to a list of rules (see `align-rules-list'), it can be used to
+override the default alignment rules that would have been used to
+align that section."
+  (interactive "r")
+  (align beg end 'entire rules exclude-rules))
+
+;;;###autoload
+(defun align-current (&optional rules exclude-rules)
+  "Call `align' on the current alignment section.
+This function assumes you want to align only the current section, and
+so saves you from having to specify the region.  If RULES or
+EXCLUDE-RULES is set to a list of rules (see `align-rules-list'), it
+can be used to override the default alignment rules that would have
+been used to align that section."
+  (interactive)
+  (align nil nil nil rules exclude-rules))
+
+;;;###autoload
+(defun align-highlight-rule (beg end title &optional rules exclude-rules)
+  "Highlight the whitespace which a given rule would have modified.
+BEG and END mark the extent of the region.  TITLE identifies the rule
+that should be highlighted.  If RULES or EXCLUDE-RULES is set to a
+list of rules (see `align-rules-list'), it can be used to override the
+default alignment rules that would have been used to identify the text
+to be colored."
+  (interactive
+   (list (min (mark) (point))
+	 (max (mark) (point))
+	 (completing-read
+	  "Title of rule to highlight: "
+	  (mapcar
+	   (function
+	    (lambda (rule)
+	      (list (symbol-name (car rule)))))
+	   (append (or align-mode-rules-list align-rules-list)
+		   (or align-mode-exclude-rules-list
+		       align-exclude-rules-list))) nil t)))
+  (let ((ex-rule (assq (intern title)
+		       (or align-mode-exclude-rules-list
+			   align-exclude-rules-list)))
+	face)
+    (align-unhighlight-rule)
+    (align-region
+     beg end 'entire
+     (or rules (if ex-rule
+		   (or exclude-rules align-mode-exclude-rules-list
+		       align-exclude-rules-list)
+		 (or align-mode-rules-list align-rules-list)))
+     (unless ex-rule (or exclude-rules align-mode-exclude-rules-list
+			 align-exclude-rules-list))
+     (function
+      (lambda (b e mode)
+	(if (and mode (listp mode))
+	    (if (equal (symbol-name (car mode)) title)
+		(setq face (cons align-highlight-change-face
+				 align-highlight-nochange-face))
+	      (setq face nil))
+	  (when face
+	    (let ((overlay (make-overlay b e)))
+	      (setq align-highlight-overlays
+		    (cons overlay align-highlight-overlays))
+	      (overlay-put overlay 'face
+			   (if mode
+			       (car face)
+			     (cdr face)))))))))))
+
+;;;###autoload
+(defun align-unhighlight-rule ()
+  "Remove any highlighting that was added by `align-highlight-rule'."
+  (interactive)
+  (while align-highlight-overlays
+    (delete-overlay (car align-highlight-overlays))
+    (setq align-highlight-overlays
+	  (cdr align-highlight-overlays))))
+
+;;; Internal Functions:
+
+(defun align-match-tex-pattern (regexp end &optional reverse)
+  "Match REGEXP in TeX mode, counting backslashes appropriately.
+END denotes the end of the region to be searched, while REVERSE, if
+non-nil, indicates that the search should proceed backward from the
+current position."
+  (let (result)
+    (while
+	(and (setq result
+		   (funcall
+		    (if reverse 're-search-backward
+		      're-search-forward)
+		    (concat "\\(\\s-*\\)" regexp
+			    "\\(\\s-*\\)") end t))
+	     (let ((pos (match-end 1))
+		   (count 0))
+	       (while (and (> pos (point-min))
+			   (eq (char-before pos) ?\\))
+		 (setq count (1+ count) pos (1- pos)))
+	       (eq (mod count 2) 1))
+	     (goto-char (match-beginning 2))))
+    result))
+
+(defun align-new-section-p (beg end separator)
+  "Is there a section divider between BEG and END?
+SEPARATOR specifies how to look for the section divider.  See the
+documentation for `align-region-separate' for more details."
+  (cond ((or (not separator)
+	     (eq separator 'entire))
+	 nil)
+	((eq separator 'group)
+	 (let ((amount 2))
+	   (save-excursion
+	     (goto-char end)
+	     (if (bolp)
+		 (setq amount 1)))
+	   (> (count-lines beg end) amount)))
+	((stringp separator)
+	 (save-excursion
+	   (goto-char beg)
+	   (re-search-forward separator end t)))
+	((functionp separator)
+	 (funcall separator beg end))
+	((listp separator)
+	 (let ((seps separator) yes)
+	   (while seps
+	     (if (and (>= (car seps) beg)
+		      (<= (car seps) end))
+		 (setq yes t seps nil)
+	     (setq seps (cdr seps))))
+	   yes))))
+
+(defun align-adjust-col-for-rule (column rule spacing tab-stop)
+  "Adjust COLUMN according to the given RULE.
+SPACING specifies how much spacing to use.
+TAB-STOP specifies whether SPACING refers to tab-stop boundaries."
+  (unless spacing
+    (setq spacing align-default-spacing))
+  (if (<= spacing 0)
+      column
+    (if (not tab-stop)
+	(+ column spacing)
+      (let ((stops tab-stop-list))
+	(while stops
+	  (if (and (> (car stops) column)
+		   (= (setq spacing (1- spacing)) 0))
+	      (setq column (car stops)
+		    stops nil)
+	    (setq stops (cdr stops)))))
+      column)))
+
+(defsubst align-column (pos)
+  "Given a position in the buffer, state what column it's in.
+POS is the position whose column will be taken.  Note that this
+function will change the location of point."
+  (goto-char pos)
+  (current-column))
+
+(defsubst align-regions (regions props rule func)
+  "Align the regions specified in REGIONS, a list of cons cells.
+PROPS describes formatting features specific to the given regions.
+RULE specifies exactly how to perform the alignments.
+If FUNC is specified, it will be called with each region that would
+have been aligned, rather than modifying the text."
+  (while regions
+    (save-excursion
+      (align-areas (car regions) (car props) rule func))
+    (setq regions (cdr regions)
+	  props (cdr props))))
+
+(defun align-areas (areas props rule func)
+  "Given a list of AREAS and formatting PROPS, align according to RULE.
+AREAS should be a list of cons cells containing beginning and ending
+markers.  This function sweeps through all of the beginning markers,
+finds out which one starts in the furthermost column, and then deletes
+and inserts text such that all of the ending markers occur in the same
+column.
+
+If FUNC is non-nil, it will be called for each text region that would
+have been aligned.  No changes will be made to the buffer."
+  (let* ((column (cdr (assq 'column rule)))
+	 (fixed (if (symbolp column)
+		    (symbol-value column)
+		  column))
+	 (justify (cdr (assq 'justify rule)))
+	 (col (or fixed 0))
+	 (width 0)
+	 ecol change look)
+
+    ;; Determine the alignment column.
+    (let ((a areas))
+      (while a
+	(unless fixed
+	  (setq col (max col (align-column (caar a)))))
+	(unless change
+	  (goto-char (cdar a))
+	  (if ecol
+	      (if (not (= ecol (current-column)))
+		  (setq change t))
+	    (setq ecol (current-column))))
+	(when justify
+	  (goto-char (caar a))
+	  (if (and (re-search-forward "\\s-*" (cdar a) t)
+		   (not (= (point) (cdar a))))
+	      (let ((bcol (current-column)))
+		(setcdr (car a) (cons (point-marker) (cdar a)))
+		(goto-char (cdr (cdar a)))
+		(setq width (max width (- (current-column) bcol))))))
+	(setq a (cdr a))))
+
+    (unless fixed
+      (setq col (+ (align-adjust-col-for-rule
+		    col rule (car props) (cdr props)) width)))
+
+    ;; Make all ending positions to occur in the goal column.  Since
+    ;; the whitespace to be modified was already deleted by
+    ;; `align-region', all we have to do here is indent.
+
+    (unless change
+      (setq change (and ecol (not (= col ecol)))))
+
+    (when (or func change)
+      (while areas
+	(let ((area (car areas))
+	      (gocol col) cur)
+	  (when area
+	    (if func
+		(funcall func (car area) (cdr area) change)
+	      (if (not (and justify
+			    (consp (cdr area))))
+		  (goto-char (cdr area))
+		(goto-char (cddr area))
+		(let ((ecol (current-column)))
+		  (goto-char (cadr area))
+		  (setq gocol (- col (- ecol (current-column))))))
+	      (setq cur (current-column))
+	      (cond ((< gocol 0) t)     ; don't do anything
+		    ((= cur gocol) t)   ; don't need to
+		    ((< cur gocol)      ; just add space
+		     (indent-to gocol))
+		    (t
+		     ;; This code works around an oddity in the
+		     ;; FORCE argument of `move-to-column', which
+		     ;; tends to screw up markers if there is any
+		     ;; tabbing.
+		     (let ((endcol (align-column
+				    (if (and justify
+					     (consp (cdr area)))
+					(cadr area)
+				      (cdr area))))
+			   (abuts (<= gocol
+				      (align-column (car area)))))
+		       (if abuts
+			   (goto-char (car area))
+			 (move-to-column gocol t))
+		       (let ((here (point)))
+			 (move-to-column endcol t)
+			 (delete-region here (point))
+			 (if abuts
+			     (indent-to (align-adjust-col-for-rule
+					 (current-column) rule
+					 (car props) (cdr props)))))))))))
+	(setq areas (cdr areas))))))
+
+(defun align-region (beg end separate rules exclude-rules
+			 &optional func)
+  "Align a region based on a given set of alignment rules.
+BEG and END specify the region to be aligned.  Either may be nil, in
+which case the range will stop at the nearest section division (see
+`align-region-separate', and `align-region-heuristic' for more
+information').
+
+The region will be divided into separate alignment sections based on
+the value of SEPARATE.
+
+RULES and EXCLUDE-RULES are a pair of lists describing how to align
+the region, and which text areas within it should be excluded from
+alignment.  See the `align-rules-list' for more information on the
+required format of these two lists.
+
+If FUNC is specified, no text will be modified.  What `align-region'
+will do with the rules is to search for the alignment areas, as it
+regularly would, taking account for exclusions, and then call FUNC,
+first with the beginning and ending of the region to be aligned
+according to that rule (this can be different for each rule, if BEG
+and END were nil), and then with the beginning and ending of each
+text region that the rule would have applied to.
+
+The signature of FUNC should thus be:
+
+ (defun my-align-function (beg end mode)
+   \"If MODE is a rule (a list), return t if BEG to END are to be searched.
+Otherwise BEG to END will be a region of text that matches the rule's
+definition, and MODE will be non-nil if any changes are necessary.\"
+   (unless (and mode (listp mode))
+     (message \"Would have aligned from %d to %d...\" beg end)))
+
+This feature (of passing a FUNC) is used internally to locate the
+position of exclusion areas, but could also be used for any other
+purpose where you might want to know where the regions that the
+aligner would have dealt with are."
+  (let ((end-mark (and end (copy-marker end t)))
+	(real-beg beg)
+	(real-end end)
+	(report (and (not func) align-large-region beg end
+		     (>= (- end beg) align-large-region)))
+	(rule-index 1)
+	(rule-count (length rules)))
+    (if (and align-indent-before-aligning real-beg end-mark)
+	(indent-region real-beg end-mark nil))
+    (while rules
+      (let* ((rule (car rules))
+	     (run-if (assq 'run-if rule))
+	     (modes (assq 'modes rule)))
+	;; unless the `run-if' form tells us not to, look for the
+	;; rule..
+	(unless (or (and modes (not (memq major-mode
+					  (eval (cdr modes)))))
+		    (and run-if (not (funcall (cdr run-if)))))
+	  (let* ((current-case-fold case-fold-search)
+		 (case-fold (assq 'case-fold rule))
+		 (regexp  (cdr (assq 'regexp rule)))
+		 (regfunc (and (functionp regexp) regexp))
+		 (rulesep (assq 'separate rule))
+		 (thissep (if rulesep (cdr rulesep) separate))
+		 same (eol 0)
+		 group group-c
+		 spacing spacing-c
+		 tab-stop tab-stop-c
+		 repeat repeat-c
+		 valid valid-c
+		 pos-list first
+		 regions index
+		 last-point b e
+		 save-match-data
+		 exclude-p
+		 align-props)
+	    (save-excursion
+	      ;; if beg and end were not given, figure out what the
+	      ;; current alignment region should be.  Depending on the
+	      ;; value of `align-region-separate' it's possible for
+	      ;; each rule to have its own definition of what that
+	      ;; current alignment section is.
+	      (if real-beg
+		  (goto-char beg)
+		(if (or (not thissep) (eq thissep 'entire))
+		    (error "Cannot determine alignment region for '%s'"
+			   (symbol-name (cdr (assq 'title rule)))))
+		(beginning-of-line)
+		(while (and (not (eobp))
+			    (looking-at "^\\s-*$"))
+		  (forward-line))
+		(let* ((here (point))
+		       (start here))
+		  (while (and here
+			      (let ((terminus
+				     (and align-region-heuristic
+					  (- (point)
+					     align-region-heuristic))))
+				(if regfunc
+				    (funcall regfunc terminus t)
+				  (re-search-backward regexp
+						      terminus t))))
+		    (if (align-new-section-p (point) here thissep)
+			(setq beg here
+			      here nil)
+		      (setq here (point))))
+		  (if (not here)
+		      (goto-char beg))
+		  (beginning-of-line)
+		  (setq beg (point))
+		  (goto-char start)
+		  (setq here (point))
+		  (while (and here
+			      (let ((terminus
+				     (and align-region-heuristic
+					  (+ (point)
+					     align-region-heuristic))))
+				(if regfunc
+				    (funcall regfunc terminus nil)
+				  (re-search-forward regexp terminus t))))
+		    (if (align-new-section-p here (point) thissep)
+			(setq end here
+			      here nil)
+		      (setq here (point))))
+		  (if (not here)
+		      (goto-char end))
+		  (forward-line)
+		  (setq end (point)
+			end-mark (copy-marker end t))
+		  (goto-char beg)))
+
+	      ;; If we have a region to align, and `func' is set and
+	      ;; reports back that the region is ok, then align it.
+	      (when (or (not func)
+			(funcall func beg end rule))
+		(unwind-protect
+		    (let (exclude-areas)
+		      ;; determine first of all where the exclusions
+		      ;; lie in this region
+		      (when exclude-rules
+			;; guard against a problem with recursion and
+			;; dynamic binding vs. lexical binding, since
+			;; the call to `align-region' below will
+			;; re-enter this function, and rebind
+			;; `exclude-areas'
+			(set (setq exclude-areas
+				   (make-symbol "align-exclude-areas"))
+			     nil)
+			(align-region
+			 beg end 'entire
+			 exclude-rules nil
+			 `(lambda (b e mode)
+			    (or (and mode (listp mode))
+				(set (quote ,exclude-areas)
+				     (cons (cons b e)
+					   ,exclude-areas)))))
+			(setq exclude-areas
+			      (sort (symbol-value exclude-areas)
+				    (function
+				     (lambda (l r)
+				       (>= (car l) (car r)))))))
+
+		      ;; set `case-fold-search' according to the
+		      ;; (optional) `case-fold' property
+		      (and case-fold
+			   (setq case-fold-search (cdr case-fold)))
+
+		      ;; while we can find the rule in the alignment
+		      ;; region..
+		      (while (and (< (point) end-mark)
+				  (if regfunc
+				      (funcall regfunc end-mark nil)
+				    (re-search-forward regexp
+						       end-mark t)))
+
+			;; give the user some indication of where we
+			;; are, if it's a very large region being
+			;; aligned
+			(if report
+			    (message
+			     "Aligning `%s' [rule %d of %d] (%d%%)..."
+			     (symbol-name (car rule))
+			     rule-index rule-count
+			     (/ (* (- (point) real-beg) 100)
+				(- end-mark real-beg))))
+
+			;; if the search ended us on the beginning of
+			;; the next line, move back to the end of the
+			;; previous line.
+			(if (bolp)
+			    (forward-char -1))
+
+			;; lookup the `group' attribute the first time
+			;; that we need it
+			(unless group-c
+			  (setq group (or (cdr (assq 'group rule)) 1))
+			  (if (listp group)
+			      (setq first (car group))
+			    (setq first group group (list group)))
+			  (setq group-c t))
+
+			(unless spacing-c
+			  (setq spacing (cdr (assq 'spacing rule))
+				spacing-c t))
+
+			(unless tab-stop-c
+			  (setq tab-stop
+				(let ((rule-ts (assq 'tab-stop rule)))
+				  (if rule-ts
+				      (cdr rule-ts)
+				    (if (symbolp align-to-tab-stop)
+					(symbol-value align-to-tab-stop)
+				      align-to-tab-stop)))
+				tab-stop-c t))
+
+			;; test whether we have found a match on the same
+			;; line as a previous match
+			(if (> (point) eol)
+			    (setq same nil
+				  eol (save-excursion
+					(end-of-line)
+					(point-marker))))
+
+			;; lookup the `repeat' attribute the first time
+			(or repeat-c
+			    (setq repeat (cdr (assq 'repeat rule))
+				  repeat-c t))
+
+			;; lookup the `valid' attribute the first time
+			(or valid-c
+			    (setq valid (assq 'valid rule)
+				  valid-c t))
+
+			;; remember the beginning position of this rule
+			;; match, and save the match-data, since either
+			;; the `valid' form, or the code that searches for
+			;; section separation, might alter it
+			(setq b (match-beginning first)
+			      save-match-data (match-data))
+
+			;; unless the `valid' attribute is set, and tells
+			;; us that the rule is not valid at this point in
+			;; the code..
+			(unless (and valid (not (funcall (cdr valid))))
+
+			  ;; look to see if this match begins a new
+			  ;; section.  If so, we should align what we've
+			  ;; collected so far, and then begin collecting
+			  ;; anew for the next alignment section
+			  (if (and last-point
+				   (align-new-section-p last-point b
+							thissep))
+			      (progn
+				(align-regions regions align-props
+					       rule func)
+				(setq last-point (copy-marker b t)
+				      regions nil
+				      align-props nil))
+			    (setq last-point (copy-marker b t)))
+
+			  ;; restore the match data
+			  (set-match-data save-match-data)
+
+			  ;; check whether the region to be aligned
+			  ;; straddles an exclusion area
+			  (let ((excls exclude-areas))
+			    (setq exclude-p nil)
+			    (while excls
+			      (if (and (< (match-beginning (car group))
+					  (cdar excls))
+				       (> (match-end (car (last group)))
+					  (caar excls)))
+				  (setq exclude-p t
+					excls nil)
+				(setq excls (cdr excls)))))
+
+			  ;; go through the list of parenthesis groups
+			  ;; matching whitespace text to be
+			  ;; contracted/expanded (or possibly
+			  ;; justified, if the `justify' attribute was
+			  ;; set)
+			  (unless exclude-p
+			    (let ((g group))
+			      (while g
+
+				;; we have to use markers, since
+				;; `align-areas' may modify the buffer
+				(setq b (copy-marker
+					 (match-beginning (car g)) t)
+				      e (copy-marker (match-end (car g)) t))
+
+				;; record this text region for alignment
+				(setq index (if same (1+ index) 0))
+				(let ((region (cons b e))
+				      (props (cons
+					      (if (listp spacing)
+						  (car spacing)
+						spacing)
+					      (if (listp tab-stop)
+						  (car tab-stop)
+						tab-stop))))
+				  (if (nth index regions)
+				      (setcar (nthcdr index regions)
+					      (cons region
+						    (nth index regions)))
+				    (if regions
+					(progn
+					  (nconc regions
+						 (list (list region)))
+					  (nconc align-props (list props)))
+				      (setq regions
+					    (list (list region)))
+				      (setq align-props (list props)))))
+
+				;; if any further rule matches are
+				;; found before `eol', then they are
+				;; on the same line as this one; this
+				;; can only happen if the `repeat'
+				;; attribute is non-nil
+				(if (listp spacing)
+				    (setq spacing (cdr spacing)))
+				(if (listp tab-stop)
+				    (setq tab-stop (cdr tab-stop)))
+				(setq same t g (cdr g))))
+
+			    ;; if `repeat' has not been set, move to
+			    ;; the next line; don't bother searching
+			    ;; anymore on this one
+			    (if (and (not repeat) (not (bolp)))
+				(forward-line)))))
+
+		      ;; when they are no more matches for this rule,
+		      ;; align whatever was left over
+		      (if regions
+			  (align-regions regions align-props rule func)))
+
+		  (setq case-fold-search current-case-fold)))))))
+      (setq rules (cdr rules)
+	    rule-index (1+ rule-index)))
+
+    (if report
+	(message "Aligning...done"))))
+
+;; Provide:
+
+(provide 'align)
+
+(run-hooks 'align-load-hook)
+
+;;; align.el ends here
--- a/lisp/cus-load.el	Fri Jan 14 12:05:52 2000 +0000
+++ b/lisp/cus-load.el	Fri Jan 14 14:02:21 2000 +0000
@@ -173,6 +173,7 @@
 (put 'vip 'custom-loads '("vip"))
 (put 'smerge 'custom-loads '("smerge-mode"))
 (put 'spell 'custom-loads '("spell"))
+(put 'align 'custom-loads '("align"))
 (put 'rmail-headers 'custom-loads '("rmail"))
 (put 'gnus-score-decay 'custom-loads '("gnus-score"))
 (put 'idlwave-shell-initial-commands 'custom-loads '("idlw-shell"))
@@ -436,7 +437,7 @@
 (put 'customize 'custom-loads '("wid-edit" "cus-edit"))
 (put 'dired 'custom-loads '("files" "dired-aux" "dired-x" "dired" "find-dired"))
 (put 'recentf 'custom-loads '("recentf"))
-(put 'fill 'custom-loads '("simple" "fill"))
+(put 'fill 'custom-loads '("simple" "fill" "align"))
 (put 'ps-print-header 'custom-loads '("ps-print"))
 (put 'outlines 'custom-loads '("allout" "outline"))
 (put 'paragraphs 'custom-loads '("paragraphs"))
--- a/lisp/loaddefs.el	Fri Jan 14 12:05:52 2000 +0000
+++ b/lisp/loaddefs.el	Fri Jan 14 14:02:21 2000 +0000
@@ -306,6 +306,80 @@
 
 ;;;***
 
+;;;### (autoloads (align-unhighlight-rule align-highlight-rule align-current
+;;;;;;  align-entire align-regexp align) "align" "align.el" (14463
+;;;;;;  7197))
+;;; Generated autoloads from align.el
+
+(autoload (quote align) "align" "\
+Attempt to align a region based on a set of alignment rules.
+BEG and END mark the region.  If BEG and END are specifically set to
+nil (this can only be done programmatically), the beginning and end of
+the current alignment section will be calculated based on the location
+of point, and the value of `align-region-separate' (or possibly each
+rule's `separate' attribute).
+
+If SEPARATE is non-nil, it overrides the value of
+`align-region-separate' for all rules, except those that have their
+`separate' attribute set.
+
+RULES and EXCLUDE-RULES, if either is non-nil, will replace the
+default rule lists defined in `align-rules-list' and
+`align-exclude-rules-list'.  See `align-rules-list' for more details
+on the format of these lists." t nil)
+
+(autoload (quote align-regexp) "align" "\
+Align the current region using an ad-hoc rule read from the minibuffer.
+BEG and END mark the limits of the region.  This function will prompt
+for the REGEXP to align with.  If no prefix arg was specified, you
+only need to supply the characters to be lined up and any preceding
+whitespace is replaced.  If a prefix arg was specified, the full
+regexp with parenthesized whitespace should be supplied; it will also
+prompt for which parenthesis GROUP within REGEXP to modify, the amount
+of SPACING to use, and whether or not to REPEAT the rule throughout
+the line.  See `align-rules-list' for more information about these
+options.
+
+For example, let's say you had a list of phone numbers, and wanted to
+align them so that the opening parentheses would line up:
+
+    Fred (123) 456-7890
+    Alice (123) 456-7890
+    Mary-Anne (123) 456-7890
+    Joe (123) 456-7890
+
+There is no predefined rule to handle this, but you could easily do it
+using a REGEXP like \"(\". All you would have to do is to mark the
+region, call `align-regexp' and type in that regular expression." t nil)
+
+(autoload (quote align-entire) "align" "\
+Align the selected region as if it were one alignment section.
+BEG and END mark the extent of the region.  If RULES or EXCLUDE-RULES
+is set to a list of rules (see `align-rules-list'), it can be used to
+override the default alignment rules that would have been used to
+align that section." t nil)
+
+(autoload (quote align-current) "align" "\
+Call `align' on the current alignment section.
+This function assumes you want to align only the current section, and
+so saves you from having to specify the region.  If RULES or
+EXCLUDE-RULES is set to a list of rules (see `align-rules-list'), it
+can be used to override the default alignment rules that would have
+been used to align that section." t nil)
+
+(autoload (quote align-highlight-rule) "align" "\
+Highlight the whitespace which a given rule would have modified.
+BEG and END mark the extent of the region.  TITLE identifies the rule
+that should be highlighted.  If RULES or EXCLUDE-RULES is set to a
+list of rules (see `align-rules-list'), it can be used to override the
+default alignment rules that would have been used to identify the text
+to be colored." t nil)
+
+(autoload (quote align-unhighlight-rule) "align" "\
+Remove any highlighting that was added by `align-highlight-rule'." t nil)
+
+;;;***
+
 ;;;### (autoloads (ange-ftp-hook-function ange-ftp-reread-dir) "ange-ftp"
 ;;;;;;  "ange-ftp.el" (14460 38616))
 ;;; Generated autoloads from ange-ftp.el