master 79c1f363a36 1/4: Merge from origin/emacs-31

Eli Zaretskii <[email protected]> Sat, 1 Aug 2026 07:38:42 -0400 (EDT)
Newsgroups gmane.emacs.diffs
Message-ID <[email protected]>
branch: master
commit 79c1f363a36c0b94a1f08ea1b02ed0e2db9ca841
Merge: a8539b7919e ad60eb855d2
Author: Eli Zaretskii <[email protected]>
Commit: Eli Zaretskii <[email protected]>

    Merge from origin/emacs-31
    
    ad60eb855d2 ; Improve documentation of "shadow cursor"
    c6d60333609 Fix attendee lookup in gnus-icalendar
    5b5cc17832c vc-working-revision: Don't bind default-directory to a re...
    ab88a0be86d Fix Dired problem with nonexistent directory (bug#81509)
    158a0391359 ; * admin/MAINTAINERS: Mark F. Jason Park as co-maintaini...
    1da79a162dd ; * src/treesit.c (treesit_cursor_helper_1): Further expl...
    037076824f1 ; * src/treesit.c (treesit_cursor_helper_1): Fix last cha...
    5e3f68c669c Fix emacs-eglot test on emba
    97e24e12d5b Fix C-x v w a/A on the repository root
    b65f8ef93d0 (sh-font-lock-paren): Fix bug#81192
    
    # Conflicts:
    #       etc/NEWS
---
 admin/MAINTAINERS                                  |  5 ++++
 doc/emacs/files.texi                               | 22 ++++++++++++-----
 etc/NEWS.31                                        |  8 +++++++
 lisp/dired.el                                      | 28 +++++++++++++++-------
 lisp/gnus/gnus-icalendar.el                        | 21 +++++++++++-----
 lisp/progmodes/sh-script.el                        |  2 +-
 lisp/vc/diff-mode.el                               |  5 +++-
 lisp/vc/vc-hooks.el                                | 13 +++++-----
 lisp/vc/vc.el                                      | 25 +++++++++----------
 src/treesit.c                                      | 10 ++++----
 test/infra/Dockerfile.emba                         |  2 +-
 .../progmodes/sh-script-resources/sh-indents.erts  | 13 ++++++++++
 test/lisp/vc/vc-tests/vc-tests.el                  | 14 +++++------
 13 files changed, 115 insertions(+), 53 deletions(-)

diff --git a/admin/MAINTAINERS b/admin/MAINTAINERS
index a2e80bf2dc8..0a5f923fd56 100644
--- a/admin/MAINTAINERS
+++ b/admin/MAINTAINERS
@@ -262,6 +262,11 @@ Stephane Marks
 Elías Gabriel Pérez
 	lisp/progmodes/hideshow.el
 
+F. Jason Park
+	ERC
+	    lisp/erc/*
+	    doc/misc/erc.texi
+
 ==============================================================================
 2. Areas that someone is willing to maintain, although he would not
 necessarily mind if someone else was the official maintainer.
diff --git a/doc/emacs/files.texi b/doc/emacs/files.texi
index b223540b97d..c271da15a1a 100644
--- a/doc/emacs/files.texi
+++ b/doc/emacs/files.texi
@@ -1808,11 +1808,20 @@ Move to the next hunk-start (@code{diff-hunk-next}).  With prefix
 argument @var{n}, move forward to the @var{n}th next hunk.
 
 @vindex diff-refine
-By default, Diff mode @dfn{refines} hunks as Emacs displays them,
-highlighting their changes with better granularity.  Alternatively, if
-you set @code{diff-refine} to the symbol @code{navigation}, Diff mode
-only refines the hunk you move to with this command or with
-@code{diff-hunk-prev}.
+By default, Diff mode automatically @dfn{refines} hunks as Emacs
+displays them, highlighting their changes with better granularity.
+Alternatively, if you set @code{diff-refine} to the symbol
+@code{navigation}, Diff mode only refines the hunk you move to with this
+command or with @code{diff-hunk-prev}.  Finally, you can set
+@code{diff-refine} to @code{nil} and manually refine hunks using
+@kbd{C-c C-b} (@code{diff-refine-hunk}), described below.
+
+@cindex shadow cursor (Diff mode)
+@vindex smerge-refine-shadow-cursor
+By default, refining a hunk in any way displays a @dfn{shadow cursor} at
+one of the two edges of the refined region, to better show where the
+refined region starts or ends.  This can be controlled by customizing
+the variable @code{smerge-refine-shadow-cursor}.
 
 @findex diff-hunk-prev
 @item M-p
@@ -1895,7 +1904,8 @@ parts of the buffer where the hunks' file names are specified.
 @item C-c C-b
 Highlight the changes of the hunk at point with a finer granularity
 (@code{diff-refine-hunk}).  This allows you to see exactly which parts
-of each changed line were actually changed.
+of each changed line were actually changed.  Refining a hunk by default
+shows a ``shadow cursor'' at one of the two edges of the refined region.
 
 @vindex diff-refine
 By default, Diff mode refines hunks as Emacs displays them, so you may
diff --git a/etc/NEWS.31 b/etc/NEWS.31
index 1270b95e622..d71204b6be0 100644
--- a/etc/NEWS.31
+++ b/etc/NEWS.31
@@ -2566,6 +2566,14 @@ started inside Emacs.
 *** 'diff-mode' now refrains from automatically refining big hunks.
 What is big is defined by the new 'diff-refine-threshold' user option.
 
++++
+*** Refining a hunk shows a "shadow cursor" at the beginning/end of region.
+By default, the shadow cursor looks like an empty rectangle the size of
+a character cell.  It is displayed at the beginning or the end of the
+refined region, to better show where the refined region starts or ends.
+This can be controlled by the new user option
+'smerge-refine-shadow-cursor', which also affects SMerge mode, see below.
+
 ---
 *** New command 'diff-kill-ring-save'.
 This command copies to the 'kill-ring' a region of text modified
diff --git a/lisp/dired.el b/lisp/dired.el
index a4bfd81b5b9..c559b7be6e2 100644
--- a/lisp/dired.el
+++ b/lisp/dired.el
@@ -1238,8 +1238,11 @@ Type \\[describe-mode] after entering Dired for more info.
 If DIRNAME is already in a Dired buffer, that buffer is used without refresh."
   ;; Cannot use (interactive "D") because of wildcards.
   (interactive (dired-read-dir-and-switches ""))
-  (prog1 (pop-to-buffer-same-window (dired-noselect dirname switches))
-    (dired--display-ls-error)))
+  (let ((buf (dired-noselect dirname switches)))
+    (prog1 (if buf
+               (pop-to-buffer-same-window buf)
+             (current-buffer))
+    (dired--display-ls-error))))
 
 ;; This lets clicks on the menu bar invoke Dired even if some feature
 ;; remaps the Dired command to another command that does not handle this
@@ -1258,24 +1261,33 @@ If this command needs to split the current window, it by default obeys
 the user options `split-height-threshold' and `split-width-threshold',
 when it decides whether to split the window horizontally or vertically."
   (interactive (dired-read-dir-and-switches "in other window "))
-  (prog1 (switch-to-buffer-other-window (dired-noselect dirname switches))
-    (dired--display-ls-error)))
+  (let ((buf (dired-noselect dirname switches)))
+    (prog1 (if buf
+               (switch-to-buffer-other-window buf)
+             (current-buffer))
+      (dired--display-ls-error))))
 
 ;;;###autoload (keymap-set ctl-x-5-map "d" #'dired-other-frame)
 ;;;###autoload
 (defun dired-other-frame (dirname &optional switches)
   "\"Edit\" directory DIRNAME.  Like `dired' but make a new frame."
   (interactive (dired-read-dir-and-switches "in other frame "))
-  (prog1 (switch-to-buffer-other-frame (dired-noselect dirname switches))
-    (dired--display-ls-error)))
+  (let ((buf (dired-noselect dirname switches)))
+    (prog1 (if buf
+               (switch-to-buffer-other-frame buf)
+             (current-buffer))
+      (dired--display-ls-error))))
 
 ;;;###autoload (keymap-set tab-prefix-map "d" #'dired-other-tab)
 ;;;###autoload
 (defun dired-other-tab (dirname &optional switches)
   "\"Edit\" directory DIRNAME.  Like `dired' but make a new tab."
   (interactive (dired-read-dir-and-switches "in other tab "))
-  (prog1 (switch-to-buffer-other-tab (dired-noselect dirname switches))
-    (dired--display-ls-error)))
+  (let ((buf (dired-noselect dirname switches)))
+    (prog1 (if buf
+               (switch-to-buffer-other-tab buf)
+             (current-buffer))
+        (dired--display-ls-error))))
 
 ;;;###autoload
 (defun dired-noselect (dir-or-list &optional switches)
diff --git a/lisp/gnus/gnus-icalendar.el b/lisp/gnus/gnus-icalendar.el
index 54f105f6fda..d29eb545572 100644
--- a/lisp/gnus/gnus-icalendar.el
+++ b/lisp/gnus/gnus-icalendar.el
@@ -149,16 +149,25 @@
 
 (defun gnus-icalendar-event--find-attendee (attendees ids)
   "Return the first `icalendar-attendee' in ATTENDEES matching IDS.
-IDS should be a list of strings. The first attendee is returned whose
-name (as `icalendar-cnparam') or email address (without \"mailto:\")
-is a member of IDS."
+IDS should be a list whose values are strings or functions. The first
+attendee is returned whose name (as `icalendar-cnparam') or email
+address (without \"mailto:\") matches a member of IDS."
   (catch 'found
     (dolist (attendee attendees)
       (ical:with-property attendee ((ical:cnparam :value name))
          (let ((email (ical:strip-mailto value)))
-           (when (or (member name ids)
-                     (member email ids))
-             (throw 'found attendee)))))))
+           (dolist (matcher ids)
+             ;; Each MATCHER can be either a string or a function.
+             ;; The previous implementation returned an attendee if:
+             ;; - its NAME was `equal' to MATCHER as a string
+             ;; - its EMAIL matched MATCHER as a regexp
+             ;; - its EMAIL matched MATCHER if it was a predicate
+             ;; See also bug#81408.
+             (when (or (and (functionp matcher) (funcall matcher email))
+                       (and (stringp matcher)
+                            (or (equal matcher name)
+                                (string-match-p matcher email))))
+               (throw 'found attendee))))))))
 
 (defun gnus-icalendar-event--attendees-by-type (attendees)
   "Return lists of required and optional participants in ATTENDEES.
diff --git a/lisp/progmodes/sh-script.el b/lisp/progmodes/sh-script.el
index 79180da8718..dce24949b5f 100644
--- a/lisp/progmodes/sh-script.el
+++ b/lisp/progmodes/sh-script.el
@@ -1037,7 +1037,7 @@ subshells can nest."
               ;; Skip through one pattern
               (while
                   (or (/= 0 (skip-syntax-backward "w_"))
-                      (/= 0 (skip-chars-backward "-$=?[]*@/\\\\"))
+                      (/= 0 (skip-chars-backward "-$=?[]*@/\\\\!%:.^~,"))
                       (and (sh-is-quoted-p (1- (point)))
                            (goto-char (- (point) 2)))
                       (when (memq (char-before) '(?\" ?\' ?\}))
diff --git a/lisp/vc/diff-mode.el b/lisp/vc/diff-mode.el
index 735c36f9d97..b9ecab7af61 100644
--- a/lisp/vc/diff-mode.el
+++ b/lisp/vc/diff-mode.el
@@ -107,7 +107,10 @@ The value `font-lock' means to refine during font-lock.
 The value `navigation' means to refine each hunk as you visit it
 with `diff-hunk-next' or `diff-hunk-prev'.
 
-You can always manually refine a hunk with `diff-refine-hunk'."
+You can always manually refine a hunk with `diff-refine-hunk'.
+
+By default, refining a hunk in any way displays the \"shadow cursor\"
+at one end of the refined region.  See `smerge-refine-shadow-cursor'."
   :version "27.1"
   :type '(choice (const :tag "Don't refine hunks" nil)
                  (const :tag "Refine hunks during font-lock" font-lock)
diff --git a/lisp/vc/vc-hooks.el b/lisp/vc/vc-hooks.el
index 7267c37851d..06f954ce9fc 100644
--- a/lisp/vc/vc-hooks.el
+++ b/lisp/vc/vc-hooks.el
@@ -587,12 +587,13 @@ If FILE is not registered, this function always returns nil.
 This function does not return nil without first confirming with the
 underlying VCS that FILE is unregistered; this is in contrast to
 `vc-symbolic-working-revision'."
-  (or (vc-file-getprop file 'vc-working-revision)
-      (let ((default-directory (file-name-directory file)))
-        (and (setq backend (or backend (vc-backend file)))
-             (vc-file-setprop file 'vc-working-revision
-                              (vc-call-backend backend 'working-revision
-                                               file))))))
+  (let ((abs (expand-file-name file)))
+    (or (vc-file-getprop abs 'vc-working-revision)
+        (let ((default-directory (file-name-directory abs)))
+          (and (setq backend (or backend (vc-backend file)))
+               (vc-file-setprop abs 'vc-working-revision
+                                (vc-call-backend backend 'working-revision
+                                                 file)))))))
 
 (defun vc-symbolic-working-revision (file &optional backend)
   "Return BACKEND's symbolic name for FILE's working revision.
diff --git a/lisp/vc/vc.el b/lisp/vc/vc.el
index c0d160d182c..e2588fc3765 100644
--- a/lisp/vc/vc.el
+++ b/lisp/vc/vc.el
@@ -5731,9 +5731,9 @@ yourself with a function like `vc-file-tree-walk'."
   ;; having to load `vc-dir' just to get access to this simple wrapper.
   (let ((morep t) results)
     (with-temp-buffer
-      (setq default-directory directory)
+      (setq default-directory (expand-file-name directory))
       (vc-call-backend (or backend (vc-responsible-backend directory))
-                       'dir-status-files directory files
+                       'dir-status-files default-directory files
                        (lambda (entries &optional more-to-come)
                          (let (entry)
                            (while (setq entry (pop entries))
@@ -6067,16 +6067,17 @@ non-ignored, non-up-to-date files within those directories."
         (remaining (cadr fileset))
         ret-val)
     (while remaining
-      (cond* ((bind* (next (pop remaining))))
-             ((atom next)
-              (push next (alist-get (vc-state next backend) ret-val)))
-             ((bind* (file (car next))))
-             ((file-directory-p file)
-              (setq remaining
-                    (nconc (vc-dir-status-files file nil backend)
-                           remaining)))
-             (t
-              (push file (alist-get (cadr next) ret-val)))))
+      (let* ((next (pop remaining))
+             (file (if (consp next) (car next) next)))
+        (if (file-directory-p file)
+            (setq remaining
+                  (nconc (vc-dir-status-files file nil backend)
+                         remaining))
+          (push file
+                (alist-get (if (consp next)
+                               (cadr next)
+                             (vc-state next backend))
+                           ret-val)))))
     ret-val))
 
 (declare-function diff-kill-creations-deletions "diff-mode")
diff --git a/src/treesit.c b/src/treesit.c
index 09a9006b876..edf728e04fa 100644
--- a/src/treesit.c
+++ b/src/treesit.c
@@ -4315,12 +4315,12 @@ treesit_cursor_helper_1 (TSTreeCursor *cursor, TSNode *target,
   /* ts_tree_cursor_goto_first_child_for_byte is significantly faster,
      so despite it having problems (see bug#60127), we try it first.
      Also, ts_tree_cursor_goto_first_child_for_byte can't find
-     zero-width nodes (which exists and are legit, e.g., markdown's
+     zero-width nodes (which exist and are legit, e.g., markdown's
      block_continuation), because a zero-width node can't contain a pos
-     (end > pos).  */
-  if (start_pos != end_pos
-      && ts_tree_cursor_goto_first_child_for_byte (cursor, start_pos) == -1
-      && !ts_tree_cursor_goto_first_child (cursor))
+     (end > pos); instead, it'll falsely return the next child.  */
+  if (!((start_pos != end_pos
+	 && ts_tree_cursor_goto_first_child_for_byte (cursor, start_pos) != -1)
+	|| ts_tree_cursor_goto_first_child (cursor)))
     return false;
 
   /* Go through each sibling that could contain TARGET.  Because of
diff --git a/test/infra/Dockerfile.emba b/test/infra/Dockerfile.emba
index ad880212e4a..c56eec00f3a 100644
--- a/test/infra/Dockerfile.emba
+++ b/test/infra/Dockerfile.emba
@@ -74,7 +74,7 @@ RUN apt-get update && \
       clangd npm node-typescript rust-analyzer cargo \
     && rm -rf /var/lib/apt/lists/*
 
-RUN npm install -g typescript-language-server typescript
+RUN npm install -g typescript-language-server
 RUN npm install -g vscode-json-languageserver
 
 # eclipse-jdt-ls is planned as Java language server.
diff --git a/test/lisp/progmodes/sh-script-resources/sh-indents.erts b/test/lisp/progmodes/sh-script-resources/sh-indents.erts
index 5a72df0e7ef..2bd09a9e9dd 100644
--- a/test/lisp/progmodes/sh-script-resources/sh-indents.erts
+++ b/test/lisp/progmodes/sh-script-resources/sh-indents.erts
@@ -49,3 +49,16 @@ Name: sh-indents5
 for i do echo 1; done
 for i; do echo 1; done
 =-=-=
+
+Name: sh-indents-case-pattern-bug#81192
+
+=-=
+case "$1" in
+    a:b)
+	echo "a b"
+	;;
+    *,*)
+	echo "other"
+	;;
+esac
+=-=-=
diff --git a/test/lisp/vc/vc-tests/vc-tests.el b/test/lisp/vc/vc-tests/vc-tests.el
index f190db103d5..3201434ab78 100644
--- a/test/lisp/vc/vc-tests/vc-tests.el
+++ b/test/lisp/vc/vc-tests/vc-tests.el
@@ -1010,13 +1010,13 @@ This checks also `vc-backend' and `vc-responsible-backend'."
                 (should (eq (vc-state "qux"  backend) 'added))
                 (should (eq (vc-state "quux" backend) 'unregistered)))
 
-              (cl-flet ((go ()
-                          (let ((default-directory first)
-                                (vc-no-confirm-moving-changes t))
-                            (vc--apply-to-other-working-tree
-                             second second `(,backend
-                                             ("foo" "bar" "baz" "qux" "quux"))
-                             nil t))))
+              (cl-flet
+                  ((go ()
+                     (let ((default-directory first)
+                           (vc-no-confirm-moving-changes t))
+                       (vc--apply-to-other-working-tree second second
+                                                        `(,backend (,first))
+                                                        nil t))))
                 (let ((default-directory second))
                   ;; Set up a series of incompatibilities, one-by-one, and
                   ;; try to move.  In each case the problem should block the