emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[nongnu] elpa/evil 8a05eb99c6: Implement n, x, o, b, r options in :sort


From: ELPA Syncer
Subject: [nongnu] elpa/evil 8a05eb99c6: Implement n, x, o, b, r options in :sort
Date: Mon, 16 Jan 2023 06:02:24 -0500 (EST)

branch: elpa/evil
commit 8a05eb99c6ee60eb502a2c586fa2e771a513c417
Author: Nathaniel Nicandro <nathanielnicandro@gmail.com>
Commit: Axel Forsman <axelsfor@gmail.com>

    Implement n, x, o, b, r options in :sort
    
    Co-authored-by: Axel Forsman <axelsfor@gmail.com>
---
 evil-commands.el | 124 +++++++++++++++++++++++++++++++++++++++++--------------
 evil-tests.el    |  35 +++++++++++++++-
 2 files changed, 128 insertions(+), 31 deletions(-)

diff --git a/evil-commands.el b/evil-commands.el
index e6c1c10fd6..eec7038604 100644
--- a/evil-commands.el
+++ b/evil-commands.el
@@ -4228,45 +4228,109 @@ Default position is the beginning of the buffer."
       (message "%d lines --%s--" nlines perc))))
 
 (defvar sort-fold-case)
-(evil-define-operator evil-ex-sort (beg end &optional options reverse)
+(evil-define-operator evil-ex-sort (beg end &optional args reverse)
   "The Ex sort command.
-\[BEG,END]sort[!] [i][u]
+\[BEG,END]sort[!] [/PATTERN/] [b][i][u][r][n][x][o]
 The following additional options are supported:
 
   * i   ignore case
   * u   remove duplicate lines
+  * r   sort the contents of pattern
+  * n   sort by the first decimal number
+  * x   sort by the first hexadecimal number (with optional \"0x\" prefix)
+  * o   sort by the first octal number
+  * b   sort by the first binary number
 
-The 'bang' argument means to sort in reverse order."
+If a pattern is supplied without supplying the \"r\" option, sort
+the contents of the lines after skipping the pattern.
+If the pattern is empty, the last search pattern is used instead.
+
+The \"!\" argument means to sort in reverse order."
   :motion mark-whole-buffer
   :move-point nil
   (interactive "<r><a><!>")
-  (let ((beg (copy-marker beg))
-        (end (copy-marker end))
-        sort-fold-case uniq)
-    (dolist (opt (append options nil))
-      (cond
-       ((eq opt ?i) (setq sort-fold-case t))
-       ((eq opt ?u) (setq uniq t))
-       (t (user-error "Unsupported sort option: %c" opt))))
-    (sort-lines reverse beg end)
-    (when uniq
-      (let (line prev-line)
-        (goto-char beg)
-        (while (and (< (point) end) (not (eobp)))
-          (setq line (buffer-substring-no-properties
-                      (line-beginning-position)
-                      (line-end-position)))
-          (if (and (stringp prev-line)
-                   (eq t (compare-strings line nil nil
-                                          prev-line nil nil
-                                          sort-fold-case)))
-              (delete-region (progn (forward-line 0) (point))
-                             (progn (forward-line 1) (point)))
-            (setq prev-line line)
-            (forward-line 1)))))
-    (goto-char beg)
-    (set-marker beg nil)
-    (set-marker end nil)))
+  (unless args (setq args ""))
+  (let ((inhibit-field-text-motion t)
+        options sort-fold-case unique base sort-pat pat)
+    ;; Handle arguments like
+    ;;     /[^,]*,/ n
+    ;; and
+    ;;     nu
+    (if (or (zerop (length args)) (memq (aref args 0) '(?i ?n ?x ?o ?b ?u ?r)))
+        (setq options args)
+      (setq args (evil-delimited-arguments args 2)
+            ;; Use last search pattern when an empty pattern is provided
+            pat (if (string= (car args) "")
+                    (evil-ex-pattern-regex evil-ex-search-pattern)
+                  (car args))
+            options (cadr args)))
+    (cl-loop
+     for opt across options do
+     (cond
+      ((eq opt ?i) (setq sort-fold-case t))
+      ((eq opt ?b) (setq base 2))
+      ((eq opt ?o) (setq base 8))
+      ((eq opt ?n) (setq base 10))
+      ((eq opt ?x) (setq base 16))
+      ((eq opt ?r) (setq sort-pat t))
+      ((eq opt ?u) (setq unique t))
+      ((eq opt ? ))
+      (t (user-error "Invalid sort option `%c'" opt))))
+    (evil-with-restriction beg end
+      (goto-char beg)
+      (let ((num-re
+             (cond
+              ((null base) nil)
+              ((= base 2) "[01]+")
+              ((= base 8) "[0-7]+")
+              ((= base 10) "-?[0-9]+")
+              ((= base 16) "\\(-\\)?\\(?:0x\\)?\\([0-9a-f]+\\)")))
+            key-end)
+        (sort-subr
+         reverse
+         #'forward-line
+         #'end-of-line
+         (lambda ()
+           ;; Find the boundary of the key to match on the line
+           (setq key-end (line-end-position))
+           (and (> (length pat) 0)
+                ;; When matching a pattern and one doesn't exist on the line,
+                ;; skip the line
+                (re-search-forward pat key-end 'move)
+                sort-pat ; Otherwise go to the start of the key
+                (progn (setq key-end (point))
+                       (goto-char (match-beginning 0))))
+           ;; Return the key for the line when sorting numbers, otherwise let
+           ;; `sort-subr' extract the key
+           (when base
+             (let ((case-fold-search t))
+               (if (not (re-search-forward num-re key-end t))
+                   ;; When sorting numbers and a number doesn't exist on the
+                   ;; line, place it above all the numeric lines
+                   most-negative-fixnum
+                 (let ((num (string-to-number
+                             (buffer-substring
+                              (match-beginning (if (= base 16) 2 0))
+                              (match-end 0))
+                             base)))
+                   (if (and (= base 16) (match-beginning 1))
+                       (- num)
+                     num))))))
+         ;; Only called when sorting lexicographically
+         (lambda () (goto-char key-end))))
+      (when unique
+        (goto-char (point-min))
+        (let ((case-fold-search sort-fold-case)
+              prev-line-beg)
+          (while (not (eobp))
+            (if (and prev-line-beg
+                     (eq 0 (compare-buffer-substrings
+                            nil prev-line-beg (1- (point))
+                            nil (point) (line-end-position))))
+                (delete-region (point) (line-beginning-position 2))
+              (setq prev-line-beg (point))
+              (forward-line)))))))
+  (goto-char beg))
 
 ;;; Window navigation
 
diff --git a/evil-tests.el b/evil-tests.el
index edf728d167..159e3a7452 100644
--- a/evil-tests.el
+++ b/evil-tests.el
@@ -8781,7 +8781,40 @@ Source
     (evil-test-buffer
       "[z]zyy\ntest\ntEst\ntesT\nTEST\ntest\n"
       (":sort iu")
-      "[t]est\nzzyy\n")))
+      "[t]est\nzzyy\n"))
+  (ert-info ("pattern sort")
+    (evil-test-buffer
+      "[t]e|z|t\nzz|a|y\n"
+      (":sort /|[a-z]|/ r")
+      ("zz|a|y\nte|z|t\n"))
+    (evil-test-buffer
+      "[a],b\nb,a\n"
+      (":sort /[^,]*,/")
+      "b,a\na,b\n")
+    (evil-test-buffer
+      "[a],b\nb,a\n"
+      ("/," [return] ":sort //")
+      "b,a\na,b\n"))
+  (ert-info ("numeric sort")
+    (ert-info ("decimal")
+      (evil-test-buffer
+        "27\n027\n2\na\n1\n"
+        (":sort n")
+        "a\n1\n2\n27\n027\n"))
+    (ert-info ("octal")
+      (evil-test-buffer
+        "9\n8\n"
+        (":sort o")
+        "9\n8\n")
+      (evil-test-buffer
+        "777\n776\n7239\n"
+        (":sort o")
+        "7239\n776\n777\n"))
+    (ert-info ("hexadecimal")
+      (evil-test-buffer
+        "0xae\n0xb\nae\nad\n"
+        (":sort x")
+        "0xb\nad\n0xae\nae\n"))))
 
 ;;; Command line window
 



reply via email to

[Prev in Thread] Current Thread [Next in Thread]