]> code.delx.au - gnu-emacs/blobdiff - lisp/sort.el
Fix delete-duplicate-lines
[gnu-emacs] / lisp / sort.el
index cd0b4433ab3f060c5f1e9874e918115e87469cf2..8bc71388ad5642c020e862dd2903c9b434521961 100644 (file)
@@ -1,10 +1,10 @@
 ;;; 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-2016 Free Software
+;; Foundation, Inc.
 
 ;; Author: Howie Kaye
-;; Maintainer: FSF
+;; Maintainer: emacs-devel@gnu.org
 ;; Keywords: unix
 
 ;; This file is part of GNU Emacs.
@@ -77,8 +77,13 @@ ENDKEYFUN moves from the start of the sort key to the end of the sort key.
 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
@@ -311,7 +316,7 @@ FIELD, BEG and END.  BEG and END specify region to sort."
 ;;                            (point)
 ;;                            (save-excursion
 ;;                              (re-search-forward
-;;                               "[+-]?[0-9]*\.?[0-9]*\\([eE][+-]?[0-9]+\\)?")
+;;                               "[+-]?[0-9]*\\.?[0-9]*\\([eE][+-]?[0-9]+\\)?")
 ;;                              (point))))))
 ;;              nil))
 
@@ -563,32 +568,35 @@ From a program takes two point or marker arguments, BEG and END."
       (insert (car ll)))))
 
 ;;;###autoload
-(defun delete-duplicate-lines (beg end &optional reverse adjacent interactive)
-  "Delete duplicate lines in the region between BEG and END.
-
-If REVERSE is nil, search and delete duplicates forward keeping the first
-occurrence of duplicate lines.  If REVERSE is non-nil (when called
-interactively with C-u prefix), search and delete duplicates backward
-keeping the last occurrence of duplicate lines.
-
-If ADJACENT is non-nil (when called interactively with two C-u prefixes),
-delete repeated lines only if they are adjacent.  It works like the utility
-`uniq' and is useful when lines are already sorted in a large file since
-this is more efficient in performance and memory usage than when ADJACENT
-is nil that uses additional memory to remember previous lines.
-
-When called from Lisp and INTERACTIVE is omitted or nil, return the number
-of deleted duplicate lines, do not print it; if INTERACTIVE is t, the
-function behaves in all respects as if it had been called interactively."
+(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 :weakness 'key :test 'equal)))
-       line prev-line
+  (let ((lines (unless adjacent (make-hash-table :test 'equal)))
+       line prev-line first-line
        (count 0)
        (beg (copy-marker beg))
        (end (copy-marker end)))
@@ -596,18 +604,21 @@ function behaves in all respects as if it had been called interactively."
       (goto-char (if reverse end beg))
       (if (and reverse (bolp)) (forward-char -1))
       (while (if reverse
-                (and (> (point) beg) (not (bobp)))
+                (not first-line)
               (and (< (point) end) (not (eobp))))
+       (setq first-line (and reverse (or (<= (point) beg) (bobp))))
        (setq line (buffer-substring-no-properties
                    (line-beginning-position) (line-end-position)))
-       (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)))))
+        (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