commit ae4878c53bbef53f9bf98d09e5ab9be91e8cef15 (HEAD, refs/remotes/origin/master) Author: Eli Zaretskii Date: Sun Sep 28 08:19:48 2025 +0300 Revert "Fix bug#79518 with 'windmove-mode' in "C-h m" display" This reverts commit bf750adc4e637d871d551bab5b5c6ff9240797ed. It caused windmove to be loaded unconditionally. The correct "fix" for bug#79518 is to load the package or to toggle the mode. diff --git a/lisp/windmove.el b/lisp/windmove.el index b572d5b76be..ed76c67ac46 100644 --- a/lisp/windmove.el +++ b/lisp/windmove.el @@ -520,10 +520,8 @@ stopped because it wouldn't move into a window marked with (define-minor-mode windmove-mode "Global minor mode for default windmove commands." :keymap windmove-mode-map - :initialize 'custom-initialize-delay :init-value t - :global t - :group 'windows) + :global t) (defun windmove-install-defaults (prefix modifiers alist &optional uninstall) "Install keys as specified by ALIST. commit b9573f8f1a624f7206124ae69077b8634900f685 Author: Stefan Monnier Date: Sat Sep 27 22:57:02 2025 -0400 peg.el: Fix bug#79502 a bit more * lisp/progmodes/peg.el (peg--detect-cycles): Accept args. (peg--detect-cycles) : New method. diff --git a/lisp/progmodes/peg.el b/lisp/progmodes/peg.el index 15372555ab5..9fb46cb05e4 100644 --- a/lisp/progmodes/peg.el +++ b/lisp/progmodes/peg.el @@ -837,7 +837,7 @@ input. PATH is the list of rules that we have visited so far." (cl-defgeneric peg--detect-cycles (head _path &rest args) (error "No detect-cycle method for: %S" (cons head args))) -(cl-defmethod peg--detect-cycles (path (_ (eql call)) name) +(cl-defmethod peg--detect-cycles (path (_ (eql call)) name &rest _args) (if (member name path) (error "Possible left recursion: %s" (mapconcat (lambda (x) (format "%s" x)) @@ -885,6 +885,13 @@ input. PATH is the list of rules that we have visited so far." (cl-defmethod peg--detect-cycles (_path (_ (eql guard)) _e) t) (cl-defmethod peg--detect-cycles (_path (_ (eql =)) _s) nil) (cl-defmethod peg--detect-cycles (_path (_ (eql syntax-class)) _n) nil) + +(cl-defmethod peg--detect-cycles (_path (_ (eql funcall)) &rest _args) + ;; There might very well be a cycle here, and we may very well match + ;; the empty string, but it's much too hard (and in general + ;; impossible) to try and figure out. + nil) + (cl-defmethod peg--detect-cycles (_path (_ (eql action)) _form) t) (defun peg-merge-errors (exps) commit 021b7065bb734ca5e880f2fb74ddd48ffed4185a Author: Stefan Monnier Date: Sat Sep 27 18:20:37 2025 -0400 peg.el: Fix bug#79502 * lisp/progmodes/peg.el (peg--merge-error): Provide a default method. (peg--merge-error) : Handle calls with arguments. (peg--merge-error) : Remove methods, now redundant with the default method. (peg--merge-error) : Delegate to the default method if we can't do better. diff --git a/lisp/progmodes/peg.el b/lisp/progmodes/peg.el index 4518d82cc37..15372555ab5 100644 --- a/lisp/progmodes/peg.el +++ b/lisp/progmodes/peg.el @@ -897,8 +897,8 @@ input. PATH is the list of rules that we have visited so far." (defun peg-merge-error (exp merged) (apply #'peg--merge-error merged exp)) -(cl-defgeneric peg--merge-error (_merged head &rest args) - (error "No merge-error method for: %S" (cons head args))) +(cl-defgeneric peg--merge-error (merged head &rest args) + (cl-adjoin (cons head args) merged :test #'equal)) (cl-defmethod peg--merge-error (merged (_ (eql or)) e1 e2) (peg-merge-error e2 (peg-merge-error e1 merged))) @@ -911,36 +911,24 @@ input. PATH is the list of rules that we have visited so far." ;;(add-to-list 'merged str) (cl-adjoin str merged :test #'equal)) -(cl-defmethod peg--merge-error (merged (_ (eql call)) rule) - ;; (add-to-list 'merged rule) - (cl-adjoin rule merged :test #'equal)) +(cl-defmethod peg--merge-error (merged (_ (eql call)) rule &rest args) + (cl-adjoin (if args (cons rule args) rule) merged :test #'equal)) (cl-defmethod peg--merge-error (merged (_ (eql char)) char) - ;; (add-to-list 'merged (string char)) (cl-adjoin (string char) merged :test #'equal)) (cl-defmethod peg--merge-error (merged (_ (eql set)) r c k) - ;; (add-to-list 'merged (peg-make-charset-regexp r c k)) (cl-adjoin (peg-make-charset-regexp r c k) merged :test #'equal)) (cl-defmethod peg--merge-error (merged (_ (eql range)) from to) - ;; (add-to-list 'merged (format "[%c-%c]" from to)) (cl-adjoin (format "[%c-%c]" from to) merged :test #'equal)) (cl-defmethod peg--merge-error (merged (_ (eql *)) exp) (peg-merge-error exp merged)) -(cl-defmethod peg--merge-error (merged (_ (eql any))) - ;; (add-to-list 'merged '(any)) - (cl-adjoin '(any) merged :test #'equal)) - -(cl-defmethod peg--merge-error (merged (_ (eql not)) x) - ;; (add-to-list 'merged `(not ,x)) - (cl-adjoin `(not ,x) merged :test #'equal)) - (cl-defmethod peg--merge-error (merged (_ (eql action)) _action) merged) (cl-defmethod peg--merge-error (merged (_ (eql guard)) e) - (if (eq e t) merged (cl-adjoin `(guard ,e) merged :test #'equal))) + (if (eq e t) merged (cl-call-next-method))) (provide 'peg) (require 'peg) commit 4e7cb37b8440bf63ce5ef715282bfbf9b263128a Author: Sean Whitton Date: Sat Sep 27 21:36:51 2025 +0100 VC prepare-patch: New :patch-start & :patch-end plist entries * lisp/vc/vc.el (prepare-patch): Specify :patch-start and :patch-end plist entries. * lisp/vc/vc-git.el (vc-git-prepare-patch): Use -n1 to avoid passing a revision range to git-format-patch, which is a bit simpler. Catch search-failed errors and signal an error with a more helpful message. Properly handle Subject: header by looking for continuation lines. Return :patch-start and :patch-end entries in the plist. * lisp/vc/vc-hg.el (vc-hg-prepare-patch): Always pass --git to 'hg export' for consistency. Catch search-failed errors and signal an error with a more helpful message. Return a :patch-start entry in the plist. diff --git a/lisp/vc/vc-git.el b/lisp/vc/vc-git.el index 5002c21747f..54475542ac4 100644 --- a/lisp/vc/vc-git.el +++ b/lisp/vc/vc-git.el @@ -2311,26 +2311,39 @@ Rebase may --autosquash your other squash!/fixup!/amend!; proceed?"))) (defun vc-git-prepare-patch (rev) (with-current-buffer (generate-new-buffer " *vc-git-prepare-patch*") - (vc-git-command - t 0 '() "format-patch" - "--no-numbered" "--stdout" - ;; From gitrevisions(7): ^ means the th parent - ;; (i.e. ^ is equivalent to ^1). As a - ;; special rule, ^0 means the commit itself and - ;; is used when is the object name of a tag - ;; object that refers to a commit object. - (concat rev "^.." rev)) - (let (subject) - ;; Extract the subject line - (goto-char (point-min)) - (search-forward-regexp "^Subject: \\(.+\\)") - (setq subject (match-string 1)) - ;; Jump to the beginning for the patch - (search-forward-regexp "\n\n") - ;; Return the extracted data - (list :subject subject - :buffer (current-buffer) - :body-start (point))))) + (vc-git-command t 0 nil "format-patch" + "--no-numbered" "--stdout" "-n1" rev) + (condition-case _ + (let (subject body-start patch-start patch-end) + (goto-char (point-min)) + (re-search-forward "^Subject: \\(.*\\)") + (setq subject (match-string 1)) + (while (progn (forward-line 1) + (looking-at "[\s\t]\\(.*\\)")) + (setq subject (format "%s %s" subject (match-string 1)))) + (goto-char (point-min)) + (re-search-forward "\n\n") + (setq body-start (point)) + (if ;; If the user has added any of these to + ;; `vc-git-diff-switches' then they expect to see the + ;; diffstat in *vc-diff* buffers. + (cl-intersection '("--stat" + "--patch-with-stat" + "--compact-summary") + (vc-switches 'git 'diff) + :test #'equal) + (progn (re-search-forward "^---$") + (setq patch-start (pos-bol 2))) + (re-search-forward "^diff --git a/") + (setq patch-start (pos-bol))) + (re-search-forward "^-- $") + (setq patch-end (pos-bol)) + (list :subject subject + :body-start body-start + :patch-start patch-start + :patch-end patch-end + :buffer (current-buffer))) + (search-failed (error "git-format-patch output parse failure"))))) ;; grep-compute-defaults autoloads grep. (declare-function grep-read-regexp "grep" ()) diff --git a/lisp/vc/vc-hg.el b/lisp/vc/vc-hg.el index a646ba079dd..0d1f1703081 100644 --- a/lisp/vc/vc-hg.el +++ b/lisp/vc/vc-hg.el @@ -1673,14 +1673,18 @@ This runs the command \"hg merge\"." (defun vc-hg-prepare-patch (rev) (with-current-buffer (generate-new-buffer " *vc-hg-prepare-patch*") - (vc-hg-command t 0 '() "export" "--rev" rev) - (let (subject) - ;; Extract the subject line - (goto-char (point-min)) - (search-forward-regexp "^[^#].*") - (setq subject (match-string 0)) - ;; Return the extracted data - (list :subject subject :buffer (current-buffer))))) + (vc-hg-command t 0 nil "export" "--git" "--rev" rev) + (condition-case _ + (let (subject patch-start) + (goto-char (point-min)) + (re-search-forward "^[^#].*") + (setq subject (match-string 0)) + (re-search-forward "\n\ndiff --git a/") + (setq patch-start (pos-bol)) + (list :subject subject + :patch-start patch-start + :buffer (current-buffer))) + (search-failed (error "'hg export' output parse failure"))))) ;;; Internal functions diff --git a/lisp/vc/vc.el b/lisp/vc/vc.el index 782f9ae12a2..006d2098c2f 100644 --- a/lisp/vc/vc.el +++ b/lisp/vc/vc.el @@ -670,15 +670,17 @@ ;; ;; - prepare-patch (rev) ;; -;; Prepare a patch and return a property list with the keys -;; `:subject' indicating the patch message as a string, `:buffer' -;; with a buffer object that contains the entire patch message and -;; `:body-start' and `:body-end' demarcating what part of said -;; buffer should be inserted into an inline patch. If the two last -;; properties are omitted, `point-min' and `point-max' will -;; respectively be used instead. If supported by the backend, the -;; patch should contain authorship identity and date information, and -;; REV's log message. +;; Prepare a patch and return a property list with the keys `:subject' +;; with the summary line (first line) of the patch message as a +;; string; `:buffer' with a buffer object that contains the entire +;; patch message; `:body-start' and `:body-end' demarcating the part +;; of that buffer which should be inserted inline into a mail message +;; body; and `:patch-start' and `:patch-end' demarcating the part of +;; the buffer that is purely the patch, excluding any log message. +;; If any of these *-start and *-end properties are omitted, they +;; default to (point-min) and (point-max), respectively. +;; If supported by the backend, the patch should contain authorship +;; identity and date information, and REV's log message. ;; ;; - clone (remote directory rev) ;; @@ -4210,7 +4212,7 @@ If nil, no default will be used. This option may be set locally." :buffer (current-buffer))))) (defun vc-prepare-patch-prompt-revisions () - "Prompt the user for a list revisions. + "Prompt the user for a list of revisions. Prepare a default value, depending on the current context. With a numerical prefix argument, use the last N revisions as the default value. If the current buffer is a log-view buffer, use commit 1d03eb590ca8bbc7d4a0d9432a8eb17d968614a3 Author: Sean Whitton Date: Sat Sep 27 21:31:38 2025 +0100 vc-test--checkin-patch: Restore alternative revert code path * test/lisp/vc/vc-tests/vc-tests.el (vc-test--checkin-patch): For the last stage of the test, restore alternative code path for Git, though this time limited to when running the test on MS-Windows. Cf. subthread starting from . diff --git a/test/lisp/vc/vc-tests/vc-tests.el b/test/lisp/vc/vc-tests/vc-tests.el index 38c659950c2..1625c3501db 100644 --- a/test/lisp/vc/vc-tests/vc-tests.el +++ b/test/lisp/vc/vc-tests/vc-tests.el @@ -881,7 +881,15 @@ This checks also `vc-backend' and `vc-responsible-backend'." ;; Should take the author, date but not the comment from ;; PATCH-STRING. (let ((patch-string (get-patch-string))) - (revert "Revert modification, second time") + ;; FIXME: Why doesn't `revert' work properly here? + (if (and (eq backend 'Git) + (eq system-type 'windows-nt)) + (with-current-buffer buf + (vc-checkin (list file) backend) + (insert "Revert modification, second time") + (let (vc-async-checkin) + (log-edit-done))) + (revert "Revert modification, second time")) (vc-call-backend backend 'checkin-patch patch-string desc2)) (check author date desc2)) commit 50ab62ad75f939edd72a0aef7262f3f19ebb96dc Author: Paul Eggert Date: Sat Sep 27 12:25:34 2025 -0700 Use up-to-date time in wait_reading_process_output In “Avoid duplicate calls to current_timespec” (2015-07-05) we started caching current_timespec results in NOW. However, this was buggy: we updated NOW only when the timeout was nonzero, but the timeout can be set temporarily to zero in several places in wait_reading_process_output (such as when checking for process status changes), which would cause us to never update NOW and therefore never detect that a timeout happened. Also, this caching was wrong even in principle: since we call Lisp code from wait_reading_process_output, substantial amounts of time can pass, and we can be left using an outdated NOW and incorrectly not time out. Also, nowadays we can use monotonic_coarse_timespec which is fast, and which is better anyway because it’s immune to manual clock changes. Co-authored-by: Spencer Baugh * src/process.c (wait_reading_process_output): Stop caching the current realtime. Instead, use the coarse monotonic clock without caching. diff --git a/src/process.c b/src/process.c index 2804409f51e..8ec420aaa2c 100644 --- a/src/process.c +++ b/src/process.c @@ -5353,9 +5353,6 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd, #endif specpdl_ref count = SPECPDL_INDEX (); - /* Close to the current time if known, an invalid timespec otherwise. */ - struct timespec now = invalid_timespec (); - eassert (wait_proc == NULL || NILP (wait_proc->thread) || XTHREAD (wait_proc->thread) == current_thread); @@ -5380,7 +5377,7 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd, else if (time_limit > 0 || nsecs > 0) { wait = TIMEOUT; - now = current_timespec (); + struct timespec now = monotonic_coarse_timespec (); end_time = timespec_add (now, make_timespec (time_limit, nsecs)); } else @@ -5467,8 +5464,7 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd, /* Exit if already run out. */ if (wait == TIMEOUT) { - if (!timespec_valid_p (now)) - now = current_timespec (); + struct timespec now = monotonic_coarse_timespec (); if (timespec_cmp (end_time, now) <= 0) break; timeout = timespec_sub (end_time, now); @@ -5721,8 +5717,7 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd, && timespec_valid_p (timer_delay) && timespec_cmp (timer_delay, timeout) < 0) { - if (!timespec_valid_p (now)) - now = current_timespec (); + struct timespec now = monotonic_coarse_timespec (); struct timespec timeout_abs = timespec_add (now, timeout); if (!timespec_valid_p (got_output_end_time) || timespec_cmp (timeout_abs, got_output_end_time) < 0) @@ -5732,10 +5727,6 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd, else got_output_end_time = invalid_timespec (); - /* NOW can become inaccurate if time can pass during pselect. */ - if (timeout.tv_sec > 0 || timeout.tv_nsec > 0) - now = invalid_timespec (); - #if defined HAVE_GETADDRINFO_A || defined HAVE_GNUTLS if (retry_for_async && (timeout.tv_sec > 0 || timeout.tv_nsec > ASYNC_RETRY_NSEC)) @@ -5873,7 +5864,7 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd, } if (timespec_cmp (cmp_time, huge_timespec) < 0) { - now = current_timespec (); + struct timespec now = monotonic_coarse_timespec (); if (timespec_cmp (cmp_time, now) <= 0) break; } @@ -8132,7 +8123,7 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd, else if (time_limit > 0 || nsecs > 0) { wait = TIMEOUT; - end_time = timespec_add (current_timespec (), + end_time = timespec_add (monotonic_coarse_timespec (), make_timespec (time_limit, nsecs)); } else @@ -8164,7 +8155,7 @@ wait_reading_process_output (intmax_t time_limit, int nsecs, int read_kbd, /* Exit if already run out. */ if (wait == TIMEOUT) { - struct timespec now = current_timespec (); + struct timespec now = monotonic_coarse_timespec (); if (timespec_cmp (end_time, now) <= 0) break; timeout = timespec_sub (end_time, now); commit 5e06aa209bc906cf16f2e3de622a189bec34aa78 Author: Paul Eggert Date: Sat Sep 27 11:23:59 2025 -0700 Prefer coarse timestamps when using X sync They are good enough for this purpose, and are cheaper to get. * src/timefns.c (monotonic_coarse_timespec): New function. * src/xterm.c [HAVE_XSYNC && !USE_GTK && HAVE_CLOCK_GETTIME]: (x_sync_current_monotonic_time): Use it. (CLOCK_MONOTONIC): Remove; no longer uneeded here. diff --git a/src/systime.h b/src/systime.h index 22ede456840..2e8fb454eee 100644 --- a/src/systime.h +++ b/src/systime.h @@ -79,6 +79,7 @@ enum { LO_TIME_BITS = 16 }; /* defined in timefns.c */ extern struct timeval make_timeval (struct timespec) ATTRIBUTE_CONST; +extern struct timespec monotonic_coarse_timespec (void); extern Lisp_Object make_lisp_time (struct timespec); extern Lisp_Object timespec_to_lisp (struct timespec); extern struct timespec list4_to_timespec (Lisp_Object, Lisp_Object, diff --git a/src/timefns.c b/src/timefns.c index 8cf424bbe7e..b4baeaaff82 100644 --- a/src/timefns.c +++ b/src/timefns.c @@ -138,6 +138,26 @@ make_timeval (struct timespec t) return tv; } +/* Return the current time with an epoch specific to this Emacs instance + (e.g., system boot). The clock should be unaffected by changes to + the system time, and should be cheap to access. Its resolution + should be appropriate for human time scales, e.g., better than 10 ms. + Make do with realtime if such a clock is not available. */ +struct timespec +monotonic_coarse_timespec (void) +{ + struct timespec ts; +#ifdef CLOCK_MONOTONIC_COARSE + if (clock_gettime (CLOCK_MONOTONIC_COARSE, &ts) == 0) + return ts; +#elif defined CLOCK_MONOTONIC + if (clock_gettime (CLOCK_MONOTONIC, &ts) == 0) + return ts; +#endif + ts = current_timespec (); + return ts; +} + /* Yield A's UTC offset, or an unspecified value if unknown. */ static long int tm_gmtoff (struct tm *a) diff --git a/src/xterm.c b/src/xterm.c index d520ac1bdf5..e47a836713a 100644 --- a/src/xterm.c +++ b/src/xterm.c @@ -7067,22 +7067,15 @@ x_sync_get_monotonic_time (struct x_display_info *dpyinfo, return ckd_sub (&t, timestamp, dpyinfo->server_time_offset) ? 0 : t; } -# ifndef CLOCK_MONOTONIC -# define CLOCK_MONOTONIC CLOCK_REALTIME -# endif - /* Return the current monotonic time in the same format as a high-resolution server timestamp, or 0 if not available. */ static uint_fast64_t x_sync_current_monotonic_time (void) { - struct timespec time; + struct timespec time = monotonic_coarse_timespec (); uint_fast64_t t; - return (((clock_gettime (CLOCK_MONOTONIC, &time) != 0 - && (CLOCK_MONOTONIC == CLOCK_REALTIME - || clock_gettime (CLOCK_REALTIME, &time) != 0)) - || ckd_mul (&t, time.tv_sec, 1000000) + return ((ckd_mul (&t, time.tv_sec, 1000000) || ckd_add (&t, t, time.tv_nsec / 1000)) ? 0 : t); } commit 3cdc615218458729b51a715bedc899e3df5bfa64 Author: Juri Linkov Date: Sat Sep 27 20:56:54 2025 +0300 Improve documentation and customization of 'derived-mode' in buffer predicate * doc/lispref/buffers.texi (Buffer List): * lisp/subr.el (buffer-match-p): Document that 'derived-mode' can be a list (bug#79481). * lisp/wid-edit.el (buffer-predicate): Support a list for 'derived-mode'. diff --git a/doc/lispref/buffers.texi b/doc/lispref/buffers.texi index 01aa620b828..e759df5746c 100644 --- a/doc/lispref/buffers.texi +++ b/doc/lispref/buffers.texi @@ -991,10 +991,13 @@ Satisfied if @emph{any} condition in @var{conds} satisfies Satisfied if @emph{all} the conditions in @var{conds} satisfy @code{buffer-match-p}, with the same buffer and @code{args}. @item derived-mode -Satisfied if the buffer's major mode derives from @var{expr}. Note -that this condition might fail to report a match if -@code{buffer-match-p} is invoked before the major mode of the buffer -has been established. +Satisfied if the buffer's major mode derives from @var{expr}. +The value of @var{expr} can be either a single mode symbol +or a list of mode symbols as accepted by the function +@code{provided-mode-derived-p} (@pxref{Derived Modes}). +Note that this condition might fail to report a match if +@code{buffer-match-p} is invoked before the major mode of +the buffer has been established. @item major-mode Satisfied if the buffer's major mode is equal to @var{expr}. Prefer using @code{derived-mode} instead, when both can work. Note that this diff --git a/lisp/subr.el b/lisp/subr.el index da208d7063f..08c22737539 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -7651,7 +7651,8 @@ CONDITION is either: - a cons-cell, where the car describes how to interpret the cdr. The car can be one of the following: * `derived-mode': the buffer matches if the buffer's major mode - is derived from the major mode in the cons-cell's cdr. + is derived from the major mode in the cons-cell's cdr, or from any + major mode in the list as accepted by `provided-mode-derived-p'. * `major-mode': the buffer matches if the buffer's major mode is eq to the cons-cell's cdr. Prefer using `derived-mode' instead when both can work. diff --git a/lisp/wid-edit.el b/lisp/wid-edit.el index ee6679f9e63..da07231a4da 100644 --- a/lisp/wid-edit.el +++ b/lisp/wid-edit.el @@ -4395,7 +4395,9 @@ is inline." (function :tag "Predicate function") (cons :tag "Derived mode" (const derived-mode) - (symbol derived-mode)) + (choice + (symbol :tag "Single mode" derived-mode) + (repeat :tag "List of modes" (symbol derived-mode)))) (cons :tag "Major mode" (const major-mode) (symbol major-mode)) commit 830272b306152666b355f4c186a8df054c31037a Author: Paul Eggert Date: Sat Sep 27 09:19:43 2025 -0700 Document coding system issues with system-*-locale diff --git a/doc/lispref/nonascii.texi b/doc/lispref/nonascii.texi index cc1f9d61401..4e749b5da3a 100644 --- a/doc/lispref/nonascii.texi +++ b/doc/lispref/nonascii.texi @@ -2158,6 +2158,8 @@ messages. Changing the locale can cause messages to come out in a different language or in a different orthography. If the variable is @code{nil}, the locale is specified by environment variables in the usual POSIX fashion. +Otherwise, the variable should specify a locale compatible with the +coding system implied by those environment variables. @end defvar @defvar system-time-locale @@ -2165,6 +2167,8 @@ This variable specifies the locale to use for formatting time values. Changing the locale can cause messages to appear according to the conventions of a different language. If the variable is @code{nil}, the locale is specified by environment variables in the usual POSIX fashion. +Otherwise, the variable should specify a locale compatible with the +coding system implied by those environment variables. @end defvar @defun locale-info item commit 32b9902603ca3157a64a27ccb56ff7a33cb30506 Author: Eshel Yaron Date: Sat Sep 27 16:00:41 2025 +0200 ; (read-string-from-buffer): Fix thinko and typo. diff --git a/lisp/textmodes/string-edit.el b/lisp/textmodes/string-edit.el index dda70437f78..857d7c3f969 100644 --- a/lisp/textmodes/string-edit.el +++ b/lisp/textmodes/string-edit.el @@ -93,7 +93,7 @@ Also see `read-string-from-buffer'." ;;;###autoload (defun read-string-from-buffer (prompt string) "Switch to a new buffer to edit STRING in a recursive edit. -The user finishes editing with \\\\[string-edit-done], or aborts with \\\\[string-edit-abort]). +The user finishes editing with \\\\[string-edit-done], or aborts with \\\\[string-edit-abort]. Insert PROMPT at the start of the buffer. If nil, no prompt is inserted. @@ -108,9 +108,7 @@ Also see `string-edit'." (lambda (edited) (setq string edited) (exit-recursive-edit)) - :abort-callback (lambda () - (exit-recursive-edit) - (error "Aborted edit"))) + :abort-callback (lambda () (throw 'exit "Aborted edit"))) (recursive-edit) string) commit 48fc9f67b7c1ee37f1de868dd44fbb18a969bd1a Merge: ade511e3163 6d35c807e5f Author: Eli Zaretskii Date: Sat Sep 27 05:56:48 2025 -0400 Merge from origin/emacs-30 6d35c807e5f ; Improve documentation of globalized minor modes 704fab0452a ; Improve documentation of handling errors 307f465f7b9 ; * doc/lispref/text.texi (Suspicious Text): Fix suspicio... 6bedbafc7ad ; * doc/lispref/text.texi (Suspicious Text): Indexing fix. commit ade511e3163d46d475a7c7dbe564fb65a18830ae Author: Eli Zaretskii Date: Sat Sep 27 12:34:25 2025 +0300 ; Improve documentation of 'defcustom' types * doc/lispref/customize.texi (Simple Types): Document the 'buffer-predicate' type. (Bug#79496) diff --git a/doc/lispref/customize.texi b/doc/lispref/customize.texi index d257bb3a462..2c6f02a088c 100644 --- a/doc/lispref/customize.texi +++ b/doc/lispref/customize.texi @@ -655,6 +655,12 @@ the symbol name. The widget provides completion. The value must be either a lambda expression or a function name. The widget provides completion for function names. +@item buffer-predicate +The value must be a form suitable for the @var{condition} argument of +the @code{buffer-match-p} function (@pxref{Buffer List}). This is handy +for options whose value is submitted to @code{buffer-match-p} to select +suitable buffers. + @item variable The value must be a variable name. The widget provides completion. commit 6d35c807e5ff5d00f734f88562e687a857515513 Author: Eli Zaretskii Date: Sat Sep 27 12:22:42 2025 +0300 ; Improve documentation of globalized minor modes * doc/lispref/modes.texi (Defining Minor Modes): Document the subtlety with ':init-value t' for globalized modes. diff --git a/doc/lispref/modes.texi b/doc/lispref/modes.texi index 421ec136c7c..c5c6c10a6fa 100644 --- a/doc/lispref/modes.texi +++ b/doc/lispref/modes.texi @@ -1861,7 +1861,17 @@ marking the @code{define-minor-mode} form as autoloaded. @item :init-value @var{init-value} This is the value to which the @var{mode} variable is initialized. Except in unusual circumstances (see below), this value must be -@code{nil}. +@code{nil}. If the mode is global (see below), and the initial value is +@code{t}, i.e., the mode is turned on by default, you should consider +forcing Emacs to run the mode function at startup, like this: + +@lisp + :initialize #'custom-initialize-delay +@end lisp + +@noindent +otherwise, the minor mode might not appear in the @file{*Help*} buffer +generated by @kbd{C-h m} (@pxref{Mode Help}). @item :lighter @var{lighter} The string @var{lighter} says what to display in the mode line commit b60e0f42941cf7db2dd396029437a1cbeec74d69 Author: Martin Rudalics Date: Sat Sep 27 10:07:14 2025 +0200 Restore mouse line dragging in character increments (Bug#79351) * lisp/mouse.el (mouse-drag-line): Restore dragging in character increments and make it the default (Bug#79351). * etc/NEWS: Mention restored behavior of mouse line dragging. diff --git a/etc/NEWS b/etc/NEWS index a6f897c602f..a402811f473 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -288,6 +288,16 @@ to extend the boundaries of the active region by dragging the mouse pointer. The menu item enables sending current file(s) or region text to external (non-Emacs) applications or services. See send-to.el for customisations. +--- +*** The mouse now drags lines in character increments again. +Dragging a horizontal or vertical line like the mode line or the lines +dividing side-by-side windows now by default happens in increments of +the corresponding frame's character size again. This is the behavior +described in the manual and was the default behavior before +'window-resize-pixelwise' was added for Emacs 24.1. To drag in pixel +increments as with Emcas 24 through Emacs 30 you now have to set +'window-resize-pixelwise' to t. + ** Windows +++ diff --git a/lisp/mouse.el b/lisp/mouse.el index b8db801b422..f75800763e6 100644 --- a/lisp/mouse.el +++ b/lisp/mouse.el @@ -854,40 +854,100 @@ This command must be bound to a mouse click." (min (max new-width first-col) last-col))))))) (defun mouse-drag-line (start-event line) - "Drag a mode line, header line, or vertical line with the mouse. + "Drag a mode, header, tab or vertical line with the mouse. START-EVENT is the starting mouse event of the drag action. LINE -must be one of the symbols `header', `mode', or `vertical'." +must be one of the symbols `header', `mode', `tab' or `vertical'." ;; Give temporary modes such as isearch a chance to turn off. (run-hooks 'mouse-leave-buffer-hook) + ;; The earlier version of this was based on using the position of the + ;; start event for each sampled mouse movement. That approach had the + ;; disadvantage that when, for example, dragging the mode line down, + ;; the 'posn-window' of that event was usually the window below the + ;; mode line and its coordinates were relative to that window. So we + ;; had to add position and height of the window above the mode line in + ;; order to get a meaningful value for comparing the old and current + ;; mouse position. However, when a user changed the direction during + ;; dragging, the mouse moved into the window above the mode line and + ;; the relative position changed to one of that window too. Since + ;; keeping track of these changes was tricky, we now simply use + ;; absolute mouse positions and do not care about the window at the + ;; mouse position any more. (let* ((echo-keystrokes 0) (start (event-start start-event)) (window (posn-window start)) (frame (window-frame window)) - ;; `position' records the x- or y-coordinate of the last - ;; sampled position. + ;; tty is needed because `mouse-absolute-pixel-position' does + ;; not return a meaningful value on ttys so there we have to + ;; use `mouse-position-in-root-frame'. + (tty (tty-type frame)) + ;; 'charwise' means to drag by character sizes on graphical + ;; displays. + (charwise (not (or window-resize-pixelwise tty))) + ;; The initial absolute position of the mouse. We + ;; intentionally do not use the value of 'posn-x-y' of + ;; START-EVENT here because that would give us coordinates for + ;; 'posn-window' of that event and we don't want that (see the + ;; comment above). + (position-x-y (if tty + (mouse-position-in-root-frame) + (mouse-absolute-pixel-position))) + ;; 'position' records the x- (for vertical dragging) or y- (for + ;; mode, header and tab line dragging) coordinate of the + ;; current mouse position (position (if (eq line 'vertical) - (+ (window-pixel-left window) - (car (posn-x-y start))) - (+ (window-pixel-top window) - (cdr (posn-x-y start))))) - ;; `last-position' records the x- or y-coordinate of the - ;; previously sampled position. The difference of `position' - ;; and `last-position' determines the size change of WINDOW. + (car position-x-y) + (cdr position-x-y))) + ;; 'last-position' records the the x- or y-coordinate of the + ;; previously sampled position. The difference of 'position' + ;; and 'last-position' determines the size change of WINDOW. (last-position position) - posn-window growth dragged) - ;; Decide on whether we are allowed to track at all and whose - ;; window's edge we drag. + ;; The next two bindings are used for characterwise dragging + ;; only. 'residue' is the remainder of the difference between + ;; 'position' and 'last-position' divided by the frame's + ;; character size and will be considered in the next difference + ;; calculation. + (residue 0) + ;; 'forward' indicates the current dragging direction and is + ;; non-nil when dragging to the right or down. Its purpose is + ;; to detect changes in the dragging direction in order to keep + ;; the mouse cursor nearer to the dragged line. + (forward t) + ;; 'char-size' is the frame's character width) for vertical + ;; dragging) or character height (for mode, header, tab line + ;; dragging). + char-size + ;; 'growth' is the position change of the mouse in pixels if + ;; 'charwise' is nil, in characters if 'charwise' is non-nil. + growth + ;; `dragged' is initially nil and sticks to non-nil after the + ;; first time growth has become non-nil. Its purpose is to + ;; give characterwise dragging a head start to avoid that the + ;; mouse cursor moves to far away from the line to drag. + dragged) + ;; Set up the window whose edge to drag. (cond ((memq line '(header tab)) - ;; Drag bottom edge of window above the header line. - (setq window (window-in-direction 'above window t))) - ((eq line 'mode)) + ;; LINE is a header or tab line. Drag the bottom edge of the + ;; window above it. + (setq window (window-in-direction 'above window t)) + (when charwise + (setq char-size (frame-char-height frame)))) + ((eq line 'mode) + ;; LINE is a mode line or a bottom window divider. Drag the bottom edge + ;; of its window. + (when charwise + (setq char-size (frame-char-height frame)))) ((eq line 'vertical) + ;; LINE is a window divider on the right. Drag the right edge of + ;; the window on its left. (let ((divider-width (frame-right-divider-width frame))) (when (and (or (not (numberp divider-width)) (zerop divider-width)) (eq (frame-parameter frame 'vertical-scroll-bars) 'left)) - (setq window (window-in-direction 'left window t)))))) + (setq window (window-in-direction 'left window t)))) + (when charwise + (setq char-size (frame-char-width frame))))) + (let* ((exitfun nil) (move (lambda (event) (interactive "e") @@ -895,74 +955,76 @@ must be one of the symbols `header', `mode', or `vertical'." ((not (consp event)) nil) ((eq line 'vertical) - ;; Drag right edge of `window'. - (setq start (event-start event)) - (setq position (car (posn-x-y start))) - ;; Set `posn-window' to the window where `event' was recorded. - ;; This can be `window' or the window on the left or right of - ;; `window'. - (when (window-live-p (setq posn-window (posn-window start))) - ;; Add left edge of `posn-window' to `position'. - (setq position (+ (window-pixel-left posn-window) position)) - (unless (posn-area start) - ;; Add width of objects on the left of the text area to - ;; `position'. - (when (eq (window-current-scroll-bars posn-window) 'left) - (setq position (+ (window-scroll-bar-width posn-window) - position))) - (setq position (+ (car (window-fringes posn-window)) - (or (car (window-margins posn-window)) 0) - position)))) - ;; When the cursor overshoots after shrinking a window to its - ;; minimum size and the dragging direction changes, have the - ;; cursor first catch up with the window edge. - (unless (or (zerop (setq growth (- position last-position))) - (and (> growth 0) - (< position (+ (window-pixel-left window) - (window-pixel-width window)))) - (and (< growth 0) - (> position (+ (window-pixel-left window) - (window-pixel-width window))))) + ;; Drag right edge of 'window'. + (setq position (if tty + (car (mouse-position-in-root-frame)) + (car (mouse-absolute-pixel-position)))) + (unless (zerop (setq growth (- position last-position))) + ;; When we drag characterwise and we either drag for + ;; the first time or the dragging direction changes, + ;; try to keep in synch cursor and dragged line. + (when (and charwise + (or (not dragged) + (if forward + (< growth 0) + (> growth 0)))) + (setq forward (> growth 0)) + (setq growth + (if (> growth 0) + (+ growth (/ char-size 2)) + (- growth (/ char-size 2))))) + (setq dragged t) - (adjust-window-trailing-edge window growth t t)) - (setq last-position position)) + (when charwise + (setq residue (% growth char-size)) + (setq growth (/ growth char-size))) + (unless (zerop growth) + (adjust-window-trailing-edge window growth t (not charwise))) + (setq last-position (- position residue)) + +;; ;; Debugging code. +;; (message "last %s pos %s growth %s residue %s char-size %s" +;; last-position position growth residue char-size) + + )) (t - ;; Drag bottom edge of `window'. - (setq start (event-start event)) - ;; Set `posn-window' to the window where `event' was recorded. - ;; This can be either `window' or the window above or below of - ;; `window'. - (setq posn-window (posn-window start)) - (setq position (cdr (posn-x-y start))) - (when (window-live-p posn-window) - ;; Add top edge of `posn-window' to `position'. - (setq position (+ (window-pixel-top posn-window) position)) - ;; If necessary, add height of header and tab line to - ;; `position'. - (when (memq (posn-area start) - '(nil left-fringe right-fringe left-margin right-margin)) - (setq position (+ (window-header-line-height posn-window) - (window-tab-line-height posn-window) - position)))) - ;; When the cursor overshoots after shrinking a window to its - ;; minimum size and the dragging direction changes, have the - ;; cursor first catch up with the window edge. - (unless (or (zerop (setq growth (- position last-position))) - (and (> growth 0) - (< position (+ (window-pixel-top window) - (window-pixel-height window)))) - (and (< growth 0) - (> position (+ (window-pixel-top window) - (window-pixel-height window))))) + ;; Drag bottom edge of 'window'. + (setq position (cdr (if tty + (mouse-position-in-root-frame) + (mouse-absolute-pixel-position)))) + (unless (zerop (setq growth (- position last-position))) + ;; When we drag characterwise and we either drag for + ;; the first time or the dragging direction changes, + ;; try to keep in synch cursor and dragged line. + (when (and charwise + (or (not dragged) + (if forward + (< growth 0) + (> growth 0)))) + (setq forward (> growth 0)) + (setq growth + (if (> growth 0) + (+ growth (/ char-size 2)) + (- growth (/ char-size 2))))) + (setq dragged t) - (adjust-window-trailing-edge window growth nil t)) - (setq last-position position))))) + (when charwise + (setq residue (% growth char-size)) + (setq growth (/ growth char-size))) + (unless (zerop growth) + (adjust-window-trailing-edge window growth nil (not charwise))) + (setq last-position (- position residue)) + +;; ;; Debugging code. +;; (message "last %s pos %s growth %s residue %s char-size %s" +;; last-position position growth residue char-size) + + ))))) (old-track-mouse track-mouse)) ;; Start tracking. The special value 'dragging' signals the ;; display engine to freeze the mouse pointer shape for as long ;; as we drag. (setq track-mouse 'dragging) - ;; Loop reading events and sampling the position of the mouse. (setq exitfun (set-transient-map (let ((map (make-sparse-keymap))) commit 704fab0452a403d2419261765242eb295677b245 Author: Eli Zaretskii Date: Fri Sep 26 09:13:10 2025 +0300 ; Improve documentation of handling errors * doc/lispref/control.texi (Processing of Errors): Document that pending input is discarded upon errors. (Bug#79510) diff --git a/doc/lispref/control.texi b/doc/lispref/control.texi index 4346840463d..689e7324d43 100644 --- a/doc/lispref/control.texi +++ b/doc/lispref/control.texi @@ -2034,7 +2034,8 @@ concept of continuable errors. @subsubsection How Emacs Processes Errors @cindex processing of errors -When an error is signaled, @code{signal} searches for an active +When a Lisp program signals an error, it calls the function +@code{signal}, which searches for an active @dfn{handler} for the error. A handler is a sequence of Lisp expressions designated to be executed if an error happens in part of the Lisp program. If the error has an applicable handler, the handler is @@ -2044,9 +2045,16 @@ established it; all functions called within that @code{condition-case} have already been exited, and the handler cannot return to them. If there is no applicable handler for the error, it terminates the -current command and returns control to the editor command loop. (The -command loop has an implicit handler for all kinds of errors.) The -command loop's handler uses the error symbol and associated data to +current command, flushes all pending unprocessed input events +(@pxref{Input Events})@footnote{ +Pending input is discarded because the user could have typed something +in advance under the assumption that execution will proceed without +errors, so keeping these input events would cause unintended +consequences, like inserting stray characters into an unrelated buffer +or execution of some unintended command. +}, and returns control to the editor command loop. +(The command loop has an implicit handler for all kinds of errors.) The +command loop's handler then uses the error symbol and associated data to print an error message. You can use the variable @code{command-error-function} to control how this is done: commit 307f465f7b9042c60b721b16f06a947f01244b70 Author: Eli Zaretskii Date: Mon Sep 22 10:59:17 2025 +0300 ; * doc/lispref/text.texi (Suspicious Text): Fix suspicious address. diff --git a/doc/lispref/text.texi b/doc/lispref/text.texi index ec30efe67a3..36f2ad1ccbc 100644 --- a/doc/lispref/text.texi +++ b/doc/lispref/text.texi @@ -5141,7 +5141,7 @@ suspicious. @var{object} should be a string. @item email-address-header Check whether a full RFC2822 email address header (e.g., -@samp{=?utf-8?Q?=C3=81?= }) looks suspicious. +@samp{=?utf-8?Q?=E2=84=AB?= }) looks suspicious. @var{object} should be a string. @end table commit 6bedbafc7ad60a9c75bf6f7ee9171408dcc3911f Author: Eli Zaretskii Date: Mon Sep 22 08:33:01 2025 +0300 ; * doc/lispref/text.texi (Suspicious Text): Indexing fix. diff --git a/doc/lispref/text.texi b/doc/lispref/text.texi index dc35e93f2da..ec30efe67a3 100644 --- a/doc/lispref/text.texi +++ b/doc/lispref/text.texi @@ -5090,6 +5090,7 @@ having an @acronym{HTML} link text that says one thing, while the underlying @acronym{URL} points somewhere else. @cindex suspicious text strings +@cindex confusable characters To help identify these @dfn{suspicious text strings}, Emacs provides a library to do a number of checks on text. (See @url{https://www.unicode.org/reports/tr39/, UTS #39: Unicode Security