aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlex Branham2018-12-17 12:15:09 +0100
committerMichael Albinus2018-12-17 12:15:09 +0100
commit36b05dc84247db1391a423df94e4b9a478e29dc5 (patch)
tree6dff27f40a20402aceec355ffc9551d4e3e3525a
parent09a6cc4778d4b90a0bb8da646425d04d8f8a6ec9 (diff)
downloademacs-36b05dc84247db1391a423df94e4b9a478e29dc5.tar.gz
emacs-36b05dc84247db1391a423df94e4b9a478e29dc5.zip
New function flatten-tree
Co-authored-by: Basil L. Contovounesios <contovob@tcd.ie> * doc/lispref/lists.texi: Document `flatten-tree'. * lisp/progmodes/js.el (js--maybe-join): * lisp/printing.el (pr-switches): * lisp/lpr.el (lpr-print-region): * lisp/gnus/nnimap.el (nnimap-find-wanted-parts): * lisp/gnus/message.el (message-talkative-question): * lisp/gnus/gnus-sum.el (gnus-remove-thread) (gnus-thread-highest-number, gnus-thread-latest-date): * lisp/eshell/esh-util.el (eshell-flatten-and-stringify): * lisp/eshell/esh-opt.el (eshell-eval-using-options): * lisp/eshell/esh-ext.el (eshell-external-command): * lisp/eshell/em-xtra.el (eshell/expr): * lisp/eshell/em-unix.el (eshell/rm, eshell-mvcpln-template) (eshell/cat, eshell/make, eshell-poor-mans-grep, eshell-grep) (eshell/du, eshell/time, eshell/diff, eshell/locate): * lisp/eshell/em-tramp.el (eshell/su, eshell/sudo): * lisp/eshell/em-term.el (eshell-exec-visual): * lisp/eshell/em-dirs.el (eshell-dirs-substitute-cd, eshell/cd): * lisp/eshell/em-basic.el (eshell/printnl): Use new flatten-tree. * lisp/progmodes/js.el (js--flatten-list): * lisp/lpr.el (lpr-flatten-list): * lisp/gnus/message.el (message-flatten-list): * lisp/eshell/esh-util.el (eshell-flatten-list): Obsolete in favor of Emacs-wide `flatten-tree'. * lisp/subr.el (flatten-list): Alias to `flatten-tree' for discoverability. * lisp/subr.el (flatten-tree): New defun. * test/lisp/subr-tests.el (subr-tests-flatten-tree): New test.
-rw-r--r--doc/lispref/lists.texi12
-rw-r--r--etc/NEWS6
-rw-r--r--lisp/eshell/em-basic.el2
-rw-r--r--lisp/eshell/em-dirs.el4
-rw-r--r--lisp/eshell/em-term.el2
-rw-r--r--lisp/eshell/em-tramp.el4
-rw-r--r--lisp/eshell/em-unix.el22
-rw-r--r--lisp/eshell/em-xtra.el2
-rw-r--r--lisp/eshell/esh-ext.el2
-rw-r--r--lisp/eshell/esh-opt.el4
-rw-r--r--lisp/eshell/esh-util.el12
-rw-r--r--lisp/gnus/gnus-sum.el10
-rw-r--r--lisp/gnus/message.el12
-rw-r--r--lisp/gnus/nnimap.el2
-rw-r--r--lisp/lpr.el20
-rw-r--r--lisp/printing.el2
-rw-r--r--lisp/progmodes/js.el8
-rw-r--r--lisp/subr.el25
-rw-r--r--test/lisp/subr-tests.el17
19 files changed, 96 insertions, 72 deletions
diff --git a/doc/lispref/lists.texi b/doc/lispref/lists.texi
index 69f93009520..31cc3190854 100644
--- a/doc/lispref/lists.texi
+++ b/doc/lispref/lists.texi
@@ -667,6 +667,18 @@ non-@code{nil}, it copies vectors too (and operates recursively on
667their elements). 667their elements).
668@end defun 668@end defun
669 669
670@defun flatten-tree tree
671Take @var{tree} and "flatten" it.
672This always returns a list containing all the terminal nodes, or
673leaves, of @var{tree}. Dotted pairs are flattened as well, and nil
674elements are removed.
675@end defun
676
677@example
678(flatten-tree '(1 (2 . 3) nil (4 5 (6)) 7))
679 @result{}(1 2 3 4 5 6 7)
680@end example
681
670@defun number-sequence from &optional to separation 682@defun number-sequence from &optional to separation
671This returns a list of numbers starting with @var{from} and 683This returns a list of numbers starting with @var{from} and
672incrementing by @var{separation}, and ending at or just before 684incrementing by @var{separation}, and ending at or just before
diff --git a/etc/NEWS b/etc/NEWS
index c88f6ef5ca4..327276eef9b 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -1359,6 +1359,12 @@ are implemented in C using the Jansson library.
1359** New function 'ring-resize'. 1359** New function 'ring-resize'.
1360'ring-resize' can be used to grow or shrink a ring. 1360'ring-resize' can be used to grow or shrink a ring.
1361 1361
1362+++
1363** New function 'flatten-tree'.
1364'flatten-list' is provided as an alias. These functions take a tree
1365and 'flatten' it such that the result is a list of all the terminal
1366nodes.
1367
1362** Mailcap 1368** Mailcap
1363 1369
1364--- 1370---
diff --git a/lisp/eshell/em-basic.el b/lisp/eshell/em-basic.el
index 5201076f485..4a99d838579 100644
--- a/lisp/eshell/em-basic.el
+++ b/lisp/eshell/em-basic.el
@@ -118,7 +118,7 @@ or `eshell-printn' for display."
118 118
119(defun eshell/printnl (&rest args) 119(defun eshell/printnl (&rest args)
120 "Print out each of the arguments, separated by newlines." 120 "Print out each of the arguments, separated by newlines."
121 (let ((elems (eshell-flatten-list args))) 121 (let ((elems (flatten-tree args)))
122 (while elems 122 (while elems
123 (eshell-printn (eshell-echo (list (car elems)))) 123 (eshell-printn (eshell-echo (list (car elems))))
124 (setq elems (cdr elems))))) 124 (setq elems (cdr elems)))))
diff --git a/lisp/eshell/em-dirs.el b/lisp/eshell/em-dirs.el
index 853382888c9..b47f76fbfb2 100644
--- a/lisp/eshell/em-dirs.el
+++ b/lisp/eshell/em-dirs.el
@@ -259,7 +259,7 @@ Thus, this does not include the current directory.")
259 (if (> (length args) 1) 259 (if (> (length args) 1)
260 (error "%s: command not found" (car args)) 260 (error "%s: command not found" (car args))
261 (throw 'eshell-replace-command 261 (throw 'eshell-replace-command
262 (eshell-parse-command "cd" (eshell-flatten-list args))))) 262 (eshell-parse-command "cd" (flatten-tree args)))))
263 263
264(defun eshell-parse-user-reference () 264(defun eshell-parse-user-reference ()
265 "An argument beginning with ~ is a filename to be expanded." 265 "An argument beginning with ~ is a filename to be expanded."
@@ -353,7 +353,7 @@ in the minibuffer:
353 353
354(defun eshell/cd (&rest args) ; all but first ignored 354(defun eshell/cd (&rest args) ; all but first ignored
355 "Alias to extend the behavior of `cd'." 355 "Alias to extend the behavior of `cd'."
356 (setq args (eshell-flatten-list args)) 356 (setq args (flatten-tree args))
357 (let ((path (car args)) 357 (let ((path (car args))
358 (subpath (car (cdr args))) 358 (subpath (car (cdr args)))
359 (case-fold-search (eshell-under-windows-p)) 359 (case-fold-search (eshell-under-windows-p))
diff --git a/lisp/eshell/em-term.el b/lisp/eshell/em-term.el
index ddde47f73d6..fdf40cae85d 100644
--- a/lisp/eshell/em-term.el
+++ b/lisp/eshell/em-term.el
@@ -175,7 +175,7 @@ allowed."
175 (let* (eshell-interpreter-alist 175 (let* (eshell-interpreter-alist
176 (interp (eshell-find-interpreter (car args) (cdr args))) 176 (interp (eshell-find-interpreter (car args) (cdr args)))
177 (program (car interp)) 177 (program (car interp))
178 (args (eshell-flatten-list 178 (args (flatten-tree
179 (eshell-stringify-list (append (cdr interp) 179 (eshell-stringify-list (append (cdr interp)
180 (cdr args))))) 180 (cdr args)))))
181 (term-buf 181 (term-buf
diff --git a/lisp/eshell/em-tramp.el b/lisp/eshell/em-tramp.el
index 9475f4ed949..f77b84d851b 100644
--- a/lisp/eshell/em-tramp.el
+++ b/lisp/eshell/em-tramp.el
@@ -62,7 +62,7 @@
62 "Alias \"su\" to call TRAMP. 62 "Alias \"su\" to call TRAMP.
63 63
64Uses the system su through TRAMP's su method." 64Uses the system su through TRAMP's su method."
65 (setq args (eshell-stringify-list (eshell-flatten-list args))) 65 (setq args (eshell-stringify-list (flatten-tree args)))
66 (let ((orig-args (copy-tree args))) 66 (let ((orig-args (copy-tree args)))
67 (eshell-eval-using-options 67 (eshell-eval-using-options
68 "su" args 68 "su" args
@@ -100,7 +100,7 @@ Become another USER during a login session.")
100 "Alias \"sudo\" to call Tramp. 100 "Alias \"sudo\" to call Tramp.
101 101
102Uses the system sudo through TRAMP's sudo method." 102Uses the system sudo through TRAMP's sudo method."
103 (setq args (eshell-stringify-list (eshell-flatten-list args))) 103 (setq args (eshell-stringify-list (flatten-tree args)))
104 (let ((orig-args (copy-tree args))) 104 (let ((orig-args (copy-tree args)))
105 (eshell-eval-using-options 105 (eshell-eval-using-options
106 "sudo" args 106 "sudo" args
diff --git a/lisp/eshell/em-unix.el b/lisp/eshell/em-unix.el
index 3aecebc2ebf..e46e1c417d4 100644
--- a/lisp/eshell/em-unix.el
+++ b/lisp/eshell/em-unix.el
@@ -231,7 +231,7 @@ Otherwise, Emacs will attempt to use rsh to invoke du on the remote machine."
231This is implemented to call either `delete-file', `kill-buffer', 231This is implemented to call either `delete-file', `kill-buffer',
232`kill-process', or `unintern', depending on the nature of the 232`kill-process', or `unintern', depending on the nature of the
233argument." 233argument."
234 (setq args (eshell-flatten-list args)) 234 (setq args (flatten-tree args))
235 (eshell-eval-using-options 235 (eshell-eval-using-options
236 "rm" args 236 "rm" args
237 '((?h "help" nil nil "show this usage screen") 237 '((?h "help" nil nil "show this usage screen")
@@ -481,7 +481,7 @@ Remove the DIRECTORY(ies), if they are empty.")
481 (error "%s: missing destination file or directory" ,command)) 481 (error "%s: missing destination file or directory" ,command))
482 (if (= len 1) 482 (if (= len 1)
483 (nconc args '("."))) 483 (nconc args '(".")))
484 (setq args (eshell-stringify-list (eshell-flatten-list args))) 484 (setq args (eshell-stringify-list (flatten-tree args)))
485 (if (and ,(not (equal command "ln")) 485 (if (and ,(not (equal command "ln"))
486 (string-match eshell-tar-regexp (car (last args))) 486 (string-match eshell-tar-regexp (car (last args)))
487 (or (> (length args) 2) 487 (or (> (length args) 2)
@@ -606,7 +606,7 @@ with `--symbolic'. When creating hard links, each TARGET must exist.")
606 "Implementation of cat in Lisp. 606 "Implementation of cat in Lisp.
607If in a pipeline, or the file is not a regular file, directory or 607If in a pipeline, or the file is not a regular file, directory or
608symlink, then revert to the system's definition of cat." 608symlink, then revert to the system's definition of cat."
609 (setq args (eshell-stringify-list (eshell-flatten-list args))) 609 (setq args (eshell-stringify-list (flatten-tree args)))
610 (if (or eshell-in-pipeline-p 610 (if (or eshell-in-pipeline-p
611 (catch 'special 611 (catch 'special
612 (dolist (arg args) 612 (dolist (arg args)
@@ -670,7 +670,7 @@ Fallback to standard make when called synchronously."
670 (compile (concat "make " (eshell-flatten-and-stringify args)))) 670 (compile (concat "make " (eshell-flatten-and-stringify args))))
671 (throw 'eshell-replace-command 671 (throw 'eshell-replace-command
672 (eshell-parse-command "*make" (eshell-stringify-list 672 (eshell-parse-command "*make" (eshell-stringify-list
673 (eshell-flatten-list args)))))) 673 (flatten-tree args))))))
674 674
675(put 'eshell/make 'eshell-no-numeric-conversions t) 675(put 'eshell/make 'eshell-no-numeric-conversions t)
676 676
@@ -705,7 +705,7 @@ available..."
705 (erase-buffer) 705 (erase-buffer)
706 (occur-mode) 706 (occur-mode)
707 (let ((files (eshell-stringify-list 707 (let ((files (eshell-stringify-list
708 (eshell-flatten-list (cdr args)))) 708 (flatten-tree (cdr args))))
709 (inhibit-redisplay t) 709 (inhibit-redisplay t)
710 string) 710 string)
711 (when (car args) 711 (when (car args)
@@ -750,11 +750,11 @@ external command."
750 (throw 'eshell-replace-command 750 (throw 'eshell-replace-command
751 (eshell-parse-command (concat "*" command) 751 (eshell-parse-command (concat "*" command)
752 (eshell-stringify-list 752 (eshell-stringify-list
753 (eshell-flatten-list args)))) 753 (flatten-tree args))))
754 (let* ((args (mapconcat 'identity 754 (let* ((args (mapconcat 'identity
755 (mapcar 'shell-quote-argument 755 (mapcar 'shell-quote-argument
756 (eshell-stringify-list 756 (eshell-stringify-list
757 (eshell-flatten-list args))) 757 (flatten-tree args)))
758 " ")) 758 " "))
759 (cmd (progn 759 (cmd (progn
760 (set-text-properties 0 (length args) 760 (set-text-properties 0 (length args)
@@ -876,7 +876,7 @@ external command."
876(defun eshell/du (&rest args) 876(defun eshell/du (&rest args)
877 "Implementation of \"du\" in Lisp, passing ARGS." 877 "Implementation of \"du\" in Lisp, passing ARGS."
878 (setq args (if args 878 (setq args (if args
879 (eshell-stringify-list (eshell-flatten-list args)) 879 (eshell-stringify-list (flatten-tree args))
880 '("."))) 880 '(".")))
881 (let ((ext-du (eshell-search-path "du"))) 881 (let ((ext-du (eshell-search-path "du")))
882 (if (and ext-du 882 (if (and ext-du
@@ -976,7 +976,7 @@ Show wall-clock time elapsed during execution of COMMAND.")
976 (eshell-parse-command (car time-args) 976 (eshell-parse-command (car time-args)
977;;; https://lists.gnu.org/r/bug-gnu-emacs/2007-08/msg00205.html 977;;; https://lists.gnu.org/r/bug-gnu-emacs/2007-08/msg00205.html
978 (eshell-stringify-list 978 (eshell-stringify-list
979 (eshell-flatten-list (cdr time-args)))))))) 979 (flatten-tree (cdr time-args))))))))
980 980
981(defun eshell/whoami (&rest _args) 981(defun eshell/whoami (&rest _args)
982 "Make \"whoami\" Tramp aware." 982 "Make \"whoami\" Tramp aware."
@@ -1000,7 +1000,7 @@ Show wall-clock time elapsed during execution of COMMAND.")
1000 1000
1001(defun eshell/diff (&rest args) 1001(defun eshell/diff (&rest args)
1002 "Alias \"diff\" to call Emacs `diff' function." 1002 "Alias \"diff\" to call Emacs `diff' function."
1003 (let ((orig-args (eshell-stringify-list (eshell-flatten-list args)))) 1003 (let ((orig-args (eshell-stringify-list (flatten-tree args))))
1004 (if (or eshell-plain-diff-behavior 1004 (if (or eshell-plain-diff-behavior
1005 (not (and (eshell-interactive-output-p) 1005 (not (and (eshell-interactive-output-p)
1006 (not eshell-in-pipeline-p) 1006 (not eshell-in-pipeline-p)
@@ -1056,7 +1056,7 @@ Show wall-clock time elapsed during execution of COMMAND.")
1056 (string-match "^-" (car args)))) 1056 (string-match "^-" (car args))))
1057 (throw 'eshell-replace-command 1057 (throw 'eshell-replace-command
1058 (eshell-parse-command "*locate" (eshell-stringify-list 1058 (eshell-parse-command "*locate" (eshell-stringify-list
1059 (eshell-flatten-list args)))) 1059 (flatten-tree args))))
1060 (save-selected-window 1060 (save-selected-window
1061 (let ((locate-history-list (list (car args)))) 1061 (let ((locate-history-list (list (car args))))
1062 (locate-with-filter (car args) (cadr args)))))) 1062 (locate-with-filter (car args) (cadr args))))))
diff --git a/lisp/eshell/em-xtra.el b/lisp/eshell/em-xtra.el
index cc84d198544..eb9847c60c3 100644
--- a/lisp/eshell/em-xtra.el
+++ b/lisp/eshell/em-xtra.el
@@ -51,7 +51,7 @@ naturally accessible within Emacs."
51 "Implementation of expr, using the calc package." 51 "Implementation of expr, using the calc package."
52 (if (not (fboundp 'calc-eval)) 52 (if (not (fboundp 'calc-eval))
53 (throw 'eshell-replace-command 53 (throw 'eshell-replace-command
54 (eshell-parse-command "*expr" (eshell-flatten-list args))) 54 (eshell-parse-command "*expr" (flatten-tree args)))
55 ;; to fool the byte-compiler... 55 ;; to fool the byte-compiler...
56 (let ((func 'calc-eval)) 56 (let ((func 'calc-eval))
57 (funcall func (eshell-flatten-and-stringify args))))) 57 (funcall func (eshell-flatten-and-stringify args)))))
diff --git a/lisp/eshell/esh-ext.el b/lisp/eshell/esh-ext.el
index 244cc7ff1f3..9e7d8bb608e 100644
--- a/lisp/eshell/esh-ext.el
+++ b/lisp/eshell/esh-ext.el
@@ -222,7 +222,7 @@ causing the user to wonder if anything's really going on..."
222 222
223(defun eshell-external-command (command args) 223(defun eshell-external-command (command args)
224 "Insert output from an external COMMAND, using ARGS." 224 "Insert output from an external COMMAND, using ARGS."
225 (setq args (eshell-stringify-list (eshell-flatten-list args))) 225 (setq args (eshell-stringify-list (flatten-tree args)))
226 (let ((interp (eshell-find-interpreter 226 (let ((interp (eshell-find-interpreter
227 command 227 command
228 args 228 args
diff --git a/lisp/eshell/esh-opt.el b/lisp/eshell/esh-opt.el
index d7a449450f9..69d10b4ccfc 100644
--- a/lisp/eshell/esh-opt.el
+++ b/lisp/eshell/esh-opt.el
@@ -77,7 +77,7 @@ arguments, some do not. The recognized :KEYWORDS are:
77 arguments. 77 arguments.
78 78
79:preserve-args 79:preserve-args
80 If present, do not pass MACRO-ARGS through `eshell-flatten-list' 80 If present, do not pass MACRO-ARGS through `flatten-tree'
81and `eshell-stringify-list'. 81and `eshell-stringify-list'.
82 82
83:parse-leading-options-only 83:parse-leading-options-only
@@ -106,7 +106,7 @@ let-bound variable `args'."
106 ,(if (memq ':preserve-args (cadr options)) 106 ,(if (memq ':preserve-args (cadr options))
107 macro-args 107 macro-args
108 (list 'eshell-stringify-list 108 (list 'eshell-stringify-list
109 (list 'eshell-flatten-list macro-args)))) 109 (list 'flatten-tree macro-args))))
110 (processed-args (eshell--do-opts ,name ,options temp-args)) 110 (processed-args (eshell--do-opts ,name ,options temp-args))
111 ,@(delete-dups 111 ,@(delete-dups
112 (delq nil (mapcar (lambda (opt) 112 (delq nil (mapcar (lambda (opt)
diff --git a/lisp/eshell/esh-util.el b/lisp/eshell/esh-util.el
index 8fe8c461fdb..b55f8733802 100644
--- a/lisp/eshell/esh-util.el
+++ b/lisp/eshell/esh-util.el
@@ -285,15 +285,7 @@ Prepend remote identification of `default-directory', if any."
285 ,@forms) 285 ,@forms)
286 (setq list-iter (cdr list-iter))))) 286 (setq list-iter (cdr list-iter)))))
287 287
288(defun eshell-flatten-list (args) 288(define-obsolete-function-alias 'eshell-flatten-list #'flatten-tree "27.1")
289 "Flatten any lists within ARGS, so that there are no sublists."
290 (let ((new-list (list t)))
291 (dolist (a args)
292 (if (and (listp a)
293 (listp (cdr a)))
294 (nconc new-list (eshell-flatten-list a))
295 (nconc new-list (list a))))
296 (cdr new-list)))
297 289
298(defun eshell-uniquify-list (l) 290(defun eshell-uniquify-list (l)
299 "Remove occurring multiples in L. You probably want to sort first." 291 "Remove occurring multiples in L. You probably want to sort first."
@@ -330,7 +322,7 @@ Prepend remote identification of `default-directory', if any."
330 322
331(defsubst eshell-flatten-and-stringify (&rest args) 323(defsubst eshell-flatten-and-stringify (&rest args)
332 "Flatten and stringify all of the ARGS into a single string." 324 "Flatten and stringify all of the ARGS into a single string."
333 (mapconcat 'eshell-stringify (eshell-flatten-list args) " ")) 325 (mapconcat 'eshell-stringify (flatten-tree args) " "))
334 326
335(defsubst eshell-directory-files (regexp &optional directory) 327(defsubst eshell-directory-files (regexp &optional directory)
336 "Return a list of files in the given DIRECTORY matching REGEXP." 328 "Return a list of files in the given DIRECTORY matching REGEXP."
diff --git a/lisp/gnus/gnus-sum.el b/lisp/gnus/gnus-sum.el
index 4baf4bc8263..3f5362ba17a 100644
--- a/lisp/gnus/gnus-sum.el
+++ b/lisp/gnus/gnus-sum.el
@@ -4773,7 +4773,7 @@ If LINE, insert the rebuilt thread starting on line LINE."
4773 (let (headers thread last-id) 4773 (let (headers thread last-id)
4774 ;; First go up in this thread until we find the root. 4774 ;; First go up in this thread until we find the root.
4775 (setq last-id (gnus-root-id id) 4775 (setq last-id (gnus-root-id id)
4776 headers (message-flatten-list (gnus-id-to-thread last-id))) 4776 headers (flatten-tree (gnus-id-to-thread last-id)))
4777 ;; We have now found the real root of this thread. It might have 4777 ;; We have now found the real root of this thread. It might have
4778 ;; been gathered into some loose thread, so we have to search 4778 ;; been gathered into some loose thread, so we have to search
4779 ;; through the threads to find the thread we wanted. 4779 ;; through the threads to find the thread we wanted.
@@ -5069,7 +5069,7 @@ Unscored articles will be counted as having a score of zero."
5069 "Return the highest article number in THREAD." 5069 "Return the highest article number in THREAD."
5070 (apply 'max (mapcar (lambda (header) 5070 (apply 'max (mapcar (lambda (header)
5071 (mail-header-number header)) 5071 (mail-header-number header))
5072 (message-flatten-list thread)))) 5072 (flatten-tree thread))))
5073 5073
5074(defun gnus-article-sort-by-most-recent-date (h1 h2) 5074(defun gnus-article-sort-by-most-recent-date (h1 h2)
5075 "Sort articles by number." 5075 "Sort articles by number."
@@ -5087,9 +5087,9 @@ Unscored articles will be counted as having a score of zero."
5087 "Return the highest article date in THREAD." 5087 "Return the highest article date in THREAD."
5088 (apply 'max 5088 (apply 'max
5089 (mapcar (lambda (header) (float-time 5089 (mapcar (lambda (header) (float-time
5090 (gnus-date-get-time 5090 (gnus-date-get-time
5091 (mail-header-date header)))) 5091 (mail-header-date header))))
5092 (message-flatten-list thread)))) 5092 (flatten-tree thread))))
5093 5093
5094(defun gnus-thread-total-score-1 (root) 5094(defun gnus-thread-total-score-1 (root)
5095 ;; This function find the total score of the thread below ROOT. 5095 ;; This function find the total score of the thread below ROOT.
diff --git a/lisp/gnus/message.el b/lisp/gnus/message.el
index fdaa4e82727..03f80616d9e 100644
--- a/lisp/gnus/message.el
+++ b/lisp/gnus/message.el
@@ -8051,7 +8051,7 @@ regular text mode tabbing command."
8051If SHOW is non-nil, the arguments TEXT... are displayed in a temp buffer. 8051If SHOW is non-nil, the arguments TEXT... are displayed in a temp buffer.
8052The following arguments may contain lists of values." 8052The following arguments may contain lists of values."
8053 (if (and show 8053 (if (and show
8054 (setq text (message-flatten-list text))) 8054 (setq text (flatten-tree text)))
8055 (save-window-excursion 8055 (save-window-excursion
8056 (with-output-to-temp-buffer " *MESSAGE information message*" 8056 (with-output-to-temp-buffer " *MESSAGE information message*"
8057 (with-current-buffer " *MESSAGE information message*" 8057 (with-current-buffer " *MESSAGE information message*"
@@ -8061,15 +8061,7 @@ The following arguments may contain lists of values."
8061 (funcall ask question)) 8061 (funcall ask question))
8062 (funcall ask question))) 8062 (funcall ask question)))
8063 8063
8064(defun message-flatten-list (list) 8064(define-obsolete-function-alias 'message-flatten-list #'flatten-tree "27.1")
8065 "Return a new, flat list that contains all elements of LIST.
8066
8067\(message-flatten-list \\='(1 (2 3 (4 5 (6))) 7))
8068=> (1 2 3 4 5 6 7)"
8069 (cond ((consp list)
8070 (apply 'append (mapcar 'message-flatten-list list)))
8071 (list
8072 (list list))))
8073 8065
8074(defun message-generate-new-buffer-clone-locals (name &optional varstr) 8066(defun message-generate-new-buffer-clone-locals (name &optional varstr)
8075 "Create and return a buffer with name based on NAME using `generate-new-buffer'. 8067 "Create and return a buffer with name based on NAME using `generate-new-buffer'.
diff --git a/lisp/gnus/nnimap.el b/lisp/gnus/nnimap.el
index 1a3b05ddb37..adbce25530d 100644
--- a/lisp/gnus/nnimap.el
+++ b/lisp/gnus/nnimap.el
@@ -804,7 +804,7 @@ textual parts.")
804 (insert "\n--" boundary "--\n"))) 804 (insert "\n--" boundary "--\n")))
805 805
806(defun nnimap-find-wanted-parts (structure) 806(defun nnimap-find-wanted-parts (structure)
807 (message-flatten-list (nnimap-find-wanted-parts-1 structure ""))) 807 (flatten-tree (nnimap-find-wanted-parts-1 structure "")))
808 808
809(defun nnimap-find-wanted-parts-1 (structure prefix) 809(defun nnimap-find-wanted-parts-1 (structure prefix)
810 (let ((num 1) 810 (let ((num 1)
diff --git a/lisp/lpr.el b/lisp/lpr.el
index 33b8da8d760..969b57d6444 100644
--- a/lisp/lpr.el
+++ b/lisp/lpr.el
@@ -258,7 +258,7 @@ for further customization of the printer command."
258 258
259(defun lpr-print-region (start end switches name) 259(defun lpr-print-region (start end switches name)
260 (let ((buf (current-buffer)) 260 (let ((buf (current-buffer))
261 (nswitches (lpr-flatten-list 261 (nswitches (flatten-tree
262 (mapcar #'lpr-eval-switch ; Dynamic evaluation 262 (mapcar #'lpr-eval-switch ; Dynamic evaluation
263 switches))) 263 switches)))
264 (switch-string (if switches 264 (switch-string (if switches
@@ -336,23 +336,7 @@ The characters tab, linefeed, space, return and formfeed are not affected."
336 ((consp arg) (apply (car arg) (cdr arg))) 336 ((consp arg) (apply (car arg) (cdr arg)))
337 (t nil))) 337 (t nil)))
338 338
339;; `lpr-flatten-list' is defined here (copied from "message.el" and 339(define-obsolete-function-alias 'lpr-flatten-list #'flatten-tree "27.1")
340;; enhanced to handle dotted pairs as well) until we can get some
341;; sensible autoloads, or `flatten-list' gets put somewhere decent.
342
343;; (lpr-flatten-list '((a . b) c (d . e) (f g h) i . j))
344;; => (a b c d e f g h i j)
345
346(defun lpr-flatten-list (&rest list)
347 (lpr-flatten-list-1 list))
348
349(defun lpr-flatten-list-1 (list)
350 (cond
351 ((null list) nil)
352 ((consp list)
353 (append (lpr-flatten-list-1 (car list))
354 (lpr-flatten-list-1 (cdr list))))
355 (t (list list))))
356 340
357(provide 'lpr) 341(provide 'lpr)
358 342
diff --git a/lisp/printing.el b/lisp/printing.el
index 2fc2323028f..c1a73df14c1 100644
--- a/lisp/printing.el
+++ b/lisp/printing.el
@@ -5672,7 +5672,7 @@ If menu binding was not done, calls `pr-menu-bind'."
5672(defun pr-switches (switches mess) 5672(defun pr-switches (switches mess)
5673 (or (listp switches) 5673 (or (listp switches)
5674 (error "%S should have a list of strings" mess)) 5674 (error "%S should have a list of strings" mess))
5675 (lpr-flatten-list ; dynamic evaluation 5675 (flatten-tree ; dynamic evaluation
5676 (mapcar #'lpr-eval-switch switches))) 5676 (mapcar #'lpr-eval-switch switches)))
5677 5677
5678 5678
diff --git a/lisp/progmodes/js.el b/lisp/progmodes/js.el
index cec48a82a20..ddba7636b4a 100644
--- a/lisp/progmodes/js.el
+++ b/lisp/progmodes/js.el
@@ -623,11 +623,7 @@ then the \".\"s will be lined up:
623 "Parse state at `js--last-parse-pos'.") 623 "Parse state at `js--last-parse-pos'.")
624(make-variable-buffer-local 'js--state-at-last-parse-pos) 624(make-variable-buffer-local 'js--state-at-last-parse-pos)
625 625
626(defun js--flatten-list (list) 626(define-obsolete-function-alias 'js--flatten-list #'flatten-tree "27.1")
627 (cl-loop for item in list
628 nconc (cond ((consp item)
629 (js--flatten-list item))
630 (item (list item)))))
631 627
632(defun js--maybe-join (prefix separator suffix &rest list) 628(defun js--maybe-join (prefix separator suffix &rest list)
633 "Helper function for `js--update-quick-match-re'. 629 "Helper function for `js--update-quick-match-re'.
@@ -636,7 +632,7 @@ elements, separated by SEPARATOR, prefixed by PREFIX, and ended
636with SUFFIX as with `concat'. Otherwise, if LIST is empty, return 632with SUFFIX as with `concat'. Otherwise, if LIST is empty, return
637nil. If any element in LIST is itself a list, flatten that 633nil. If any element in LIST is itself a list, flatten that
638element." 634element."
639 (setq list (js--flatten-list list)) 635 (setq list (flatten-tree list))
640 (when list 636 (when list
641 (concat prefix (mapconcat #'identity list separator) suffix))) 637 (concat prefix (mapconcat #'identity list separator) suffix)))
642 638
diff --git a/lisp/subr.el b/lisp/subr.el
index d3bc007293b..7a7c175db4a 100644
--- a/lisp/subr.el
+++ b/lisp/subr.el
@@ -5448,5 +5448,30 @@ This function is called from lisp/Makefile and leim/Makefile."
5448 (setq file (concat (substring file 1 2) ":" (substring file 2)))) 5448 (setq file (concat (substring file 1 2) ":" (substring file 2))))
5449 file) 5449 file)
5450 5450
5451(defun flatten-tree (tree)
5452 "Take TREE and \"flatten\" it.
5453This always returns a list containing all the terminal nodes, or
5454\"leaves\", of TREE. Dotted pairs are flattened as well, and nil
5455elements are removed.
5456
5457\(flatten-tree \\='(1 (2 . 3) nil (4 5 (6)) 7))
5458=> (1 2 3 4 5 6 7)
5459
5460TREE can be anything that can be made into a list. For each
5461element in TREE, if it is a cons cell return its car
5462recursively. Otherwise return the element."
5463 (let (elems)
5464 (setq tree (list tree))
5465 (while (let ((elem (pop tree)))
5466 (cond ((consp elem)
5467 (setq tree (cons (car elem) (cons (cdr elem) tree))))
5468 (elem
5469 (push elem elems)))
5470 tree))
5471 (nreverse elems)))
5472
5473;; Technically, `flatten-list' is a misnomer, but we provide it here
5474;; for discoverability:
5475(defalias 'flatten-list 'flatten-tree)
5451 5476
5452;;; subr.el ends here 5477;;; subr.el ends here
diff --git a/test/lisp/subr-tests.el b/test/lisp/subr-tests.el
index f218a7663e0..08f9a697a3c 100644
--- a/test/lisp/subr-tests.el
+++ b/test/lisp/subr-tests.el
@@ -372,5 +372,22 @@ See https://debbugs.gnu.org/cgi/bugreport.cgi?bug=19350."
372 (shell-quote-argument "%ca%"))) 372 (shell-quote-argument "%ca%")))
373 "without-caret %ca%")))) 373 "without-caret %ca%"))))
374 374
375(ert-deftest subr-tests-flatten-tree ()
376 "Test `flatten-tree' behavior."
377 (should (equal (flatten-tree '(1 (2 . 3) nil (4 5 (6)) 7))
378 '(1 2 3 4 5 6 7)))
379 (should (equal (flatten-tree '((1 . 2)))
380 '(1 2)))
381 (should (equal (flatten-tree '(1 nil 2))
382 '(1 2)))
383 (should (equal (flatten-tree 42)
384 '(42)))
385 (should (equal (flatten-tree t)
386 '(t)))
387 (should (equal (flatten-tree nil)
388 nil))
389 (should (equal (flatten-tree '(1 ("foo" "bar") 2))
390 '(1 "foo" "bar" 2))))
391
375(provide 'subr-tests) 392(provide 'subr-tests)
376;;; subr-tests.el ends here 393;;; subr-tests.el ends here