emacs-diffs
[Top][All Lists]
Advanced

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

scratch/etags-regen 1daad17 2/8: Merge branch 'master' into scratch/etag


From: Dmitry Gutov
Subject: scratch/etags-regen 1daad17 2/8: Merge branch 'master' into scratch/etags-regen
Date: Sun, 7 Feb 2021 21:11:59 -0500 (EST)

branch: scratch/etags-regen
commit 1daad1784f991d268515b1fa62b7787fe12bb89f
Merge: 8d00e2f 4ca808e
Author: Dmitry Gutov <dgutov@yandex.ru>
Commit: Dmitry Gutov <dgutov@yandex.ru>

    Merge branch 'master' into scratch/etags-regen
---
 doc/lispref/processes.texi    |  27 ++-
 etc/HELLO                     |  12 +-
 etc/NEWS                      |   4 +
 lisp/cedet/ede/base.el        |   5 +-
 lisp/cedet/ede/proj.el        |   2 +-
 lisp/epa.el                   |   4 +-
 lisp/gnus/gnus-agent.el       | 383 +++++++++++++-----------------------------
 lisp/gnus/gnus-async.el       |   9 +-
 lisp/gnus/gnus-cache.el       | 126 ++++----------
 lisp/gnus/gnus-cloud.el       |  16 +-
 lisp/gnus/gnus-sum.el         |  65 ++++---
 lisp/gnus/gnus.el             |   9 +-
 lisp/gnus/nnvirtual.el        | 172 +++++--------------
 lisp/ibuf-ext.el              |  16 +-
 lisp/ibuffer.el               |   9 +-
 lisp/international/fontset.el |   1 +
 lisp/language/cham.el         |   3 +-
 lisp/leim/quail/cham.el       | 116 +++++++++++++
 lisp/obsolete/nnir.el         |   1 -
 lisp/progmodes/xref.el        |  11 +-
 lisp/replace.el               |  11 +-
 src/nsselect.m                |  15 +-
 src/nsterm.h                  |   9 +
 src/nsterm.m                  |  21 ++-
 src/process.c                 |   9 +
 test/Makefile.in              |  20 ++-
 test/README                   |   9 +-
 test/infra/gitlab-ci.yml      |  77 ++++-----
 test/lisp/time-stamp-tests.el | 127 ++++++++------
 test/src/process-tests.el     | 115 +++++++++++++
 test/src/xdisp-tests.el       |  33 ++--
 31 files changed, 749 insertions(+), 688 deletions(-)

diff --git a/doc/lispref/processes.texi b/doc/lispref/processes.texi
index 535cebe..6dedaa3 100644
--- a/doc/lispref/processes.texi
+++ b/doc/lispref/processes.texi
@@ -729,7 +729,9 @@ coding systems (@pxref{Default Coding Systems}).  On the 
other hand,
 it will use @var{query-flag} as its query-on-exit flag (@pxref{Query
 Before Exit}).  It will be associated with the @var{stderr} buffer
 (@pxref{Process Buffers}) and send its output (which is the standard
-error of the main process) there.
+error of the main process) there.  To get the process object for the
+standard error process, pass the @var{stderr} buffer to
+@code{get-buffer-process}.
 
 If @var{stderr} is a pipe process, Emacs will use it as standard error
 process for the new process.
@@ -1942,6 +1944,29 @@ code:
 (while (accept-process-output stderr-process))
 @end example
 
+If you passed a buffer to the @var{stderr} argument of
+@code{make-process}, you still have to wait for the standard error
+process, like so:
+
+@example
+(let* ((stdout (generate-new-buffer "stdout"))
+       (stderr (generate-new-buffer "stderr"))
+       (process (make-process :name "test"
+                              :command '("my-program")
+                              :buffer stdout
+                              :stderr stderr))
+       (stderr-process (get-buffer-process stderr)))
+  (unless (and process stderr-process)
+    (error "Process unexpectedly nil"))
+  (while (accept-process-output process))
+  (while (accept-process-output stderr-process)))
+@end example
+
+@noindent
+Only when both @code{accept-process-output} forms return @code{nil},
+you can be sure that the process has exited and Emacs has read all its
+output.
+
 Reading pending standard error from a process running on a remote host
 is not possible this way.
 
diff --git a/etc/HELLO b/etc/HELLO
index 9a1f5d3..0cebb2b 100644
--- a/etc/HELLO
+++ b/etc/HELLO
@@ -30,22 +30,16 @@ Bengali (বাংলা)     নমস্কার
 Braille        ⠓⠑⠇⠇⠕
 Burmese (မြန်မာ)       မင်္ဂလာပါ
 C      printf ("Hello, world!\n");
-Cham (ꨌꩌ)      ꨦꨤꩌ ꨦꨰꨁ
-
+Cham (ꨌꩌ)      ꨦꨤꩌ ꨦꨁꨰ
 Cherokee (ᏣᎳᎩ ᎦᏬᏂᎯᏍᏗ)  ᎣᏏᏲ / ᏏᏲ
 Comanche /kəˈmæntʃiː/  Haa marʉ́awe
-
 Cree (ᓀᐦᐃᔭᐍᐏᐣ) ᑕᓂᓯ / ᐙᒋᔮ
-
 Czech (čeština)        Dobrý den
 Danish (dansk) Hej / Goddag / Halløj
 Dutch (Nederlands)     Hallo / Dag
 Efik  /ˈɛfɪk/  Mɔkɔm
-
 Egyptian Hieroglyphs (𓂋𓐰𓏤𓈖𓆎𓅓𓏏𓐰𓊖)       𓅓𓊵𓐰𓐷𓏏𓊪𓐸, 𓇍𓇋𓂻𓍘𓇋
-
 Emacs  emacs --no-splash -f view-hello-file
-
 Emoji  👋
 English /ˈɪŋɡlɪʃ/      Hello
 Esperanto      Saluton (Eĥoŝanĝo ĉiuĵaŭde)
@@ -61,7 +55,6 @@ Hebrew (עִבְרִית)     שָׁלוֹם
 Hungarian (magyar)     Szép jó napot!
 Hindi (हिंदी)  नमस्ते / नमस्कार ।
 Inuktitut (ᐃᓄᒃᑎᑐᑦ)     ᐊᐃ
-
 Italian (italiano)     Ciao / Buon giorno
 Javanese (ꦧꦱꦗꦮꦶ)       console.log("ꦲꦭꦺꦴ");
 Kannada (ಕನ್ನಡ)        ನಮಸ್ಕಾರ
@@ -69,7 +62,6 @@ Khmer (ភាសាខ្មែរ)     ជំរាបសួរ
 Lao (ພາສາລາວ)  ສະບາຍດີ / ຂໍໃຫ້ໂຊກດີ
 Malayalam (മലയാളം)     നമസ്കാരം
 Maldivian (ދިވެހި)     އައްސަލާމު ޢަލައިކުމް / ކިހިނެހް؟
-
 Maltese (il-Malti)     Bonġu / Saħħa
 Mathematics    ∀ p ∈ world • hello p  □
 Mongolian (монгол хэл) Сайн байна уу?
@@ -85,7 +77,6 @@ Swedish (svenska)     Hej / Goddag / Hallå
 Tamil (தமிழ்)  வணக்கம்
 Telugu (తెలుగు)        నమస్కారం
 TaiViet (ꪁꪫꪱꪣ ꪼꪕ)      ꪅꪰꪙꫂ ꪨꪮꫂ ꪁꪫꪱ / ꪅꪽ ꪨꪷ ꪁꪫꪱ
-
 Thai (ภาษาไทย) สวัสดีครับ / สวัสดีค่ะ
 Tibetan (བོད་སྐད་)     བཀྲ་ཤིས་བདེ་ལེགས༎
 Tigrigna (ትግርኛ)        ሰላማት
@@ -99,7 +90,6 @@ Vietnamese (tiếng Việt)       Chào bạn
 </x-charset><x-charset><param>chinese-gb2312</param>Chinese (中文,普通话,汉语)        
你好
 </x-charset><x-charset><param>chinese-big5-1</param>Cantonese (粵語,廣東話) 早晨, 你好
 </x-charset><x-charset><param>korean-ksc5601</param>Korean (한글)        안녕하세요 / 
안녕하십니까
-
 </x-charset>
 
 
diff --git a/etc/NEWS b/etc/NEWS
index 359d308..d632283 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -827,6 +827,10 @@ so e.g. like 'C-x 8 [' inserts a left single quotation 
mark,
 Added a new Mozhi scheme.  The inapplicable ITRANS scheme is now
 deprecated.  Errors in the Inscript method were corrected.
 
+---
+*** New input method 'cham'.
+There's also a Cham greeting in 'etc/HELLO'.
+
 ** Ispell
 
 +++
diff --git a/lisp/cedet/ede/base.el b/lisp/cedet/ede/base.el
index 7799746..810d6ef 100644
--- a/lisp/cedet/ede/base.el
+++ b/lisp/cedet/ede/base.el
@@ -160,16 +160,13 @@ and querying them will cause the actual project to get 
loaded.")
 ;; Projects can also affect how EDE works, by changing what appears in
 ;; the EDE menu, or how some keys are bound.
 ;;
-(unless (fboundp 'ede-target-list-p)
-  (cl-deftype ede-target-list () '(list-of ede-target)))
-
 (defclass ede-project (ede-project-placeholder)
   ((subproj :initform nil
            :type list
            :documentation "Sub projects controlled by this project.
 For Automake based projects, each directory is treated as a project.")
    (targets :initarg :targets
-           :type ede-target-list
+           :type (list-of ede-target)
            :custom (repeat (object :objectcreatefcn ede-new-target-custom))
            :label "Local Targets"
            :group (targets)
diff --git a/lisp/cedet/ede/proj.el b/lisp/cedet/ede/proj.el
index 59628eb..4af8b41 100644
--- a/lisp/cedet/ede/proj.el
+++ b/lisp/cedet/ede/proj.el
@@ -184,7 +184,7 @@ Target variables are always renamed such as foo_CFLAGS, 
then included into
 commands where the variable would usually appear.")
    (rules :initarg :rules
          :initform nil
-         :type list
+         :type (list-of ede-makefile-rule)
          :custom (repeat (object :objecttype ede-makefile-rule))
          :label "Additional Rules"
          :group (make)
diff --git a/lisp/epa.el b/lisp/epa.el
index db2b127..197cd92 100644
--- a/lisp/epa.el
+++ b/lisp/epa.el
@@ -359,8 +359,8 @@ DOC is documentation text to insert at the start."
 
     ;; Find the end of the documentation text at the start.
     ;; Set POINT to where it ends, or nil if ends at eob.
-    (unless (get-text-property point 'epa-list-keys)
-      (setq point (next-single-property-change point 'epa-list-keys)))
+    (unless (get-text-property point 'epa-key)
+      (setq point (next-single-property-change point 'epa-key)))
 
     ;; If caller specified documentation text for that, replace the old
     ;; documentation text (if any) with what was specified.
diff --git a/lisp/gnus/gnus-agent.el b/lisp/gnus/gnus-agent.el
index 56640ea..6866230 100644
--- a/lisp/gnus/gnus-agent.el
+++ b/lisp/gnus/gnus-agent.el
@@ -1789,6 +1789,7 @@ variables.  Returns the first non-nil value found."
                  . gnus-agent-enable-expiration)
                 (agent-predicate . gnus-agent-predicate)))))))
 
+;; FIXME: This looks an awful lot like `gnus-agent-retrieve-headers'.
 (defun gnus-agent-fetch-headers (group)
   "Fetch interesting headers into the agent.  The group's overview
 file will be updated to include the headers while a list of available
@@ -1810,10 +1811,9 @@ article numbers will be returned."
                                    (cdr active))))
                         (gnus-uncompress-range (gnus-active group)))
                      (gnus-list-of-unread-articles group)))
-         (gnus-decode-encoded-word-function 'identity)
-        (gnus-decode-encoded-address-function 'identity)
          (file (gnus-agent-article-name ".overview" group))
-        (file-name-coding-system nnmail-pathname-coding-system))
+        (file-name-coding-system nnmail-pathname-coding-system)
+        headers fetched-headers)
 
     (unless fetch-all
       ;; Add articles with marks to the list of article headers we want to
@@ -1824,7 +1824,7 @@ article numbers will be returned."
       (dolist (arts (gnus-info-marks (gnus-get-info group)))
         (unless (memq (car arts) '(seen recent killed cache))
           (setq articles (gnus-range-add articles (cdr arts)))))
-      (setq articles (sort (gnus-uncompress-sequence articles) '<)))
+      (setq articles (sort (gnus-uncompress-range articles) '<)))
 
     ;; At this point, I have the list of articles to consider for
     ;; fetching.  This is the list that I'll return to my caller. Some
@@ -1867,38 +1867,52 @@ article numbers will be returned."
         10 "gnus-agent-fetch-headers: undownloaded articles are `%s'"
         (gnus-compress-sequence articles t)))
 
-      (with-current-buffer nntp-server-buffer
-        (if articles
-            (progn
-             (gnus-message 8 "Fetching headers for %s..." group)
-
-              ;; Fetch them.
-              (gnus-make-directory (nnheader-translate-file-chars
-                                    (file-name-directory file) t))
-
-              (unless (eq 'nov (gnus-retrieve-headers articles group))
-                (nnvirtual-convert-headers))
-              (gnus-agent-check-overview-buffer)
-              ;; Move these headers to the overview buffer so that
-              ;; gnus-agent-braid-nov can merge them with the contents
-              ;; of FILE.
-              (copy-to-buffer
-              gnus-agent-overview-buffer (point-min) (point-max))
-             ;; NOTE: Call g-a-brand-nov even when the file does not
-             ;; exist.  As a minimum, it will validate the article
-             ;; numbers already in the buffer.
-             (gnus-agent-braid-nov articles file)
-              (let ((coding-system-for-write
-                     gnus-agent-file-coding-system))
-                (gnus-agent-check-overview-buffer)
-                (write-region (point-min) (point-max) file nil 'silent))
-             (gnus-agent-update-view-total-fetched-for group t)
-              (gnus-agent-save-alist group articles nil)
-              articles)
-          (ignore-errors
-            (erase-buffer)
-            (nnheader-insert-file-contents file)))))
-    articles))
+      ;; Parse known headers from FILE.
+      (if (file-exists-p file)
+         (with-current-buffer gnus-agent-overview-buffer
+           (erase-buffer)
+           (let ((nnheader-file-coding-system
+                  gnus-agent-file-coding-system))
+             (nnheader-insert-nov-file file (car articles))
+             (with-current-buffer nntp-server-buffer
+               (erase-buffer)
+               (insert-buffer-substring gnus-agent-overview-buffer)
+               (setq headers
+                     (gnus-get-newsgroup-headers-xover
+                      articles nil (buffer-local-value
+                                    'gnus-newsgroup-dependencies
+                                    gnus-summary-buffer)
+                      gnus-newsgroup-name)))))
+       (gnus-make-directory (nnheader-translate-file-chars
+                              (file-name-directory file) t)))
+
+      ;; Fetch our new headers.
+      (gnus-message 8 "Fetching headers for %s..." group)
+      (if articles
+         (setq fetched-headers (gnus-fetch-headers articles)))
+
+      ;; Merge two sets of headers.
+      (setq headers
+           (if (and headers fetched-headers)
+               (delete-dups
+                (sort (append headers (copy-sequence fetched-headers))
+                      (lambda (l r)
+                        (< (mail-header-number l)
+                           (mail-header-number r)))))
+             (or headers fetched-headers)))
+
+      ;; Save the new set of headers to FILE.
+      (let ((coding-system-for-write
+             gnus-agent-file-coding-system))
+       (with-current-buffer gnus-agent-overview-buffer
+         (goto-char (point-max))
+         (mapc #'nnheader-insert-nov fetched-headers)
+         (sort-numeric-fields 1 (point-min) (point-max))
+          (gnus-agent-check-overview-buffer)
+         (write-region (point-min) (point-max) file nil 'silent))
+       (gnus-agent-update-view-total-fetched-for group t)
+       (gnus-agent-save-alist group articles nil)))
+    headers))
 
 (defsubst gnus-agent-read-article-number ()
   "Read the article number at point.
@@ -1924,96 +1938,6 @@ Return nil when a valid article number can not be read."
       (set-buffer nntp-server-buffer)
       (insert-buffer-substring gnus-agent-overview-buffer b e))))
 
-(defun gnus-agent-braid-nov (articles file)
-  "Merge agent overview data with given file.
-Takes unvalidated headers for ARTICLES from
-`gnus-agent-overview-buffer' and validated headers from the given
-FILE and places the combined valid headers into
-`nntp-server-buffer'.  This function can be used, when file
-doesn't exist, to valid the overview buffer."
-  (let (start last)
-    (set-buffer gnus-agent-overview-buffer)
-    (goto-char (point-min))
-    (set-buffer nntp-server-buffer)
-    (erase-buffer)
-    (when (file-exists-p file)
-      (nnheader-insert-file-contents file))
-    (goto-char (point-max))
-    (forward-line -1)
-
-    (unless (or (= (point-min) (point-max))
-               (< (setq last (read (current-buffer))) (car articles)))
-      ;; Old and new overlap -- We do it the hard way.
-      (when (nnheader-find-nov-line (car articles))
-        ;; Replacing existing NOV entry
-        (delete-region (point) (progn (forward-line 1) (point))))
-      (gnus-agent-copy-nov-line (pop articles))
-
-      (ignore-errors
-       (while articles
-         (while (let ((art (read (current-buffer))))
-                  (cond ((< art (car articles))
-                         (forward-line 1)
-                         t)
-                        ((= art (car articles))
-                         (beginning-of-line)
-                         (delete-region
-                          (point) (progn (forward-line 1) (point)))
-                         nil)
-                        (t
-                         (beginning-of-line)
-                         nil))))
-
-         (gnus-agent-copy-nov-line (pop articles)))))
-
-    (goto-char (point-max))
-
-    ;; Append the remaining lines
-    (when articles
-      (when last
-       (set-buffer gnus-agent-overview-buffer)
-       (setq start (point))
-       (set-buffer nntp-server-buffer))
-
-      (let ((p (point)))
-       (insert-buffer-substring gnus-agent-overview-buffer start)
-       (goto-char p))
-
-      (setq last (or last -134217728))
-      (while (catch 'problems
-              (let (sort art)
-                (while (not (eobp))
-                  (setq art (gnus-agent-read-article-number))
-                  (cond ((not art)
-                         ;; Bad art num - delete this line
-                         (beginning-of-line)
-                         (delete-region (point) (progn (forward-line 1) 
(point))))
-                        ((< art last)
-                         ;; Art num out of order - enable sort
-                         (setq sort t)
-                         (forward-line 1))
-                        ((= art last)
-                         ;; Bad repeat of art number - delete this line
-                         (beginning-of-line)
-                         (delete-region (point) (progn (forward-line 1) 
(point))))
-                        (t
-                         ;; Good art num
-                         (setq last art)
-                         (forward-line 1))))
-                (when sort
-                  ;; something is seriously wrong as we simply shouldn't see 
out-of-order data.
-                  ;; First, we'll fix the sort.
-                  (sort-numeric-fields 1 (point-min) (point-max))
-
-                  ;; but now we have to consider that we may have duplicate 
rows...
-                  ;; so reset to beginning of file
-                  (goto-char (point-min))
-                  (setq last -134217728)
-
-                  ;; and throw a code that restarts this scan
-                  (throw 'problems t))
-                nil))))))
-
 ;; Keeps the compiler from warning about the free variable in
 ;; gnus-agent-read-agentview.
 (defvar gnus-agent-read-agentview)
@@ -2386,10 +2310,9 @@ modified) original contents, they are first saved to 
their own file."
        (gnus-orphan-score gnus-orphan-score)
        ;; Maybe some other gnus-summary local variables should also
        ;; be put here.
-
+       fetched-headers
         gnus-headers
         gnus-score
-        articles
         predicate info marks
        )
     (unless (gnus-check-group group)
@@ -2410,38 +2333,35 @@ modified) original contents, they are first saved to 
their own file."
                                          (setq info (gnus-get-info group)))))))
               (when arts
                 (setq marked-articles (nconc (gnus-uncompress-range arts)
-                                             marked-articles))
-                ))))
+                                             marked-articles))))))
         (setq marked-articles (sort marked-articles '<))
 
-        ;; Fetch any new articles from the server
-        (setq articles (gnus-agent-fetch-headers group))
+       (setq gnus-newsgroup-dependencies
+              (or gnus-newsgroup-dependencies
+                  (gnus-make-hashtable)))
 
-        ;; Merge new articles with marked
-        (setq articles (sort (append marked-articles articles) '<))
+        ;; Fetch headers for any new articles from the server.
+        (setq fetched-headers (gnus-agent-fetch-headers group))
 
-        (when articles
-          ;; Parse them and see which articles we want to fetch.
-          (setq gnus-newsgroup-dependencies
-                (or gnus-newsgroup-dependencies
-                    (gnus-make-hashtable (length articles))))
+        (when fetched-headers
           (setq gnus-newsgroup-headers
-                (or gnus-newsgroup-headers
-                    (gnus-get-newsgroup-headers-xover articles nil nil
-                                                      group)))
-          ;; `gnus-agent-overview-buffer' may be killed for
-          ;; timeout reason.  If so, recreate it.
+               (or gnus-newsgroup-headers
+                    fetched-headers)))
+       (when marked-articles
+          ;; `gnus-agent-overview-buffer' may be killed for timeout
+          ;; reason.  If so, recreate it.
           (gnus-agent-create-buffer)
 
           (setq predicate
-                (gnus-get-predicate
-                 (gnus-agent-find-parameter group 'agent-predicate)))
+               (gnus-get-predicate
+                (gnus-agent-find-parameter group 'agent-predicate)))
+
+          ;; If the selection predicate requires scoring, score each header.
 
-          ;; If the selection predicate requires scoring, score each header
           (unless (memq predicate '(gnus-agent-true gnus-agent-false))
             (let ((score-param
                    (gnus-agent-find-parameter group 'agent-score-file)))
-              ;; Translate score-param into real one
+              ;; Translate score-param into real one.
               (cond
                ((not score-param))
                ((eq score-param 'file)
@@ -3661,11 +3581,9 @@ has been fetched."
 (defun gnus-agent-retrieve-headers (articles group &optional fetch-old)
   (save-excursion
     (gnus-agent-create-buffer)
-    (let ((gnus-decode-encoded-word-function 'identity)
-         (gnus-decode-encoded-address-function 'identity)
-         (file (gnus-agent-article-name ".overview" group))
-          uncached-articles
-         (file-name-coding-system nnmail-pathname-coding-system))
+    (let ((file (gnus-agent-article-name ".overview" group))
+         (file-name-coding-system nnmail-pathname-coding-system)
+         uncached-articles headers fetched-headers)
       (gnus-make-directory (nnheader-translate-file-chars
                            (file-name-directory file) t))
 
@@ -3676,122 +3594,63 @@ has been fetched."
                                1)
                              (car (last articles))))))
 
-      ;; Populate temp buffer with known headers
+      ;; See if we've got cached headers for ARTICLES and put them in
+      ;; HEADERS.  Articles with no cached headers go in
+      ;; UNCACHED-ARTICLES to be fetched from the server.
       (when (file-exists-p file)
        (with-current-buffer gnus-agent-overview-buffer
          (erase-buffer)
          (let ((nnheader-file-coding-system
                 gnus-agent-file-coding-system))
-           (nnheader-insert-nov-file file (car articles)))))
-
-      (if (setq uncached-articles (gnus-agent-uncached-articles articles group
-                                                                t))
-         (progn
-            ;; Populate nntp-server-buffer with uncached headers
-           (set-buffer nntp-server-buffer)
-           (erase-buffer)
-            (cond ((not (eq 'nov (let (gnus-agent) ; Turn off agent
-                                   (gnus-retrieve-headers
-                                    uncached-articles group))))
-                   (nnvirtual-convert-headers))
-                  ((eq 'nntp (car gnus-current-select-method))
-                   ;; The author of gnus-get-newsgroup-headers-xover
-                   ;; reports that the XOVER command is commonly
-                   ;; unreliable. The problem is that recently
-                   ;; posted articles may not be entered into the
-                   ;; NOV database in time to respond to my XOVER
-                   ;; query.
-                   ;;
-                   ;; I'm going to use his assumption that the NOV
-                   ;; database is updated in order of ascending
-                   ;; article ID.  Therefore, a response containing
-                   ;; article ID N implies that all articles from 1
-                   ;; to N-1 are up-to-date.  Therefore, missing
-                   ;; articles in that range have expired.
-
-                   (set-buffer nntp-server-buffer)
-                   (let* ((fetched-articles (list nil))
-                          (tail-fetched-articles fetched-articles)
-                          (min (car articles))
-                          (max (car (last articles))))
-
-                     ;; Get the list of articles that were fetched
-                     (goto-char (point-min))
-                     (let ((pm (point-max))
-                          art)
-                       (while (< (point) pm)
-                        (when (setq art (gnus-agent-read-article-number))
-                           (gnus-agent-append-to-list tail-fetched-articles 
art))
-                         (forward-line 1)))
-
-                     ;; Clip this list to the headers that will
-                     ;; actually be returned
-                     (setq fetched-articles (gnus-list-range-intersection
-                                             (cdr fetched-articles)
-                                             (cons min max)))
-
-                     ;; Clip the uncached articles list to exclude
-                     ;; IDs after the last FETCHED header.  The
-                     ;; excluded IDs may be fetchable using HEAD.
-                     (if (car tail-fetched-articles)
-                         (setq uncached-articles
-                               (gnus-list-range-intersection
-                                uncached-articles
-                                (cons (car uncached-articles)
-                                      (car tail-fetched-articles)))))
-
-                     ;; Create the list of articles that were
-                     ;; "successfully" fetched.  Success, in this
-                     ;; case, means that the ID should not be
-                     ;; fetched again.  In the case of an expired
-                     ;; article, the header will not be fetched.
-                     (setq uncached-articles
-                           (gnus-sorted-nunion fetched-articles
-                                               uncached-articles))
-                     )))
-
-            ;; Erase the temp buffer
-           (set-buffer gnus-agent-overview-buffer)
-           (erase-buffer)
-
-            ;; Copy the nntp-server-buffer to the temp buffer
-           (set-buffer nntp-server-buffer)
-           (copy-to-buffer gnus-agent-overview-buffer (point-min) (point-max))
-
-           ;; Merge the temp buffer with the known headers (found on
-           ;; disk in FILE) into the nntp-server-buffer
-           (when uncached-articles
-             (gnus-agent-braid-nov uncached-articles file))
-
-           ;; Save the new set of known headers to FILE
-           (set-buffer nntp-server-buffer)
+           (nnheader-insert-nov-file file (car articles))
+           (with-current-buffer nntp-server-buffer
+             (erase-buffer)
+             (insert-buffer-substring gnus-agent-overview-buffer)
+             (setq headers
+                   (gnus-get-newsgroup-headers-xover
+                    articles nil (buffer-local-value
+                                  'gnus-newsgroup-dependencies
+                                  gnus-summary-buffer)
+                    gnus-newsgroup-name))))))
+
+      (setq uncached-articles
+           (gnus-agent-uncached-articles articles group t))
+
+      (when uncached-articles
+       (let ((gnus-newsgroup-name group)
+             gnus-agent)               ; Prevent loop.
+          ;; Fetch additional headers for the uncached articles.
+         (setq fetched-headers (gnus-fetch-headers uncached-articles))
+         ;; Merge headers we got from the overview file with our
+         ;; newly-fetched headers.
+         (when fetched-headers
+           (setq headers
+                 (delete-dups
+                  (sort (append headers (copy-sequence fetched-headers))
+                        (lambda (l r)
+                          (< (mail-header-number l)
+                             (mail-header-number r))))))
+
+           ;; Add the new set of known headers to the overview file.
            (let ((coding-system-for-write
                   gnus-agent-file-coding-system))
-             (gnus-agent-check-overview-buffer)
-             (write-region (point-min) (point-max) file nil 'silent))
-
-           (gnus-agent-update-view-total-fetched-for group t)
-
-            ;; Update the group's article alist to include the newly
-            ;; fetched articles.
-           (gnus-agent-load-alist group)
-           (gnus-agent-save-alist group uncached-articles nil)
-            )
-
-        ;; Copy the temp buffer to the nntp-server-buffer
-        (set-buffer nntp-server-buffer)
-       (erase-buffer)
-       (insert-buffer-substring gnus-agent-overview-buffer)))
-
-    (if (and fetch-old
-            (not (numberp fetch-old)))
-       t                               ; Don't remove anything.
-      (nnheader-nov-delete-outside-range
-       (car articles)
-       (car (last articles)))
-      t)
-
-    'nov))
+             (with-current-buffer gnus-agent-overview-buffer
+               ;; We stick the new headers in at the end, then
+               ;; re-sort the whole buffer with
+               ;; `sort-numeric-fields'.  If this turns out to be
+               ;; slow, we could consider a loop to add the headers
+               ;; in sorted order to begin with.
+               (goto-char (point-max))
+               (mapc #'nnheader-insert-nov fetched-headers)
+               (sort-numeric-fields 1 (point-min) (point-max))
+               (gnus-agent-check-overview-buffer)
+               (write-region (point-min) (point-max) file nil 'silent)
+               (gnus-agent-update-view-total-fetched-for group t)
+               ;; Update the group's article alist to include the
+               ;; newly fetched articles.
+               (gnus-agent-load-alist group)
+               (gnus-agent-save-alist group uncached-articles nil))))))
+      headers)))
 
 (defun gnus-agent-request-article (article group)
   "Retrieve ARTICLE in GROUP from the agent cache."
diff --git a/lisp/gnus/gnus-async.el b/lisp/gnus/gnus-async.el
index fefd02c..ed948a2 100644
--- a/lisp/gnus/gnus-async.el
+++ b/lisp/gnus/gnus-async.el
@@ -357,8 +357,13 @@ that was fetched."
        (let ((nntp-server-buffer (current-buffer))
              (nnheader-callback-function
               (lambda (_arg)
-                 (setq gnus-async-header-prefetched
-                       (cons group unread)))))
+                (setq gnus-async-header-prefetched
+                      (cons group unread)))))
+         ;; FIXME: If header prefetch is ever put into use, we'll
+         ;; have to handle the possibility that
+         ;; `gnus-retrieve-headers' might return a list of header
+         ;; vectors directly, rather than writing them into the
+         ;; current buffer.
          (gnus-retrieve-headers unread group gnus-fetch-old-headers))))))
 
 (defun gnus-async-retrieve-fetched-headers (articles group)
diff --git a/lisp/gnus/gnus-cache.el b/lisp/gnus/gnus-cache.el
index 36657e4..9423d9f 100644
--- a/lisp/gnus/gnus-cache.el
+++ b/lisp/gnus/gnus-cache.el
@@ -294,49 +294,47 @@ it's not cached."
 (defun gnus-cache-retrieve-headers (articles group &optional fetch-old)
   "Retrieve the headers for ARTICLES in GROUP."
   (let ((cached
-        (setq gnus-newsgroup-cached (gnus-cache-articles-in-group group))))
+        (setq gnus-newsgroup-cached (gnus-cache-articles-in-group group)))
+       (gnus-newsgroup-name group)
+       (gnus-fetch-old-headers fetch-old))
     (if (not cached)
        ;; No cached articles here, so we just retrieve them
        ;; the normal way.
        (let ((gnus-use-cache nil))
-         (gnus-retrieve-headers articles group fetch-old))
+         (gnus-retrieve-headers articles group))
       (let ((uncached-articles (gnus-sorted-difference articles cached))
            (cache-file (gnus-cache-file-name group ".overview"))
-           type
-           (file-name-coding-system nnmail-pathname-coding-system))
+           (file-name-coding-system nnmail-pathname-coding-system)
+           headers)
        ;; We first retrieve all the headers that we don't have in
        ;; the cache.
        (let ((gnus-use-cache nil))
          (when uncached-articles
-           (setq type (and articles
-                           (gnus-retrieve-headers
-                            uncached-articles group fetch-old)))))
+           (setq headers (and articles
+                              (gnus-fetch-headers uncached-articles)))))
        (gnus-cache-save-buffers)
-       ;; Then we insert the cached headers.
-       (save-excursion
-         (cond
-          ((not (file-exists-p cache-file))
-           ;; There are no cached headers.
-           type)
-          ((null type)
-           ;; There were no uncached headers (or retrieval was
-           ;; unsuccessful), so we use the cached headers exclusively.
-           (set-buffer nntp-server-buffer)
-           (erase-buffer)
-           (let ((coding-system-for-read
-                  gnus-cache-overview-coding-system))
-             (insert-file-contents cache-file))
-           'nov)
-          ((eq type 'nov)
-           ;; We have both cached and uncached NOV headers, so we
-           ;; braid them.
-           (gnus-cache-braid-nov group cached)
-           type)
-          (t
-           ;; We braid HEADs.
-           (gnus-cache-braid-heads group (gnus-sorted-intersection
-                                          cached articles))
-           type)))))))
+       ;; Then we include the cached headers.
+       (when (file-exists-p cache-file)
+         (setq headers
+               (delete-dups
+                (sort
+                 (append headers
+                         (let ((coding-system-for-read
+                                gnus-cache-overview-coding-system))
+                           (with-current-buffer nntp-server-buffer
+                             (erase-buffer)
+                             (insert-file-contents cache-file)
+                             (gnus-get-newsgroup-headers-xover
+                              (gnus-sorted-difference
+                               cached uncached-articles)
+                              nil (buffer-local-value
+                                   'gnus-newsgroup-dependencies
+                                   gnus-summary-buffer)
+                              group))))
+                 (lambda (l r)
+                   (< (mail-header-number l)
+                      (mail-header-number r)))))))
+       headers))))
 
 (defun gnus-cache-enter-article (&optional n)
   "Enter the next N articles into the cache.
@@ -529,70 +527,6 @@ Returns the list of articles removed."
          (setq gnus-cache-active-altered t)))
       articles)))
 
-(defun gnus-cache-braid-nov (group cached &optional file)
-  (let ((cache-buf (gnus-get-buffer-create " *gnus-cache*"))
-       beg end)
-    (gnus-cache-save-buffers)
-    (with-current-buffer cache-buf
-      (erase-buffer)
-      (let ((coding-system-for-read gnus-cache-overview-coding-system)
-           (file-name-coding-system nnmail-pathname-coding-system))
-       (insert-file-contents
-        (or file (gnus-cache-file-name group ".overview"))))
-      (goto-char (point-min))
-      (insert "\n")
-      (goto-char (point-min)))
-    (set-buffer nntp-server-buffer)
-    (goto-char (point-min))
-    (while cached
-      (while (and (not (eobp))
-                 (< (read (current-buffer)) (car cached)))
-       (forward-line 1))
-      (beginning-of-line)
-      (set-buffer cache-buf)
-      (if (search-forward (concat "\n" (int-to-string (car cached)) "\t")
-                         nil t)
-         (setq beg (point-at-bol)
-               end (progn (end-of-line) (point)))
-       (setq beg nil))
-      (set-buffer nntp-server-buffer)
-      (when beg
-       (insert-buffer-substring cache-buf beg end)
-       (insert "\n"))
-      (setq cached (cdr cached)))
-    (kill-buffer cache-buf)))
-
-(defun gnus-cache-braid-heads (group cached)
-  (let ((cache-buf (gnus-get-buffer-create " *gnus-cache*")))
-    (with-current-buffer cache-buf
-      (erase-buffer))
-    (set-buffer nntp-server-buffer)
-    (goto-char (point-min))
-    (dolist (entry cached)
-      (while (and (not (eobp))
-                 (looking-at "2.. +\\([0-9]+\\) ")
-                 (< (progn (goto-char (match-beginning 1))
-                           (read (current-buffer)))
-                    entry))
-       (search-forward "\n.\n" nil 'move))
-      (beginning-of-line)
-      (set-buffer cache-buf)
-      (erase-buffer)
-      (let ((coding-system-for-read gnus-cache-coding-system)
-           (file-name-coding-system nnmail-pathname-coding-system))
-       (insert-file-contents (gnus-cache-file-name group entry)))
-      (goto-char (point-min))
-      (insert "220 ")
-      (princ (pop cached) (current-buffer))
-      (insert " Article retrieved.\n")
-      (search-forward "\n\n" nil 'move)
-      (delete-region (point) (point-max))
-      (forward-char -1)
-      (insert ".")
-      (set-buffer nntp-server-buffer)
-      (insert-buffer-substring cache-buf))
-    (kill-buffer cache-buf)))
-
 ;;;###autoload
 (defun gnus-jog-cache ()
   "Go through all groups and put the articles into the cache.
diff --git a/lisp/gnus/gnus-cloud.el b/lisp/gnus/gnus-cloud.el
index f7c71f4..00b85f5 100644
--- a/lisp/gnus/gnus-cloud.el
+++ b/lisp/gnus/gnus-cloud.el
@@ -30,6 +30,8 @@
 
 (require 'parse-time)
 (require 'nnimap)
+(declare-function gnus-fetch-headers "gnus-sum")
+(defvar gnus-alter-header-function)
 
 (eval-when-compile (require 'epg)) ;; setf-method for `epg-context-armor'
 (autoload 'epg-make-context "epg")
@@ -391,8 +393,6 @@ When FULL is t, upload everything, not just a difference 
from the last full."
             (gnus-group-refresh-group group))
         (gnus-error 2 "Failed to upload Gnus Cloud data to %s" group)))))
 
-(defvar gnus-alter-header-function)
-
 (defun gnus-cloud-add-timestamps (elems)
   (dolist (elem elems)
     (let* ((file-name (plist-get elem :file-name))
@@ -407,14 +407,10 @@ When FULL is t, upload everything, not just a difference 
from the last full."
   (gnus-activate-group gnus-cloud-group-name nil nil gnus-cloud-method)
   (let* ((group (gnus-group-full-name gnus-cloud-group-name gnus-cloud-method))
          (active (gnus-active group))
-         headers head)
-    (when (gnus-retrieve-headers (gnus-uncompress-range active) group)
-      (with-current-buffer nntp-server-buffer
-        (goto-char (point-min))
-       (while (setq head (nnheader-parse-head))
-          (when gnus-alter-header-function
-            (funcall gnus-alter-header-function head))
-          (push head headers))))
+        (gnus-newsgroup-name group)
+         (headers (gnus-fetch-headers (gnus-uncompress-range active))))
+    (when gnus-alter-header-function
+      (mapc gnus-alter-header-function headers))
     (sort (nreverse headers)
           (lambda (h1 h2)
             (> (gnus-cloud-chunk-sequence (mail-header-subject h1))
diff --git a/lisp/gnus/gnus-sum.el b/lisp/gnus/gnus-sum.el
index b0f9ed4..5bd58b6 100644
--- a/lisp/gnus/gnus-sum.el
+++ b/lisp/gnus/gnus-sum.el
@@ -5658,10 +5658,21 @@ or a straight list of headers."
          (setf (mail-header-subject header) subject))))))
 
 (defun gnus-fetch-headers (articles &optional limit force-new dependencies)
-  "Fetch headers of ARTICLES."
+  "Fetch headers of ARTICLES.
+This calls the `gnus-retrieve-headers' function of the current
+group's backend server.  The server can do one of two things:
+
+1. Write the headers for ARTICLES into the
+   `nntp-server-buffer' (the current buffer) in a parseable format, or
+2. Return the headers directly as a list of vectors.
+
+In the first case, `gnus-retrieve-headers' returns a symbol
+value, either `nov' or `headers'.  This value determines which
+parsing function is used to read the headers.  It is also stored
+into the variable `gnus-headers-retrieved-by', which is consulted
+later when possibly building full threads."
   (gnus-message 7 "Fetching headers for %s..." gnus-newsgroup-name)
-  (prog1
-      (pcase (setq gnus-headers-retrieved-by
+  (let ((res (setq gnus-headers-retrieved-by
                   (gnus-retrieve-headers
                    articles gnus-newsgroup-name
                    (or limit
@@ -5671,22 +5682,34 @@ or a straight list of headers."
                                  (not (eq gnus-fetch-old-headers 'some))
                                  (not (numberp gnus-fetch-old-headers)))
                                 (> (length articles) 1))
-                            gnus-fetch-old-headers))))
-    ('nov
-     (gnus-get-newsgroup-headers-xover
-      articles force-new dependencies gnus-newsgroup-name t))
-    ('headers
-     (gnus-get-newsgroup-headers dependencies force-new))
-    ((pred listp)
-     (let ((dependencies
-           (or dependencies
-               (with-current-buffer gnus-summary-buffer
-                 gnus-newsgroup-dependencies))))
-     (delq nil (mapcar   #'(lambda (header)
-                            (gnus-dependencies-add-header
-                             header dependencies force-new))
-                        gnus-headers-retrieved-by)))))
-  (gnus-message 7 "Fetching headers for %s...done" gnus-newsgroup-name)))
+                            gnus-fetch-old-headers))))))
+    (prog1
+       (pcase res
+         ('nov
+          (gnus-get-newsgroup-headers-xover
+           articles force-new dependencies gnus-newsgroup-name t))
+         ;; For now, assume that any backend returning its own
+         ;; headers takes some effort to do so, so return `headers'.
+         ((pred listp)
+          (setq gnus-headers-retrieved-by 'headers)
+          (let ((dependencies
+                 (or dependencies
+                     (buffer-local-value
+                      'gnus-newsgroup-dependencies gnus-summary-buffer))))
+            (when (functionp gnus-alter-header-function)
+              (mapc gnus-alter-header-function res))
+            (mapc (lambda (header)
+                    ;; The agent or the cache may have already
+                    ;; registered this header in the dependency
+                    ;; table.
+                    (unless (gethash (mail-header-id header) dependencies)
+                      (gnus-dependencies-add-header
+                       header dependencies force-new)))
+                  res)
+            res))
+         (_ (gnus-get-newsgroup-headers dependencies force-new)))
+      (gnus-message 7 "Fetching headers for %s...done"
+                   gnus-newsgroup-name))))
 
 (defun gnus-select-newsgroup (group &optional read-all select-articles)
   "Select newsgroup GROUP.
@@ -6443,6 +6466,10 @@ The resulting hash table is returned, or nil if no Xrefs 
were found."
        (unless (gnus-ephemeral-group-p group)
          (gnus-group-update-group group t))))))
 
+;; FIXME: Refactor this with `gnus-get-newsgroup-headers-xover' and
+;; extract the necessary bits for the direct-header-return case.  Also
+;; look at this and see how similar it is to
+;; `nnheader-parse-naked-head'.
 (defun gnus-get-newsgroup-headers (&optional dependencies force-new)
   (let ((dependencies
         (or dependencies
diff --git a/lisp/gnus/gnus.el b/lisp/gnus/gnus.el
index 3b172db..2e9ee71 100644
--- a/lisp/gnus/gnus.el
+++ b/lisp/gnus/gnus.el
@@ -2388,7 +2388,14 @@ Typical marks are those that make no sense in a 
standalone back end,
 such as a mark that says whether an article is stored in the cache
 \(which doesn't make sense in a standalone back end).")
 
-(defvar gnus-headers-retrieved-by nil)
+(defvar gnus-headers-retrieved-by nil
+  "Holds the return value of `gnus-retrieve-headers'.
+This is either the symbol `nov' or the symbol `headers'.  This
+value is checked during the summary creation process, when
+building threads.  A value of `nov' indicates that header
+retrieval is relatively cheap and threading is encouraged to
+include more old articles.  A value of `headers' indicates that
+retrieval is expensive and should be minimized.")
 (defvar gnus-article-reply nil)
 (defvar gnus-override-method nil)
 (defvar gnus-opened-servers nil)
diff --git a/lisp/gnus/nnvirtual.el b/lisp/gnus/nnvirtual.el
index 1e2feda..ba29343 100644
--- a/lisp/gnus/nnvirtual.el
+++ b/lisp/gnus/nnvirtual.el
@@ -101,15 +101,10 @@ It is computed from the marks of individual component 
groups.")
       (erase-buffer)
       (if (stringp (car articles))
          'headers
-       (let ((vbuf (nnheader-set-temp-buffer
-                    (gnus-get-buffer-create " *virtual headers*")))
-             (carticles (nnvirtual-partition-sequence articles))
+       (let ((carticles (nnvirtual-partition-sequence articles))
              (sysname (system-name))
-             cgroup carticle article result prefix)
-         (while carticles
-           (setq cgroup (caar carticles))
-           (setq articles (cdar carticles))
-           (pop carticles)
+             cgroup headers all-headers article prefix)
+         (pcase-dolist (`(,cgroup . ,articles) carticles)
            (when (and articles
                       (gnus-check-server
                        (gnus-find-method-for-group cgroup) t)
@@ -119,69 +114,37 @@ It is computed from the marks of individual component 
groups.")
                       ;; This is probably evil if people have set
                       ;; gnus-use-cache to nil themselves, but I
                       ;; have no way of finding the true value of it.
-                      (let ((gnus-use-cache t))
-                        (setq result (gnus-retrieve-headers
-                                      articles cgroup nil))))
-             (set-buffer nntp-server-buffer)
-             ;; If we got HEAD headers, we convert them into NOV
-             ;; headers.  This is slow, inefficient and, come to think
-             ;; of it, downright evil.  So sue me.  I couldn't be
-             ;; bothered to write a header parse routine that could
-             ;; parse a mixed HEAD/NOV buffer.
-             (when (eq result 'headers)
-               (nnvirtual-convert-headers))
-             (goto-char (point-min))
-             (while (not (eobp))
-               (delete-region (point)
-                              (progn
-                                (setq carticle (read nntp-server-buffer))
-                                (point)))
-
-               ;; We remove this article from the articles list, if
-               ;; anything is left in the articles list after going through
-               ;; the entire buffer, then those articles have been
-               ;; expired or canceled, so we appropriately update the
-               ;; component group below.  They should be coming up
-               ;; generally in order, so this shouldn't be slow.
-               (setq articles (delq carticle articles))
-
-               (setq article (nnvirtual-reverse-map-article cgroup carticle))
-               (if (null article)
-                   ;; This line has no reverse mapping, that means it
-                   ;; was an extra article reference returned by nntp.
-                   (progn
-                     (beginning-of-line)
-                     (delete-region (point) (progn (forward-line 1) (point))))
-                 ;; Otherwise insert the virtual article number,
-                 ;; and clean up the xrefs.
-                 (princ article nntp-server-buffer)
-                 (nnvirtual-update-xref-header cgroup carticle
-                                               prefix sysname)
-                 (forward-line 1))
-               )
-
-             (set-buffer vbuf)
-             (goto-char (point-max))
-             (insert-buffer-substring nntp-server-buffer))
-           ;; Anything left in articles is expired or canceled.
-           ;; Could be smart and not tell it about articles already known?
-           (when articles
-             (gnus-group-make-articles-read cgroup articles))
-           )
-
-         ;; The headers are ready for reading, so they are inserted into
-         ;; the nntp-server-buffer, which is where Gnus expects to find
-         ;; them.
-         (prog1
-             (with-current-buffer nntp-server-buffer
-               (erase-buffer)
-               (insert-buffer-substring vbuf)
-               ;; FIX FIX FIX, we should be able to sort faster than
-               ;; this if needed, since each cgroup is sorted, we just
-               ;; need to merge
-               (sort-numeric-fields 1 (point-min) (point-max))
-               'nov)
-           (kill-buffer vbuf)))))))
+                      (let ((gnus-use-cache t)
+                            (gnus-newsgroup-name cgroup)
+                            (gnus-fetch-old-headers nil))
+                        (setq headers (gnus-fetch-headers articles))))
+             (erase-buffer)
+             ;; Remove all header article numbers from `articles'.
+             ;; If there's anything left, those are expired or
+             ;; canceled articles, so we update the component group
+             ;; below.
+             (dolist (h headers)
+               (setq articles (delq (mail-header-number h) articles)
+                     article (nnvirtual-reverse-map-article
+                              cgroup (mail-header-number h)))
+               ;; Update all the header numbers according to their
+               ;; reverse mapping, and drop any with no such mapping.
+               (when article
+                 ;; Do this first, before we re-set the header's
+                 ;; article number.
+                 (nnvirtual-update-xref-header
+                  h cgroup prefix sysname)
+                 (setf (mail-header-number h) article)
+                 (push h all-headers)))
+             ;; Anything left in articles is expired or canceled.
+             ;; Could be smart and not tell it about articles already
+             ;; known?
+             (when articles
+               (gnus-group-make-articles-read cgroup articles))))
+
+         (sort all-headers (lambda (h1 h2)
+                             (< (mail-header-number h1)
+                                (mail-header-number h2)))))))))
 
 
 (defvoo nnvirtual-last-accessed-component-group nil)
@@ -372,61 +335,18 @@ It is computed from the marks of individual component 
groups.")
 
 ;;; Internal functions.
 
-(defun nnvirtual-convert-headers ()
-  "Convert HEAD headers into NOV headers."
-  (with-current-buffer nntp-server-buffer
-    (let* ((dependencies (make-hash-table :test #'equal))
-          (headers (gnus-get-newsgroup-headers dependencies)))
-      (erase-buffer)
-      (mapc 'nnheader-insert-nov headers))))
-
-
-(defun nnvirtual-update-xref-header (group article prefix sysname)
-  "Edit current NOV header in current buffer to have an xref to the component 
group, and also server prefix any existing xref lines."
-  ;; Move to beginning of Xref field, creating a slot if needed.
-  (beginning-of-line)
-  (looking-at
-   "[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t[^\t]*\t")
-  (goto-char (match-end 0))
-  (unless (search-forward "\t" (point-at-eol) 'move)
-    (insert "\t"))
-
-  ;; Remove any spaces at the beginning of the Xref field.
-  (while (eq (char-after (1- (point))) ? )
-    (forward-char -1)
-    (delete-char 1))
-
-  (insert "Xref: " sysname " " group ":")
-  (princ article (current-buffer))
-  (insert " ")
-
-  ;; If there were existing xref lines, clean them up to have the correct
-  ;; component server prefix.
-  (save-restriction
-    (narrow-to-region (point)
-                     (or (search-forward "\t" (point-at-eol) t)
-                         (point-at-eol)))
-    (goto-char (point-min))
-    (when (re-search-forward "Xref: *[^\n:0-9 ]+ *" nil t)
-      (replace-match "" t t))
-    (goto-char (point-min))
-    (when (re-search-forward
-          (concat (regexp-quote (gnus-group-real-name group)) ":[0-9]+")
-          nil t)
-      (replace-match "" t t))
-    (unless (eobp)
-      (insert " ")
-      (when (not (string= "" prefix))
-       (while (re-search-forward "[^ ]+:[0-9]+" nil t)
-         (save-excursion
-           (goto-char (match-beginning 0))
-           (insert prefix))))))
-
-  ;; Ensure a trailing \t.
-  (end-of-line)
-  (or (eq (char-after (1- (point))) ?\t)
-      (insert ?\t)))
-
+(defun nnvirtual-update-xref-header (header group prefix sysname)
+  "Add xref to component GROUP to HEADER.
+Also add a server PREFIX any existing xref lines."
+  (let ((bits (split-string (mail-header-xref header)
+                           nil t "[[:blank:]]"))
+       (art-no (mail-header-number header)))
+    (setf (mail-header-xref header)
+         (concat
+          (format "%s %s:%d " sysname group art-no)
+          (mapconcat (lambda (bit)
+                       (concat prefix bit))
+                     bits " ")))))
 
 (defun nnvirtual-possibly-change-server (server)
   (or (not server)
diff --git a/lisp/ibuf-ext.el b/lisp/ibuf-ext.el
index 7be1b3d..ed5c9c0 100644
--- a/lisp/ibuf-ext.el
+++ b/lisp/ibuf-ext.el
@@ -1823,18 +1823,12 @@ When BUF nil, default to the buffer at current line."
 ;;;###autoload
 (defun ibuffer-mark-by-file-name-regexp (regexp)
   "Mark all buffers whose file name matches REGEXP."
-  (interactive "sMark by file name (regexp): ")
+  (interactive (list (read-regexp "Mark by file name (regexp)")))
   (ibuffer-mark-on-buffer
-   #'(lambda (buf)
-       (let ((name (or (buffer-file-name buf)
-                      (with-current-buffer buf
-                        (and
-                         (boundp 'dired-directory)
-                         (stringp dired-directory)
-                         dired-directory)))))
-        (when name
-           ;; Match on the displayed file name (which is abbreviated).
-          (string-match regexp (abbreviate-file-name name)))))))
+   (lambda (buf)
+     (when-let ((name (with-current-buffer buf (ibuffer-buffer-file-name))))
+       ;; Match on the displayed file name (which is abbreviated).
+       (string-match-p regexp (ibuffer--abbreviate-file-name name))))))
 
 ;;;###autoload
 (defun ibuffer-mark-by-content-regexp (regexp &optional all-buffers)
diff --git a/lisp/ibuffer.el b/lisp/ibuffer.el
index 4800e02..84c53b1 100644
--- a/lisp/ibuffer.el
+++ b/lisp/ibuffer.el
@@ -1308,6 +1308,11 @@ a new window in the current frame, splitting vertically."
                           (car dired-directory)))))
        (and dirname (expand-file-name dirname))))))
 
+(defun ibuffer--abbreviate-file-name (filename)
+  "Abbreviate FILENAME using `ibuffer-directory-abbrev-alist'."
+  (let ((directory-abbrev-alist ibuffer-directory-abbrev-alist))
+    (abbreviate-file-name filename)))
+
 (define-ibuffer-op ibuffer-do-save ()
   "Save marked buffers as with `save-buffer'."
   (:complex t
@@ -1885,9 +1890,7 @@ If point is on a group name, this function operates on 
that group."
        (cond ((zerop total) "No files")
             ((= 1 total) "1 file")
             (t (format "%d files" total))))))
-  (let ((directory-abbrev-alist ibuffer-directory-abbrev-alist))
-    (abbreviate-file-name
-     (or (ibuffer-buffer-file-name) ""))))
+  (ibuffer--abbreviate-file-name (or (ibuffer-buffer-file-name) "")))
 
 (define-ibuffer-column filename-and-process
   (:name "Filename/Process"
diff --git a/lisp/international/fontset.el b/lisp/international/fontset.el
index 14e7b89..8f0f263 100644
--- a/lisp/international/fontset.el
+++ b/lisp/international/fontset.el
@@ -719,6 +719,7 @@
                    georgian
                    cherokee
                    canadian-aboriginal
+                    cham
                    ogham
                    runic
                    symbol
diff --git a/lisp/language/cham.el b/lisp/language/cham.el
index 194574f..089988d 100644
--- a/lisp/language/cham.el
+++ b/lisp/language/cham.el
@@ -35,7 +35,8 @@
  "Cham" '((charset unicode)
              (coding-system utf-8)
              (coding-priority utf-8)
-              (sample-text . "Cham (ꨌꩌ)\tꨦꨤꩌ ꨦꨰꨁ")
+              (input-method . "cham")
+              (sample-text . "Cham (ꨌꩌ)\tꨦꨤꩌ ꨦꨁꨰ")
               (documentation . "\
 The Cham script is a Brahmic script used to write Cham,
 an Austronesian language spoken by some 245,000 Chams
diff --git a/lisp/leim/quail/cham.el b/lisp/leim/quail/cham.el
new file mode 100644
index 0000000..d12ae6c
--- /dev/null
+++ b/lisp/leim/quail/cham.el
@@ -0,0 +1,116 @@
+;;; cham.el --- Quail package for inputting Cham characters  -*- coding: 
utf-8; lexical-binding:t -*-
+
+;; Copyright (C) 2021 Free Software Foundation, Inc.
+
+;; Author: Eli Zaretskii <eliz@gnu.org>
+;; Keywords: i18n
+
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs 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.
+
+;; GNU Emacs 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 GNU Emacs.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This file defines the following Cham keyboards:
+;;
+;; - QWERTY-based Cham.
+
+;;; Code:
+
+(require 'quail)
+
+(quail-define-package
+ "cham" "Cham" "ꨌꩌ" t
+ "A QWERTY-based Cham input method."
+ nil t nil nil t nil nil nil nil nil t)
+
+(quail-define-rules
+ ("a" ?ꨀ)
+ ("A" ?ꨄ)
+ ("i" ?ꨁ)
+ ("u" ?ꨂ)
+ ("e" ?ꨃ)
+ ("o" ?ꨅ)
+ ("k" ?ꨆ)
+ ("K" ?ꨇ)
+ ("g" ?ꨈ)
+ ("G" ?ꨉ)
+ ("q" ?ꨊ)
+ ("Q" ?ꨋ)
+ ("c" ?ꨌ)
+ ("C" ?ꨍ)
+ ("j" ?ꨎ)
+ ("J" ?ꨏ)
+ ("z" ?ꨐ)
+ ("Z" ?ꨑ)
+ ("zz" ?ꨒ)
+ ("t" ?ꨓ)
+ ("T" ?ꨔ)
+ ("d" ?ꨕ)
+ ("D" ?ꨖ)
+ ("n" ?ꨗ)
+ ("N" ?ꨘ)
+ ("p" ?ꨚ)
+ ("P" ?ꨛ)
+ ("f" ?ꨜ)
+ ("b" ?ꨝ)
+ ("B" ?ꨞ)
+ ("m" ?ꨟ)
+ ("M" ?ꨠ)
+ ("mm" ?ꨡ)
+ ("y" ?ꨢ)
+ ("r" ?ꨣ)
+ ("l" ?ꨤ)
+ ("w" ?ꨥ)
+ ("v" ?ꨥ)
+ ("x" ?ꨦ)
+ ("s" ?ꨧ)
+ ("h" ?ꨨ)
+ ("kk" ?ꩀ)
+ ("ww" ?ꩁ)
+ ("vv" ?ꩁ)
+ ("qq" ?ꩂ)
+ ("cc" ?ꩄ)
+ ("tt" ?ꩅ)
+ ("nn" ?ꩆ)
+ ("pp" ?ꩇ)
+ ("yy" ?ꩈ)
+ ("rr" ?ꩉ)
+ ("ll" ?ꩊ)
+ ("gg" ?ꩊ)
+ ("xx" ?ꩋ)
+ ("." ?ꩌ)
+ ("H" ?ꩍ)
+ ("0" ?꩐)
+ ("1" ?꩑)
+ ("2" ?꩒)
+ ("3" ?꩓)
+ ("4" ?꩔)
+ ("5" ?꩕)
+ ("6" ?꩖)
+ ("7" ?꩗)
+ ("8" ?꩘)
+ ("9" ?꩙)
+ ("!" ?ꨩ)
+ ("#" ?ꨪ)
+ ("$" ?ꨫ)
+ ("^" ?ꨬ)
+ ("&" ?ꨮ)
+ ("`" ?꩜)
+ ("=" ?ꨱ)
+ ("-" ?ꩃ)
+ ("~" ?꩟)
+ )
+
+;;; cham.el ends here
diff --git a/lisp/obsolete/nnir.el b/lisp/obsolete/nnir.el
index 147efed..0b7d1e4 100644
--- a/lisp/obsolete/nnir.el
+++ b/lisp/obsolete/nnir.el
@@ -504,7 +504,6 @@ Add an entry here when adding a new search engine.")
                        ,@(mapcar (lambda (elem) (list 'const (car elem)))
                                  nnir-engines)))))
 
-
 (defmacro nnir-add-result (dirnam artno score prefix server artlist)
   "Construct a result vector and add it to ARTLIST.
 DIRNAM, ARTNO, SCORE, PREFIX and SERVER are passed to
diff --git a/lisp/progmodes/xref.el b/lisp/progmodes/xref.el
index b6778de..aecb30a 100644
--- a/lisp/progmodes/xref.el
+++ b/lisp/progmodes/xref.el
@@ -1001,8 +1001,12 @@ When only one definition found, jump to it right away 
instead."
 When there is more than one definition, split the selected window
 and show the list in a small window at the bottom.  And use a
 local keymap that binds `RET' to `xref-quit-and-goto-xref'."
-  (let ((xrefs (funcall fetcher))
-        (dd default-directory))
+  (let* ((xrefs (funcall fetcher))
+         (dd default-directory)
+         ;; XXX: Make percentage customizable maybe?
+         (max-height (/ (window-height) 2))
+         (size-fun (lambda (window)
+                     (fit-window-to-buffer window max-height))))
     (cond
      ((not (cdr xrefs))
       (xref-pop-to-location (car xrefs)
@@ -1013,7 +1017,8 @@ local keymap that binds `RET' to 
`xref-quit-and-goto-xref'."
         (xref--transient-buffer-mode)
         (xref--show-common-initialize (xref--analyze xrefs) fetcher alist)
         (pop-to-buffer (current-buffer)
-                       '(display-buffer-in-direction . ((direction . below))))
+                       `(display-buffer-in-direction . ((direction . below)
+                                                        (window-height . 
,size-fun))))
         (current-buffer))))))
 
 (define-obsolete-function-alias 'xref--show-defs-buffer-at-bottom
diff --git a/lisp/replace.el b/lisp/replace.el
index d41dc98..8f8cbfa 100644
--- a/lisp/replace.el
+++ b/lisp/replace.el
@@ -866,13 +866,10 @@ If nil, uses `regexp-history'."
         ;; Do not automatically add default to the history for empty input.
         (history-add-new-input nil)
         (input (read-from-minibuffer
-                (cond ((string-match-p ":[ \t]*\\'" prompt)
-                       prompt)
-                      ((and default (> (length default) 0))
-                        (format "%s (default %s): " prompt
-                                (query-replace-descr default)))
-                      (t
-                       (format "%s: " prompt)))
+                 (if (string-match-p ":[ \t]*\\'" prompt)
+                     prompt
+                   (format-prompt prompt (and (length> default 0)
+                                              (query-replace-descr default))))
                 nil nil nil (or history 'regexp-history) suggestions t)))
     (if (equal input "")
        ;; Return the default value when the user enters empty input.
diff --git a/src/nsselect.m b/src/nsselect.m
index 27db924..5ab3ef7 100644
--- a/src/nsselect.m
+++ b/src/nsselect.m
@@ -78,7 +78,13 @@ ns_string_to_symbol (NSString *t)
     return QSECONDARY;
   if ([t isEqualToString: NSPasteboardTypeString])
     return QTEXT;
-  if ([t isEqualToString: NSFilenamesPboardType])
+  if ([t isEqualToString:
+#if NS_USE_NSPasteboardTypeFileURL != 0
+           NSPasteboardTypeFileURL
+#else
+           NSFilenamesPboardType
+#endif
+       ])
     return QFILE_NAME;
   if ([t isEqualToString: NSPasteboardTypeTabularText])
     return QTEXT;
@@ -467,7 +473,12 @@ nxatoms_of_nsselect (void)
             [NSNumber numberWithLong:0], NXPrimaryPboard,
             [NSNumber numberWithLong:0], NXSecondaryPboard,
             [NSNumber numberWithLong:0], NSPasteboardTypeString,
-            [NSNumber numberWithLong:0], NSFilenamesPboardType,
+            [NSNumber numberWithLong:0],
+#if NS_USE_NSPasteboardTypeFileURL != 0
+                                          NSPasteboardTypeFileURL,
+#else
+                                          NSFilenamesPboardType,
+#endif
             [NSNumber numberWithLong:0], NSPasteboardTypeTabularText,
         nil] retain];
 }
diff --git a/src/nsterm.h b/src/nsterm.h
index 2c9d8e8..eae1d07 100644
--- a/src/nsterm.h
+++ b/src/nsterm.h
@@ -39,6 +39,15 @@ typedef CGFloat EmacsCGFloat;
 typedef float EmacsCGFloat;
 #endif
 
+/* NSFilenamesPboardType is deprecated in macOS 10.14, but
+   NSPasteboardTypeFileURL is only available in 10.13 (and GNUstep
+   probably lacks it too). */
+#if defined NS_IMPL_COCOA && MAC_OS_X_VERSION_MIN_REQUIRED >= 101300
+#define NS_USE_NSPasteboardTypeFileURL 1
+#else
+#define NS_USE_NSPasteboardTypeFileURL 0
+#endif
+
 /* ==========================================================================
 
    Trace support
diff --git a/src/nsterm.m b/src/nsterm.m
index 2defb9e..c5815ce 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -5602,7 +5602,11 @@ ns_term_init (Lisp_Object display_name)
   ns_drag_types = [[NSArray arrayWithObjects:
                             NSPasteboardTypeString,
                             NSPasteboardTypeTabularText,
+#if NS_USE_NSPasteboardTypeFileURL != 0
+                            NSPasteboardTypeFileURL,
+#else
                             NSFilenamesPboardType,
+#endif
                             NSPasteboardTypeURL, nil] retain];
 
   /* If fullscreen is in init/default-frame-alist, focus isn't set
@@ -8533,9 +8537,19 @@ not_in_argv (NSString *arg)
     {
       return NO;
     }
-  /* FIXME: NSFilenamesPboardType is deprecated in 10.14, but the
-     NSURL method can only handle one file at a time.  Stick with the
-     existing code at the moment.  */
+#if NS_USE_NSPasteboardTypeFileURL != 0
+  else if ([type isEqualToString: NSPasteboardTypeFileURL])
+    {
+      type_sym = Qfile;
+
+      NSArray *urls = [pb readObjectsForClasses: @[[NSURL self]]
+                                        options: nil];
+      NSEnumerator *uenum = [urls objectEnumerator];
+      NSURL *url;
+      while ((url = [uenum nextObject]))
+        strings = Fcons ([[url path] lispString], strings);
+    }
+#else  // !NS_USE_NSPasteboardTypeFileURL
   else if ([type isEqualToString: NSFilenamesPboardType])
     {
       NSArray *files;
@@ -8551,6 +8565,7 @@ not_in_argv (NSString *arg)
       while ( (file = [fenum nextObject]) )
         strings = Fcons ([file lispString], strings);
     }
+#endif   // !NS_USE_NSPasteboardTypeFileURL
   else if ([type isEqualToString: NSPasteboardTypeURL])
     {
       NSURL *url = [NSURL URLFromPasteboard: pb];
diff --git a/src/process.c b/src/process.c
index aca87f8..09f8790 100644
--- a/src/process.c
+++ b/src/process.c
@@ -5323,6 +5323,15 @@ wait_reading_process_output (intmax_t time_limit, int 
nsecs, int read_kbd,
             compute_input_wait_mask (&Atemp);
          compute_write_mask (&Ctemp);
 
+         /* If a process status has changed, the child signal pipe
+            will likely be readable.  We want to ignore it for now,
+            because otherwise we wouldn't run into a timeout
+            below.  */
+         int fd = child_signal_read_fd;
+         eassert (fd < FD_SETSIZE);
+         if (0 <= fd)
+           FD_CLR (fd, &Atemp);
+
          timeout = make_timespec (0, 0);
          if ((thread_select (pselect, max_desc + 1,
                              &Atemp,
diff --git a/test/Makefile.in b/test/Makefile.in
index 2d595d9..4ca43c8 100644
--- a/test/Makefile.in
+++ b/test/Makefile.in
@@ -246,11 +246,17 @@ endef
 
 $(foreach test,${TESTS},$(eval $(call test_template,${test})))
 
-# Get the tests for only a specific directory
-NET_TESTS := $(patsubst %.el,%,$(wildcard lisp/net/*.el))
-LISP_TESTS := $(patsubst %.el,%,$(wildcard lisp/*.el))
-check-net: ${NET_TESTS}
-check-lisp: ${LISP_TESTS}
+## Get the tests for only a specific directory.
+SUBDIRS = $(sort $(shell find lisp src -type d ! -path "*resources*" -print))
+
+define subdir_template
+  .PHONY: check-$(subst /,-,$(1))
+  check-$(subst /,-,$(1)):
+       @${MAKE} check LOGFILES="$(patsubst %.el,%.log, \
+               $(patsubst $(srcdir)/%,%,$(wildcard $(1)/*.el)))"
+endef
+
+$(foreach subdir, $(SUBDIRS), $(eval $(call subdir_template,$(subdir))))
 
 ifeq (@HAVE_MODULES@, yes)
 # -fPIC is a no-op on Windows, but causes a compiler warning
@@ -318,10 +324,10 @@ check-doit:
 ifeq ($(TEST_INTERACTIVE), yes)
        HOME=$(TEST_HOME) $(emacs) \
          -l ert ${ert_opts} \
-         $(patsubst %,-l %,$(if $(findstring 
$(TEST_LOAD_EL),yes),$ELFILES,$(ELFILES:.el=)))  \
+         $(patsubst %,-l %,$(if $(findstring 
$(TEST_LOAD_EL),yes),$ELFILES,$(ELFILES:.el=))) \
          $(TEST_RUN_ERT)
 else
-       -@${MAKE} -k  ${LOGFILES}
+       -@${MAKE} -k ${LOGFILES}
        @$(emacs) --batch -l ert --eval \
        "(ert-summarize-tests-batch-and-exit ${SUMMARIZE_TESTS})" ${LOGFILES}
 endif
diff --git a/test/README b/test/README
index 38f4a10..58f5f38 100644
--- a/test/README
+++ b/test/README
@@ -39,11 +39,10 @@ The Makefile in this directory supports the following 
targets:
 * make check-all
   Like "make check", but run all tests.
 
-* make check-lisp
-  Like "make check", but run only the tests in test/lisp/*.el
-
-* make check-net
-  Like "make check", but run only the tests in test/lisp/net/*.el
+* make check-<dirname>
+  Like "make check", but run only the tests in test/<dirname>/*.el.
+  <dirname> is a relative directory path, which has replaced "/" by "-",
+  like in "check-src" or "check-lisp-net".
 
 * make <filename>  -or-  make <filename>.log
   Run all tests declared in <filename>.el.  This includes expensive
diff --git a/test/infra/gitlab-ci.yml b/test/infra/gitlab-ci.yml
index f9c0e0c..3214f01 100644
--- a/test/infra/gitlab-ci.yml
+++ b/test/infra/gitlab-ci.yml
@@ -59,6 +59,30 @@ default:
     - docker login -u ${CI_REGISTRY_USER} -p ${CI_REGISTRY_PASSWORD} 
${CI_REGISTRY}
 
 .job-template:
+  rules:
+    - changes:
+        - "**/Makefile.in"
+        - .gitlab-ci.yml
+        - aclocal.m4
+        - autogen.sh
+        - configure.ac
+        - lib/*.{h,c}
+        - lisp/**/*.el
+        - src/*.{h,c}
+        - test/infra/*
+        - test/lisp/**/*.el
+        - test/src/*.el
+    - changes:
+        # gfilemonitor, kqueue
+        - src/gfilenotify.c
+        - src/kqueue.c
+        # MS Windows
+        - "**/w32*"
+        # GNUstep
+        - lisp/term/ns-win.el
+        - src/ns*.{h,m}
+        - src/macfont.{h,m}
+      when: never
   # these will be cached across builds
   cache:
     key: ${CI_COMMIT_SHA}
@@ -70,6 +94,11 @@ default:
     paths: []
       # - "test/**/*.log"
       # - "**/*.log"
+  # using the variables for each job
+  script:
+    - docker pull ${CI_REGISTRY_IMAGE}:${target}-${CI_COMMIT_SHA}
+    # TODO: with make -j4 several of the tests were failing, for example 
shadowfile-tests, but passed without it
+    - docker run -i --rm -e EMACS_EMBA_CI=${EMACS_EMBA_CI} 
${CI_REGISTRY_IMAGE}:${target}-${CI_COMMIT_SHA} make ${make_params}
 
 .build-template:
   script:
@@ -103,38 +132,6 @@ default:
         - test/lisp/autorevert-tests.el
         - test/lisp/filenotify-tests.el
 
-.test-template:
-  rules:
-    - changes:
-        - "**/Makefile.in"
-        - .gitlab-ci.yml
-        - aclocal.m4
-        - autogen.sh
-        - configure.ac
-        - lib/*.{h,c}
-        - lisp/**/*.el
-        - src/*.{h,c}
-        - test/infra/*
-        - test/lisp/**/*.el
-        - test/src/*.el
-    - changes:
-        # gfilemonitor, kqueue
-        - src/gfilenotify.c
-        - src/kqueue.c
-        # MS Windows
-        - "**/w32*"
-        # GNUstep
-        - lisp/term/ns-win.el
-        - src/ns*.{h,m}
-        - src/macfont.{h,m}
-      when: never
-
-  # using the variables for each job
-  script:
-    - docker pull ${CI_REGISTRY_IMAGE}:${target}-${CI_COMMIT_SHA}
-    # TODO: with make -j4 several of the tests were failing, for example 
shadowfile-tests, but passed without it
-    - docker run -i --rm -e EMACS_EMBA_CI=${EMACS_EMBA_CI} 
${CI_REGISTRY_IMAGE}:${target}-${CI_COMMIT_SHA} make ${make_params}
-
 stages:
   - prep-images
   - build-images
@@ -158,7 +155,7 @@ build-image-inotify:
 
 test-fast-inotify:
   stage: fast
-  extends: [.job-template, .test-template]
+  extends: [.job-template]
   variables:
     target: emacs-inotify
     make_params: "-C test check"
@@ -177,22 +174,22 @@ build-image-gnustep:
 
 test-lisp-inotify:
   stage: normal
-  extends: [.job-template, .test-template]
+  extends: [.job-template]
   variables:
     target: emacs-inotify
     make_params: "-C test check-lisp"
 
-test-net-inotify:
+test-lisp-net-inotify:
   stage: normal
-  extends: [.job-template, .test-template]
+  extends: [.job-template]
   variables:
     target: emacs-inotify
-    make_params: "-C test check-net"
+    make_params: "-C test check-lisp-net"
 
 test-filenotify-gio:
   # This tests file monitor libraries gfilemonitor and gio.
   stage: platforms
-  extends: [.job-template, .test-template, .filenotify-gio-template]
+  extends: [.job-template, .filenotify-gio-template]
   variables:
     target: emacs-filenotify-gio
     make_params: "-k -C test autorevert-tests filenotify-tests"
@@ -200,7 +197,7 @@ test-filenotify-gio:
 test-gnustep:
   # This tests the GNUstep build process
   stage: platforms
-  extends: [.job-template, .test-template, .gnustep-template]
+  extends: [.job-template, .gnustep-template]
   variables:
     target: emacs-gnustep
     make_params: install
@@ -208,7 +205,7 @@ test-gnustep:
 test-all-inotify:
   # This tests also file monitor libraries inotify and inotifywatch.
   stage: slow
-  extends: [.job-template, .test-template]
+  extends: [.job-template]
   rules:
     # note there's no "changes" section, so this always runs on a schedule
     - if: '$CI_PIPELINE_SOURCE == "schedule"'
diff --git a/test/lisp/time-stamp-tests.el b/test/lisp/time-stamp-tests.el
index 81488c3..4ae3c19 100644
--- a/test/lisp/time-stamp-tests.el
+++ b/test/lisp/time-stamp-tests.el
@@ -262,40 +262,48 @@
 (ert-deftest time-stamp-format-day-of-week ()
   "Test time-stamp formats for named day of week."
   (with-time-stamp-test-env
-    ;; implemented and documented since 1997
-    (should (equal (time-stamp-string "%3a" ref-time1) "Mon"))
-    (should (equal (time-stamp-string "%#A" ref-time1) "MONDAY"))
-    ;; documented 1997-2019
-    (should (equal (time-stamp-string "%3A" ref-time1) "MON"))
-    (should (equal (time-stamp-string "%:a" ref-time1) "Monday"))
-    ;; implemented since 2001, documented since 2019
-    (should (equal (time-stamp-string "%#a" ref-time1) "MON"))
-    (should (equal (time-stamp-string "%:A" ref-time1) "Monday"))
-    ;; allowed but undocumented since 2019 (warned 1997-2019)
-    (should (equal (time-stamp-string "%^A" ref-time1) "MONDAY"))
-    ;; warned 1997-2019, changed in 2019
-    (should (equal (time-stamp-string "%a" ref-time1) "Mon"))
-    (should (equal (time-stamp-string "%^a" ref-time1) "MON"))
-    (should (equal (time-stamp-string "%A" ref-time1) "Monday"))))
+   (let ((Mon (format-time-string "%a" ref-time1 t))
+         (MON (format-time-string "%^a" ref-time1 t))
+         (Monday (format-time-string "%A" ref-time1 t))
+         (MONDAY (format-time-string "%^A" ref-time1 t)))
+     ;; implemented and documented since 1997
+     (should (equal (time-stamp-string "%3a" ref-time1) Mon))
+     (should (equal (time-stamp-string "%#A" ref-time1) MONDAY))
+     ;; documented 1997-2019
+     (should (equal (time-stamp-string "%3A" ref-time1) MON))
+     (should (equal (time-stamp-string "%:a" ref-time1) Monday))
+     ;; implemented since 2001, documented since 2019
+     (should (equal (time-stamp-string "%#a" ref-time1) MON))
+     (should (equal (time-stamp-string "%:A" ref-time1) Monday))
+     ;; allowed but undocumented since 2019 (warned 1997-2019)
+     (should (equal (time-stamp-string "%^A" ref-time1) MONDAY))
+     ;; warned 1997-2019, changed in 2019
+     (should (equal (time-stamp-string "%a" ref-time1) Mon))
+     (should (equal (time-stamp-string "%^a" ref-time1) MON))
+     (should (equal (time-stamp-string "%A" ref-time1) Monday)))))
 
 (ert-deftest time-stamp-format-month-name ()
   "Test time-stamp formats for month name."
   (with-time-stamp-test-env
-    ;; implemented and documented since 1997
-    (should (equal (time-stamp-string "%3b" ref-time1) "Jan"))
-    (should (equal (time-stamp-string "%#B" ref-time1) "JANUARY"))
-    ;; documented 1997-2019
-    (should (equal (time-stamp-string "%3B" ref-time1) "JAN"))
-    (should (equal (time-stamp-string "%:b" ref-time1) "January"))
-    ;; implemented since 2001, documented since 2019
-    (should (equal (time-stamp-string "%#b" ref-time1) "JAN"))
-    (should (equal (time-stamp-string "%:B" ref-time1) "January"))
-    ;; allowed but undocumented since 2019 (warned 1997-2019)
-    (should (equal (time-stamp-string "%^B" ref-time1) "JANUARY"))
-    ;; warned 1997-2019, changed in 2019
-    (should (equal (time-stamp-string "%b" ref-time1) "Jan"))
-    (should (equal (time-stamp-string "%^b" ref-time1) "JAN"))
-    (should (equal (time-stamp-string "%B" ref-time1) "January"))))
+   (let ((Jan (format-time-string "%b" ref-time1 t))
+         (JAN (format-time-string "%^b" ref-time1 t))
+         (January (format-time-string "%B" ref-time1 t))
+         (JANUARY (format-time-string "%^B" ref-time1 t)))
+     ;; implemented and documented since 1997
+     (should (equal (time-stamp-string "%3b" ref-time1) Jan))
+     (should (equal (time-stamp-string "%#B" ref-time1) JANUARY))
+     ;; documented 1997-2019
+     (should (equal (time-stamp-string "%3B" ref-time1) JAN))
+     (should (equal (time-stamp-string "%:b" ref-time1) January))
+     ;; implemented since 2001, documented since 2019
+     (should (equal (time-stamp-string "%#b" ref-time1) JAN))
+     (should (equal (time-stamp-string "%:B" ref-time1) January))
+     ;; allowed but undocumented since 2019 (warned 1997-2019)
+     (should (equal (time-stamp-string "%^B" ref-time1) JANUARY))
+     ;; warned 1997-2019, changed in 2019
+     (should (equal (time-stamp-string "%b" ref-time1) Jan))
+     (should (equal (time-stamp-string "%^b" ref-time1) JAN))
+     (should (equal (time-stamp-string "%B" ref-time1) January)))))
 
 (ert-deftest time-stamp-format-day-of-month ()
   "Test time-stamp formats for day of month."
@@ -483,14 +491,18 @@
 (ert-deftest time-stamp-format-am-pm ()
   "Test time-stamp formats for AM and PM strings."
   (with-time-stamp-test-env
-    ;; implemented and documented since 1997
-    (should (equal (time-stamp-string "%#p" ref-time1) "pm"))
-    (should (equal (time-stamp-string "%#p" ref-time3) "am"))
-    (should (equal (time-stamp-string "%P" ref-time1) "PM"))
-    (should (equal (time-stamp-string "%P" ref-time3) "AM"))
-    ;; warned 1997-2019, changed in 2019
-    (should (equal (time-stamp-string "%p" ref-time1) "PM"))
-    (should (equal (time-stamp-string "%p" ref-time3) "AM"))))
+    (let ((pm (format-time-string "%#p" ref-time1 t))
+          (am (format-time-string "%#p" ref-time3 t))
+          (PM (format-time-string "%p" ref-time1 t))
+          (AM (format-time-string "%p" ref-time3 t)))
+      ;; implemented and documented since 1997
+      (should (equal (time-stamp-string "%#p" ref-time1) pm))
+      (should (equal (time-stamp-string "%#p" ref-time3) am))
+      (should (equal (time-stamp-string "%P" ref-time1) PM))
+      (should (equal (time-stamp-string "%P" ref-time3) AM))
+      ;; warned 1997-2019, changed in 2019
+      (should (equal (time-stamp-string "%p" ref-time1) PM))
+      (should (equal (time-stamp-string "%p" ref-time3) AM)))))
 
 (ert-deftest time-stamp-format-day-number-in-week ()
   "Test time-stamp formats for day number in week."
@@ -567,10 +579,15 @@
 (ert-deftest time-stamp-format-ignored-modifiers ()
   "Test additional args allowed (but ignored) to allow for future expansion."
   (with-time-stamp-test-env
-    ;; allowed modifiers
-    (should (equal (time-stamp-string "%.,@-+_ ^(stuff)P" ref-time3) "AM"))
-    ;; not all punctuation is allowed
-    (should-not (equal (time-stamp-string "%&P" ref-time3) "AM"))))
+   (let ((May (format-time-string "%B" ref-time3 t)))
+     ;; allowed modifiers
+     (should (equal (time-stamp-string "%.,@+ (stuff)B" ref-time3) May))
+     ;; parens nest
+     (should (equal (time-stamp-string "%(st(u)ff)B" ref-time3) May))
+     ;; escaped parens do not change the nesting level
+     (should (equal (time-stamp-string "%(st\\)u\\(ff)B" ref-time3) May))
+     ;; not all punctuation is allowed
+     (should-not (equal (time-stamp-string "%&B" ref-time3) May)))))
 
 (ert-deftest time-stamp-format-non-conversions ()
   "Test that without a %, the text is copied literally."
@@ -580,16 +597,22 @@
 (ert-deftest time-stamp-format-string-width ()
   "Test time-stamp string width modifiers."
   (with-time-stamp-test-env
-    ;; strings truncate on the right or are blank-padded on the left
-    (should (equal (time-stamp-string "%0P" ref-time3) ""))
-    (should (equal (time-stamp-string "%1P" ref-time3) "A"))
-    (should (equal (time-stamp-string "%2P" ref-time3) "AM"))
-    (should (equal (time-stamp-string "%3P" ref-time3) " AM"))
-    (should (equal (time-stamp-string "%0%" ref-time3) ""))
-    (should (equal (time-stamp-string "%1%" ref-time3) "%"))
-    (should (equal (time-stamp-string "%2%" ref-time3) " %"))
-    (should (equal (time-stamp-string "%#3a" ref-time3) "SUN"))
-    (should (equal (time-stamp-string "%#3b" ref-time2) "NOV"))))
+   (let ((May (format-time-string "%b" ref-time3 t))
+         (SUN (format-time-string "%^a" ref-time3 t))
+         (NOV (format-time-string "%^b" ref-time2 t)))
+     ;; strings truncate on the right or are blank-padded on the left
+     (should (equal (time-stamp-string "%0b" ref-time3) ""))
+     (should (equal (time-stamp-string "%1b" ref-time3) (substring May 0 1)))
+     (should (equal (time-stamp-string "%2b" ref-time3) (substring May 0 2)))
+     (should (equal (time-stamp-string "%3b" ref-time3) May))
+     (should (equal (time-stamp-string "%4b" ref-time3) (concat " " May)))
+     (should (equal (time-stamp-string "%0%" ref-time3) ""))
+     (should (equal (time-stamp-string "%1%" ref-time3) "%"))
+     (should (equal (time-stamp-string "%2%" ref-time3) " %"))
+     (should (equal (time-stamp-string "%9%" ref-time3) "        %"))
+     (should (equal (time-stamp-string "%10%" ref-time3) "         %"))
+     (should (equal (time-stamp-string "%#3a" ref-time3) SUN))
+     (should (equal (time-stamp-string "%#3b" ref-time2) NOV)))))
 
 ;;; Tests of helper functions
 
diff --git a/test/src/process-tests.el b/test/src/process-tests.el
index dad3642..949f735 100644
--- a/test/src/process-tests.el
+++ b/test/src/process-tests.el
@@ -734,5 +734,120 @@ Return nil if that can't be determined."
                 (match-string-no-properties 1))))))
   process-tests--EMFILE-message)
 
+(ert-deftest process-tests/sentinel-called ()
+  "Check that sentinels are called after processes finish"
+  (let ((command (process-tests--emacs-command)))
+    (skip-unless command)
+    (dolist (conn-type '(pipe pty))
+      (ert-info ((format "Connection type: %s" conn-type))
+        (process-tests--with-processes processes
+          (let* ((calls ())
+                 (process (make-process
+                           :name "echo"
+                           :command (process-tests--eval
+                                     command '(print "first"))
+                           :noquery t
+                           :connection-type conn-type
+                           :coding 'utf-8-unix
+                           :sentinel (lambda (process message)
+                                       (push (list process message)
+                                             calls)))))
+            (push process processes)
+            (while (accept-process-output process))
+            (should (equal calls
+                           (list (list process "finished\n"))))))))))
+
+(ert-deftest process-tests/sentinel-with-multiple-processes ()
+  "Check that sentinels are called in time even when other processes
+have written output."
+  (let ((command (process-tests--emacs-command)))
+    (skip-unless command)
+    (dolist (conn-type '(pipe pty))
+      (ert-info ((format "Connection type: %s" conn-type))
+        (process-tests--with-processes processes
+          (let* ((calls ())
+                 (process (make-process
+                           :name "echo"
+                           :command (process-tests--eval
+                                     command '(print "first"))
+                           :noquery t
+                           :connection-type conn-type
+                           :coding 'utf-8-unix
+                           :sentinel (lambda (process message)
+                                       (push (list process message)
+                                             calls)))))
+            (push process processes)
+            (push (make-process
+                   :name "bash"
+                   :command (process-tests--eval
+                             command
+                             '(progn (sleep-for 10) (print "second")))
+                   :noquery t
+                   :connection-type conn-type)
+                  processes)
+            (while (accept-process-output process))
+            (should (equal calls
+                           (list (list process "finished\n"))))))))))
+
+(defun process-tests--eval (command form)
+  "Return a command that evaluates FORM in an Emacs subprocess.
+COMMAND must be a list returned by
+`process-tests--emacs-command'."
+  (let ((print-gensym t)
+        (print-circle t)
+        (print-length nil)
+        (print-level nil)
+        (print-escape-control-characters t)
+        (print-escape-newlines t)
+        (print-escape-multibyte t)
+        (print-escape-nonascii t))
+    `(,@command "--quick" "--batch" ,(format "--eval=%S" form))))
+
+(defun process-tests--emacs-command ()
+  "Return a command to reinvoke the current Emacs instance.
+Return nil if that doesn't appear to be possible."
+  (when-let ((binary (process-tests--emacs-binary))
+             (dump (process-tests--dump-file)))
+    (cons binary
+          (unless (eq dump :not-needed)
+            (list (concat "--dump-file="
+                          (file-name-unquote dump)))))))
+
+(defun process-tests--emacs-binary ()
+  "Return the filename of the currently running Emacs binary.
+Return nil if that can't be determined."
+  (and (stringp invocation-name)
+       (not (file-remote-p invocation-name))
+       (not (file-name-absolute-p invocation-name))
+       (stringp invocation-directory)
+       (not (file-remote-p invocation-directory))
+       (file-name-absolute-p invocation-directory)
+       (when-let ((file (process-tests--usable-file-for-reinvoke
+                         (expand-file-name invocation-name
+                                           invocation-directory))))
+         (and (file-executable-p file) file))))
+
+(defun process-tests--dump-file ()
+  "Return the filename of the dump file used to start Emacs.
+Return nil if that can't be determined.  Return `:not-needed' if
+Emacs wasn't started with a dump file."
+  (if-let ((stats (and (fboundp 'pdumper-stats) (pdumper-stats))))
+      (when-let ((file (process-tests--usable-file-for-reinvoke
+                        (cdr (assq 'dump-file-name stats)))))
+        (and (file-readable-p file) file))
+    :not-needed))
+
+(defun process-tests--usable-file-for-reinvoke (filename)
+  "Return a version of FILENAME that can be used to reinvoke Emacs.
+Return nil if FILENAME doesn't exist."
+  (when (and (stringp filename)
+             (not (file-remote-p filename)))
+    (cl-callf file-truename filename)
+    (and (stringp filename)
+         (not (file-remote-p filename))
+         (file-name-absolute-p filename)
+         (file-regular-p filename)
+         filename)))
+
 (provide 'process-tests)
 ;;; process-tests.el ends here
diff --git a/test/src/xdisp-tests.el b/test/src/xdisp-tests.el
index ec96d77..4e7d2ad 100644
--- a/test/src/xdisp-tests.el
+++ b/test/src/xdisp-tests.el
@@ -75,31 +75,28 @@
 (ert-deftest xdisp-tests--window-text-pixel-size () ;; bug#45748
   (with-temp-buffer
     (insert "xxx")
-    (let* ((window
-            (display-buffer (current-buffer) '(display-buffer-in-child-frame . 
nil)))
-          (char-width (frame-char-width))
-          (size (window-text-pixel-size nil t t)))
-      (delete-frame (window-frame window))
-      (should (equal (/ (car size) char-width) 3)))))
+    (switch-to-buffer (current-buffer))
+    (let* ((char-width (frame-char-width))
+           (size (window-text-pixel-size nil t t))
+           (width-in-chars (/ (car size) char-width)))
+      (should (equal width-in-chars 3)))))
 
 (ert-deftest xdisp-tests--window-text-pixel-size-leading-space () ;; bug#45748
   (with-temp-buffer
     (insert " xx")
-    (let* ((window
-            (display-buffer (current-buffer) '(display-buffer-in-child-frame . 
nil)))
-          (char-width (frame-char-width))
-          (size (window-text-pixel-size nil t t)))
-      (delete-frame (window-frame window))
-      (should (equal (/ (car size) char-width) 3)))))
+    (switch-to-buffer (current-buffer))
+    (let* ((char-width (frame-char-width))
+           (size (window-text-pixel-size nil t t))
+           (width-in-chars (/ (car size) char-width)))
+      (should (equal width-in-chars 3)))))
 
 (ert-deftest xdisp-tests--window-text-pixel-size-trailing-space () ;; bug#45748
   (with-temp-buffer
     (insert "xx ")
-    (let* ((window
-            (display-buffer (current-buffer) '(display-buffer-in-child-frame . 
nil)))
-          (char-width (frame-char-width))
-          (size (window-text-pixel-size nil t t)))
-      (delete-frame (window-frame window))
-      (should (equal (/ (car size) char-width) 3)))))
+    (switch-to-buffer (current-buffer))
+    (let* ((char-width (frame-char-width))
+           (size (window-text-pixel-size nil t t))
+           (width-in-chars (/ (car size) char-width)))
+      (should (equal width-in-chars 3)))))
 
 ;;; xdisp-tests.el ends here



reply via email to

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