commit 24535f95c8f04397187952390b59ef39b594c0c4 (HEAD, refs/remotes/origin/master) Author: Elías Gabriel Pérez Date: Sun Nov 9 12:32:53 2025 -0600 ; * lisp/progmodes/hideshow.el (hs-minor-mode, hs-grok-mode-type): Fix code. diff --git a/lisp/progmodes/hideshow.el b/lisp/progmodes/hideshow.el index 77b3df54360..affcdd475e3 100644 --- a/lisp/progmodes/hideshow.el +++ b/lisp/progmodes/hideshow.el @@ -1058,7 +1058,7 @@ adjust-block-beginning function." (hs--set-variable 'hs-block-end-regexp 2) (hs--set-variable 'hs-c-start-regexp 3 (string-trim-right (regexp-quote comment-start))) - (hs--set-variable 'hs-forward-sexp-function 4 #'forward-sexp) + (hs--set-variable 'hs-forward-sexp-function 4) (hs--set-variable 'hs-adjust-block-beginning-function 5) (hs--set-variable 'hs-find-block-beginning-function 6) (hs--set-variable 'hs-find-next-block-function 7) @@ -1381,12 +1381,12 @@ Key bindings: :keymap hs-minor-mode-map (setq hs-headline nil) - (unless (and comment-start comment-end) - (setq hs-minor-mode nil) - (user-error "%S doesn't support Hideshow Minor Mode" major-mode)) - (if hs-minor-mode (progn + (unless (and comment-start comment-end) + (setq hs-minor-mode nil) + (user-error "%S doesn't support Hideshow Minor Mode" major-mode)) + ;; Set the variables (hs-grok-mode-type) ;; Turn off this mode if we change major modes. commit 60cadb7d18c4f5b1edb0af7343aa6fb3ef2a9600 Author: Elías Gabriel Pérez Date: Mon Nov 10 09:21:00 2025 +0200 * lisp/progmodes/grep.el (grep-menu-map): Add "Edit Grep Buffer" (bug#79793). diff --git a/lisp/progmodes/grep.el b/lisp/progmodes/grep.el index f91cd2cc400..ab131fddf9d 100644 --- a/lisp/progmodes/grep.el +++ b/lisp/progmodes/grep.el @@ -341,7 +341,9 @@ This keymap inherits from `compilation-minor-mode-map'." :help "Kill the currently running grep process"] "----" ["Toggle command abbreviation" grep-find-toggle-abbreviation - :help "Toggle showing verbose command options"])) + :help "Toggle showing verbose command options"] + ["Edit Grep Buffer" grep-change-to-grep-edit-mode + :help "Edit the *grep* buffer and apply changes to the original buffers."])) (defvar grep-mode-tool-bar-map ;; When bootstrapping, tool-bar-map is not properly initialized yet, commit 6a724b396386f7dfe153617fdaac50ca305deecc Author: Po Lu Date: Mon Nov 10 12:16:53 2025 +0800 Port Android build system to Autoconf < 2.70 * m4/ndk-build.m4 (ndk_INIT): Don't suppose that $ac_aux_dir will be terminated with a trailing slash on Autoconf 2.69 and earlier. diff --git a/m4/ndk-build.m4 b/m4/ndk-build.m4 index c02e9119d1b..45b111b238d 100644 --- a/m4/ndk-build.m4 +++ b/m4/ndk-build.m4 @@ -45,7 +45,13 @@ for file in $with_ndk_path; do done AC_REQUIRE_AUX_FILE([ndk-build-helper.mk]) -ndk_AUX_DIR=$ac_aux_dir +m4_if(m4_version_compare(m4_defn([AC_AUTOCONF_VERSION]), [2.70]), [-1], + dnl $ac_aux_dir is an internal variable in 2.69 that + dnl is not guaranteed to be terminated with a separator character. + [AS_CASE([$ac_aux_dir], + [*/], [ndk_AUX_DIR=$ac_aux_dir], + [ndk_AUX_DIR=$ac_aux_dir/])], + [ndk_AUX_DIR=$ac_aux_dir]) ndk_ABI=$1 ndk_MODULES= ndk_MAKEFILES= commit 04520bfd7ef593b77a7fe84ab1f4f4353365b717 Author: Michael R. Mauger Date: Sun Nov 9 19:40:48 2025 -0500 Zone multi-window and -frame support * lisp/play/zone.el: Multi-window and -frame support. (zone): New group. (zone-buffer-name): New constant. (zone-add-program, zone-remove-program): New functions. * : User configuration (zone-all-frames, zone-all-windows-in-frame) (zone-delete-other-windows): New boolean options. (zone-time-elapsed-while-zoning): New var. (zone-start-hook, zone-finish-hook): New hooks. * : Preserve frame configuration (zone-frame-configuration-alist): New Alist of cursor type and window configuration per frame. (zone--save-frame-configuration) (zone--restore-frame-configuration) (zone--restore-all-frame-configuration): New internal functions to restore windows and frames. * : Rewrite/modularization of zone logic (zone): Refactor function. (zone--buffer-empty-p, zone--buffer-encrypted-p): New functions. (zone--choose-window-and-buffer): New function. (zone-ignored-buffers, zone--buffer-zoneable-p): New var and function. (zone--build-zone-buffer): New function to create zone buffer. (zone--prepare-frames): New function to configure multi-frames and -windows. (zone--apologize-seconds, zone--apologize-for-failing): New var and function when zone fails. diff --git a/etc/NEWS b/etc/NEWS index 3b66f4baff2..7168cc5bb92 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -3007,6 +3007,92 @@ CPerl mode creates imenu entries for ":writer" generated accessors and recognizes the new functions "all" and "any". See https://perldoc.perl.org/5.42.0/perldelta for details. +** Zone +Zone can scramble multiple windows across multiple frames; it may also +reorganize frames to be a single window. As before, when a key or mouse +event occurs, all of the frames and windows are restored to their +original state. This is controlled by three new customization flags +which control the use of frames and windows beyond the currently active +ones. It is identifies suitable buffers for zoning out so that +potentially important buffer contents are not exposed. + +When a zone program encounters an error, it will apologize for a minute +and then start a new round of zoning. Previously, it just kept +apologizing. Finally, zone does not pollute the *Messages* buffer with +extraneous messages. + +**** New function 'zone-add-program' +This function accepts a symbol, whose name starts with "zone-pgm-", that +runs a zone program when 'zone' is invoked. It adds the program to the +'zone-programs' vector if it is not already present.'' + +**** New functipon 'zone-remove-program' +This function removes a program from the 'zone-programs' vector. If the +parameter is a symbol, and the symbol is present in the vector, it is +removed. If the parameter is a string, it is a regular expression that +will remove any programs whose name matches the parameter pattern. + +*** Multi-window and -frame customization options +Prior to this update, zone would only (safely) scramble the contents of +the current window on the current frame. Now, with the use of these +three options, it is possible to scramble to contents of all windows +across all frames. It is also possible to make frames single window +displays while zoning. But when zoning is interrupted by a key- or +mouse-press, the original window layout across all frames is restored. + +**** New user option 'zone-delete-other-windows' +When non-nil, the frame is made into a single full frame window to hold +the zoned buffer. If all frames were to be used (`zone-all-frames' set +to non-nil), then all frames are converted to single window frames. + +**** New user option 'zone-all-frames' +When non-nil, zone will appear on all visible frames. While the buffer +scrambling will appear on each frame, it will be the same buffer so they +will all behave the same way. + +**** New user option 'zone-all-windows-in-frame' +When non-nil, the zoned buffer will be mapped to all of the windows +present on the frame. If the option is nil, then only the selected +window will show the zoned buffer. Note, however, that each window +holding the zoned buffer is showing the same zoned buffer. + +*** Selecting source buffers suitable for zoning +When the idle timer, or the user, invokes `zone', the current buffer may +not be appropriate as the source of the zone buffer. For example, +encrypted buffers, empty buffers, or specialized buffers like +`*Messages*' probably shouldn't have their content splashed across zoned +windows. So the selection of a suitable buffer for zoning can be controlled with a variable that identifies buffers with concerns. + +**** New variable 'zone-ignored-buffers' +The variable is a list of criteria for excluding a buffer from +consideration as the source of zoning. The list has entries that are +tested against each buffer until a suitable one is found. The criteria +can be a symbol that ends in `-mode' which excludes buffers that are in +a mode derived from the specified mode. It may also be a function-bound +symbol or lambda expression that is called with a buffer that returns a +non-nil value if it should not be the zone source. Finally, an entry +can also be a regular expression string that must not match the buffer's +name. + +Initially, the list excludes buffers in a special-mode, in an +image-mode, contains an encrypted file, is an empty buffer, is a hidden +buffer, or is the `*scratch*' buffer. If it cannot locate any +acceptable buffers, it will begrudgingly use the scratch buffer. + +*** Zone hooks +Hooks have been added to notify programs of the start and end of zone activity. For example, you may want to credit time spent zoning as a Pomodoro break. + +**** New hook 'zone-start-hook' +This hook contains functions that are invoked when zoning is about to begin. + +**** New hook 'zone-finish-hook' +This hook contains functions that are invoked when zoning has finished. + +**** New variable 'zone-time-elapsed-while-zoning' +This is the elapsed time between the start and finish hooks. So this +represents how long Emacs was zoning. Zone calculates this so that the +finish hook can communicate this to other modes if necessary. + * New Modes and Packages in Emacs 31.1 diff --git a/lisp/play/zone.el b/lisp/play/zone.el index 5f817c10371..0d33a9783d6 100644 --- a/lisp/play/zone.el +++ b/lisp/play/zone.el @@ -36,6 +36,14 @@ ;;; Code: +(defgroup zone nil + "Zone related settings." + :prefix "zone-" + :group 'play) + +(defconst zone-buffer-name "*zone*" + "Name of the zone buffer that holds zoned text.") + (defvar zone-timer nil "The timer we use to decide when to zone out, or nil if none.") @@ -71,11 +79,32 @@ If nil, don't interrupt for about 1^26 seconds.") zone-pgm-random-life ]) +(defun zone-add-program (pgm) + "Add a zone program PGM to `zone-programs'." + (unless (seq-contains-p zone-programs pgm #'eq) + (setq zone-programs (vconcat zone-programs (list pgm))))) + +(defun zone-remove-program (pgm) + "Remove a zone program PGM from `zone-programs'. +If PGM is a symbol, remove it from the `zone-programs'; if it is a +string, assume it is a regular expression that will remove programs +whose name matches the pattern." + (setq zone-programs + (vector (seq-remove + (lambda (v) + (cond + ((symbolp pgm) (eq pgm v)) + ((stringp pgm) (string-match-p pgm (symbol-name v))) + (t nil))) + zone-programs)))) + (defmacro zone-orig (&rest body) + "Perform BODY in the original source buffer of the zone buffer." `(with-current-buffer (get 'zone 'orig-buffer) ,@body)) (defmacro zone-hiding-mode-line (&rest body) + "Perform BODY without a window mode line." ;; This formerly worked by temporarily altering face `mode-line', ;; which did not even work right, it seems. `(let (mode-line-format) @@ -102,6 +131,79 @@ If the element is a function or a list of a function and a number, (t (error "Bad `zone-call' elem: %S" elem)))) program)))) +;;;; Customization flags to control what is zoned + +(defcustom zone-all-frames nil + "When non-nil, zone in all open frames. +Displays the `*zone*' buffer in all windows in all frames." + :type 'boolean) + +(defcustom zone-all-windows-in-frame nil + "When non-nil, zone in all windows in the current frame." + :type 'boolean) + +(defcustom zone-delete-other-windows nil + "When non-nil, make the frame a single window before zoning. +The original windows and their content will be restored when zoning +completes." + :type 'boolean) + +;;;; Hooks to detect the start and finish of zone activity + +(defvar zone-time-elapsed-while-zoning nil + "In the `zone-finish-hook', will report the time spent zoning.") + +(defvar zone-start-hook nil + "Hook at the start of zoning.") + +(defvar zone-finish-hook nil + "Hook at the finish of Zoning. +When this is invoked, `zone-time-elapsed-while-zoning' will be properly set.") + +;;;; Save frame configuration so it can be restored when we finish zoning + +(defvar zone-frame-configuration-alist nil + "An Alist of frames and their cursor and window configuration. + +Before zone starts using a frame, it saves the configuration before it +touches anything. At the end of zoning, the frame configuration is +restored. + +The Alist key is the frame object, then value is the cons cell +containing the window configuration and cursor type.") + +(defun zone--save-frame-configuration (frm &optional reset) + "Save the frame FRM's configuration. + +When RESET is non-nil, the `zone-frame-configuration-alist' will contain +this frame only, otherwise the frame's configuration will be appended to +the Alist." + (when reset + (setq zone-frame-configuration-alist nil)) + (when (frame-visible-p frm) + (push (cons frm + (cons + (current-window-configuration frm) + (frame-parameter frm 'cursor-type))) + zone-frame-configuration-alist))) + +(defun zone--restore-frame-configuration (frm) + "Restore the frame FRM's configuration from the Alist." + (when-let* ((config (alist-get frm zone-frame-configuration-alist))) + (with-selected-frame frm + (set-window-configuration (car config)) + (modify-frame-parameters frm (list (cons 'cursor-type (cdr config))))))) + +(defun zone--restore-all-frame-configurations () + "Restore all of the saved frame configurations." + (mapc #'zone--restore-frame-configuration + (mapcar #'car zone-frame-configuration-alist)) + (setq zone-frame-configuration-alist nil) + (when (get-buffer zone-buffer-name) + (kill-buffer zone-buffer-name))) + +;;;; Here we zone... + ;;;###autoload (defun zone (&optional pgm) "Zone out, completely. @@ -121,62 +223,230 @@ run a specific program. The program must be a member of (list (intern (concat "zone-pgm-" choice)))))) (unless pgm (setq pgm (aref zone-programs (random (length zone-programs))))) - (save-window-excursion - (let ((f (selected-frame)) - (outbuf (get-buffer-create "*zone*")) - (text (buffer-substring (window-start) (window-end nil t))) - (wp (1+ (- (window-point) - (window-start))))) - (put 'zone 'orig-buffer (current-buffer)) - (switch-to-buffer outbuf) - (setq mode-name "Zone") + (run-hooks 'zone-start-hook) + (let* ((start-time (current-time)) + (zone-again nil) + (src-winbuf (zone--choose-window-and-buffer)) + (src-win (car src-winbuf)) + (src-buf (cdr src-winbuf)) + (src-frm (window-frame src-win))) + (setq zone-frame-configuration-alist nil) + (unwind-protect + (progn + (zone--save-frame-configuration src-frm) + (zone--build-zone-buffer src-win src-buf) + (zone--prepare-frames src-frm) + (condition-case zone-err + (progn + (message "Zoning... (%s)" pgm) + (garbage-collect) + ;; If some input is pending, zone says "sorry", which + ;; isn't nice; this might happen e.g. when they invoke the + ;; game by clicking the menu bar. So discard any pending + ;; input before zoning out. + (if (input-pending-p) + (discard-input)) + (zone-call pgm) + (message "Zoning...sorry")) + + (error + (message "%s error: %S" (or pgm 'zone) zone-err) + (zone--apologize-for-failing pgm) + (setq zone-again t)) + + (quit + (ding) + (message "Zoning...sorry")))) + (zone--restore-all-frame-configurations)) + (when (and zone-again + (not (input-pending-p))) + (zone)) + (setq zone-time-elapsed-while-zoning (time-since start-time))) + (run-hooks 'zone-finish-hook)) + +;;;; Identify the current window and the best buffer to use as zone source + +(defun zone--buffer-empty-p (buffer) + "Is BUFFER empty?" + (zerop (buffer-size buffer))) + +(defun zone--buffer-encrypted-p (buffer) + "Is BUFFER encrypted with `epa'?" + (require 'epa-hook) + (when-let* ((name (buffer-file-name buffer))) + (epa-file-name-p name))) + +(defun zone--choose-window-and-buffer () + "Choose the current window and an acceptable buffer. +Check each buffer to determine whether it is suitable for zoning, +starting with the buffer in the current window. For example, encrypted +files, certain source modules, or command sessions may be inappropriate +if they might expose privileged or secret information." + (cons + (selected-window) + (or (seq-find #'zone--buffer-zoneable-p + (buffer-list (selected-frame))) + ;; only create *scratch* if we need one as fall back + (get-scratch-buffer-create)))) + +(defvar zone-ignored-buffers + '( "\\`\s" ;; Hidden buffers + zone--buffer-empty-p ;; Empty buffers (not very interesting) + special-mode ;; Special/internal buffers + image-mode ;; image buffers + authinfo-mode ;; encrypted buffers + zone--buffer-encrpted-p + "\\`\\*scratch\\*\\'" ;; zone will fallback to scratch , but + ;; ignore it in the first pass + ) + "Buffers that satisfy any of these rules are ignored as a zone buffer. +Each entry in the list must be one of the following: + ++ MODE: all derived modes of the MODE are considered unacceptable, ++ REGEXP: a buffer name that matches the REGEXP is not acceptable, and ++ PRED: a buffer that satisfies the PRED, such that it returns a non-nil + value when invoked as a function with the buffer supplied as a + parameter, is considered not acceptable.") + +(defun zone--buffer-zoneable-p (buffer) + "Is BUFFER suitable for zoning? +For example, buffers containing passwords, critical source files, and +command line transcripts might not be appropriate for a zone buffer. + +To be acceptable, the buffer must NOT satisfy any of the entries on +`zone-ignored-buffers'." + (not (any + (lambda (ign) + (cond + ((stringp ign) + (string-match-p ign (buffer-name buffer))) + ((and (symbolp ign) + (string-suffix-p "-mode" (symbol-name ign))) + (with-current-buffer buffer + (derived-mode-p ign))) + ((functionp ign) + (funcall ign buffer)))) + zone-ignored-buffers))) + +;;;; Prepare the *zone* buffer with a copy of the source buffer + +(defun zone--build-zone-buffer (win buf) + "Construct the *zone* buffer in window WIN, based on buffer BUF. +Remove other windows if `zone-delete-other-windows' is non-nil. The +selected buffer is then placed in the window. If only a portion of the +buffer is visible, try to recenter it to expose more." + ;; Make us single window if desired + (when zone-delete-other-windows + (delete-other-windows win)) + ;; Switch in the source buffer into the window + (unless (eq (current-buffer) buf) + (set-window-buffer win buf nil)) + ;; Try to scroll the buffer into the window + (unless (< (window-end) (point-max)) + (let ((scroll-margin 0)) + (recenter -1))) + (redisplay) + ;; Create the zone buffer and populate it + (let* ((win-end (window-end win t)) + (win-beg (window-start)) + (win-pt (window-point)) + (new-pt (1+ (- win-pt win-beg))) + (win-ht (line-pixel-height))) + (with-current-buffer (get-buffer-create zone-buffer-name) + (put 'zone 'orig-buffer buf) (erase-buffer) - (setq buffer-undo-list t - truncate-lines t - tab-width (zone-orig tab-width) - line-spacing (zone-orig line-spacing)) - (insert text) - (untabify (point-min) (point-max)) - (set-window-start (selected-window) (point-min)) - (set-window-point (selected-window) wp) - (sit-for 0.500) - (let ((ct (and f (frame-parameter f 'cursor-type))) - (show-trailing-whitespace nil) - restore) - (when ct - (modify-frame-parameters f '((cursor-type . (bar . 0))))) - ;; Make `restore' a self-disabling one-shot thunk. - (setq restore - (lambda () - (when ct - (modify-frame-parameters - f (list (cons 'cursor-type ct)))) - (kill-buffer outbuf) - (setq restore nil))) - (condition-case nil - (progn - (message "Zoning... (%s)" pgm) - (garbage-collect) - ;; If some input is pending, zone says "sorry", which - ;; isn't nice; this might happen e.g. when they invoke the - ;; game by clicking the menu bar. So discard any pending - ;; input before zoning out. - (if (input-pending-p) - (discard-input)) - (zone-call pgm) - (message "Zoning...sorry")) - (error - (funcall restore) - (while (not (input-pending-p)) - (message "We were zoning when we wrote %s..." pgm) - (sit-for 3) - (message "...here's hoping we didn't hose your buffer!") - (sit-for 3))) - (quit - (funcall restore) - (ding) - (message "Zoning...sorry"))) - (when restore (funcall restore)))))) + (setq-local mode-name "Zone" + buffer-undo-list t + truncate-lines t + scroll-margin 0 + scroll-conservatively 1000 + scroll-up-aggressively 0 + scroll-down-aggressively 0 + show-trailing-whitespace nil + tab-width (buffer-local-value 'tab-width buf) + line-spacing (buffer-local-value 'line-spacing buf)) + ;; Grab the visible portion of the source buffer + (insert-buffer-substring buf win-beg win-end) + ;; Remove read-only property so zone can play with all of it + (let ((inhibit-read-only t) + (beg (point-min)) + (end (point-max))) + (remove-text-properties beg end '(read-only nil)) + ;; Adjust line height with the settings from the original buffer + (add-text-properties beg end `(line-height ,win-ht)) + ;; Get rid of tab characters and position the window + (untabify beg end))) + ;; Move the zone buffer to the window + (set-window-buffer win zone-buffer-name nil) + ;; Position the zone buffer in the window + ;; Position point and then fix the top. + ;; These with scroll settings above fix + ;; the content boundaries in the window + (set-window-point win new-pt) + (set-window-start win (point-min)) + (redisplay))) + +;;;; Configure frames and windows based on customization flags + +(defun zone--prepare-frames (prim-frm) + "Reorganize frames and their windows to show the zone out. +This is based on the settings of three customization flags: ++ `zone-all-frames', ++ `zone-all-windows-in-frame', and ++ `zone-delete-other-windows' + +These have been partially performed on the primary frame PRIM-FRM. +Based on the settings, the other frames may be similarly adjusted." + (let* ((z (get-buffer zone-buffer-name)) + (prim-win (frame-selected-window prim-frm)) + (f prim-frm) + (no-cursor '((cursor-type . (bar . 0)))) + w1) + ;; Handle the primary frame + (select-frame f t) + (setq w1 (frame-selected-window f)) + ;; Put zone in current or every window + (dolist (w (if zone-all-windows-in-frame + (window-list f 'no-minibuf w1) + (list w1))) + (set-window-buffer w z nil)) + (modify-frame-parameters f no-cursor) + ;; Handle the remaining frames + (dolist (f (visible-frame-list)) + (unless (eq f prim-frm) + (select-frame f t) + (setq w1 (frame-selected-window f)) + ;; Single window frame + (when zone-delete-other-windows + (delete-other-windows)) + ;; Put zone in current or every window + (dolist (w (if zone-all-windows-in-frame + (window-list f 'no-minibuf w1) + (list w1))) + (set-window-buffer w z nil)) + (modify-frame-parameters f no-cursor) + (set-frame-selected-window f w1 t))) + (select-frame prim-frm) + (set-frame-selected-window prim-frm prim-win t))) + +;;;; If the zone program fails, apologize and try again + +(defvar zone-apologize-seconds 60 ;; 1 minute + "Number of seconds to apologize for failing. +This value is broken into 6 second cycles to allow for two messages +displayed for 3 seconds each in every cycle.") + +(defun zone--apologize-for-failing (pgm) + "Apologize for PGM failing for a minute." + (let ((cycle 0) + (n-cycles (floor zone-apologize-seconds 6))) + (while (and (not (input-pending-p)) + (<= (incf cycle) n-cycles)) + (let ((message-log-max (= cycle 1))) ;; Log message first time only + (message "We were zoning when we wrote %s..." pgm) + (sit-for 3) + (message "...here's hoping we didn't hose your buffer!") + (sit-for 3))))) ;;;; Zone when idle, or not. @@ -598,7 +868,8 @@ run a specific program. The program must be a member of lines (cons (buffer-substring p (point)) lines)))) (sit-for 5) (zone-hiding-mode-line - (let ((msg "Zoning... (zone-pgm-stress)")) + (let ((message-log-max nil) + (msg "Zoning... (zone-pgm-stress)")) (while (not (string= msg "")) (message (setq msg (substring msg 1))) (sit-for 0.05))) @@ -609,7 +880,8 @@ run a specific program. The program must be a member of (delete-region (point) (line-beginning-position 2)) (goto-char (point-min)) (insert (seq-random-elt lines))) - (message (concat (make-string (random (- (frame-width) 5)) ? ) "grrr")) + (let ((message-log-max nil)) + (message (concat (make-string (random (- (frame-width) 5)) ? ) "grrr"))) (sit-for 0.1))))) (defun zone-pgm-stress-destress () commit d2bc774ec9c7bcdd47d3f893a690ca5683ce9a08 Author: Michael Heerdegen Date: Tue Oct 21 17:42:37 2025 +0200 ; * lisp/progmodes/hideshow.el: Spelling fixes (bug#79585) diff --git a/lisp/progmodes/hideshow.el b/lisp/progmodes/hideshow.el index c8b4e68f00f..77b3df54360 100644 --- a/lisp/progmodes/hideshow.el +++ b/lisp/progmodes/hideshow.el @@ -293,11 +293,12 @@ line belongs. If set to `after-cursor', hide the block after cursor position. -This only have effect in `hs-hide-block' and `hs-toggle-hiding' +This only has effect in `hs-hide-block' and `hs-toggle-hiding' commands." - :type '(choice - (const :tag "Hide the block after cursor" after-bol) - (const :tag "Hide the block after beginning of current line" after-cursor)) + :type + '(choice + (const :tag "Hide the block after cursor" after-cursor) + (const :tag "Hide the block after beginning of current line" after-bol)) :version "31.1") (defcustom hs-display-lines-hidden nil @@ -343,7 +344,7 @@ The possible values can be: - `margin', display the indicators in the margin. - nil, display the indicators at end-of-line. -This only have effect if `hs-show-indicators' is non-nil." +This only has effect if `hs-show-indicators' is non-nil." :type '(choice (const :tag "Fringes" fringe) (const :tag "Margins" margin) @@ -471,11 +472,11 @@ Use the command `hs-minor-mode' to toggle or set this variable.") :doc "Keymap for hideshow minor mode." "S-" #'hs-toggle-hiding "C-c @" hs-prefix-map - " " #'hs-indicator-mouse-toggle-hidding) + " " #'hs-indicator-mouse-toggle-hiding) (defvar-keymap hs-indicators-map :doc "Keymap for hideshow indicators." - " " #'hs-indicator-mouse-toggle-hidding + " " #'hs-indicator-mouse-toggle-hiding "" #'hs-toggle-hiding) (easy-menu-define hs-minor-mode-menu hs-minor-mode-map @@ -507,10 +508,12 @@ Use the command `hs-minor-mode' to toggle or set this variable.") :help "Show hidden comment blocks when isearch matches inside them" :active t :style radio :selected (eq hs-isearch-open 'comment)] ["Code and Comment blocks" (setq hs-isearch-open t) - :help "Show both hidden code and comment blocks when isearch matches inside them" + :help "\ +Show both hidden code and comment blocks when isearch matches inside them" :active t :style radio :selected (eq hs-isearch-open t)] ["None" (setq hs-isearch-open nil) - :help "Do not hidden code or comment blocks when isearch matches inside them" + :help "\ +Do not show hidden code or comment blocks when isearch matches inside them" :active t :style radio :selected (eq hs-isearch-open nil)]))) (defvar hs-hide-all-non-comment-function nil @@ -622,7 +625,7 @@ It should reposition point at next block start. It is called with three arguments REGEXP, MAXP, and COMMENTS. REGEXP is a regexp representing block start. When block start is found, `match-data' should be set using REGEXP. MAXP is a buffer -position that bounds the search. When COMMENTS is nil, comments +position that limits the search. When COMMENTS is nil, comments should be skipped. When COMMENTS is not nil, REGEXP matches not only beginning of a block but also beginning of a comment. In this case, the function should find nearest block or comment. @@ -726,7 +729,7 @@ to call with the newly initialized overlay." (defun hs-block-positions () "Return the current code block positions. -This return a cons-cell with the current code block beginning and end +This returns a cons-cell with the current code block beginning and end positions. This does nothing if there is not a code block at current point." (save-match-data @@ -815,8 +818,8 @@ point." (_ (save-excursion (goto-char b-beg) (funcall hs-looking-at-block-start-predicate))) - ;; `catch' is used here if the search fail due - ;; unbalanced parenthesis or any other unknown error + ;; `catch' is used here if the search fails due + ;; unbalanced parentheses or any other unknown error ;; caused in `hs-forward-sexp'. (b-end (catch 'hs-indicator-error (save-excursion @@ -833,7 +836,7 @@ point." `(jit-lock-bounds ,beg . ,end)) (defun hs--refresh-indicators (from to) - "Update indicators appearance in FROM and TO." + "Update indicator appearance in FROM and TO." (when (and hs-show-indicators hs-minor-mode) (save-match-data (save-excursion @@ -945,7 +948,7 @@ a comment. The block beginning is adjusted by `hs-adjust-block-beginning-function' and then further adjusted to be at the end of the line. -If hidding the block is successful, return non-nil. +If hiding the block is successful, return non-nil. Otherwise, return nil." (if comment-reg (hs-hide-comment-region (car comment-reg) (cadr comment-reg) end) @@ -1247,8 +1250,8 @@ Upon completion, point is repositioned and the normal hook (line-end-position) nil)) (goto-char (match-beginning 0))) (funcall hs-looking-at-block-start-predicate)) - ;; If hidding the block fails (due the block is not hideable) - ;; Then just hide the parent block (if possible) + ;; If hiding the block fails (due the block is not hideable) + ;; then just hide the parent block (if possible) (unless (save-excursion (hs-hide-block-at-point end)) (goto-char (1- (point))) (funcall hs-find-block-beginning-function) @@ -1320,10 +1323,11 @@ Argument E should be the event that triggered this action." (hs-show-block) (hs-hide-block)))) -(define-obsolete-function-alias 'hs-mouse-toggle-hiding #'hs-toggle-hiding "27.1") +(define-obsolete-function-alias + 'hs-mouse-toggle-hiding #'hs-toggle-hiding "27.1") -(defun hs-indicator-mouse-toggle-hidding (event) - "Toggle block hidding with indicators." +(defun hs-indicator-mouse-toggle-hiding (event) + "Toggle block hiding with indicators." (interactive "e") (hs-life-goes-on (when hs-show-indicators commit 41701df12a9a0a72b0f68dffdcd69c8ab8f40f7c Author: Juri Linkov Date: Sun Nov 9 20:48:19 2025 +0200 * lisp/progmodes/flymake.el (flymake-start): Use 'when-let*' (bug#79796). This fixes the case when 'flymake-autoresize-margins' is nil, but 'flymake--suitably-fringed-p' returns nil and still resizes margins. diff --git a/lisp/progmodes/flymake.el b/lisp/progmodes/flymake.el index 60a6bacf640..460969dd9a4 100644 --- a/lisp/progmodes/flymake.el +++ b/lisp/progmodes/flymake.el @@ -1420,10 +1420,11 @@ Interactively, with a prefix arg, FORCE is t." (flymake-mode ;; The buffer about to be annotated is visible. Check ;; necessary conditions to auto-set margins here (bug#77313) - (let* ((w (and (eq flymake-indicator-type 'auto) - flymake-autoresize-margins - (visible-buffer-window)))) - (unless (flymake--suitably-fringed-p w) (flymake--resize-margins))) + (when-let* ((w (and (eq flymake-indicator-type 'auto) + flymake-autoresize-margins + (visible-buffer-window)))) + (unless (flymake--suitably-fringed-p w) + (flymake--resize-margins))) (setq flymake-check-start-time (float-time)) (let ((backend-args (and commit e2531721b45868398369ef738641e5fb8ddd59fc Author: Juri Linkov Date: Sun Nov 9 20:14:12 2025 +0200 * lisp/vc/diff-mode.el (diff-changed-unspecified): Restore old colors. Restore yellow colors previously used in the 'diff-changed' face. Copy color values from 'smerge-base'. This will distinguish them from grey 'diff-file-header' and 'diff-hunk-header' in context diffs. diff --git a/lisp/vc/diff-mode.el b/lisp/vc/diff-mode.el index 5c6db195807..f3810f7754d 100644 --- a/lisp/vc/diff-mode.el +++ b/lisp/vc/diff-mode.el @@ -410,11 +410,11 @@ well." '((default :inherit diff-changed) (((class color) (min-colors 88) (background light)) - :background "grey90" :extend t) + :background "#ffffaa" :extend t) (((class color) (min-colors 88) (background dark)) - :background "grey20" :extend t) + :background "#888833" :extend t) (((class color)) - :foreground "grey" :extend t)) + :foreground "yellow" :extend t)) "`diff-mode' face used to highlight changed lines." :version "28.1") commit 1f32ff090abfdabe09c5ce1cd3ff31167038729e Author: Juri Linkov Date: Sun Nov 9 19:49:29 2025 +0200 * lisp/simple.el (minibuffer-default-add-shell-commands): Fix old bug. Don't treat the first element of the list of commands as a file name. diff --git a/lisp/simple.el b/lisp/simple.el index 8d7237e58c6..fdf8b79444d 100644 --- a/lisp/simple.el +++ b/lisp/simple.el @@ -4317,8 +4317,7 @@ stdout will be intermixed in the output stream.") This function is used to add all related commands retrieved by `shell-command-guess' to the end of the list of defaults just after the default value." - (let* ((filename (if (listp minibuffer-default) - (car minibuffer-default) + (let* ((filename (unless (consp minibuffer-default) minibuffer-default)) (commands (and filename (require 'dired-aux) (shell-command-guess (list filename))))) commit 79e1226238e61dcc356276ba6232c34944e64c40 Author: Eli Zaretskii Date: Sun Nov 9 18:52:32 2025 +0200 * src/w32fns.c (Fw32_system_idle_time): New function. diff --git a/src/w32fns.c b/src/w32fns.c index 3fc0f55244f..679e8197fd8 100644 --- a/src/w32fns.c +++ b/src/w32fns.c @@ -10983,6 +10983,31 @@ DEFUN ("w32-set-wallpaper", Fw32_set_wallpaper, Sw32_set_wallpaper, 1, 1, 0, return Qnil; } + +/* Return time in milliseconds since the last input event. */ +typedef BOOL (WINAPI *GetLastInputInfo_Proc) (PLASTINPUTINFO); +static GetLastInputInfo_Proc get_last_input_info_fn = NULL; + +DEFUN ("w32-system-idle-time", Fw32_system_idle_time, Sw32_system_idle_time, + 0, 0, 0, + doc: /* Return the time in milliseconds since last system-wide input event. + +Return -1 if the required system API is not available or fails. */) + (void) +{ + LASTINPUTINFO info; + info.cbSize = sizeof info; + + if (get_last_input_info_fn && get_last_input_info_fn (&info)) + { + DWORD time_since_last_input = GetTickCount () - info.dwTime; + if (time_since_last_input > EMACS_INT_MAX) + return Vmost_positive_fixnum; + return make_fixnum (time_since_last_input); + } + return make_fixnum (-1); +} + #endif /*********************************************************************** @@ -11467,6 +11492,7 @@ keys when IME input is received. */); #ifdef WINDOWSNT defsubr (&Ssystem_move_file_to_trash); defsubr (&Sw32_set_wallpaper); + defsubr (&Sw32_system_idle_time); #endif DEFSYM (Qnot_useful, "not-useful"); @@ -11764,6 +11790,8 @@ globals_of_w32fns (void) #ifndef CYGWIN system_parameters_info_w_fn = (SystemParametersInfoW_Proc) get_proc_addr (user32_lib, "SystemParametersInfoW"); + get_last_input_info_fn = (GetLastInputInfo_Proc) + get_proc_addr (user32_lib, "GetLastInputInfo"); #endif RegisterTouchWindow_fn = (RegisterTouchWindow_proc) get_proc_addr (user32_lib, commit 4fab1265f00c5e6f0d5c1a07261b4d0506d071be Author: Sean Whitton Date: Sun Nov 9 14:56:07 2025 +0000 ; vc-do-command: Delete spurious with-current-buffer * lisp/vc/vc-dispatcher.el (vc-do-command): Delete spurious with-current-buffer. diff --git a/lisp/vc/vc-dispatcher.el b/lisp/vc/vc-dispatcher.el index a4cf42ff6a9..8d23ab13edd 100644 --- a/lisp/vc/vc-dispatcher.el +++ b/lisp/vc/vc-dispatcher.el @@ -473,9 +473,8 @@ case, and the process object in the asynchronous case." (goto-char (point-min)) (shrink-window-if-larger-than-buffer)) (when-let* (noninteractive - (out (string-trim (buffer-string))) - (_ (not (string-empty-p out)))) - (with-current-buffer buffer + (out (string-trim (buffer-string)))) + (unless (string-empty-p out) (message "%s" out))) (error "Failed (%s): %s" (if (integerp status) commit 782ca15a8866ca4ef4d39ad0762e65fbe3612237 Author: Michael Albinus Date: Sun Nov 9 13:11:49 2025 +0100 Improve handling of Tramp internal shell scripts * doc/misc/tramp.texi (New operations): Mention tramp-expand-script. * lisp/net/tramp-sh.el (tramp-uudecode) (tramp-readlink-file-truename, tramp-perl-file-truename) (tramp-perl-file-name-all-completions) (tramp-shell-file-name-all-completions) (tramp-perl-file-attributes) (tramp-perl-directory-files-and-attributes, tramp-perl-id) (tramp-python-id, tramp-perl-encode, tramp-perl-decode) (tramp-awk-encode, tramp-awk-decode) (tramp-bundle-read-file-names): Indent script for better readability. (tramp-perl-file-name-all-completions) (tramp-shell-file-name-all-completions) (tramp-ls-file-attributes, tramp-bundle-read-file-names): Adapt docstring. (tramp-shell-print-quoted-string): New defconst. (tramp-shell-file-name-all-completions): Use "%b" format specifier. (tramp-bundle-read-file-names): Use "%k" format specifier. (tramp-sh-handle-file-name-all-completions): Don't send `tramp-bundle-read-file-names'. (tramp-expand-script): Adapt docstring. Apply always `format-spec'. Handle also "%b" and "%k" format specifiers. diff --git a/doc/misc/tramp.texi b/doc/misc/tramp.texi index 1193167ffc4..77f73196383 100644 --- a/doc/misc/tramp.texi +++ b/doc/misc/tramp.texi @@ -6848,6 +6848,11 @@ they are kept. Example: @end lisp @end defun +@findex tramp-expand-script +Shell scripts intended for the @code{tramp-sh} backend are used as a +format string. They must observe the restrictions for format +specifiers, as documented in @code{tramp-expand-script}. + @node Traces and Profiles @chapter How to Customize Traces diff --git a/lisp/net/tramp-sh.el b/lisp/net/tramp-sh.el index aa0f6f72c9a..fb5f8a21ceb 100644 --- a/lisp/net/tramp-sh.el +++ b/lisp/net/tramp-sh.el @@ -687,8 +687,8 @@ This list is used for copying/renaming with out-of-band methods. See `tramp-actions-before-shell' for more info.") -(defconst tramp-uudecode - "(echo begin 600 %t; tail -n +2) | uudecode +(defconst tramp-uudecode "\ +(echo begin 600 %t; tail -n +2) | uudecode cat %t rm -f %t" "Shell function to implement `uudecode' to standard output. @@ -698,16 +698,16 @@ we have this shell function. Format specifiers are replaced by `tramp-expand-script', percent characters need to be doubled.") -(defconst tramp-readlink-file-truename - "if %m -h \"$1\"; then echo t; else echo nil; fi +(defconst tramp-readlink-file-truename "\ +if %m -h \"$1\"; then echo t; else echo nil; fi %r \"$1\"" "Shell script to produce output suitable for use with `file-truename' on the remote file system. Format specifiers are replaced by `tramp-expand-script', percent characters need to be doubled.") -(defconst tramp-perl-file-truename - "%p -e ' +(defconst tramp-perl-file-truename "\ +%p -e ' use File::Spec; use Cwd \"realpath\"; @@ -755,8 +755,8 @@ on the remote file system. Format specifiers are replaced by `tramp-expand-script', percent characters need to be doubled.") -(defconst tramp-perl-file-name-all-completions - "%p -e ' +(defconst tramp-perl-file-name-all-completions "\ +%p -e ' $dir = $ARGV[0]; if ($dir ne \"/\") { $dir =~ s#/+$##; @@ -778,20 +778,28 @@ print \")\\n\"; ' \"$1\" %n" "Perl script to produce output suitable for use with `file-name-all-completions' on the remote file system. It returns the -same format as `tramp-bundle-read-file-names'. Format specifiers are -replaced by `tramp-expand-script', percent characters need to be -doubled.") - -(defconst tramp-shell-file-name-all-completions - "cd \"$1\" 2>&1; %l -a %n | while IFS= read file; do - quoted=`echo \"$1/$file\" | sed -e \"s#//#/#g\"` - printf \"%%s\\n\" \"$quoted\" - done | tramp_bundle_read_file_names" +same format as `tramp-bundle-read-file-names'. +Format specifiers are replaced by `tramp-expand-script', percent +characters need to be doubled.") + +(defconst tramp-shell-print-quoted-string "\ +quoted=`echo \"$1\" | sed -e \"s/\\\"/\\\\\\\\\\\\\\\\\\\"/g\"` +printf \"\\\"%%s\\\"\" \"$quoted\"" + "Shell script to print a lispy string. +Format specifiers are replaced by `tramp-expand-script', percent +characters need to be doubled.") + +(defconst tramp-shell-file-name-all-completions "\ +cd \"$1\" 2>&1; %l -a %n | while IFS= read file; do + quoted=`echo \"$1/$file\" | sed -e \"s#//#/#g\"` + printf \"%%s\\n\" \"$quoted\" +done | %b" "Shell script to produce output suitable for use with `file-name-all-completions' on the remote file system. It returns the -same format as `tramp-bundle-read-file-names'. Format specifiers are -replaced by `tramp-expand-script', percent characters need to be -doubled.") +same format as `tramp-bundle-read-file-names', which must be declared on +the remote host as well. +Format specifiers are replaced by `tramp-expand-script', percent +characters need to be doubled.") ;; Perl script to implement `file-attributes' in a Lisp `read'able ;; output. If you are hacking on this, note that you get *no* output @@ -799,8 +807,8 @@ doubled.") ;; end. ;; The device number is returned as "-1", because there will be a virtual ;; device number set in `tramp-sh-handle-file-attributes'. -(defconst tramp-perl-file-attributes - "%p -e ' +(defconst tramp-perl-file-attributes "\ +%p -e ' @stat = lstat($ARGV[0]); if (!@stat) { print \"nil\\n\"; @@ -882,12 +890,11 @@ characters need to be doubled.") (defconst tramp-ls-file-attributes "%s -ild %s \"$1\" || return\n%s -lnd%s %s \"$1\"" "Shell function to produce output suitable for use with `file-attributes' -on the remote file system. -Format specifiers are replaced by `tramp-expand-script', percent -characters need to be doubled.") +on the remote file system. The \"%s\" format specifiers are replaced +when called in `tramp-do-file-attributes-with-ls'.") -(defconst tramp-perl-directory-files-and-attributes - "%p -e ' +(defconst tramp-perl-directory-files-and-attributes "\ +%p -e ' chdir($ARGV[0]) or printf(\"\\\"Cannot change to $ARGV[0]: $''!''\\\"\\n\"), exit(); opendir(DIR,\".\") or printf(\"\\\"Cannot open directory $ARGV[0]: $''!''\\\"\\n\"), exit(); @list = readdir(DIR); @@ -991,8 +998,8 @@ characters need to be doubled.") Format specifiers are replaced by `tramp-expand-script', percent characters need to be doubled.") -(defconst tramp-perl-id - "%p -e ' +(defconst tramp-perl-id "\ +%p -e ' use strict; use warnings; use POSIX qw(getgroups); @@ -1007,8 +1014,8 @@ printf \"uid=%%d(%%s) gid=%%d(%%s) groups=%%s\\n\", Format specifiers are replaced by `tramp-expand-script', percent characters need to be doubled.") -(defconst tramp-python-id - "%y -c ' +(defconst tramp-python-id "\ +%y -c ' import os, pwd, grp; def idform(id): @@ -1043,9 +1050,9 @@ on the remote host. Format specifiers are replaced by `tramp-expand-script', percent characters need to be doubled.") -(defconst tramp-perl-encode - "%p -e ' -# This script contributed by Juanma Barranquero . +;; This script contributed by Juanma Barranquero . +(defconst tramp-perl-encode "\ +%p -e ' use strict; my %%trans = do { @@ -1081,9 +1088,9 @@ while (read STDIN, $data, 54) { Format specifiers are replaced by `tramp-expand-script', percent characters need to be doubled.") -(defconst tramp-perl-decode - "%p -e ' -# This script contributed by Juanma Barranquero . +;; This script contributed by Juanma Barranquero . +(defconst tramp-perl-decode "\ +%p -e ' use strict; my %%trans = do { @@ -1141,8 +1148,8 @@ characters need to be doubled.") Format specifiers are replaced by `tramp-expand-script', percent characters need to be doubled.") -(defconst tramp-awk-encode - "%a '\\ +(defconst tramp-awk-encode "\ +%a '\\ BEGIN { b64 = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\" b16 = \"0123456789abcdef\" @@ -1192,8 +1199,8 @@ characters need to be doubled.") Format specifiers are replaced by `tramp-expand-script', percent characters need to be doubled.") -(defconst tramp-awk-decode - "%a '\\ +(defconst tramp-awk-decode "\ +%a '\\ BEGIN { b64 = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\" } @@ -1219,24 +1226,23 @@ BEGIN { Format specifiers are replaced by `tramp-expand-script', percent characters need to be doubled.") -(defconst tramp-bundle-read-file-names - "echo \"(\" +(defconst tramp-bundle-read-file-names "\ +printf \"(\\n\" while IFS= read file; do - quoted=`echo \"$file\" | sed -e \"s/\\\"/\\\\\\\\\\\\\\\\\\\"/g\"` - printf \"(%%s\" \"\\\"$quoted\\\"\" + printf \"(\"; %k \"$file\" if %q \"$file\"; then printf \" %%s\" t; else printf \" %%s\" nil; fi if %m -r \"$file\"; then printf \" %%s\" t; else printf \" %%s\" nil; fi if %m -d \"$file\"; then printf \" %%s\" t; else printf \" %%s\" nil; fi if %m -x \"$file\"; then printf \" %%s)\\n\" t; else printf \" %%s)\\n\" nil; fi done -echo \")\"" +printf \")\\n\"" "Shell script to check file attributes of a bundle of files. For every file, it returns a list with the absolute file name, and the tests for file existence, file readability, file directory, and file executable. Input shall be read via here-document, otherwise the -command could exceed maximum length of command line. Format specifiers -\"%s\" are replaced before the script is used, percent characters need -to be doubled.") +command could exceed maximum length of command line. +Format specifiers are replaced by `tramp-expand-script', percent +characters need to be doubled.") ;; New handlers should be added here. ;;;###tramp-autoload @@ -2001,9 +2007,6 @@ ID-FORMAT valid values are `string' and `integer'." (tramp-maybe-send-script v tramp-perl-file-name-all-completions "tramp_perl_file_name_all_completions") - ;; Used in `tramp-shell-file-name-all-completions'. - (tramp-maybe-send-script - v tramp-bundle-read-file-names "tramp_bundle_read_file_names") (tramp-maybe-send-script v tramp-shell-file-name-all-completions "tramp_shell_file_name_all_completions")) @@ -4052,79 +4055,91 @@ Fall back to normal file name handler if no Tramp handler exists." \"%a\", \"%h\", \"%l\", \"%m\", \"%o\", \"%p\", \"%q\", \"%r\", \"%s\" and \"%y\" format specifiers are replaced by the respective `awk', `hexdump', `ls', `test', od', `perl', `test -e', `readlink', `stat' and -`python' commands. \"%n\" is replaced by \"2>/dev/null\", and \"%t\" is -replaced by a temporary file name. If VEC is nil, the respective local -commands are used. +`python' commands. +\"%b\" is replaced by a call of \"tramp_bundle_read_file-names\", \"%k\" +is replaced by a call of \"tramp_shell_print_quoted_string\", \"%n\" is +replaced by \"2>/dev/null\", and \"%t\" is replaced by a temporary file +name. \"%%\" is replaced by \"%\". If one of the format specifiers cannot be expanded, this function returns nil. If there are only other format -specifiers, SCRIPT is returned unchanged." - (if (not (string-match-p (rx "%" (any "ahlmnopqrsty%")) script)) - script - (catch 'wont-work - (let ((awk (when (string-match-p (rx (| bol (not "%")) "%a") script) - (or - (if vec (tramp-get-remote-awk vec) (executable-find "awk")) - (throw 'wont-work nil)))) - (hdmp (when (string-match-p (rx (| bol (not "%")) "%h") script) - (or - (if vec (tramp-get-remote-hexdump vec) - (executable-find "hexdump")) - (throw 'wont-work nil)))) - (dev (when (string-match-p (rx (| bol (not "%")) "%n") script) - (or - (if vec (concat "2>" (tramp-get-remote-null-device vec)) - (if (eq system-type 'windows-nt) "" - (concat "2>" null-device))) - (throw 'wont-work nil)))) - (ls (when (string-match-p (rx (| bol (not "%")) "%l") script) - (format "%s %s" - (or (tramp-get-ls-command vec) - (throw 'wont-work nil)) - (tramp-sh--quoting-style-options vec)))) - (test (when (string-match-p (rx (| bol (not "%")) "%m") script) - (or (tramp-get-test-command vec) - (throw 'wont-work nil)))) - (test-e (when (string-match-p (rx (| bol (not "%")) "%q") script) - (or (tramp-get-file-exists-command vec) - (throw 'wont-work nil)))) - (od (when (string-match-p (rx (| bol (not "%")) "%o") script) - (or (if vec (tramp-get-remote-od vec) (executable-find "od")) +specifiers, SCRIPT is returned unchanged. + +If VEC is nil, the respective local commands are used." + (catch 'wont-work + (let ((awk (when (string-match-p (rx (| bol (not "%")) "%a") script) + (or + (if vec (tramp-get-remote-awk vec) (executable-find "awk")) + (throw 'wont-work nil)))) + (bundle (when (string-match-p (rx (| bol (not "%")) "%b") script) + (tramp-maybe-send-script + vec tramp-bundle-read-file-names + "tramp_bundle_read_file-names") + "tramp_bundle_read_file-names")) + (hdmp (when (string-match-p (rx (| bol (not "%")) "%h") script) + (or + (if vec (tramp-get-remote-hexdump vec) + (executable-find "hexdump")) + (throw 'wont-work nil)))) + (dev (when (string-match-p (rx (| bol (not "%")) "%n") script) + (or + (if vec (concat "2>" (tramp-get-remote-null-device vec)) + (if (eq system-type 'windows-nt) "" + (concat "2>" null-device))) + (throw 'wont-work nil)))) + (lispy (when (string-match-p (rx (| bol (not "%")) "%k") script) + (tramp-maybe-send-script + vec tramp-shell-print-quoted-string + "tramp_shell_print_quoted_string") + "tramp_shell_print_quoted_string")) + (ls (when (string-match-p (rx (| bol (not "%")) "%l") script) + (format "%s %s" + (or (tramp-get-ls-command vec) + (throw 'wont-work nil)) + (tramp-sh--quoting-style-options vec)))) + (test (when (string-match-p (rx (| bol (not "%")) "%m") script) + (or (tramp-get-test-command vec) (throw 'wont-work nil)))) - (perl (when (string-match-p (rx (| bol (not "%")) "%p") script) - (or - (if vec - (tramp-get-remote-perl vec) (executable-find "perl")) - (throw 'wont-work nil)))) - (python (when (string-match-p (rx (| bol (not "%")) "%y") script) - (or - (if vec - (tramp-get-remote-python vec) - (executable-find "python")) - (throw 'wont-work nil)))) - (readlink (when (string-match-p (rx (| bol (not "%")) "%r") script) - (format "%s %s" - (or - (if vec - (tramp-get-remote-readlink vec) - (executable-find "readlink")) - (throw 'wont-work nil)) - "--canonicalize-missing"))) - (stat (when (string-match-p (rx (| bol (not "%")) "%s") script) + (test-e (when (string-match-p (rx (| bol (not "%")) "%q") script) + (or (tramp-get-file-exists-command vec) + (throw 'wont-work nil)))) + (od (when (string-match-p (rx (| bol (not "%")) "%o") script) + (or (if vec (tramp-get-remote-od vec) (executable-find "od")) + (throw 'wont-work nil)))) + (perl (when (string-match-p (rx (| bol (not "%")) "%p") script) + (or + (if vec + (tramp-get-remote-perl vec) (executable-find "perl")) + (throw 'wont-work nil)))) + (python (when (string-match-p (rx (| bol (not "%")) "%y") script) (or (if vec - (tramp-get-remote-stat vec) (executable-find "stat")) + (tramp-get-remote-python vec) + (executable-find "python")) (throw 'wont-work nil)))) - (tmp (when (string-match-p (rx (| bol (not "%")) "%t") script) - (or - (if vec - (tramp-file-local-name (tramp-make-tramp-temp-name vec)) - (tramp-compat-make-temp-name)) - (throw 'wont-work nil))))) - (format-spec - script - (format-spec-make - ?a awk ?h hdmp ?l ls ?m test ?n dev ?o od ?p perl - ?q test-e ?r readlink ?s stat ?t tmp ?y python)))))) + (readlink (when (string-match-p (rx (| bol (not "%")) "%r") script) + (format "%s %s" + (or + (if vec + (tramp-get-remote-readlink vec) + (executable-find "readlink")) + (throw 'wont-work nil)) + "--canonicalize-missing"))) + (stat (when (string-match-p (rx (| bol (not "%")) "%s") script) + (or + (if vec + (tramp-get-remote-stat vec) (executable-find "stat")) + (throw 'wont-work nil)))) + (tmp (when (string-match-p (rx (| bol (not "%")) "%t") script) + (or + (if vec + (tramp-file-local-name (tramp-make-tramp-temp-name vec)) + (tramp-compat-make-temp-name)) + (throw 'wont-work nil))))) + (format-spec + script + (format-spec-make + ?a awk ?b bundle ?h hdmp ?k lispy ?l ls ?m test ?n dev ?o od + ?p perl ?q test-e ?r readlink ?s stat ?t tmp ?y python))))) (defun tramp-maybe-send-script (vec script name) "Define in remote shell function NAME implemented as SCRIPT. commit 04831782198bfe49620e981012d7d73bd4945280 Author: Sean Whitton Date: Sun Nov 9 11:58:39 2025 +0000 ; * lisp/vc/vc-dispatcher.el (vc-do-command): Reduce indentation. diff --git a/lisp/vc/vc-dispatcher.el b/lisp/vc/vc-dispatcher.el index 0b7d1fd1a64..a4cf42ff6a9 100644 --- a/lisp/vc/vc-dispatcher.el +++ b/lisp/vc/vc-dispatcher.el @@ -394,101 +394,102 @@ that is inserted into the command line before the filename. Return the return value of the slave command in the synchronous case, and the process object in the asynchronous case." - (let (;; Keep entire commands in *Messages* but avoid resizing the - ;; echo area. Messages in this function are formatted in - ;; a such way that the important parts are at the beginning, - ;; due to potential truncation of long messages. - (message-truncate-lines t) - (vc-inhibit-message - (or (eq vc-command-messages 'log) - (eq (selected-window) (active-minibuffer-window))))) + (pcase-let (;; Keep entire commands in *Messages* but avoid resizing the + ;; echo area. Messages in this function are formatted in + ;; a such way that the important parts are at the beginning, + ;; due to potential truncation of long messages. + (message-truncate-lines t) + (vc-inhibit-message + (or (eq vc-command-messages 'log) + (eq (selected-window) (active-minibuffer-window)))) + (`(,command ,file-or-list ,flags) + (funcall vc-filter-command-function + command file-or-list flags))) (save-current-buffer (unless (or (eq buffer t) (eq (current-buffer) (get-buffer buffer))) (vc-setup-buffer buffer)) - (cl-destructuring-bind (command file-or-list flags) - (funcall vc-filter-command-function command file-or-list flags) - (when vc-tor - (push command flags) - (setq command "torsocks")) - (let* (;; FIXME: file-relative-name can return a bogus result - ;; because it doesn't look at the actual file-system to - ;; see if symlinks come into play. - (files - (mapcar (lambda (f) - (file-relative-name (expand-file-name f))) - (ensure-list file-or-list))) - (full-command - (concat (if (equal (substring command -1) "\n") - (substring command 0 -1) - command) - " " (vc-delistify flags) - (and files (concat " " (vc-delistify files))))) - (squeezed (remq nil flags)) - (inhibit-read-only t) - (status 0)) - ;; If there's some previous async process still running, - ;; just kill it. - (when files - (setq squeezed (nconc squeezed files))) - (let (;; Since some functions need to parse the output - ;; from external commands, set LC_MESSAGES to C. - (process-environment - (cons "LC_MESSAGES=C" process-environment)) - (w32-quote-process-args t)) - (if (eq okstatus 'async) - ;; Run asynchronously. - (let ((proc - (let (process-connection-type) - (apply #'start-file-process command - (current-buffer) command squeezed)))) - (when vc-command-messages - (let ((inhibit-message vc-inhibit-message)) - (message "Running in background: %s" - full-command))) - ;; Get rid of the default message insertion, in case - ;; we don't set a sentinel explicitly. - (set-process-sentinel proc #'ignore) - (set-process-filter proc #'vc-process-filter) - (setq status proc) - (when vc-command-messages - (vc-run-delayed - (let ((message-truncate-lines t) - (inhibit-message vc-inhibit-message)) - (message "Done in background: %s" - full-command))))) - ;; Run synchronously - (when vc-command-messages - (let ((inhibit-message vc-inhibit-message)) - (message "Running in foreground: %s" full-command))) - (let ((buffer-undo-list t)) - (setq status (apply #'process-file - command nil t nil squeezed))) - (when (and (not (eq t okstatus)) - (or (not (integerp status)) - (and okstatus (< okstatus status)))) - (unless (eq ?\s (aref (buffer-name (current-buffer)) 0)) - (pop-to-buffer (current-buffer)) - (goto-char (point-min)) - (shrink-window-if-larger-than-buffer)) - (when-let* (noninteractive - (out (string-trim (buffer-string))) - (_ (not (string-empty-p out)))) - (with-current-buffer buffer - (message "%s" out))) - (error "Failed (%s): %s" - (if (integerp status) - (format "status %d" status) - status) - full-command)) - (when vc-command-messages - (let ((inhibit-message vc-inhibit-message)) - (message "Done (status=%d): %s" - status full-command))))) - (vc-run-delayed - (run-hook-with-args 'vc-post-command-functions - command file-or-list flags)) - status))))) + (when vc-tor + (push command flags) + (setq command "torsocks")) + (let* (;; FIXME: file-relative-name can return a bogus result + ;; because it doesn't look at the actual file-system to + ;; see if symlinks come into play. + (files + (mapcar (lambda (f) + (file-relative-name (expand-file-name f))) + (ensure-list file-or-list))) + (full-command + (concat (if (equal (substring command -1) "\n") + (substring command 0 -1) + command) + " " (vc-delistify flags) + (and files (concat " " (vc-delistify files))))) + (squeezed (remq nil flags)) + (inhibit-read-only t) + (status 0)) + ;; If there's some previous async process still running, + ;; just kill it. + (when files + (setq squeezed (nconc squeezed files))) + (let (;; Since some functions need to parse the output + ;; from external commands, set LC_MESSAGES to C. + (process-environment + (cons "LC_MESSAGES=C" process-environment)) + (w32-quote-process-args t)) + (if (eq okstatus 'async) + ;; Run asynchronously. + (let ((proc + (let (process-connection-type) + (apply #'start-file-process command + (current-buffer) command squeezed)))) + (when vc-command-messages + (let ((inhibit-message vc-inhibit-message)) + (message "Running in background: %s" + full-command))) + ;; Get rid of the default message insertion, in case + ;; we don't set a sentinel explicitly. + (set-process-sentinel proc #'ignore) + (set-process-filter proc #'vc-process-filter) + (setq status proc) + (when vc-command-messages + (vc-run-delayed + (let ((message-truncate-lines t) + (inhibit-message vc-inhibit-message)) + (message "Done in background: %s" + full-command))))) + ;; Run synchronously + (when vc-command-messages + (let ((inhibit-message vc-inhibit-message)) + (message "Running in foreground: %s" full-command))) + (let ((buffer-undo-list t)) + (setq status (apply #'process-file + command nil t nil squeezed))) + (when (and (not (eq t okstatus)) + (or (not (integerp status)) + (and okstatus (< okstatus status)))) + (unless (eq ?\s (aref (buffer-name (current-buffer)) 0)) + (pop-to-buffer (current-buffer)) + (goto-char (point-min)) + (shrink-window-if-larger-than-buffer)) + (when-let* (noninteractive + (out (string-trim (buffer-string))) + (_ (not (string-empty-p out)))) + (with-current-buffer buffer + (message "%s" out))) + (error "Failed (%s): %s" + (if (integerp status) + (format "status %d" status) + status) + full-command)) + (when vc-command-messages + (let ((inhibit-message vc-inhibit-message)) + (message "Done (status=%d): %s" + status full-command))))) + (vc-run-delayed + (run-hook-with-args 'vc-post-command-functions + command file-or-list flags)) + status)))) (defvar vc--inhibit-async-window nil) commit 786f9f1fd4b3049b16e7368e9cb0994a77f8c62f Author: Sean Whitton Date: Sun Nov 9 11:52:00 2025 +0000 ; * lisp/vc/vc-dispatcher.el: Fix last change. diff --git a/lisp/vc/vc-dispatcher.el b/lisp/vc/vc-dispatcher.el index 2c955c14735..0b7d1fd1a64 100644 --- a/lisp/vc/vc-dispatcher.el +++ b/lisp/vc/vc-dispatcher.el @@ -404,7 +404,7 @@ case, and the process object in the asynchronous case." (eq (selected-window) (active-minibuffer-window))))) (save-current-buffer (unless (or (eq buffer t) - (eq buffer (get-buffer buffer))) + (eq (current-buffer) (get-buffer buffer))) (vc-setup-buffer buffer)) (cl-destructuring-bind (command file-or-list flags) (funcall vc-filter-command-function command file-or-list flags) commit a86f8c9cbea923e3326457b57d7e4b6094be70c2 Author: Sean Whitton Date: Sun Nov 9 11:43:22 2025 +0000 vc-do-command: Error when BUFFER is nil, untabify This function is shortly to gain support for separating standard output and standard error from the child. Commit untabifying it first to make the diff adding the new features more readable. This will include documented support for BUFFER nil, meaning to discard output. * lisp/vc/vc-dispatcher.el (vc-do-command): Untabify the source in preparation for work on this function. If BUFFER is nil, signal an error, in keeping with the current docstring. Use ensure-list. Use equal not string= for string comparison. diff --git a/lisp/vc/vc-dispatcher.el b/lisp/vc/vc-dispatcher.el index 14b57f23864..2c955c14735 100644 --- a/lisp/vc/vc-dispatcher.el +++ b/lisp/vc/vc-dispatcher.el @@ -395,18 +395,16 @@ that is inserted into the command line before the filename. Return the return value of the slave command in the synchronous case, and the process object in the asynchronous case." (let (;; Keep entire commands in *Messages* but avoid resizing the - ;; echo area. Messages in this function are formatted in - ;; a such way that the important parts are at the beginning, - ;; due to potential truncation of long messages. - (message-truncate-lines t) + ;; echo area. Messages in this function are formatted in + ;; a such way that the important parts are at the beginning, + ;; due to potential truncation of long messages. + (message-truncate-lines t) (vc-inhibit-message - (or (eq vc-command-messages 'log) - (eq (selected-window) (active-minibuffer-window))))) + (or (eq vc-command-messages 'log) + (eq (selected-window) (active-minibuffer-window))))) (save-current-buffer (unless (or (eq buffer t) - (and (stringp buffer) - (string= (buffer-name) buffer)) - (eq buffer (current-buffer))) + (eq buffer (get-buffer buffer))) (vc-setup-buffer buffer)) (cl-destructuring-bind (command file-or-list flags) (funcall vc-filter-command-function command file-or-list flags) @@ -417,60 +415,58 @@ case, and the process object in the asynchronous case." ;; because it doesn't look at the actual file-system to ;; see if symlinks come into play. (files - (mapcar (lambda (f) + (mapcar (lambda (f) (file-relative-name (expand-file-name f))) - (if (listp file-or-list) - file-or-list - (list file-or-list)))) - (full-command - (concat (if (string= (substring command -1) "\n") - (substring command 0 -1) - command) - " " (vc-delistify flags) - (and files (concat " " (vc-delistify files))))) + (ensure-list file-or-list))) + (full-command + (concat (if (equal (substring command -1) "\n") + (substring command 0 -1) + command) + " " (vc-delistify flags) + (and files (concat " " (vc-delistify files))))) (squeezed (remq nil flags)) - (inhibit-read-only t) - (status 0)) + (inhibit-read-only t) + (status 0)) ;; If there's some previous async process still running, ;; just kill it. (when files - (setq squeezed (nconc squeezed files))) - (let (;; Since some functions need to parse the output - ;; from external commands, set LC_MESSAGES to C. - (process-environment + (setq squeezed (nconc squeezed files))) + (let (;; Since some functions need to parse the output + ;; from external commands, set LC_MESSAGES to C. + (process-environment (cons "LC_MESSAGES=C" process-environment)) - (w32-quote-process-args t)) - (if (eq okstatus 'async) - ;; Run asynchronously. - (let ((proc - (let ((process-connection-type nil)) - (apply #'start-file-process command + (w32-quote-process-args t)) + (if (eq okstatus 'async) + ;; Run asynchronously. + (let ((proc + (let (process-connection-type) + (apply #'start-file-process command (current-buffer) command squeezed)))) - (when vc-command-messages - (let ((inhibit-message vc-inhibit-message)) - (message "Running in background: %s" + (when vc-command-messages + (let ((inhibit-message vc-inhibit-message)) + (message "Running in background: %s" full-command))) ;; Get rid of the default message insertion, in case ;; we don't set a sentinel explicitly. - (set-process-sentinel proc #'ignore) - (set-process-filter proc #'vc-process-filter) - (setq status proc) - (when vc-command-messages - (vc-run-delayed - (let ((message-truncate-lines t) - (inhibit-message vc-inhibit-message)) - (message "Done in background: %s" + (set-process-sentinel proc #'ignore) + (set-process-filter proc #'vc-process-filter) + (setq status proc) + (when vc-command-messages + (vc-run-delayed + (let ((message-truncate-lines t) + (inhibit-message vc-inhibit-message)) + (message "Done in background: %s" full-command))))) - ;; Run synchronously - (when vc-command-messages - (let ((inhibit-message vc-inhibit-message)) - (message "Running in foreground: %s" full-command))) - (let ((buffer-undo-list t)) - (setq status (apply #'process-file + ;; Run synchronously + (when vc-command-messages + (let ((inhibit-message vc-inhibit-message)) + (message "Running in foreground: %s" full-command))) + (let ((buffer-undo-list t)) + (setq status (apply #'process-file command nil t nil squeezed))) - (when (and (not (eq t okstatus)) - (or (not (integerp status)) - (and okstatus (< okstatus status)))) + (when (and (not (eq t okstatus)) + (or (not (integerp status)) + (and okstatus (< okstatus status)))) (unless (eq ?\s (aref (buffer-name (current-buffer)) 0)) (pop-to-buffer (current-buffer)) (goto-char (point-min)) @@ -480,19 +476,19 @@ case, and the process object in the asynchronous case." (_ (not (string-empty-p out)))) (with-current-buffer buffer (message "%s" out))) - (error "Failed (%s): %s" - (if (integerp status) + (error "Failed (%s): %s" + (if (integerp status) (format "status %d" status) status) - full-command)) - (when vc-command-messages - (let ((inhibit-message vc-inhibit-message)) - (message "Done (status=%d): %s" + full-command)) + (when vc-command-messages + (let ((inhibit-message vc-inhibit-message)) + (message "Done (status=%d): %s" status full-command))))) - (vc-run-delayed - (run-hook-with-args 'vc-post-command-functions - command file-or-list flags)) - status))))) + (vc-run-delayed + (run-hook-with-args 'vc-post-command-functions + command file-or-list flags)) + status))))) (defvar vc--inhibit-async-window nil)