;;; sort.el --- commands to sort text in an Emacs buffer.
-;; Copyright (C) 1986, 1987 Free Software Foundation, Inc.
+;; Copyright (C) 1986, 1987, 1994, 1995 Free Software Foundation, Inc.
;; Author: Howie Kaye
;; Maintainer: FSF
;; along with GNU Emacs; see the file COPYING. If not, write to
;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+;;; Commentary:
+
+;;; This package provides the sorting facilities documented in the Emacs
+;;; user's manual.
+
;;; Code:
+(defvar sort-fold-case nil
+ "*Non-nil if the buffer sort functions should ignore case.")
+
+;;;###autoload
(defun sort-subr (reverse nextrecfun endrecfun &optional startkeyfun endkeyfun)
"General text sorting routine to divide buffer into records and sort them.
Arguments are REVERSE NEXTRECFUN ENDRECFUN &optional STARTKEYFUN ENDKEYFUN.
(if messages (message "Finding sort keys..."))
(let* ((sort-lists (sort-build-lists nextrecfun endrecfun
startkeyfun endkeyfun))
- (old (reverse sort-lists)))
+ (old (reverse sort-lists))
+ (case-fold-search sort-fold-case))
(if (null sort-lists)
()
(or reverse (setq sort-lists (nreverse sort-lists)))
'string<)))
(sort sort-lists
(cond ((numberp (car (car sort-lists)))
- (function
- (lambda (a b)
- (< (car a) (car b)))))
+ 'car-less-than-car)
((consp (car (car sort-lists)))
(function
(lambda (a b)
(narrow-to-region beg end)
(goto-char (point-min))
(sort-subr reverse
- (function (lambda () (skip-chars-forward "\n \t\f")))
+ (function
+ (lambda ()
+ (while (and (not (eobp)) (looking-at paragraph-separate))
+ (forward-line 1))))
'forward-paragraph))))
;;;###autoload
(interactive "p\nr")
(sort-fields-1 field beg end
(function (lambda ()
- (sort-skip-fields (1- field))
- (string-to-int
+ (sort-skip-fields field)
+ (string-to-number
(buffer-substring
(point)
(save-excursion
(point))))))
nil))
-(defun sort-float-fields (field beg end)
- "Sort lines in region numerically by the ARGth field of each line.
-Fields are separated by whitespace and numbered from 1 up. Specified field
-must contain a floating point number in each line of the region. With a
-negative arg, sorts by the ARGth field counted from the right. Called from a
-program, there are three arguments: FIELD, BEG and END. BEG and END specify
-region to sort."
- (interactive "p\nr")
- (sort-fields-1 field beg end
- (function (lambda ()
- (sort-skip-fields (1- field))
- (string-to-float
- (buffer-substring
- (point)
- (save-excursion
- (re-search-forward
- "[+-]?[0-9]*\.?[0-9]*\\([eE][+-]?[0-9]+\\)?")
- (point))))))
- nil))
+;;;;;###autoload
+;;(defun sort-float-fields (field beg end)
+;; "Sort lines in region numerically by the ARGth field of each line.
+;;Fields are separated by whitespace and numbered from 1 up. Specified field
+;;must contain a floating point number in each line of the region. With a
+;;negative arg, sorts by the ARGth field counted from the right. Called from a
+;;program, there are three arguments: FIELD, BEG and END. BEG and END specify
+;;region to sort."
+;; (interactive "p\nr")
+;; (sort-fields-1 field beg end
+;; (function (lambda ()
+;; (sort-skip-fields field)
+;; (string-to-number
+;; (buffer-substring
+;; (point)
+;; (save-excursion
+;; (re-search-forward
+;; "[+-]?[0-9]*\.?[0-9]*\\([eE][+-]?[0-9]+\\)?")
+;; (point))))))
+;; nil))
;;;###autoload
(defun sort-fields (field beg end)
(interactive "p\nr")
(sort-fields-1 field beg end
(function (lambda ()
- (sort-skip-fields (1- field))
+ (sort-skip-fields field)
nil))
(function (lambda () (skip-chars-forward "^ \t\n")))))
startkeyfun endkeyfun)))
(set-syntax-table tbl))))
+;; Position at the beginning of field N on the current line,
+;; assuming point is initially at the beginning of the line.
(defun sort-skip-fields (n)
- (let ((bol (point))
- (eol (save-excursion (end-of-line 1) (point))))
- (if (> n 0) (forward-word n)
- (end-of-line)
- (forward-word (1+ n)))
- (if (or (and (>= (point) eol) (> n 0))
- ;; this is marginally wrong; if the first line of the sort
- ;; at bob has the wrong number of fields the error won't be
- ;; reported until the next short line.
- (and (< (point) bol) (< n 0)))
+ (if (> n 0)
+ ;; Skip across N - 1 fields.
+ (let ((i (1- n)))
+ (while (> i 0)
+ (skip-chars-forward " \t")
+ (skip-chars-forward "^ \t\n")
+ (setq i (1- i)))
+ (skip-chars-forward " \t")
+ (if (eolp)
+ (error "Line has too few fields: %s"
+ (buffer-substring
+ (save-excursion (beginning-of-line) (point))
+ (save-excursion (end-of-line) (point))))))
+ (end-of-line)
+ ;; Skip back across - N - 1 fields.
+ (let ((i (1- (- n))))
+ (while (> i 0)
+ (skip-chars-backward " \t")
+ (skip-chars-backward "^ \t\n")
+ (setq i (1- i)))
+ (skip-chars-backward " \t"))
+ (if (bolp)
(error "Line has too few fields: %s"
- (buffer-substring bol eol)))
- (skip-chars-forward " \t")))
-
+ (buffer-substring
+ (save-excursion (beginning-of-line) (point))
+ (save-excursion (end-of-line) (point)))))
+ ;; Position at the front of the field
+ ;; even if moving backwards.
+ (skip-chars-backward "^ \t\n")))
\f
+(defvar sort-regexp-fields-regexp)
+(defvar sort-regexp-record-end)
+
+;; Move to the beginning of the next match for record-regexp,
+;; and set sort-regexp-record-end to the end of that match.
+;; If the next match is empty and does not advance point,
+;; skip one character and try again.
+(defun sort-regexp-fields-next-record ()
+ (let ((oldpos (point)))
+ (and (re-search-forward sort-regexp-fields-regexp nil 'move)
+ (setq sort-regexp-record-end (match-end 0))
+ (if (= sort-regexp-record-end oldpos)
+ (progn
+ (forward-char 1)
+ (re-search-forward sort-regexp-fields-regexp nil 'move)
+ (setq sort-regexp-record-end (match-end 0)))
+ t)
+ (goto-char (match-beginning 0)))))
+
;;;###autoload
(defun sort-regexp-fields (reverse record-regexp key-regexp beg end)
- "Sort the region lexicographically as specifed by RECORD-REGEXP and KEY.
+ "Sort the region lexicographically as specified by RECORD-REGEXP and KEY.
RECORD-REGEXP specifies the textual units which should be sorted.
For example, to sort lines RECORD-REGEXP would be \"^.*$\"
KEY specifies the part of each record (ie each match for RECORD-REGEXP)
(save-restriction
(narrow-to-region beg end)
(goto-char (point-min))
- (let (sort-regexp-record-end) ;isn't dynamic scoping wonderful?
- (re-search-forward record-regexp)
+ (let (sort-regexp-record-end
+ (sort-regexp-fields-regexp record-regexp))
+ (re-search-forward sort-regexp-fields-regexp)
(setq sort-regexp-record-end (point))
(goto-char (match-beginning 0))
(sort-subr reverse
- (function (lambda ()
- (and (re-search-forward record-regexp nil 'move)
- (setq sort-regexp-record-end (match-end 0))
- (goto-char (match-beginning 0)))))
+ 'sort-regexp-fields-next-record
(function (lambda ()
(goto-char sort-regexp-record-end)))
(function (lambda ()