Subject: Proposed feature (with patch): sort on multipart keys with sort-regexp-fields

From: Eric Roode <[email-masked]>
Date: Tue, 4 Mar 2014 11:52:21 -0500

***Abstract:

    To `sort-regexp-fields', add the ability to sort records by
multi-part (non-contiguous) keys.

***Description:

    `sort-regexp-fields' can sort entire records, or a subset of each
record specified by a secondary regexp or by a single backreference to
the record as matched by the record-defining regexp.

    This fairly minor patch gives the user (or caller) the additional
ability to sort by multiple backreferences; e.g., \3\1\4

    It does this by storing a list of backreference numbers in the
`key-regexp' parameter to `sort-regexp-fields'. Then in the
`startkeyfun' function passed to `sort-subr', those backreference
strings are concatenated (separated by a NUL character) to form the
sort key.

***ChangeLog:

2014-03-04  Eric Roode  <[email-masked]>

* lisp/sort.el (sort-regexp-fields): add multi-key sorting
* doc/lispref/text.texi (sort-regexp-fields): document multi-key sorting

***Patch 1 of 2 (lisp/sort.el):

*** /c/usr/emacs-24.3/lisp/sort.el 2013-08-07 12:18:53.540292800 -0400
--- /ejr/dev/emacs/sort.el 2014-03-04 10:59:02.280611700 -0500
*************** KEY-REGEXP specifies the part of each re
*** 417,422 ****
--- 417,424 ----
    RECORD-REGEXP) to be used for sorting.
    If it is \"\\\\digit\", use the digit'th \"\\\\(...\\\\)\"
    match field specified by RECORD-REGEXP.
+   If it is a sequence of such match fields: \"\\\\digit\\\\digit...\",
+   use a concatenation of those backreferences, in that order.
    If it is \"\\\\&\", use the whole record.
    Otherwise, KEY-REGEXP should be a regular expression with which
    to search within the record.  If a match for KEY-REGEXP is not
*************** sRegexp specifying key within record: \n
*** 438,444 ****
    (cond ((or (equal key-regexp "") (equal key-regexp "\\&"))
  (setq key-regexp 0))
  ((string-match "\\`\\\\[1-9]\\'" key-regexp)
! (setq key-regexp (- (aref key-regexp 1) ?0))))
    (save-excursion
      (save-restriction
        (narrow-to-region beg end)
--- 440,452 ----
    (cond ((or (equal key-regexp "") (equal key-regexp "\\&"))
  (setq key-regexp 0))
  ((string-match "\\`\\\\[1-9]\\'" key-regexp)
! (setq key-regexp (- (aref key-regexp 1) ?0)))
! ((string-match "\\`\\(\\\\[1-9]\\)+\\'" key-regexp)
!          (let ((st 1) (en (length key-regexp)) mlist)
!            (while (< st en)
!              (push (- (aref key-regexp st) ?0) mlist)
!              (setq st (+ 2 st)))
!            (setq key-regexp (nreverse mlist)))))
    (save-excursion
      (save-restriction
        (narrow-to-region beg end)
*************** sRegexp specifying key within record: \n
*** 453,470 ****
    (function (lambda ()
        (goto-char sort-regexp-record-end)))
    (function (lambda ()
!       (let ((n 0))
! (cond ((numberp key-regexp)
! (setq n key-regexp))
!       ((re-search-forward
!  key-regexp sort-regexp-record-end t)
! (setq n 0))
!       (t (throw 'key nil)))
! (condition-case ()
!     (cons (match-beginning n)
!   (match-end n))
!   ;; if there was no such register
!   (error (throw 'key nil)))))))))))

  (defvar sort-columns-subprocess t)
--- 461,482 ----
    (function (lambda ()
        (goto-char sort-regexp-record-end)))
    (function (lambda ()
!       (if (listp key-regexp)
!                                    (mapconcat
!                                     (function (lambda (ix) (match-string
ix)))
!                                     key-regexp "\0")
!                                  (let ((n 0))
!                                    (cond ((numberp key-regexp)
!                                           (setq n key-regexp))
!                                          ((re-search-forward
!                                            key-regexp
sort-regexp-record-end t)
!                                           (setq n 0))
!                                          (t (throw 'key nil)))
!                                    (condition-case ()
!                                        (cons (match-beginning n)
!                                              (match-end n))
!                                      ;; if there was no such register
!                                      (error (throw 'key nil))))))))))))

  (defvar sort-columns-subprocess t)

***Patch 2 of 2 (doc/lispref/text.texi):

*** text.texi~  2013-02-18 20:17:07.000000000 -0500
--- text.texi   2014-03-04 11:38:48.136225100 -0500
*************** on its own.
*** 1988,1999 ****
If @var{key-regexp} is:

@table @asis
@item @samp{\@var{digit}}
then the text matched by the @var{digit}th @samp{\(...\)} parenthesis
grouping in @var{record-regexp} is the sort key.

! @item @samp{\&}
! then the whole record is the sort key.

@item a regular expression
then @code{sort-regexp-fields} searches for a match for the regular
--- 1988,2003 ----
If @var{key-regexp} is:

@table @asis
+ @item @samp{\&}
+ then the whole record is the sort key.
+
@item @samp{\@var{digit}}
then the text matched by the @var{digit}th @samp{\(...\)} parenthesis
grouping in @var{record-regexp} is the sort key.

! @item a series of such backreferences: @samp{\@var{digit}\@var{digit}...}
! then the multiple subexpressions specified by the respective parenthesis
! groupings in @var{record-regexp} are combined to be the sort key.

@item a regular expression
then @code{sort-regexp-fields} searches for a match for the regular