;;; sort.el --- commands to sort text in an Emacs buffer
-;; Copyright (C) 1986-1987, 1994-1995, 2001-2012
-;; Free Software Foundation, Inc.
+;; Copyright (C) 1986-1987, 1994-1995, 2001-2014 Free Software
+;; Foundation, Inc.
;; Author: Howie Kaye
-;; Maintainer: FSF
+;; Maintainer: emacs-devel@gnu.org
;; Keywords: unix
;; This file is part of GNU Emacs.
ENDKEYFUN may be nil if STARTKEYFUN returns a value or if it would be the
same as ENDRECFUN.
-PREDICATE is the function to use to compare keys. If keys are numbers,
-it defaults to `<', otherwise it defaults to `string<'."
+PREDICATE, if non-nil, is the predicate function for comparing
+keys; it is called with two arguments, the keys to compare, and
+should return non-nil if the first key should sort before the
+second key. If PREDICATE is nil, comparison is done with `<' if
+the keys are numbers, with `compare-buffer-substrings' if the
+keys are cons cells (the car and cdr of each cons cell are taken
+as start and end positions), and with `string<' otherwise."
;; Heuristically try to avoid messages if sorting a small amt of text.
(let ((messages (> (- (point-max) (point-min)) 50000)))
(save-excursion
;;;###autoload
(defun sort-regexp-fields (reverse record-regexp key-regexp beg end)
- "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)
- is to be used for sorting.
- If it is \"\\\\digit\" then the digit'th \"\\\\(...\\\\)\" match field from
- RECORD-REGEXP is used.
- If it is \"\\\\&\" then the whole record is used.
- Otherwise, it is a regular-expression for which to search within the record.
-If a match for KEY is not found within a record then that record is ignored.
-
-With a negative prefix arg sorts in reverse order.
+ "Sort the text in the region region lexicographically.
+If called interactively, prompt for two regular expressions,
+RECORD-REGEXP and KEY-REGEXP.
+
+RECORD-REGEXP specifies the textual units to be sorted.
+ For example, to sort lines, RECORD-REGEXP would be \"^.*$\".
+
+KEY-REGEXP specifies the part of each record (i.e. each match for
+ RECORD-REGEXP) to be used for sorting.
+ If it is \"\\\\digit\", use the digit'th \"\\\\(...\\\\)\"
+ match field specified by RECORD-REGEXP.
+ 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
+ found within a record, that record is ignored.
+
+With a negative prefix arg, sort in reverse order.
The variable `sort-fold-case' determines whether alphabetic case affects
the sort order.
;; using negative prefix arg to mean "reverse" is now inconsistent with
;; other sort-.*fields functions but then again this was before, since it
;; didn't use the magnitude of the arg to specify anything.
- (interactive "P\nsRegexp specifying records to sort:
+ (interactive "P\nsRegexp specifying records to sort: \n\
sRegexp specifying key within record: \nr")
(cond ((or (equal key-regexp "") (equal key-regexp "\\&"))
(setq key-regexp 0))
(setq ll (cdr ll)))
(insert (car ll)))))
+;;;###autoload
+(defun delete-duplicate-lines (beg end &optional reverse adjacent keep-blanks
+ interactive)
+ "Delete all but one copy of any identical lines in the region.
+Non-interactively, arguments BEG and END delimit the region.
+Normally it searches forwards, keeping the first instance of
+each identical line. If REVERSE is non-nil (interactively, with
+a C-u prefix), it searches backwards and keeps the last instance of
+each repeated line.
+
+Identical lines need not be adjacent, unless the argument
+ADJACENT is non-nil (interactively, with a C-u C-u prefix).
+This is a more efficient mode of operation, and may be useful
+on large regions that have already been sorted.
+
+If the argument KEEP-BLANKS is non-nil (interactively, with a
+C-u C-u C-u prefix), it retains repeated blank lines.
+
+Returns the number of deleted lines. Interactively, or if INTERACTIVE
+is non-nil, it also prints a message describing the number of deletions."
+ (interactive
+ (progn
+ (barf-if-buffer-read-only)
+ (list (region-beginning) (region-end)
+ (equal current-prefix-arg '(4))
+ (equal current-prefix-arg '(16))
+ (equal current-prefix-arg '(64))
+ t)))
+ (let ((lines (unless adjacent (make-hash-table :test 'equal)))
+ line prev-line
+ (count 0)
+ (beg (copy-marker beg))
+ (end (copy-marker end)))
+ (save-excursion
+ (goto-char (if reverse end beg))
+ (if (and reverse (bolp)) (forward-char -1))
+ (while (if reverse
+ (and (> (point) beg) (not (bobp)))
+ (and (< (point) end) (not (eobp))))
+ (setq line (buffer-substring-no-properties
+ (line-beginning-position) (line-end-position)))
+ (if (and keep-blanks (string= "" line))
+ (forward-line 1)
+ (if (if adjacent (equal line prev-line) (gethash line lines))
+ (progn
+ (delete-region (progn (forward-line 0) (point))
+ (progn (forward-line 1) (point)))
+ (if reverse (forward-line -1))
+ (setq count (1+ count)))
+ (if adjacent (setq prev-line line) (puthash line t lines))
+ (forward-line (if reverse -1 1))))))
+ (set-marker beg nil)
+ (set-marker end nil)
+ (when interactive
+ (message "Deleted %d %sduplicate line%s%s"
+ count
+ (if adjacent "adjacent " "")
+ (if (= count 1) "" "s")
+ (if reverse " backward" "")))
+ count))
+
(provide 'sort)
;;; sort.el ends here