commit e408f2fab24ea81f74d9b84dc83ee67d562846dc (HEAD, refs/remotes/origin/master) Author: Stefan Monnier Date: Mon Sep 14 16:04:22 2020 -0400 * test/lisp/emacs-lisp/find-func-tests.el: New file (for bug#43393) * lisp/emacs-lisp/ert-x.el (ert-simulate-keys): New macro. * test/lisp/international/mule-tests.el (mule-cmds--test-universal-coding-system-argument): Use it and enable the test also in batch mode. diff --git a/lisp/emacs-lisp/ert-x.el b/lisp/emacs-lisp/ert-x.el index 622f5654b2..6569b8ccc8 100644 --- a/lisp/emacs-lisp/ert-x.el +++ b/lisp/emacs-lisp/ert-x.el @@ -177,6 +177,18 @@ test for `called-interactively' in the command will fail." (cl-assert (not unread-command-events) t) return-value)) +(defmacro ert-simulate-keys (keys &rest body) + "Execute BODY with KEYS as pseudo-interactive input." + (declare (debug t) (indent 1)) + `(let ((unread-command-events + ;; Add some C-g to try and make sure we still exit + ;; in case something goes wrong. + (append ,keys '(?\C-g ?\C-g ?\C-g))) + ;; Tell `read-from-minibuffer' not to read from stdin when in + ;; batch mode. + (executing-kbd-macro t)) + ,@body)) + (defun ert-run-idle-timers () "Run all idle timers (from `timer-idle-list')." (dolist (timer (copy-sequence timer-idle-list)) diff --git a/test/lisp/emacs-lisp/find-func-tests.el b/test/lisp/emacs-lisp/find-func-tests.el new file mode 100644 index 0000000000..f505e78566 --- /dev/null +++ b/test/lisp/emacs-lisp/find-func-tests.el @@ -0,0 +1,45 @@ +;;; find-func-tests.el --- Unit tests for find-func.el -*- lexical-binding: t; -*- + +;; Copyright (C) 2020 Free Software Foundation, Inc. + +;; Author: Stefan Monnier +;; Keywords: + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; + +;;; Code: + +(require 'ert-x) ;For `ert-run-keys'. + +(ert-deftest find-func-tests--library-completion () ;bug#43393 + ;; FIXME: How can we make this work in batch (see also + ;; `mule-cmds--test-universal-coding-system-argument')? + ;; (skip-unless (not noninteractive)) + ;; Check that `partial-completion' works when completing library names. + (should (equal "org/org" + (ert-simulate-keys + (kbd "o / o r g TAB RET") + (read-library-name)))) + ;; Check that absolute file names also work. + (should (equal (expand-file-name "nxml/" data-directory) + (ert-simulate-keys + (concat data-directory (kbd "n x / TAB RET")) + (read-library-name))))) + +(provide 'find-func-tests) +;;; find-func-tests.el ends here diff --git a/test/lisp/international/mule-tests.el b/test/lisp/international/mule-tests.el index 5f8e653d7c..9520d9d863 100644 --- a/test/lisp/international/mule-tests.el +++ b/test/lisp/international/mule-tests.el @@ -23,6 +23,8 @@ ;;; Code: +(require 'ert-x) ;For `ert-run-keys'. + (ert-deftest find-auto-coding--bug27391 () "Check that Bug#27391 is fixed." (with-temp-buffer @@ -41,12 +43,11 @@ (should (not (multibyte-string-p (encode-coding-char ?a 'utf-8))))) (ert-deftest mule-cmds--test-universal-coding-system-argument () - (skip-unless (not noninteractive)) (should (equal "ccccccccccccccccab" - (let ((enable-recursive-minibuffers t) - (unread-command-events - (append (kbd "C-x RET c u t f - 8 RET C-u C-u c a b RET") nil))) - (read-string "prompt:"))))) + (let ((enable-recursive-minibuffers t)) + (ert-simulate-keys + (kbd "C-x RET c u t f - 8 RET C-u C-u c a b RET") + (read-string "prompt:")))))) (ert-deftest mule-utf-7 () ;; utf-7 and utf-7-imap are not ASCII-compatible. commit 15d2f6af000625640b3c47d2aeec3114ac1ac544 Author: Kevin Ryde Date: Mon Sep 14 15:40:07 2020 +0200 Tweak the `C-x v =' command when done from a diff buffer * lisp/vc/vc.el (vc-diff): Offer to save the relevant buffer(s) when doing `C-x v =' from a diff buffer (bug#5773). diff --git a/lisp/vc/vc.el b/lisp/vc/vc.el index 4cbd2658f8..3852a64550 100644 --- a/lisp/vc/vc.el +++ b/lisp/vc/vc.el @@ -1895,9 +1895,16 @@ saving the buffer." (interactive (list current-prefix-arg t)) (if historic (call-interactively 'vc-version-diff) - (when buffer-file-name (vc-buffer-sync not-urgent)) - (vc-diff-internal t (vc-deduce-fileset t) nil nil - (called-interactively-p 'interactive)))) + (let ((fileset (vc-deduce-fileset t))) + (vc-buffer-sync-fileset fileset not-urgent) + (vc-diff-internal t fileset nil nil + (called-interactively-p 'interactive))))) + +(defun vc-buffer-sync-fileset (fileset not-urgent) + (dolist (filename (cadr fileset)) + (when-let ((buffer (find-buffer-visiting filename))) + (with-current-buffer buffer + (vc-buffer-sync not-urgent))))) ;;;###autoload (defun vc-diff-mergebase (_files rev1 rev2) commit 312da4871925b9a575c5e3bffd4bf4db4c1108da Author: Pip Cet Date: Mon Sep 14 15:18:52 2020 +0200 Don't retry reading after receiving EINVAL * src/process.c (wait_reading_process_output): Don't retry reading from an fd after an unknown error (bug#6074). diff --git a/src/process.c b/src/process.c index 3aa105ae34..53f4a1d853 100644 --- a/src/process.c +++ b/src/process.c @@ -5413,14 +5413,16 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd, /* If data can be read from the process, do so until exhausted. */ if (wait_proc->infd >= 0) { + unsigned int count = 0; XSETPROCESS (proc, wait_proc); while (true) { int nread = read_process_output (proc, wait_proc->infd); + rarely_quit (++count); if (nread < 0) { - if (errno == EIO || would_block (errno)) + if (errno != EINTR) break; } else commit f450e53d5c01c589986bea07671acebaaf501c5f Author: Stephen Berman Date: Mon Sep 14 14:55:52 2020 +0200 Fix problem of marking files with hidden subdirs * lisp/dired.el (dired-unhide-subdir): (dired-subdir-hidden-p): (dired-subdir-min): Moved from subr-x. (dired-get-filename): Get the correct filename when directories are hidden (bug#8484). diff --git a/lisp/dired-aux.el b/lisp/dired-aux.el index 3ee877ee8d..cf2926ad37 100644 --- a/lisp/dired-aux.el +++ b/lisp/dired-aux.el @@ -2718,12 +2718,6 @@ When called interactively and not on a subdir line, go to this subdir's line." (if (dired-get-subdir) 1 0)))) (dired-next-subdir (- arg) no-error-if-not-found no-skip)) -(defun dired-subdir-min () - (save-excursion - (if (not (dired-prev-subdir 0 t t)) - (error "Not in a subdir!") - (point)))) - ;;;###autoload (defun dired-goto-subdir (dir) "Go to end of header line of DIR in this dired buffer. @@ -2816,15 +2810,6 @@ Lower levels are unaffected." ;;; hiding -(defun dired-unhide-subdir () - (with-silent-modifications - (dired--unhide (dired-subdir-min) (dired-subdir-max)))) - -(defun dired-subdir-hidden-p (dir) - (save-excursion - (dired-goto-subdir dir) - (dired--hidden-p))) - ;;;###autoload (defun dired-hide-subdir (arg) "Hide or unhide the current subdirectory and move to next directory. diff --git a/lisp/dired.el b/lisp/dired.el index 15592ceb08..1b6da1f961 100644 --- a/lisp/dired.el +++ b/lisp/dired.el @@ -2585,6 +2585,21 @@ Otherwise, display it in another buffer." ;;; Functions for extracting and manipulating file names in Dired buffers. +(defun dired-unhide-subdir () + (with-silent-modifications + (dired--unhide (dired-subdir-min) (dired-subdir-max)))) + +(defun dired-subdir-hidden-p (dir) + (save-excursion + (dired-goto-subdir dir) + (dired--hidden-p))) + +(defun dired-subdir-min () + (save-excursion + (if (not (dired-prev-subdir 0 t t)) + (error "Not in a subdir!") + (point)))) + (defun dired-get-filename (&optional localp no-error-if-not-filep) "In Dired, return name of file mentioned on this line. Value returned normally includes the directory name. @@ -2595,10 +2610,15 @@ it occurs in the buffer, and a value of t means construct name relative to Optional arg NO-ERROR-IF-NOT-FILEP means treat `.' and `..' as regular filenames and return nil if no filename on this line. Otherwise, an error occurs in these cases." - (let (case-fold-search file p1 p2 already-absolute) + (let ((hidden (dired-subdir-hidden-p (dired-current-directory))) + case-fold-search file p1 p2 already-absolute) + (when hidden + (dired-unhide-subdir)) (save-excursion (if (setq p1 (dired-move-to-filename (not no-error-if-not-filep))) (setq p2 (dired-move-to-end-of-filename no-error-if-not-filep)))) + (when hidden + (dired-hide-subdir 1)) ;; nil if no file on this line, but no-error-if-not-filep is t: (if (setq file (and p1 p2 (buffer-substring p1 p2))) (progn commit 14486c44885ffe4532118676aaa6e3783a0417bb Author: Lars Ingebrigtsen Date: Mon Sep 14 14:22:06 2020 +0200 Allow preserving (some) text properties from completion tables * doc/lispref/minibuf.texi (Text from Minibuffer): Document it. * lisp/minibuffer.el (completion--replace): Preserve text properties on completed items (bug#43218). diff --git a/doc/lispref/minibuf.texi b/doc/lispref/minibuf.texi index d30114f768..da27805f86 100644 --- a/doc/lispref/minibuf.texi +++ b/doc/lispref/minibuf.texi @@ -316,8 +316,22 @@ input before returning it. However, @code{read-no-blanks-input} (see below), as well as @code{read-minibuffer} and related functions (@pxref{Object from Minibuffer,, Reading Lisp Objects With the Minibuffer}), and all -functions that do minibuffer input with completion, discard text -properties unconditionally, regardless of the value of this variable. +functions that do minibuffer input with completion, remove the @code{face} +property unconditionally, regardless of the value of this variable. + +If this variable is non-@code{nil}, most text properties on strings +from the completion table are preserved---but only on the part of the +strings that were completed. + +@lisp +(let ((minibuffer-allow-text-properties t)) + (completing-read "String: " (list (propertize "foobar" 'data 'zot)))) +=> #("foobar" 3 6 (data zot)) +@end lisp + +In this example, the user typed @samp{foo} and then hit the @kbd{TAB} +key, so the text properties are only preserved on the last three +characters. @end defvar @defvar minibuffer-local-map diff --git a/etc/NEWS b/etc/NEWS index 52092f2ef7..4076630bf2 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1190,6 +1190,12 @@ directory instead of the default directory. * Incompatible Lisp Changes in Emacs 28.1 ++++ +** Some properties from completion tables are now preserved. +If 'minibuffer-allow-text-properties' is non-nil, doing completion +over a table of strings with properties will no longer remove all the +properties before returning. This affects things like 'completing-read'. + ** 'equal' no longer examines some contents of window configurations. Instead, it considers window configurations to be equal only if they are 'eq'. To compare contents, use 'compare-window-configurations' diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el index 62a33f3e2d..3e23c05bb0 100644 --- a/lisp/minibuffer.el +++ b/lisp/minibuffer.el @@ -1065,10 +1065,16 @@ in the last `cdr'." (defun completion--replace (beg end newtext) "Replace the buffer text between BEG and END with NEWTEXT. Moves point to the end of the new text." - ;; The properties on `newtext' include things like - ;; completions-first-difference, which we don't want to include - ;; upon insertion. - (set-text-properties 0 (length newtext) nil newtext) + ;; The properties on `newtext' include things like the + ;; `completions-first-difference' face, which we don't want to + ;; include upon insertion. + (if minibuffer-allow-text-properties + ;; If we're preserving properties, then just remove the faces + ;; and other properties added by the completion machinery. + (remove-text-properties 0 (length newtext) '(face completion-score) + newtext) + ;; Remove all text properties. + (set-text-properties 0 (length newtext) nil newtext)) ;; Maybe this should be in subr.el. ;; You'd think this is trivial to do, but details matter if you want ;; to keep markers "at the right place" and be robust in the face of commit cbfa41154467c6a6e3016a5689bc3f165f4e0032 Author: Lars Ingebrigtsen Date: Mon Sep 14 13:49:04 2020 +0200 Allow hitting RET on info node names with multiple whitespace chars * lisp/info.el (info--node-canonicalize-whitespace): New function (bug#10784). (Info-extract-menu-node-name): Use it. (Info-find-node): Use it. diff --git a/lisp/info.el b/lisp/info.el index dc1102aab3..e4f75b481f 100644 --- a/lisp/info.el +++ b/lisp/info.el @@ -956,6 +956,7 @@ This function first looks for a case-sensitive match for NODENAME; if none is found it then tries a case-insensitive match (unless STRICT-CASE is non-nil)." (info-initialize) + (setq nodename (info--node-canonicalize-whitespace nodename)) (setq filename (Info-find-file filename)) ;; Go into Info buffer. (or (derived-mode-p 'Info-mode) (switch-to-buffer "*info*")) @@ -2684,14 +2685,16 @@ Because of ambiguities, this should be concatenated with something like ;;; (setq Info-point-loc ;;; (buffer-substring (match-beginning 0) (1- (match-beginning 1)))) ) - (replace-regexp-in-string - "[ \n]+" " " + (info--node-canonicalize-whitespace (or (and (not (equal (match-string-no-properties 2) "")) (match-string-no-properties 2)) ;; If the node name is the menu entry name (using `entry::'). (buffer-substring-no-properties (match-beginning 0) (1- (match-beginning 1))))))) +(defun info--node-canonicalize-whitespace (string) + (replace-regexp-in-string "[ \t\n]+" " " string)) + ;; No one calls this. ;;(defun Info-menu-item-sequence (list) ;; (while list commit 7629aa74091d43370da751d86b0584ba3516ea75 Author: Lars Ingebrigtsen Date: Mon Sep 14 13:26:17 2020 +0200 Revert "Fix completion in `read-library-name'" This reverts commit ed44217d3245ddc8f2cf75c9499d5bb37848cfd7. This commit broke the use case of `M-x load-library RET o/or TAB' to expand to org/org. diff --git a/lisp/emacs-lisp/find-func.el b/lisp/emacs-lisp/find-func.el index 9e252d8c52..f3f3944a7f 100644 --- a/lisp/emacs-lisp/find-func.el +++ b/lisp/emacs-lisp/find-func.el @@ -286,20 +286,10 @@ Interactively, prompt for LIBRARY using the one at or near point." A library name is the filename of an Emacs Lisp library located in a directory under `load-path' (or `find-function-source-path', if non-nil)." - (let* ((suffix-regexp (mapconcat - (lambda (suffix) - (concat (regexp-quote suffix) "\\'")) - (find-library-suffixes) - "\\|")) - (table (cl-loop for dir in (or find-function-source-path load-path) - for dir-or-default = (or dir default-directory) - when (file-readable-p dir-or-default) - append (mapcar - (lambda (file) - (replace-regexp-in-string suffix-regexp - "" file)) - (directory-files dir-or-default nil - suffix-regexp)))) + (let* ((dirs (or find-function-source-path load-path)) + (suffixes (find-library-suffixes)) + (table (apply-partially 'locate-file-completion-table + dirs suffixes)) (def (if (eq (function-called-at-point) 'require) ;; `function-called-at-point' may return 'require ;; with `point' anywhere on this line. So wrap the commit 7cc161433832e6a518701d2b47fef7b298dc4a0a Author: Lars Ingebrigtsen Date: Mon Sep 14 13:14:21 2020 +0200 Further fixups in minibuffer-default--in-prompt-regexps * lisp/minibuf-eldef.el (minibuffer-default--in-prompt-regexps): Really allow shortening the default prompt format to [foo] (if requested) (bug#12443). diff --git a/lisp/minibuf-eldef.el b/lisp/minibuf-eldef.el index 6cd858093c..363899d265 100644 --- a/lisp/minibuf-eldef.el +++ b/lisp/minibuf-eldef.el @@ -44,12 +44,12 @@ (concat (regexp-quote (substring minibuffer-default-prompt-format 0 (match-beginning 0))) - ".*" + "\\(.*?\\)" (regexp-quote (substring minibuffer-default-prompt-format (match-end 0)))) (regexp-quote minibuffer-default-prompt-format)) "\\): ") - 1) + 1 (and minibuffer-eldef-shorten-default " [\\2]")) `(("\\( (default\\(?: is\\)? \\(.*\\))\\):? \\'" 1 ,(if minibuffer-eldef-shorten-default " [\\2]")) ("([^(]+?\\(, default\\(?: is\\)? \\(.*\\)\\)):? \\'" 1) commit b40a7056e53afd9d4ffdac03fd7b1b0fabb2a85d Author: Lars Ingebrigtsen Date: Mon Sep 14 12:34:56 2020 +0200 Support build of Emacs on ARM Macos machines * configure.ac: Add support for aarch64-* on Macos (i.e., 64-bit ARM) (bug#43369). diff --git a/configure.ac b/configure.ac index 33948fd776..ae0c0d2a2e 100644 --- a/configure.ac +++ b/configure.ac @@ -724,7 +724,7 @@ case "${canonical}" in *-apple-darwin* ) case "${canonical}" in *-apple-darwin[0-9].*) unported=yes ;; - i[3456]86-* | x86_64-* | arm-* ) ;; + i[3456]86-* | x86_64-* | arm-* | aarch64-* ) ;; * ) unported=yes ;; esac opsys=darwin commit 0e00f199cd3599977f75326bb7adc9d70390661e Author: Mattias Engdegård Date: Fri Sep 11 11:43:15 2020 +0200 Calc: fix binomial coefficients for negative arguments (bug#16999) For some values outside integers 0≤k≤n, (n choose k) gave wrong results, entered infinite recursion or used unreasonably amounts of stack space. This change fixes that and extends the function to all integer arguments using the definitions from M. J. Kronenburg (https://arxiv.org/abs/1105.3689). * lisp/calc/calc-comb.el (calcFunc-choose): Fix sign error to prevent infinite recursion and extend function to handle all integer arguments. (math-choose-iter, math-choose-float-iter): Rewrite in iterative form; no TCO in elisp yet. * test/lisp/calc/calc-tests.el (calc-tests--fac, calc-tests--choose) (calc-tests--check-choose, calc-tests--explain-choose) (calc-tests--calc-to-number): New helper functions. (calc-choose): New test. diff --git a/lisp/calc/calc-comb.el b/lisp/calc/calc-comb.el index 2efeb7f0f2..f7e29c6e52 100644 --- a/lisp/calc/calc-comb.el +++ b/lisp/calc/calc-comb.el @@ -439,12 +439,25 @@ (math-div (calcFunc-fact (math-float n)) (math-mul (calcFunc-fact m) (calcFunc-fact (math-sub n m)))))) - ((math-negp m) 0) - ((math-negp n) - (let ((val (calcFunc-choose (math-add (math-add n m) -1) m))) + ;; For the extension to negative integer arguments we follow + ;; M. J. Kronenburg, The Binomial Coefficient for Negative Arguments, + ;; arXiv:1105.3689v2 + ((and (math-negp n) (not (math-negp m))) + ;; n<0≤m: (n choose m) = (-1)^m (-n+m-1 choose m) + (let ((val (calcFunc-choose (math-add (math-sub m n) -1) m))) (if (math-evenp (math-trunc m)) val (math-neg val)))) + ((and (math-negp n) (math-num-integerp n)) + (if (math-lessp n m) + 0 + ;; m≤n<0: (n choose m) = (-1)^(n-m) (-m-1 choose n-m) + (let ((val (calcFunc-choose (math-sub (math-neg m) 1) + (math-sub n m)))) + (if (math-evenp (math-sub n m)) + val + (math-neg val))))) + ((math-negp m) 0) ((and (math-num-integerp n) (Math-lessp n m)) 0) @@ -461,20 +474,23 @@ (math-choose-float-iter tm n 1 1))))))) (defun math-choose-iter (m n i c) - (if (and (= (% i 5) 1) (> i 5)) + (while (<= i m) + (when (and (= (% i 5) 1) (> i 5)) (math-working (format "choose(%d)" (1- i)) c)) - (if (<= i m) - (math-choose-iter m (1- n) (1+ i) - (math-quotient (math-mul c n) i)) - c)) + (setq c (math-quotient (math-mul c n) i)) + (setq n (1- n)) + (setq i (1+ i))) + c) (defun math-choose-float-iter (count n i c) - (if (= (% i 5) 1) + (while (> count 0) + (when (= (% i 5) 1) (math-working (format "choose(%d)" (1- i)) c)) - (if (> count 0) - (math-choose-float-iter (1- count) (math-sub n 1) (1+ i) - (math-div (math-mul c n) i)) - c)) + (setq c (math-div (math-mul c n) i)) + (setq n (math-sub n 1)) + (setq i (1+ i)) + (setq count (1- count))) + c) ;;; Stirling numbers. diff --git a/test/lisp/calc/calc-tests.el b/test/lisp/calc/calc-tests.el index 0909756468..dce82b6f53 100644 --- a/test/lisp/calc/calc-tests.el +++ b/test/lisp/calc/calc-tests.el @@ -391,6 +391,73 @@ An existing calc stack is reused, otherwise a new one is created." (var n var-n) -1 1)) 8))) +(defun calc-tests--fac (n) + (apply #'* (number-sequence 1 n))) + +(defun calc-tests--choose (n k) + "N choose K, reference implementation." + (cond + ((and (integerp n) (integerp k)) + (if (<= 0 n) + (if (<= 0 k n) + (/ (calc-tests--fac n) + (* (calc-tests--fac k) (calc-tests--fac (- n k)))) + 0) ; 0≤n %S, expected %S" n k got expected))) + +(put 'calc-tests--check-choose 'ert-explainer 'calc-tests--explain-choose) + +(defun calc-tests--calc-to-number (x) + "Convert a Calc object to a Lisp number." + (pcase x + ((pred numberp) x) + (`(frac ,p ,q) (/ (float p) q)) + (`(float ,m ,e) (* m (expt 10 e))) + (_ (error "calc object not converted: %S" x)))) + +(ert-deftest calc-choose () + "Test computation of binomial coefficients (bug#16999)." + ;; Integral arguments + (dolist (n (number-sequence -6 6)) + (dolist (k (number-sequence -6 6)) + (should (calc-tests--check-choose n k)))) + + ;; Fractional n, natural k + (should (equal (calc-tests--calc-to-number + (calcFunc-choose '(frac 15 2) 3)) + (calc-tests--choose 7.5 3))) + + (should (equal (calc-tests--calc-to-number + (calcFunc-choose '(frac 1 2) 2)) + (calc-tests--choose 0.5 2))) + + (should (equal (calc-tests--calc-to-number + (calcFunc-choose '(frac -15 2) 3)) + (calc-tests--choose -7.5 3)))) + (provide 'calc-tests) ;;; calc-tests.el ends here commit f66829e0f47a54a51fd1378acbf2b1085673790f Author: Mattias Engdegård Date: Mon Sep 14 11:17:49 2020 +0200 * lisp/simple.el (undo-redo): Rephrase error message. diff --git a/lisp/simple.el b/lisp/simple.el index b00f6bba06..6ef327ba40 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -2755,7 +2755,7 @@ Interactively, ARG is the prefix numeric argument and defaults to 1." (interactive "*p") (cond ((not (undo--last-change-was-undo-p buffer-undo-list)) - (user-error "No undo to undo")) + (user-error "No undone changes to redo")) (t (let* ((ul buffer-undo-list) (new-ul