]> code.delx.au - gnu-emacs/blobdiff - lisp/align.el
Update docs for `customize-mode'
[gnu-emacs] / lisp / align.el
index 0af5e56c6689f89bc12dd9e822d1ac5a3888e4d7..f09f57032d44a737ba74f683a18c0d7339af2dc6 100644 (file)
@@ -1,9 +1,9 @@
-;;; align.el --- align text to a specific column, by regexp
+;;; align.el --- align text to a specific column, by regexp -*- lexical-binding:t -*-
 
-;; Copyright (C) 1999-201 Free Software Foundation, Inc.
+;; Copyright (C) 1999-2016 Free Software Foundation, Inc.
 
 ;; Author: John Wiegley <johnw@gnu.org>
-;; Maintainer: FSF
+;; Maintainer: emacs-devel@gnu.org
 ;; Keywords: convenience languages lisp
 
 ;; This file is part of GNU Emacs.
@@ -888,15 +888,15 @@ on the format of these lists."
       (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))))))
+                     nil ; rules
+                      (or exclude-rules
+                         align-mode-exclude-rules-list
+                         align-exclude-rules-list)
+                      (lambda (b e mode)
+                        (when (consp 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)
@@ -906,15 +906,8 @@ on the format of these lists."
 ;;;###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.
+BEG and END mark the limits of the region.  Interactively, this function
+prompts for the regular expression REGEXP to align with.
 
 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:
@@ -925,8 +918,29 @@ align them so that the opening parentheses would line up:
     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."
+using a REGEXP like \"(\".  Interactively, all you would have to do is
+to mark the region, call `align-regexp' and enter that regular expression.
+
+REGEXP must contain at least one parenthesized subexpression, typically
+whitespace of the form \"\\\\(\\\\s-*\\\\)\".  In normal interactive use,
+this is automatically added to the start of your regular expression after
+you enter it.  You only need to supply the characters to be lined up, and
+any preceding whitespace is replaced.
+
+If you specify a prefix argument (or use this function non-interactively),
+you must enter the full regular expression, including the subexpression.
+The function also then prompts for which subexpression parenthesis GROUP
+\(default 1) within REGEXP to modify, the amount of SPACING (default
+`align-default-spacing') to use, and whether or not to REPEAT the rule
+throughout the line.
+
+See `align-rules-list' for more information about these options.
+
+The non-interactive form of the previous example would look something like:
+  (align-regexp (point-min) (point-max) \"\\\\(\\\\s-*\\\\)(\")
+
+This function is a nothing more than a small wrapper that helps you
+construct a rule to pass to `align-region', which does the real work."
   (interactive
    (append
     (list (region-beginning) (region-end))
@@ -1037,7 +1051,9 @@ to be colored."
 
 ;;;###autoload
 (defun align-newline-and-indent ()
-  "A replacement function for `newline-and-indent', aligning as it goes."
+  "A replacement function for `newline-and-indent', aligning as it goes.
+The alignment is done by calling `align' on the region that was
+indented."
   (interactive)
   (let ((separate (or (if (and (symbolp align-region-separate)
                               (boundp align-region-separate))
@@ -1116,13 +1132,8 @@ TAB-STOP specifies whether SPACING refers to tab-stop boundaries."
       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)))))
+      (dotimes (_ spacing)
+       (setq column (indent-next-tab-stop column)))
       column)))
 
 (defsubst align-column (pos)
@@ -1311,7 +1322,7 @@ aligner would have dealt with are."
        (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)
+         (let* ((case-fold-search case-fold-search)
                 (case-fold (assq 'case-fold rule))
                 (regexp  (cdr (assq 'regexp rule)))
                 (regfunc (and (functionp regexp) regexp))
@@ -1339,7 +1350,7 @@ aligner would have dealt with are."
              (if real-beg
                  (goto-char beg)
                (if (or (not thissep) (eq thissep 'entire))
-                   (error "Cannot determine alignment region for '%s'"
+                   (error "Cannot determine alignment region for `%s'"
                           (symbol-name (cdr (assq 'title rule)))))
                (beginning-of-line)
                (while (and (not (eobp))
@@ -1389,217 +1400,207 @@ aligner would have dealt with are."
              ;; reports back that the region is ok, then align it.
              (when (or (not func)
                        (funcall func beg end rule))
-               (unwind-protect
-                   (let (rule-beg 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)
-                                 (setq search-start (point))
-                                 (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
-                           (let ((symbol (car rule)))
-                             (if (and symbol (symbolp symbol))
-                                 (message
-                                  "Aligning `%s' (rule %d of %d) %d%%..."
-                                  (symbol-name symbol) rule-index rule-count
-                                  (/ (* (- (point) real-beg) 100)
-                                     (- end-mark real-beg)))
-                               (message
-                                "Aligning %d%%..."
-                                (/ (* (- (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 (and (bolp) (> (point) search-start))
-                           (forward-char -1))
-
-                       ;; lookup the `group' attribute the first time
-                       ;; that we need it
-                       (unless group-c
-                         (setq groups (or (cdr (assq 'group rule)) 1))
-                         (unless (listp groups)
-                           (setq groups (list groups)))
-                         (setq first (car groups)))
-
-                       (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)))
-                                 (cond (rule-ts
-                                        (cdr rule-ts))
-                                       ((symbolp align-to-tab-stop)
-                                        (symbol-value align-to-tab-stop))
-                                       (t
-                                        align-to-tab-stop)))
-                               tab-stop-c t))
-
-                       ;; test whether we have found a match on the same
-                       ;; line as a previous match
-                       (when (> (point) eol)
-                         (setq same nil)
-                         (align--set-marker eol (line-end-position)))
-
-                       ;; 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 rule-beg (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
-                         (when (and last-point
-                                    (align-new-section-p last-point rule-beg
-                                                         thissep))
-                           (align-regions regions align-props rule func)
-                           (setq regions nil)
-                           (setq align-props nil))
-                          (align--set-marker last-point rule-beg 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 groups))
-                                         (cdar excls))
-                                      (> (match-end (car (last groups)))
-                                         (caar excls)))
-                                 (setq exclude-p t
-                                       excls nil)
-                               (setq excls (cdr excls)))))
-
-                         ;; go through the parenthesis groups
-                         ;; matching whitespace to be contracted or
-                         ;; expanded (or possibly justified, if the
-                         ;; `justify' attribute was set)
-                         (unless exclude-p
-                           (dolist (g groups)
-                             ;; We must use markers, since
-                             ;; `align-areas' may modify the buffer.
-                             ;; Avoid polluting the markers.
-                             (let* ((group-beg (copy-marker
-                                                (match-beginning g) t))
-                                    (group-end (copy-marker
-                                                (match-end g) t))
-                                    (region (cons group-beg group-end))
-                                    (props (cons (if (listp spacing)
-                                                     (car spacing)
-                                                   spacing)
-                                                 (if (listp tab-stop)
-                                                     (car tab-stop)
-                                                   tab-stop))))
-                               (push group-beg markers)
-                               (push group-end markers)
-                               (setq index (if same (1+ index) 0))
-                               (cond
-                                ((nth index regions)
-                                 (setcar (nthcdr index regions)
-                                         (cons region
-                                               (nth index regions))))
-                                (regions
-                                 (nconc regions
-                                        (list (list region)))
-                                 (nconc align-props (list props)))
-                                (t
-                                 (setq regions
-                                       (list (list region)))
-                                 (setq align-props (list props)))))
-                             ;; If any further rule matches are found
-                             ;; before `eol', 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))
-
-                           ;; 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))
-
-                           ;; if the search did not change point,
-                           ;; move forward to avoid an infinite loop
-                           (if (= (point) search-start)
-                               (forward-char)))))
-
-                     ;; 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)))))))
+                (let (rule-beg exclude-areas)
+                  ;; determine first of all where the exclusions
+                  ;; lie in this region
+                  (when exclude-rules
+                    (align-region
+                     beg end 'entire
+                     exclude-rules nil
+                     (lambda (b e mode)
+                       (or (and mode (listp mode))
+                           (setq exclude-areas
+                                 (cons (cons b e)
+                                       exclude-areas)))))
+                    (setq exclude-areas
+                          (nreverse
+                           (sort exclude-areas #'car-less-than-car))))
+
+                  ;; 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)
+                              (setq search-start (point))
+                              (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
+                        (let ((symbol (car rule)))
+                          (if (and symbol (symbolp symbol))
+                              (message
+                               "Aligning `%s' (rule %d of %d) %d%%..."
+                               (symbol-name symbol) rule-index rule-count
+                               (floor (* (- (point) real-beg) 100.0)
+                                      (- end-mark real-beg)))
+                            (message
+                             "Aligning %d%%..."
+                             (floor (* (- (point) real-beg) 100.0)
+                                    (- 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 (and (bolp) (> (point) search-start))
+                        (forward-char -1))
+
+                    ;; lookup the `group' attribute the first time
+                    ;; that we need it
+                    (unless group-c
+                      (setq groups (or (cdr (assq 'group rule)) 1))
+                      (unless (listp groups)
+                        (setq groups (list groups)))
+                      (setq first (car groups)))
+
+                    (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)))
+                              (cond (rule-ts
+                                     (cdr rule-ts))
+                                    ((symbolp align-to-tab-stop)
+                                     (symbol-value align-to-tab-stop))
+                                    (t
+                                     align-to-tab-stop)))
+                            tab-stop-c t))
+
+                    ;; test whether we have found a match on the same
+                    ;; line as a previous match
+                    (when (> (point) eol)
+                      (setq same nil)
+                      (align--set-marker eol (line-end-position)))
+
+                    ;; 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 rule-beg (match-beginning first)
+                          save-match-data (match-data))
+
+                    (or rule-beg
+                        (error "No match for subexpression %s" first))
+
+                    ;; 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
+                      (when (and last-point
+                                 (align-new-section-p last-point rule-beg
+                                                      thissep))
+                        (align-regions regions align-props rule func)
+                        (setq regions nil)
+                        (setq align-props nil))
+                      (align--set-marker last-point rule-beg 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 groups))
+                                      (cdar excls))
+                                   (> (match-end (car (last groups)))
+                                      (caar excls)))
+                              (setq exclude-p t
+                                    excls nil)
+                            (setq excls (cdr excls)))))
+
+                      ;; go through the parenthesis groups
+                      ;; matching whitespace to be contracted or
+                      ;; expanded (or possibly justified, if the
+                      ;; `justify' attribute was set)
+                      (unless exclude-p
+                        (dolist (g groups)
+                          ;; We must use markers, since
+                          ;; `align-areas' may modify the buffer.
+                          ;; Avoid polluting the markers.
+                          (let* ((group-beg (copy-marker
+                                             (match-beginning g) t))
+                                 (group-end (copy-marker
+                                             (match-end g) t))
+                                 (region (cons group-beg group-end))
+                                 (props (cons (if (listp spacing)
+                                                  (car spacing)
+                                                spacing)
+                                              (if (listp tab-stop)
+                                                  (car tab-stop)
+                                                tab-stop))))
+                            (push group-beg markers)
+                            (push group-end markers)
+                            (setq index (if same (1+ index) 0))
+                            (cond
+                             ((nth index regions)
+                              (setcar (nthcdr index regions)
+                                      (cons region
+                                            (nth index regions))))
+                             (regions
+                              (nconc regions
+                                     (list (list region)))
+                              (nconc align-props (list props)))
+                             (t
+                              (setq regions
+                                    (list (list region)))
+                              (setq align-props (list props)))))
+                          ;; If any further rule matches are found
+                          ;; before `eol', 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))
+
+                        ;; 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))
+
+                        ;; if the search did not change point,
+                        ;; move forward to avoid an infinite loop
+                        (if (= (point) search-start)
+                            (forward-char)))))
+
+                  ;; when they are no more matches for this rule,
+                  ;; align whatever was left over
+                  (if regions
+                      (align-regions regions align-props rule func))))))))
       (setq rules (cdr rules)
            rule-index (1+ rule-index)))
     ;; This function can use a lot of temporary markers, so instead of
     ;; waiting for the next GC we delete them immediately (Bug#10047).
-    (set-marker end-mark nil)
+    (when end-mark (set-marker end-mark nil))
     (dolist (m markers)
       (set-marker m nil))