]> git.eshelyaron.com Git - emacs.git/commitdiff
Allow add-to-ordered-list to use a test predicate
authorLars Ingebrigtsen <larsi@gnus.org>
Thu, 31 Dec 2020 04:28:47 +0000 (05:28 +0100)
committerLars Ingebrigtsen <larsi@gnus.org>
Thu, 31 Dec 2020 04:28:47 +0000 (05:28 +0100)
* doc/lispref/lists.texi (List Variables): Update manual.

* lisp/subr.el (add-to-ordered-list): Allow using a test
predicate, and make slightly more efficient (bug#45539).

doc/lispref/lists.texi
etc/NEWS
lisp/subr.el
test/lisp/subr-tests.el

index ae793d5e15efb19cdbe18b94426a67dd2bd44a17..21ee386335ef485e4b4d870b965406962d388c4c 100644 (file)
@@ -807,13 +807,14 @@ foo                       ;; @r{@code{foo} was changed.}
   (setq @var{var} (cons @var{value} @var{var})))
 @end example
 
-@defun add-to-ordered-list symbol element &optional order
+@defun add-to-ordered-list symbol element &optional order test-function
 This function sets the variable @var{symbol} by inserting
 @var{element} into the old value, which must be a list, at the
 position specified by @var{order}.  If @var{element} is already a
-member of the list, its position in the list is adjusted according
-to @var{order}.  Membership is tested using @code{eq}.
-This function returns the resulting list, whether updated or not.
+member of the list, its position in the list is adjusted according to
+@var{order}.  Membership is tested using @var{test-function},
+defaulting to @code{eq} if @var{test-function} isn't present.  This
+function returns the resulting list, whether updated or not.
 
 The @var{order} is typically a number (integer or float), and the
 elements of the list are sorted in non-decreasing numerical order.
index 1b49b015608193f2d9f631effa64ca3d52842220..865dbdf516c6dd6b1ceaa3a1ba6d948308fa838f 100644 (file)
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -1482,6 +1482,9 @@ that makes it a valid button.
 
 ** Miscellaneous
 
++++
+*** 'add-to-ordered-list' can now take a test predicate.
+
 +++
 *** New predicate functions 'length<', 'length>' and 'length='.
 Using these functions may be more efficient than using 'length' (if
index ed0d6978d030ed37f9d9827900d4ae77bd492ece..77b142c415293f1cd4d4c9aac2883a562a441702 100644 (file)
@@ -1971,13 +1971,13 @@ can do the job."
           (cons element (symbol-value list-var))))))
 
 
-(defun add-to-ordered-list (list-var element &optional order)
+(defun add-to-ordered-list (list-var element &optional order test-function)
   "Add ELEMENT to the value of LIST-VAR if it isn't there yet.
-The test for presence of ELEMENT is done with `eq'.
+TEST-FUNCTION is used to test for the presence of ELEMENT, and
+defaults to `eq'.
 
-The resulting list is reordered so that the elements are in the
-order given by each element's numeric list order.  Elements
-without a numeric list order are placed at the end of the list.
+The value of LIST-VAR is kept ordered based on the ORDER
+parameter.
 
 If the third optional argument ORDER is a number (integer or
 float), set the element's list order to the given value.  If
@@ -1990,21 +1990,30 @@ The list order for each element is stored in LIST-VAR's
 LIST-VAR cannot refer to a lexical variable.
 
 The return value is the new value of LIST-VAR."
-  (let ((ordering (get list-var 'list-order)))
+  (let ((ordering (get list-var 'list-order))
+        missing)
+    ;; Make a hash table for storing the ordering.
     (unless ordering
       (put list-var 'list-order
-           (setq ordering (make-hash-table :weakness 'key :test 'eq))))
-    (when order
-      (puthash element (and (numberp order) order) ordering))
-    (unless (memq element (symbol-value list-var))
+           (setq ordering (make-hash-table :weakness 'key
+                                           :test (or test-function #'eq)))))
+    (when (and test-function
+               (not (eq test-function (hash-table-test ordering))))
+      (error "Conflicting test functions given"))
+    ;; Add new values.
+    (when (setq missing (eq (gethash element ordering 'missing) 'missing))
       (set list-var (cons element (symbol-value list-var))))
-    (set list-var (sort (symbol-value list-var)
-                       (lambda (a b)
-                         (let ((oa (gethash a ordering))
-                               (ob (gethash b ordering)))
-                           (if (and oa ob)
-                               (< oa ob)
-                             oa)))))))
+    ;; Set/change the order.
+    (when (or order missing)
+      (setf (gethash element ordering) (and (numberp order) order)))
+    (set list-var
+         (sort (symbol-value list-var)
+              (lambda (a b)
+                (let ((oa (gethash a ordering))
+                      (ob (gethash b ordering)))
+                  (if (and oa ob)
+                      (< oa ob)
+                    oa)))))))
 
 (defun add-to-history (history-var newelt &optional maxelt keep-all)
   "Add NEWELT to the history list stored in the variable HISTORY-VAR.
index 3154135ce17869eb044786f7a3f0e88b41fe10c2..5be3b8915a2e83ff1c8035407d83de11990ba83c 100644 (file)
@@ -600,7 +600,7 @@ See https://debbugs.gnu.org/cgi/bugreport.cgi?bug=19350."
 
 (defvar subr--ordered nil)
 
-(ert-deftest subr--add-to-ordered-list ()
+(ert-deftest subr--add-to-ordered-list-eq ()
   (setq subr--ordered nil)
   (add-to-ordered-list 'subr--ordered 'b 2)
   (should (equal subr--ordered '(b)))
@@ -611,7 +611,31 @@ See https://debbugs.gnu.org/cgi/bugreport.cgi?bug=19350."
   (add-to-ordered-list 'subr--ordered 'e)
   (should (equal subr--ordered '(a b c e)))
   (add-to-ordered-list 'subr--ordered 'd 4)
-  (should (equal subr--ordered '(a b c d e))))
+  (should (equal subr--ordered '(a b c d e)))
+  (add-to-ordered-list 'subr--ordered 'e)
+  (should (equal subr--ordered '(a b c d e)))
+  (add-to-ordered-list 'subr--ordered 'b 5)
+  (should (equal subr--ordered '(a c d b e))))
+
+(defvar subr--ordered-s nil)
+
+(ert-deftest subr--add-to-ordered-list-equal ()
+  (setq subr--ordered-s nil)
+  (add-to-ordered-list 'subr--ordered-s "b" 2 #'equal)
+  (should (equal subr--ordered-s '("b")))
+  (add-to-ordered-list 'subr--ordered-s "c" 3)
+  (should (equal subr--ordered-s '("b" "c")))
+  (add-to-ordered-list 'subr--ordered-s "a" 1)
+  (should (equal subr--ordered-s '("a" "b" "c")))
+  (add-to-ordered-list 'subr--ordered-s "e")
+  (should (equal subr--ordered-s '("a" "b" "c" "e")))
+  (add-to-ordered-list 'subr--ordered-s "d" 4)
+  (should (equal subr--ordered-s '("a" "b" "c" "d" "e")))
+  (add-to-ordered-list 'subr--ordered-s "e")
+  (should (equal subr--ordered-s '("a" "b" "c" "d" "e")))
+  (add-to-ordered-list 'subr--ordered-s "b" 5)
+  (should (equal subr--ordered-s '("a" "c" "d" "b" "e")))
+  (should-error (add-to-ordered-list 'subr--ordered-s "b" 5 #'eql)))
 
 \f
 ;;; Apropos.