commit de2daa74bd5304364931fd19ad81a94f5f7d8d09 (HEAD, refs/remotes/origin/master) Author: Juri Linkov Date: Wed Jun 11 09:43:45 2025 +0300 * test/lisp/repeat-tests.el: Add a command for global continue. (repeat-tests-call-g): New command (bug#78742). Put 'repeat-continue' with t on this symbol. (repeat-tests-global-map): Bind it to 'C-M-g'. (repeat-tests-continue-another): Add 'C-M-g' that should continue. diff --git a/test/lisp/repeat-tests.el b/test/lisp/repeat-tests.el index 6bf9badf31d..3f17978468c 100644 --- a/test/lisp/repeat-tests.el +++ b/test/lisp/repeat-tests.el @@ -27,6 +27,7 @@ ;; Key mnemonics: a - activate (enter, also b, s), ;; c - continue (also d, t, also o, u), ;; e - continue-only (not activate), +;; g - global continue, ;; q - quit (exit) (defvar repeat-tests-calls nil) @@ -51,6 +52,10 @@ (interactive "p") (push `(,arg e) repeat-tests-calls)) +(defun repeat-tests-call-g (&optional arg) + (interactive "p") + (push `(,arg g) repeat-tests-calls)) + (defun repeat-tests-call-o (&optional arg) (interactive "p") (push `(,arg o) repeat-tests-calls)) @@ -78,6 +83,7 @@ "C-M-a" 'repeat-tests-call-a "C-M-b" 'repeat-tests-call-b "C-M-e" 'repeat-tests-call-e + "C-M-g" 'repeat-tests-call-g "C-M-o" 'repeat-tests-call-o "C-M-s" 'repeat-tests-call-s "C-M-u" 'repeat-tests-call-u) @@ -108,6 +114,8 @@ ;; Test using a variable instead of the symbol: (put 'repeat-tests-call-b 'repeat-map repeat-tests-repeat-map) +(put 'repeat-tests-call-g 'repeat-continue t) + (defmacro with-repeat-mode (map &rest body) "Create environment for testing `repeat-mode'." (declare (indent 1) (debug (symbol body))) @@ -215,6 +223,10 @@ (with-repeat-mode repeat-tests-global-map (let ((repeat-echo-function 'ignore) (repeat-check-key nil)) + ;; Global 'C-M-g' used as continue + (repeat-tests--check + "C-M-a c C-M-g c z" + '((1 a) (1 c) (1 g) (1 c)) "z") ;; 'C-M-e' and 'C-M-o' used as continue (repeat-tests--check "C-M-a c C-M-e C-M-o c z" commit 3a0def802cf576902faafde404d15f079786a2f8 Author: Paul Nelson Date: Tue Jun 10 09:21:52 2025 +0200 Improve repeat-continue property handling * lisp/repeat.el (repeat-get-map-sym): Support 'repeat-continue' property value of t to continue any active repeat map (suggested by Karthik Chikmagalur ). Simplify logic so that repeat-continue does not interfere with repeat-map activation. (repeat-check-map): Allow commands with 'repeat-continue' property value of t to continue without checking the key. (repeat-mode): Update and tweak docstring. * lisp/bind-key.el (bind-keys-form): Update handling of ':continue-only' keyword: only add keymap to 'repeat-continue' if current value is a list. (bind-keys): Update documentation, mirroring bind-keys-form. * lisp/keymap.el (defvar-keymap): Update handling of ':continue' keyword: only add keymap to 'repeat-continue' if current value is a list. * test/lisp/repeat-tests.el (repeat-tests-continue) (repeat-tests-continue-another): Enable previously commented tests that now work correctly. * etc/NEWS: Update announcement of 'repeat-continue' (bug#78742). diff --git a/etc/NEWS b/etc/NEWS index e641376d500..23b76da8cb2 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -2448,12 +2448,15 @@ will be calculated based on the window width. +++ ** New symbol property 'repeat-continue' for 'repeat-mode'. -A command with this symbol property whose value is a list of repeat -maps will not activate the repeat map in 'repeat-mode'. It will only -continue the already activated repeating sequence. Also 'defvar-keymap' -supports a new keyword ':continue' with a list of commands that only -continue the active repeating sequence, and the 'use-package' and -'bind-keys' macros support a similar keyword ':continue-only'. +A command with the 'repeat-continue' symbol property, which can be a +list of keymaps or t, will continue an already active repeating sequence +for a keymap in that list (resp. all keymaps). The new property does +not affect whether the command starts a repeating sequence, which +remains governed by the 'repeat-map' property. 'defvar-keymap' supports +a new keyword ':continue', a list of commands, and adds the keymap to +the 'repeat-continue' property of each command in that list. The +'use-package' and 'bind-keys' macros support a similar keyword +':continue-only'. ** New function 'completion-table-with-metadata'. It offers a more concise way to create a completion table with metadata. diff --git a/lisp/bind-key.el b/lisp/bind-key.el index 12417106783..49f5bd30351 100644 --- a/lisp/bind-key.el +++ b/lisp/bind-key.el @@ -402,13 +402,11 @@ function symbol (unquoted)." ;; repeat-map is non-nil, map is always ;; non-nil (if (eq repeat-type :continue-only) - `((unless (memq ',repeat-map - (or (get ,fun 'repeat-continue) - '())) - (put ,fun 'repeat-continue - (append (or (get ,fun 'repeat-continue) - '()) - (list ',repeat-map)))) + `((let ((cur (get ,fun 'repeat-continue))) + (when (and (listp cur) + (not (memq ',repeat-map cur))) + (put ,fun 'repeat-continue + (append cur (list ',repeat-map))))) (bind-key ,(car form) ,fun ,map ,filter)) `(,@(when (and repeat-map (not (eq repeat-type :exit))) `((put ,fun 'repeat-map ',repeat-map))) @@ -446,6 +444,9 @@ Accepts keyword arguments: same behavior as if no special keyword had been used (that is, the command is bound, and it's `repeat-map' property set) +:continue-only BINDINGS - Within the scope of `:repeat-map', will make + the command continue but not enter the repeat + map, via the `repeat-continue' property :filter FORM - optional form to determine when bindings apply The rest of the arguments are conses of keybinding string and a diff --git a/lisp/keymap.el b/lisp/keymap.el index a12084a3ad4..cf4c8d90bb1 100644 --- a/lisp/keymap.el +++ b/lisp/keymap.el @@ -754,8 +754,10 @@ in the echo area. (dolist (def (plist-get repeat :enter)) (push `(put ',def 'repeat-map ',variable-name) props)) (dolist (def (plist-get repeat :continue)) - (push `(put ',def 'repeat-continue - (cons ',variable-name (get ',def 'repeat-continue))) + (push `(let ((val (get ',def 'repeat-continue))) + (when (listp val) + (put ',def 'repeat-continue + (cons ',variable-name val)))) props)) (while defs (pop defs) diff --git a/lisp/repeat.el b/lisp/repeat.el index daa53fe7195..9308f972a91 100644 --- a/lisp/repeat.el +++ b/lisp/repeat.el @@ -427,9 +427,18 @@ the map can't be set on the command symbol property `repeat-map'.") When Repeat mode is enabled, certain commands bound to multi-key sequences can be repeated by typing a single key, after typing the full key sequence once. -The commands which can be repeated like that are those whose symbol - has the property `repeat-map' which specifies a keymap of single -keys for repeating. + +The commands that can be repeated in this way are those whose symbols +have the `repeat-map' property, which specifies a keymap of single keys +for repeating. + +Normally, invoking a command outside that keymap terminates the +repeating sequence. However, if the command's `repeat-continue' +property is non-nil, it may instead continue the current repeating +sequence: if the property is a list of keymaps, then the command +continues when the current repeat map is in the list; if the property is +t, the command always continues the sequence. + See `describe-repeat-maps' for a list of all repeatable commands." :global t :group 'repeat (if (not repeat-mode) @@ -460,12 +469,11 @@ See `describe-repeat-maps' for a list of all repeatable commands." (when repeat-mode (let ((map-sym (or repeat-map (repeat--command-property 'repeat-map))) (continue (repeat--command-property 'repeat-continue))) - (when continue - (if repeat-in-progress - (when (and (consp continue) - (memq repeat-in-progress continue)) - (setq map-sym repeat-in-progress)) - (setq map-sym nil))) + (when (and repeat-in-progress + (or (eq continue t) + (and (consp continue) + (memq repeat-in-progress continue)))) + (setq map-sym repeat-in-progress)) map-sym))) (defun repeat-get-map (map) @@ -495,7 +503,8 @@ See `describe-repeat-maps' for a list of all repeatable commands." ;; in the middle of repeating sequence (bug#47566). (or (< (minibuffer-depth) (car repeat--prev-mb)) (eq current-minibuffer-command (cdr repeat--prev-mb))) - (repeat-check-key last-command-event map) + (or (eq (repeat--command-property 'repeat-continue) t) + (repeat-check-key last-command-event map)) t)) (defun repeat-pre-hook () diff --git a/test/lisp/repeat-tests.el b/test/lisp/repeat-tests.el index f96d8df2ebd..6bf9badf31d 100644 --- a/test/lisp/repeat-tests.el +++ b/test/lisp/repeat-tests.el @@ -224,9 +224,9 @@ "C-M-e c z" '((1 e)) "cz") ;; 'C-M-o' should also activate - ;; (repeat-tests--check - ;; "C-M-o c z" - ;; '((1 o) (1 c)) "z") + (repeat-tests--check + "C-M-o c z" + '((1 o) (1 c)) "z") ))) (ert-deftest repeat-tests-continue-another () @@ -246,9 +246,9 @@ "C-M-e t z" '((1 e)) "tz") ;; 'C-M-u' should also activate - ;; (repeat-tests--check - ;; "C-M-u t z" - ;; '((1 u) (1 t)) "z") + (repeat-tests--check + "C-M-u t z" + '((1 u) (1 t)) "z") ;; 'C-M-o' shared with another map should continue current map (repeat-tests--check "C-M-s t C-M-o C-M-o t z" commit 38c57855ae2b5d4245bce0bb444ee86c35dfcdc5 Author: Jim Porter Date: Wed May 28 10:16:02 2025 -0700 ; Remove superfluous POSITION argument from 'visual-wrap--apply-to-line' * lisp/visual-wrap.el (visual-wrap--apply-to-line): Remove POSITION and just use point instead. Update caller. diff --git a/lisp/visual-wrap.el b/lisp/visual-wrap.el index 3dcffa7c7e9..24ca8ae7cf4 100644 --- a/lisp/visual-wrap.el +++ b/lisp/visual-wrap.el @@ -143,29 +143,27 @@ members of `visual-wrap--safe-display-specs' (which see)." (t ""))))) -(defun visual-wrap--apply-to-line (position) - "Apply visual-wrapping properties to the logical line starting at POSITION." - (save-excursion - (goto-char position) - (when-let* ((first-line-prefix (fill-match-adaptive-prefix)) - (next-line-prefix (visual-wrap--content-prefix - first-line-prefix position))) - (when (numberp next-line-prefix) - ;; Set a minimum width for the prefix so it lines up correctly - ;; with subsequent lines. Make sure not to do this past the end - ;; of the line though! (`fill-match-adaptive-prefix' could - ;; potentially return a prefix longer than the current line in - ;; the buffer.) - (add-display-text-property - position (min (+ position (length first-line-prefix)) - (pos-eol)) - 'min-width `((,next-line-prefix . width)))) - (setq next-line-prefix (visual-wrap--adjust-prefix next-line-prefix)) - (put-text-property - position (pos-eol) 'wrap-prefix - (if (numberp next-line-prefix) - `(space :align-to (,next-line-prefix . width)) - next-line-prefix))))) +(defun visual-wrap--apply-to-line () + "Apply visual-wrapping properties to the logical line starting at point." + (when-let* ((first-line-prefix (fill-match-adaptive-prefix)) + (next-line-prefix (visual-wrap--content-prefix + first-line-prefix (point)))) + (when (numberp next-line-prefix) + ;; Set a minimum width for the prefix so it lines up correctly + ;; with subsequent lines. Make sure not to do this past the end + ;; of the line though! (`fill-match-adaptive-prefix' could + ;; potentially return a prefix longer than the current line in the + ;; buffer.) + (add-display-text-property + (point) (min (+ (point) (length first-line-prefix)) + (pos-eol)) + 'min-width `((,next-line-prefix . width)))) + (setq next-line-prefix (visual-wrap--adjust-prefix next-line-prefix)) + (put-text-property + (point) (pos-eol) 'wrap-prefix + (if (numberp next-line-prefix) + `(space :align-to (,next-line-prefix . width)) + next-line-prefix)))) (defun visual-wrap--content-prefix (prefix position) "Get the next-line prefix for the specified first-line PREFIX. @@ -254,7 +252,7 @@ by `visual-wrap-extra-indent'." ;; If so, we can apply our visual wrapping properties to this ;; line and continue to the next line. (progn - (visual-wrap--apply-to-line (point)) + (visual-wrap--apply-to-line) (forward-line)) ;; Otherwise, skip ahead until the end of any unsafe display ;; properties. NOTE: We do this out of an abundance of caution to commit 90c0c9a01ed11944c5502f809817a028a1096ee6 Author: Jim Porter Date: Wed May 28 09:44:34 2025 -0700 Clean up text properties in 'visual-wrap-prefix-mode' Before refontifying a region, remove any text properties we care about so that we don't end up with stray properties. Additionally, make sure to remove all the properties when deactivating the mode. * lisp/emacs-lisp/subr-x.el (add-remove--display-text-property): New function, extracted from... (add-display-text-property): ... here. (remove-display-text-property): New function. * lisp/visual-wrap.el (visual-wrap--remove-properties): New function... (visual-wrap-prefix-function, visual-wrap-prefix-mode): ... call it. * test/lisp/emacs-lisp/subr-x-tests.el (subr-x-test-remove-display-text-property): New test. * test/lisp/visual-wrap-tests.el (visual-wrap-tests/wrap-prefix-stickiness, visual-wrap-tests/cleanup): New tests. * doc/lispref/display.texi (Display Property): Document 'remove-display-text-property'. * etc/NEWS: Announce 'remove-display-text-property' (bug#76018). diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi index e29273c46d7..48d6e85195b 100644 --- a/doc/lispref/display.texi +++ b/doc/lispref/display.texi @@ -5302,6 +5302,44 @@ specification. If omitted, @var{object} defaults to the current buffer. @end defun +@defun remove-display-text-property start end spec &optional object +Remove the display specification @var{spec} from the text from +@var{start} to @var{end}. @var{spec} is the @sc{car} of the display +specification to remove, e.g.@: @code{height} or @code{'(margin nil)}. + +If any text in the region has any other @code{display} properties, those +properties are retained. For instance: + +@lisp +@group +(add-display-text-property 1 8 'raise 0.5) +(add-display-text-property 4 8 'height 2.0) +(remove-display-text-property 2 6 'raise) +@end group +@end lisp + +After doing this, the text will have the following @code{display} +properties: + +@itemize @bullet +@item +The region from 1 to 2, only @code{raise} + +@item +The region from 2 to 4, no properties + +@item +The region from 4 to 6, only @code{height} + +@item +The region from 6 to 8, both @code{raise} and @code{height} + +@end itemize + +@var{object} is either a string or a buffer to remove the specification +from. If omitted, @var{object} defaults to the current buffer. +@end defun + @cindex display property, unsafe evaluation @cindex security, and display specifications Some of the display specifications allow inclusion of Lisp forms, diff --git a/etc/NEWS b/etc/NEWS index d5b62b9850a..e641376d500 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -2413,6 +2413,12 @@ This 'display' property was previously supported only as text property. Now overlays can also have this property, with the same effect for the text "covered" by the overlay. ++++ +** New function 'remove-display-text-property'. +This function removes a display property from the specified region of +text, preserving any other display properties already set for that +region. + +++ ** New macro 'cond*'. The new macro 'cond*' is an alternative to 'cond' and 'pcase'. diff --git a/lisp/emacs-lisp/subr-x.el b/lisp/emacs-lisp/subr-x.el index 168e5c0f227..ab5abf9cbed 100644 --- a/lisp/emacs-lisp/subr-x.el +++ b/lisp/emacs-lisp/subr-x.el @@ -416,28 +416,25 @@ indivisible unit." (setq start (1+ start)))) (nreverse result))) -;;;###autoload -(defun add-display-text-property (start end spec value &optional object) - "Add the display specification (SPEC VALUE) to the text from START to END. -If any text in the region has a non-nil `display' property, the existing -display specifications are retained. - -OBJECT is either a string or a buffer to add the specification to. -If omitted, OBJECT defaults to the current buffer." +(defun add-remove--display-text-property (start end spec value + &optional object remove) (let ((sub-start start) (sub-end 0) + (limit (if (stringp object) + (min (length object) end) + (min end (point-max)))) disp) (while (< sub-end end) (setq sub-end (next-single-property-change sub-start 'display object - (if (stringp object) - (min (length object) end) - (min end (point-max))))) + limit)) (if (not (setq disp (get-text-property sub-start 'display object))) ;; No old properties in this range. - (put-text-property sub-start sub-end 'display (list spec value) - object) + (unless remove + (put-text-property sub-start sub-end 'display (list spec value) + object)) ;; We have old properties. - (let (type) + (let ((changed nil) + type) ;; Make disp into a list. (setq disp (cond @@ -460,14 +457,41 @@ If omitted, OBJECT defaults to the current buffer." ;; regions of text. (setq disp (if (eq type 'list) (remove old disp) - (delete old disp)))) - (setq disp (cons (list spec value) disp)) - (when (eq type 'vector) - (setq disp (seq-into disp 'vector))) - ;; Finally update the range. - (put-text-property sub-start sub-end 'display disp object))) + (delete old disp)) + changed t)) + (unless remove + (setq disp (cons (list spec value) disp) + changed t)) + (when changed + (if (not disp) + (remove-text-properties sub-start sub-end '(display nil) object) + (when (eq type 'vector) + (setq disp (seq-into disp 'vector))) + ;; Finally update the range. + (put-text-property sub-start sub-end 'display disp object))))) (setq sub-start sub-end)))) +;;;###autoload +(defun add-display-text-property (start end spec value &optional object) + "Add the display specification (SPEC VALUE) to the text from START to END. +If any text in the region has a non-nil `display' property, the existing +display specifications are retained. + +OBJECT is either a string or a buffer to add the specification to. +If omitted, OBJECT defaults to the current buffer." + (add-remove--display-text-property start end spec value object)) + +;;;###autoload +(defun remove-display-text-property (start end spec &optional object) + "Remove the display specification SPEC from the text from START to END. +SPEC is the car of the display specification to remove, e.g. `height'. +If any text in the region has other display specifications, those specs +are retained. + +OBJECT is either a string or a buffer to remove the specification from. +If omitted, OBJECT defaults to the current buffer." + (add-remove--display-text-property start end spec nil object 'remove)) + ;;;###autoload (defun read-process-name (prompt) "Query the user for a process and return the process object." diff --git a/lisp/visual-wrap.el b/lisp/visual-wrap.el index 227afe71006..3dcffa7c7e9 100644 --- a/lisp/visual-wrap.el +++ b/lisp/visual-wrap.el @@ -226,6 +226,14 @@ by `visual-wrap-extra-indent'." (propertize prefix 'face face) prefix))) +(defun visual-wrap--remove-properties (start end) + "Remove visual wrapping text properties from START to END." + ;; Remove `min-width' from any prefixes we detected. + (remove-display-text-property start end 'min-width) + ;; Remove `wrap-prefix' related properties from any lines with + ;; prefixes we detected. + (remove-text-properties start end '(wrap-prefix nil))) + (defun visual-wrap-prefix-function (beg end) "Indent the region between BEG and END with visual filling." ;; Any change at the beginning of a line might change its wrap @@ -238,6 +246,7 @@ by `visual-wrap-extra-indent'." (goto-char beg) (forward-line 0) (setq beg (point)) + (visual-wrap--remove-properties beg end) (while (< (point) end) ;; Check if the display property at the end of this line is "safe". (if (visual-wrap--display-property-safe-p @@ -283,7 +292,7 @@ To enable this minor mode across all buffers, enable (with-silent-modifications (save-restriction (widen) - (remove-text-properties (point-min) (point-max) '(wrap-prefix nil)))))) + (visual-wrap--remove-properties (point-min) (point-max)))))) ;;;###autoload (define-globalized-minor-mode global-visual-wrap-prefix-mode diff --git a/test/lisp/emacs-lisp/subr-x-tests.el b/test/lisp/emacs-lisp/subr-x-tests.el index 5ffbe64ae40..e0eecc3e934 100644 --- a/test/lisp/emacs-lisp/subr-x-tests.el +++ b/test/lisp/emacs-lisp/subr-x-tests.el @@ -740,6 +740,44 @@ 4 8 (display ((raise 0.5) (height 2.0))) 8 12 (display (raise 0.5))))))) +(ert-deftest subr-x-test-remove-display-text-property () + (with-temp-buffer + (insert "Foo bar zot gazonk") + (add-display-text-property 4 12 'height 2.0) + (add-display-text-property 2 8 'raise 0.5) + (remove-display-text-property 6 10 'height) + (should (equal-including-properties + (buffer-string) + #("Foo bar zot gazonk" + 1 3 (display (raise 0.5)) + 3 5 (display ((raise 0.5) (height 2.0))) + 5 7 (display ((raise 0.5))) + 9 11 (display (height 2.0)))))) + (with-temp-buffer + (insert "Foo bar zot gazonk") + (put-text-property 4 12 'display [(height 2.0)]) + (add-display-text-property 2 8 'raise 0.5) + (remove-display-text-property 6 10 'height) + (should (equal-including-properties + (buffer-string) + #("Foo bar zot gazonk" + 1 3 (display (raise 0.5)) + 3 5 (display [(raise 0.5) (height 2.0)]) + 5 7 (display [(raise 0.5)]) + 9 11 (display [(height 2.0)]))))) + (with-temp-buffer + (should (equal-including-properties + (let ((str (copy-sequence "Foo bar zot gazonk"))) + (add-display-text-property 3 11 'height 2.0 str) + (add-display-text-property 1 7 'raise 0.5 str) + (remove-display-text-property 5 9 'height str) + str) + #("Foo bar zot gazonk" + 1 3 (display (raise 0.5)) + 3 5 (display ((raise 0.5) (height 2.0))) + 5 7 (display ((raise 0.5))) + 9 11 (display (height 2.0))))))) + (ert-deftest subr-x-named-let () (let ((funs ())) (named-let loop diff --git a/test/lisp/visual-wrap-tests.el b/test/lisp/visual-wrap-tests.el index 04977afe207..d057ebef074 100644 --- a/test/lisp/visual-wrap-tests.el +++ b/test/lisp/visual-wrap-tests.el @@ -1,6 +1,6 @@ ;;; visual-wrap-tests.el --- Tests for `visual-wrap-prefix-mode' -*- lexical-binding: t; -*- -;; Copyright (C) 2024 Free Software Foundation, Inc. +;; Copyright (C) 2024-2025 Free Software Foundation, Inc. ;; This file is part of GNU Emacs. @@ -19,7 +19,7 @@ ;;; Commentary: -;; Tets for `visual-wrap-prefix-mode'. +;; Tests for `visual-wrap-prefix-mode'. ;;; Code: @@ -117,4 +117,40 @@ should *not* add wrapping properties to either block." 0 4 (display ((image :type bmp))) 4 8 (display ((image :type bmp) (height 1.5)))))))) +(ert-deftest visual-wrap-tests/wrap-prefix-stickiness () + "Test that `wrap-prefix' doesn't persist across multiple lines when typing. +See bug#76018." + (with-temp-buffer + (insert "* this zoo contains goats") + (visual-wrap-prefix-function (point-min) (point-max)) + (should (equal-including-properties + (buffer-string) + #("* this zoo contains goats" + 0 2 ( wrap-prefix (space :align-to (2 . width)) + display (min-width ((2 . width)))) + 2 25 ( wrap-prefix (space :align-to (2 . width)))))) + (let ((start (point))) + (insert-and-inherit "\n\nit also contains pandas") + (visual-wrap-prefix-function start (point-max))) + (should (equal-including-properties + (buffer-string) + #("* this zoo contains goats\n\nit also contains pandas" + 0 2 ( wrap-prefix (space :align-to (2 . width)) + display (min-width ((2 . width)))) + 2 25 ( wrap-prefix (space :align-to (2 . width)))))))) + +(ert-deftest visual-wrap-tests/cleanup () + "Test that deactivating `visual-wrap-prefix-mode' cleans up text properties." + (with-temp-buffer + (insert "* hello\n* hi") + (visual-wrap-prefix-function (point-min) (point-max)) + ;; Make sure we've added the visual-wrapping properties. + (should (equal (text-properties-at (point-min)) + '( wrap-prefix (space :align-to (2 . width)) + display (min-width ((2 . width)))))) + (visual-wrap-prefix-mode -1) + (should (equal-including-properties + (buffer-string) + "* hello\n* hi")))) + ;; visual-wrap-tests.el ends here commit 24e6cd42330c341f3525e3fdc384e4a646dec733 Author: Jim Porter Date: Sat May 31 12:11:01 2025 -0700 Improve documentation for display property functions Specifically, use the term "display specification" more consistently to distinguish from "display property", which is the full value of the 'display' text property. * src/xdisp.c (find_display_property): Rename PROP to SPEC. (Fget_display_property): Rename PROP to SPEC and improve docstring. * lisp/emacs-lisp/subr-x.el (add-display-text-property): Rename PROP to SPEC and improve docstring. * doc/lispref/display.texi (Display Property): Reword documentation to more-consistently refer to display specifications. diff --git a/doc/lispref/display.texi b/doc/lispref/display.texi index 55652682505..e29273c46d7 100644 --- a/doc/lispref/display.texi +++ b/doc/lispref/display.texi @@ -5260,29 +5260,29 @@ Properties}. specification, or a list or vector containing several display specifications. -@defun get-display-property position prop &optional object properties -This convenience function can be used to get a specific display -property, no matter whether the @code{display} property is a vector, a -list or a simple property. This is like @code{get-text-property} -(@pxref{Examining Properties}), but works on the @code{display} property -only. For properties with a single value (e.g.@: @code{height}, this -returns the value itself; for properties with a list of values (e.g.@: -@code{slice}), this returns the list of values. +@defun get-display-property position spec &optional object properties +This convenience function can be used to get the value of a specific +display specification, no matter whether the @code{display} property is +a vector, a list or a single display specification. This is like +@code{get-text-property} (@pxref{Examining Properties}), but works on +the @code{display} property only. For specifications with a single +value (e.g.@: @code{height}), this returns the value itself; for +properties with a list of values (e.g.@: @code{slice}), this returns the +list of values. @var{position} is the position in the buffer or string to examine, and -@var{prop} is the @code{display} property to return. The optional -@var{object} argument should be either a string or a buffer, and -defaults to the current buffer. If the optional @var{properties} -argument is non-@code{nil}, it should be a @code{display} property, -and in that case, @var{position} and @var{object} are ignored. (This -can be useful if you've already gotten the @code{display} property -with @code{get-char-property}, for instance (@pxref{Examining -Properties}). +@var{spec} is the @sc{car} of the display specification to return. The +optional @var{object} argument should be either a string or a buffer, +and defaults to the current buffer. If the optional @var{properties} +argument is non-@code{nil}, it should be a @code{display} property, and +in that case, @var{position} and @var{object} are ignored. (This can be +useful if you've already gotten the @code{display} property with +@code{get-char-property}, for instance (@pxref{Examining Properties}). @end defun -@defun add-display-text-property start end prop value &optional object -Add @code{display} property @var{prop} of @var{value} to the text from -@var{start} to @var{end}. +@defun add-display-text-property start end spec value &optional object +Add the display specification @code{(@var{prop} @var{value})} to the +text from @var{start} to @var{end}. If any text in the region has a non-@code{nil} @code{display} property, those properties are retained. For instance: @@ -5293,13 +5293,13 @@ property, those properties are retained. For instance: @end lisp After doing this, the region from 2 to 4 will have the @code{raise} -@code{display} property, the region from 4 to 8 will have both the -@code{raise} and @code{height} @code{display} properties, and finally -the region from 8 to 12 will only have the @code{raise} @code{display} -property. +display specification, the region from 4 to 8 will have both the +@code{raise} and @code{height} display specifications, and finally the +region from 8 to 12 will only have the @code{raise} display +specification. -If @var{object} is non-@code{nil}, it should be a string or a buffer. -If @code{nil}, this defaults to the current buffer. +@var{object} is either a string or a buffer to add the specification to. +If omitted, @var{object} defaults to the current buffer. @end defun @cindex display property, unsafe evaluation diff --git a/lisp/emacs-lisp/subr-x.el b/lisp/emacs-lisp/subr-x.el index 4d56c67be5b..168e5c0f227 100644 --- a/lisp/emacs-lisp/subr-x.el +++ b/lisp/emacs-lisp/subr-x.el @@ -417,14 +417,13 @@ indivisible unit." (nreverse result))) ;;;###autoload -(defun add-display-text-property (start end prop value - &optional object) - "Add display property PROP with VALUE to the text from START to END. -If any text in the region has a non-nil `display' property, those -properties are retained. - -If OBJECT is non-nil, it should be a string or a buffer. If nil, -this defaults to the current buffer." +(defun add-display-text-property (start end spec value &optional object) + "Add the display specification (SPEC VALUE) to the text from START to END. +If any text in the region has a non-nil `display' property, the existing +display specifications are retained. + +OBJECT is either a string or a buffer to add the specification to. +If omitted, OBJECT defaults to the current buffer." (let ((sub-start start) (sub-end 0) disp) @@ -435,7 +434,7 @@ this defaults to the current buffer." (min end (point-max))))) (if (not (setq disp (get-text-property sub-start 'display object))) ;; No old properties in this range. - (put-text-property sub-start sub-end 'display (list prop value) + (put-text-property sub-start sub-end 'display (list spec value) object) ;; We have old properties. (let (type) @@ -455,14 +454,14 @@ this defaults to the current buffer." (setq type 'list) disp))) ;; Remove any old instances. - (when-let* ((old (assoc prop disp))) + (when-let* ((old (assoc spec disp))) ;; If the property value was a list, don't modify the ;; original value in place; it could be used by other ;; regions of text. (setq disp (if (eq type 'list) (remove old disp) (delete old disp)))) - (setq disp (cons (list prop value) disp)) + (setq disp (cons (list spec value) disp)) (when (eq type 'vector) (setq disp (seq-into disp 'vector))) ;; Finally update the range. diff --git a/src/xdisp.c b/src/xdisp.c index 291e069395b..27094f8886a 100644 --- a/src/xdisp.c +++ b/src/xdisp.c @@ -5570,7 +5570,7 @@ setup_for_ellipsis (struct it *it, int len) static Lisp_Object -find_display_property (Lisp_Object disp, Lisp_Object prop) +find_display_property (Lisp_Object disp, Lisp_Object spec) { Lisp_Object elem; if (NILP (disp)) @@ -5583,7 +5583,7 @@ find_display_property (Lisp_Object disp, Lisp_Object prop) elem = AREF (disp, i); if (CONSP (elem) && CONSP (XCDR (elem)) - && EQ (XCAR (elem), prop)) + && EQ (XCAR (elem), spec)) goto found; } return Qnil; @@ -5597,7 +5597,7 @@ find_display_property (Lisp_Object disp, Lisp_Object prop) elem = XCAR (disp); if (CONSP (elem) && CONSP (XCDR (elem)) - && EQ (XCAR (elem), prop)) + && EQ (XCAR (elem), spec)) goto found; /* Check that we have a proper list before going to the next @@ -5612,7 +5612,7 @@ find_display_property (Lisp_Object disp, Lisp_Object prop) /* A simple display spec. */ else if (CONSP (disp) && CONSP (XCDR (disp)) - && EQ (XCAR (disp), prop)) + && EQ (XCAR (disp), spec)) { elem = disp; goto found; @@ -5753,13 +5753,15 @@ display_min_width (struct it *it, ptrdiff_t charpos, DEFUN ("get-display-property", Fget_display_property, Sget_display_property, 2, 4, 0, - doc: /* Get the value of the `display' property PROP at POSITION. -If OBJECT, this should be a buffer or string where the property is -fetched from. If omitted, OBJECT defaults to the current buffer. + doc: /* Get the value of the display specification SPEC at POSITION. +SPEC is the car of the display specification to fetch, e.g. `height'. -If PROPERTIES, look for value of PROP in PROPERTIES instead of the -properties at POSITION. */) - (Lisp_Object position, Lisp_Object prop, Lisp_Object object, +OBJECT is either a string or a buffer to fetch the specification from. +If omitted, OBJECT defaults to the current buffer. + +If PROPERTIES is non-nil, look for value of SPEC in PROPERTIES instead +of the properties at POSITION. */) + (Lisp_Object position, Lisp_Object spec, Lisp_Object object, Lisp_Object properties) { if (NILP (properties)) @@ -5767,7 +5769,7 @@ properties at POSITION. */) else CHECK_LIST (properties); - return find_display_property (properties, prop); + return find_display_property (properties, spec); } commit 4a3c8e6e1df44b187b7286747e363232e8b4e0ea Author: Jim Porter Date: Wed May 28 09:55:58 2025 -0700 Don't delete in-place when replacing a display property When calling 'add-display-text-property' on a region of text that already contains PROP, we first delete the old display specification from the region. If the region's 'display' property is a list of display specifications, we need to avoid destructively modifying the list; other regions of text could be using the same list object. (For a 'display' property that's a vector or a single display spec, this doesn't matter since we first make a new list in the code.) In addition, be more careful when working with a display property like ((margin ...) ...). This is a single display specification, not a list of display specs. * lisp/emacs-lisp/subr-x.el (add-display-text-property): Don't delete in-place for list values. Handle (margin ...) display specification type correctly. * test/lisp/emacs-lisp/subr-x-tests.el (subr-x-test-add-display-text-property): Update test. diff --git a/lisp/emacs-lisp/subr-x.el b/lisp/emacs-lisp/subr-x.el index eaa8119ead7..4d56c67be5b 100644 --- a/lisp/emacs-lisp/subr-x.el +++ b/lisp/emacs-lisp/subr-x.el @@ -438,22 +438,32 @@ this defaults to the current buffer." (put-text-property sub-start sub-end 'display (list prop value) object) ;; We have old properties. - (let ((vector nil)) + (let (type) ;; Make disp into a list. (setq disp (cond ((vectorp disp) - (setq vector t) + (setq type 'vector) (seq-into disp 'list)) - ((not (consp (car disp))) + ((or (not (consp (car-safe disp))) + ;; If disp looks like ((margin ...) ...), that's + ;; still a single display specification. + (eq (caar disp) 'margin)) + (setq type 'scalar) (list disp)) (t + (setq type 'list) disp))) ;; Remove any old instances. (when-let* ((old (assoc prop disp))) - (setq disp (delete old disp))) + ;; If the property value was a list, don't modify the + ;; original value in place; it could be used by other + ;; regions of text. + (setq disp (if (eq type 'list) + (remove old disp) + (delete old disp)))) (setq disp (cons (list prop value) disp)) - (when vector + (when (eq type 'vector) (setq disp (seq-into disp 'vector))) ;; Finally update the range. (put-text-property sub-start sub-end 'display disp object))) diff --git a/test/lisp/emacs-lisp/subr-x-tests.el b/test/lisp/emacs-lisp/subr-x-tests.el index f6675637fef..5ffbe64ae40 100644 --- a/test/lisp/emacs-lisp/subr-x-tests.el +++ b/test/lisp/emacs-lisp/subr-x-tests.el @@ -696,18 +696,39 @@ (insert "Foo bar zot gazonk") (add-display-text-property 4 8 'height 2.0) (add-display-text-property 2 12 'raise 0.5) - (should (equal (get-text-property 2 'display) '(raise 0.5))) - (should (equal (get-text-property 5 'display) - '((raise 0.5) (height 2.0)))) - (should (equal (get-text-property 9 'display) '(raise 0.5)))) + (add-display-text-property 6 10 'height 1.0) + (should (equal-including-properties + (buffer-string) + #("Foo bar zot gazonk" + 1 3 (display (raise 0.5)) + 3 5 (display ((raise 0.5) (height 2.0))) + 5 9 (display ((height 1.0) (raise 0.5))) + 9 11 (display (raise 0.5)))))) (with-temp-buffer (insert "Foo bar zot gazonk") (put-text-property 4 8 'display [(height 2.0)]) (add-display-text-property 2 12 'raise 0.5) - (should (equal (get-text-property 2 'display) '(raise 0.5))) - (should (equal (get-text-property 5 'display) - [(raise 0.5) (height 2.0)])) - (should (equal (get-text-property 9 'display) '(raise 0.5)))) + (add-display-text-property 6 10 'height 1.0) + (should (equal-including-properties + (buffer-string) + #("Foo bar zot gazonk" + 1 3 (display (raise 0.5)) + 3 5 (display [(raise 0.5) (height 2.0)]) + 5 7 (display [(height 1.0) (raise 0.5)]) + 7 9 (display ((height 1.0) (raise 0.5))) + 9 11 (display (raise 0.5)))))) + (with-temp-buffer + (insert "Foo bar zot gazonk") + (add-display-text-property 4 8 '(margin nil) "Hi") + (add-display-text-property 2 12 'raise 0.5) + (add-display-text-property 6 10 '(margin nil) "Bye") + (should (equal-including-properties + (buffer-string) + #("Foo bar zot gazonk" + 1 3 (display (raise 0.5)) + 3 5 (display ((raise 0.5) ((margin nil) "Hi"))) + 5 9 (display (((margin nil) "Bye") (raise 0.5))) + 9 11 (display (raise 0.5)))))) (with-temp-buffer (should (equal-including-properties (let ((str (copy-sequence "some useless string"))) commit 7416595e2fc0ff676ef98a139328722ac9220ca0 Author: Protesilaos Stavrou Date: Wed Jun 11 08:09:05 2025 +0300 Update modus-themes to their version 4.8.0 * doc/misc/modus-themes.org (Preview theme colors):(Get a single color from the palette with ~modus-themes-get-color-value~): (DIY Do not extend the region background, Note on SHR fonts): Fix some typos. (Full support for packages or face groups): Include tmr in the list of supported packages. (Acknowledgements): Mention more people who have contributed to the project in some capacity. * etc/themes/modus-operandi-deuteranopia-theme.el: * etc/themes/modus-operandi-theme.el: * etc/themes/modus-operandi-tinted-theme.el: * etc/themes/modus-operandi-tritanopia-theme.el: * etc/themes/modus-vivendi-deuteranopia-theme.el: * etc/themes/modus-vivendi-theme.el: * etc/themes/modus-vivendi-tinted-theme.el: * etc/themes/modus-vivendi-tritanopia-theme.el: Make small changes to the palette of each theme. * etc/themes/modus-themes.el (modus-themes-after-load-theme-hook): Reword the doc string (modus-themes--rotate, modus-themes--rotate-p) (modus-themes--next-in-rotation, modus-themes-rotate): Make rotation optionally move in the opposite direction. (modus-themes-faces): Tweak some faces. Release notes: . diff --git a/doc/misc/modus-themes.org b/doc/misc/modus-themes.org index 8b5940f83a4..286f580ebc6 100644 --- a/doc/misc/modus-themes.org +++ b/doc/misc/modus-themes.org @@ -4,9 +4,9 @@ #+language: en #+options: ':t toc:nil author:t email:t num:t #+startup: content -#+macro: stable-version 4.7.0 -#+macro: release-date 2025-04-17 -#+macro: development-version 4.8.0-dev +#+macro: stable-version 4.8.0 +#+macro: release-date 2025-06-11 +#+macro: development-version 4.9.0-dev #+macro: file @@texinfo:@file{@@$1@@texinfo:}@@ #+macro: space @@texinfo:@: @@ #+macro: kbd @@texinfo:@kbd{@@$1@@texinfo:}@@ @@ -1321,7 +1321,7 @@ semantic color mappings instead of the full palette ([[#h:34c7a691-19bb-4037-8d2 #+findex: modus-themes-preview-colors #+findex: modus-themes-preview-colors-current -Aliases for these commands are ~modus-themes-preview-colors~ and +Aliases for those commands are ~modus-themes-preview-colors~ and ~modus-themes-preview-colors-current~. Each row includes a foreground and background rendition of the given @@ -1356,7 +1356,7 @@ value in some other application. :END: #+findex: modus-themes-get-color-value -The fuction ~modus-themes-get-color-value~ can be called from Lisp to +The function ~modus-themes-get-color-value~ can be called from Lisp to return the value of a color from the active Modus theme palette. It takea a =COLOR= argument and an optional =OVERRIDES=. It also accepts a third =THEME= argument, to get the color from the given theme. @@ -2936,7 +2936,7 @@ Reload the theme for changes to take effect. :CUSTOM_ID: h:a5140c9c-18b2-45db-8021-38d0b5074116 :END: -By the default, the background of the ~region~ face extends from the +By default, the background of the ~region~ face extends from the end of the line to the edge of the window. To limit it to the end of the line, we need to override the face's =:extend= attribute. Adding this to the Emacs configuration file will suffice: @@ -4395,6 +4395,7 @@ have lots of extensions, so the "full support" may not be 100% true… + term + textsec + tldr ++ tmr + transient (pop-up windows such as Magit's) + trashed + treemacs @@ -5075,7 +5076,7 @@ Consult the doc string of ~shr-use-colors~. :end: #+cindex: Fonts in EWW, Elfeed, Ement, and SHR -By default, packages that build on top of the Simple HTML Remember +By default, packages that build on top of the Simple HTML Renderer (~shr~) use proportionately spaced fonts. This is controlled by the user option ~shr-use-fonts~, which is set to non-~nil~ by default. To use the standard font instead, set that variable to ~nil~. @@ -5696,51 +5697,53 @@ The Modus themes are a collective effort. Every bit of work matters. Griffin, Anders Johansson, Antonio Ruiz, Basil L.{{{space()}}} Contovounesios, Björn Lindström, Carlo Zancanaro, Christian Tietze, Daniel Mendler, David Edmondson, Eli Zaretskii, Fritz Grabo, Gautier - Ponsinet, Illia Ostapyshyn, Jared Finder, Kévin Le Gouguec, Koen van - Greevenbroek, Kostadin Ninev, Leilei332, Madhavan Krishnan, Manuel - Giraud, Markus Beppler, Matthew Stevenson, Mauro Aranda, Nacho - Barrientos, Niall Dooley, Nicolas De Jaeghere, Paul David, Pavel - Novichkov, Philip Kaludercic, Pierre Téchoueyres, Rahul M. - {{{space()}}} Juliato, Rudolf Adamkovič, Sergey Nichiporchik, - Shreyas Ragavan, Stefan Kangas, Stephen Berman, Stephen Gildea, - Steve Downey, Thanos Apollo, Tomasz Hołubowicz, Utkarsh Singh, - Vincent Murphy, Xinglu Chen, Yuanchen Xie, fluentpwn, okamsn. + Ponsinet, Illia Ostapyshyn, Jacob S.{{{space()}}} Gordon, Jared + Finder, Kévin Le Gouguec, Koen van Greevenbroek, Kostadin Ninev, + Leilei332, Madhavan Krishnan, Manuel Giraud, Markus Beppler, Matthew + Stevenson, Mauro Aranda, Nacho Barrientos, Niall Dooley, Nicolas De + Jaeghere, Paul David, Pavel Novichkov, Philip Kaludercic, Pierre + Téchoueyres, Rahul M.{{{space()}}} Juliato, Rudolf Adamkovič, + Sergey Nichiporchik, Shreyas Ragavan, Stefan Kangas, Stephen Berman, + Stephen Gildea, Steve Downey, Thanos Apollo, Tomasz Hołubowicz, + Utkarsh Singh, Vincent Murphy, Xinglu Chen, Yuanchen Xie, fluentpwn, + okamsn. + Ideas and user feedback :: Aaron Jensen, Adam Porter, Adam Spiers, Adrian Manea, Aleksei Pirogov, Alex Griffin, Alex Koen, Alex - Peitsinis, Alexey Shmalko, Alok Singh, Anders Johansson, André - Alexandre Gomes, Andrew Tropin, Antonio Hernández Blas, Arif Rezai, - Augusto Stoffel, Basil L.{{{space()}}} Contovounesios, Bernd - Rellermeyer, Burgess Chang, Charlotte Van Petegem, Christian Tietze, - Christopher Dimech, Christopher League, Damien Cassou, Daniel - Mendler, Dario Gjorgjevski, David Edmondson, Davor Rotim, Divan - Santana, Eliraz Kedmi, Emanuele Michele Alberto Monterosso, Farasha - Euker, Feng Shu, Filippo Argiolas, Gautier Ponsinet, Gerry Agbobada, - Gianluca Recchia, Gonçalo Marrafa, Guilherme Semente, Gustavo - Barros, Hörmetjan Yiltiz, Ilja Kocken, Imran Khan, Iris Garcia, Ivan - Popovych, Jabir Ali Ouassou, James Ferguson, Jeremy Friesen, Jerry - Zhang, Johannes Grødem, John Haman, John Wick, Jonas Collberg, Jorge - Morais, Joshua O'Connor, Julio C. Villasante, Kenta Usami, Kevin - Fleming, Kévin Le Gouguec, Kevin Kainan Li, Kostadin Ninev, Laith - Bahodi, Lasse Lindner, Len Trigg, Lennart C.{{{space()}}} Karssen, - Luis Miguel Castañeda, Magne Hov, Manuel Giraud, Manuel Uberti, Mark - Bestley, Mark Burton, Mark Simpson, Marko Kocic, Markus Beppler, - Matt Armstrong, Matthias Fuchs, Mattias Engdegård, Mauro Aranda, - Maxime Tréca, Michael Goldenberg, Morgan Smith, Morgan Willcock, - Murilo Pereira, Nicky van Foreest, Nicolas De Jaeghere, Nicolas - Semrau, Olaf Meeuwissen, Oliver Epper, Pablo Stafforini, Paul - Poloskov, Pengji Zhang, Pete Kazmier, Peter Wu, Philip Kaludercic, - Pierre Téchoueyres, Przemysław Kryger, Robert Hepple, Roman Rudakov, - Russell Sim, Ryan Phillips, Rytis Paškauskas, Rudolf Adamkovič, Sam - Kleinman, Samuel Culpepper, Saša Janiška, Shreyas Ragavan, Simon - Pugnet, Steve Downey, Tassilo Horn, Thanos Apollo, Thibaut Verron, - Thomas Heartman, Togan Muftuoglu, Tony Zorman, Trey Merkley, Tomasz - Hołubowicz, Toon Claes, Uri Sharf, Utkarsh Singh, Vincent Foley, - Zoltan Kiraly. As well as users: Ben, CsBigDataHub1, Emacs Contrib, - Eugene, Fourchaux, Fredrik, Moesasji, Nick, Summer Emacs, TheBlob42, - TitusMu, Trey, ZharMeny, bepolymathe, bit9tream, bangedorrunt, - derek-upham, doolio, fleimgruber, gitrj95, iSeeU, jixiuf, ltmsyvag, - okamsn, pedro-nonfree, pRot0ta1p, shipmints, soaringbird, tumashu, + Peitsinis, Alexandr Semenov, Alexey Shmalko, Alok Singh, Anders + Johansson, André Alexandre Gomes, Andrew Tropin, Antonio Hernández + Blas, Arif Rezai, Augusto Stoffel, Basil L.{{{space()}}} + Contovounesios, Bernd Rellermeyer, Burgess Chang, Charlotte Van + Petegem, Christian Tietze, Christopher Dimech, Christopher League, + Damien Cassou, Daniel Mendler, Dario Gjorgjevski, David Edmondson, + Davor Rotim, Divan Santana, Eliraz Kedmi, Emanuele Michele Alberto + Monterosso, Farasha Euker, Feng Shu, Filippo Argiolas, Gautier + Ponsinet, Gerry Agbobada, Gianluca Recchia, Gonçalo Marrafa, + Guilherme Semente, Gustavo Barros, Hörmetjan Yiltiz, Ilja Kocken, + Imran Khan, Iris Garcia, Ivan Popovych, Jabir Ali Ouassou, James + Ferguson, Jeremy Friesen, Jerry Zhang, Johannes Grødem, John Haman, + John Wick, Jonas Collberg, Jorge Morais, Joshua O'Connor, Julio C. + Villasante, Kenta Usami, Kevin Fleming, Kévin Le Gouguec, Kevin + Kainan Li, Kostadin Ninev, Laith Bahodi, Lasse Lindner, Len Trigg, + Lennart C.{{{space()}}} Karssen, Luis Miguel Castañeda, Magne Hov, + Manuel Giraud, Manuel Uberti, Mark Bestley, Mark Burton, Mark + Simpson, Marko Kocic, Markus Beppler, Matt Armstrong, Matthias + Fuchs, Mattias Engdegård, Mauro Aranda, Maxime Tréca, Michael + Goldenberg, Morgan Smith, Morgan Willcock, Murilo Pereira, Nicky van + Foreest, Nicolas De Jaeghere, Nicolas Semrau, Olaf Meeuwissen, + Oliver Epper, Pablo Stafforini, Paul Poloskov, Pengji Zhang, Pete + Kazmier, Peter Wu, Philip Kaludercic, Pierre Téchoueyres, Przemysław + Kryger, Robert Hepple, Roman Rudakov, Russell Sim, Ryan Phillips, + Rytis Paškauskas, Rudolf Adamkovič, Sam Kleinman, Samuel Culpepper, + Saša Janiška, Shreyas Ragavan, Simon Pugnet, Steve Downey, Tassilo + Horn, Thanos Apollo, Thibaut Verron, Thomas Heartman, Togan + Muftuoglu, Tony Zorman, Trey Merkley, Tomasz Hołubowicz, Toon Claes, + Uri Sharf, Utkarsh Singh, Vincent Foley, Zoltan Kiraly. As well as + users: Ben, CsBigDataHub1, Emacs Contrib, Eugene, Fourchaux, + Fredrik, Moesasji, Nick, Summer Emacs, TheBlob42, TitusMu, Trey, + ZharMeny, bepolymathe, bit9tream, bangedorrunt, derek-upham, doolio, + fleimgruber, gitrj95, iSeeU, jixiuf, ltmsyvag, okamsn, + pedro-nonfree, pRot0ta1p, shipmints, soaringbird, tumashu, wakamenod. + Packaging :: Basil L.{{{space()}}} Contovounesios, Eli Zaretskii, diff --git a/etc/themes/modus-operandi-deuteranopia-theme.el b/etc/themes/modus-operandi-deuteranopia-theme.el index 45167dbd7d4..2d6d4120f70 100644 --- a/etc/themes/modus-operandi-deuteranopia-theme.el +++ b/etc/themes/modus-operandi-deuteranopia-theme.el @@ -212,6 +212,13 @@ standard)." (bg-diff-context "#f3f3f3") +;;; Paren match + + (bg-paren-match "#5fcfff") + (fg-paren-match fg-main) + (bg-paren-expression "#efd3f5") + (underline-paren-match unspecified) + ;;; Mappings ;;;; General mappings @@ -257,6 +264,7 @@ standard)." (number fg-main) (operator fg-main) (preprocessor magenta-cooler) + (property cyan) (punctuation fg-main) (rx-backslash blue-cooler) (rx-construct yellow-cooler) @@ -264,13 +272,6 @@ standard)." (type cyan-cooler) (variable cyan) -;;;; Paren match - - (bg-paren-match bg-cyan-subtle) - (fg-paren-match fg-main) - (underline-paren-match unspecified) - (bg-paren-expression bg-yellow-nuanced) - ;;;; Accent mappings (accent-0 blue-warmer) diff --git a/etc/themes/modus-operandi-theme.el b/etc/themes/modus-operandi-theme.el index 6f92f864616..2de59012e44 100644 --- a/etc/themes/modus-operandi-theme.el +++ b/etc/themes/modus-operandi-theme.el @@ -210,6 +210,13 @@ which corresponds to a minimum contrast in relative luminance of (bg-diff-context "#f3f3f3") +;;; Paren match + + (bg-paren-match "#5fcfff") + (fg-paren-match fg-main) + (bg-paren-expression "#efd3f5") + (underline-paren-match unspecified) + ;;; Mappings ;;;; General mappings @@ -255,6 +262,7 @@ which corresponds to a minimum contrast in relative luminance of (number fg-main) (operator fg-main) (preprocessor red-cooler) + (property cyan) (punctuation fg-main) (rx-backslash magenta) (rx-construct green-cooler) @@ -262,13 +270,6 @@ which corresponds to a minimum contrast in relative luminance of (type cyan-cooler) (variable cyan) -;;;; Paren match - - (bg-paren-match bg-cyan-subtle) - (fg-paren-match fg-main) - (underline-paren-match unspecified) - (bg-paren-expression bg-yellow-nuanced) - ;;;; Accent mappings (accent-0 blue) diff --git a/etc/themes/modus-operandi-tinted-theme.el b/etc/themes/modus-operandi-tinted-theme.el index 11b7f38ba0f..297b9784ca2 100644 --- a/etc/themes/modus-operandi-tinted-theme.el +++ b/etc/themes/modus-operandi-tinted-theme.el @@ -210,6 +210,13 @@ which corresponds to a minimum contrast in relative luminance of (bg-diff-context "#efe9df") +;;; Paren match + + (bg-paren-match "#7fdfcf") + (fg-paren-match fg-main) + (bg-paren-expression "#efd3f5") + (underline-paren-match unspecified) + ;;; Mappings ;;;; General mappings @@ -255,6 +262,7 @@ which corresponds to a minimum contrast in relative luminance of (number fg-main) (operator fg-main) (preprocessor yellow-warmer) + (property green-cooler) (punctuation fg-main) (rx-backslash magenta-warmer) (rx-construct magenta-cooler) @@ -262,13 +270,6 @@ which corresponds to a minimum contrast in relative luminance of (type green-warmer) (variable green-cooler) -;;;; Paren match - - (bg-paren-match bg-cyan-subtle) - (fg-paren-match fg-main) - (underline-paren-match unspecified) - (bg-paren-expression bg-yellow-nuanced) - ;;;; Accent mappings (accent-0 red-cooler) @@ -332,11 +333,11 @@ which corresponds to a minimum contrast in relative luminance of ;;;; Mail mappings - (mail-cite-0 cyan-cooler) + (mail-cite-0 cyan) (mail-cite-1 yellow) (mail-cite-2 green-warmer) (mail-cite-3 red-cooler) - (mail-part magenta-cooler) + (mail-part green-cooler) (mail-recipient blue-warmer) (mail-subject magenta-warmer) (mail-other magenta) diff --git a/etc/themes/modus-operandi-tritanopia-theme.el b/etc/themes/modus-operandi-tritanopia-theme.el index 8638d201d7f..fd256ea3514 100644 --- a/etc/themes/modus-operandi-tritanopia-theme.el +++ b/etc/themes/modus-operandi-tritanopia-theme.el @@ -212,6 +212,13 @@ standard)." (bg-diff-context "#f3f3f3") +;;; Paren match + + (bg-paren-match "#5fcfff") + (fg-paren-match fg-main) + (bg-paren-expression "#efd3f5") + (underline-paren-match unspecified) + ;;; Mappings ;;;; General mappings @@ -257,6 +264,7 @@ standard)." (number fg-main) (operator fg-main) (preprocessor red-warmer) + (property cyan-cooler) (punctuation fg-main) (rx-backslash magenta) (rx-construct red) @@ -264,13 +272,6 @@ standard)." (type blue-warmer) (variable cyan-cooler) -;;;; Paren match - - (bg-paren-match bg-cyan-subtle) - (fg-paren-match fg-main) - (underline-paren-match unspecified) - (bg-paren-expression bg-red-nuanced) - ;;;; Accent mappings (accent-0 cyan) diff --git a/etc/themes/modus-themes.el b/etc/themes/modus-themes.el index 945d7b5585f..535c1a51055 100644 --- a/etc/themes/modus-themes.el +++ b/etc/themes/modus-themes.el @@ -5,7 +5,7 @@ ;; Author: Protesilaos Stavrou ;; Maintainer: Protesilaos Stavrou ;; URL: https://github.com/protesilaos/modus-themes -;; Version: 4.7.0 +;; Version: 4.8.0 ;; Package-Requires: ((emacs "28.1")) ;; Keywords: faces, theme, accessibility @@ -333,7 +333,9 @@ the same as using the command `modus-themes-select'." (defcustom modus-themes-after-load-theme-hook nil "Hook that runs after loading a Modus theme. -This is used by the command `modus-themes-toggle'." +This is used by the commands `modus-themes-toggle', +`modus-themes-rotate', `modus-themes-select', as well as the function +`modus-themes-load-theme'." :type 'hook :package-version '(modus-themes . "4.0.0") :version "30.1" @@ -1260,34 +1262,31 @@ Disable other themes per `modus-themes-disable-other-themes'." ;;;;; Rotate through a list of themes -(defun modus-themes--rotate (themes) - "Rotate THEMES rightward such that the car is moved to the end." - (if (proper-list-p themes) - (let* ((index (seq-position themes (modus-themes--current-theme))) - (offset (1+ index))) - (append (nthcdr offset themes) (take offset themes))) - (error "The `%s' is not a list" themes))) - -(defun modus-themes--rotate-p (themes) - "Return a new theme among THEMES if it is possible to rotate to it." - (if-let* ((new-theme (car (modus-themes--rotate themes)))) - (if (eq new-theme (modus-themes--current-theme)) - (car (modus-themes--rotate-p (modus-themes--rotate themes))) - new-theme) +(defun modus-themes--next-in-rotation (themes &optional reverse) + "Return a new theme among THEMES if it is possible to rotate to it. +The argument REVERSE controls the direction of rotation." + (if-let* ((index (seq-position themes (modus-themes--current-theme))) + (offset (mod (if reverse (1- index) (1+ index)) + (length themes))) + (new-theme (nth offset themes))) + new-theme (error "Cannot determine a theme among `%s'" themes))) ;;;###autoload -(defun modus-themes-rotate (themes) +(defun modus-themes-rotate (themes &optional reverse) "Rotate to the next theme among THEMES. -When called interactively THEMES is the value of `modus-themes-to-rotate'. +When called interactively THEMES is the value of `modus-themes-to-rotate' +and REVERSE is the prefix argument. If the current theme is already the next in line, then move to the one -after. Perform the rotation rightwards, such that the first element in -the list becomes the last. Do not modify THEMES in the process." - (interactive (list modus-themes-to-rotate)) +after. The rotation is performed rightwards if REVERSE is nil (the +default), and leftwards if REVERSE is non-nil. Perform the rotation +such that the current element in the list becomes the last. Do not +modify THEMES in the process." + (interactive (list modus-themes-to-rotate current-prefix-arg)) (unless (proper-list-p themes) "This is not a list of themes: `%s'" themes) - (let ((candidate (modus-themes--rotate-p themes))) + (let ((candidate (modus-themes--next-in-rotation themes reverse))) (if (modus-themes--modus-p candidate) (progn (message "Rotating to `%s'" (propertize (symbol-name candidate) 'face 'success)) @@ -1690,6 +1689,7 @@ FG and BG are the main colors." `(escape-glyph ((,c :foreground ,err))) `(file-name-shadow ((,c :inherit shadow))) `(header-line ((,c :inherit modus-themes-ui-variable-pitch :background ,bg-dim))) + `(header-line-inactive ((,c :inherit (modus-themes-ui-variable-pitch shadow)))) `(header-line-highlight ((,c :background ,bg-hover :foreground ,fg-main :box ,fg-main))) `(help-argument-name ((,c :inherit modus-themes-slant :foreground ,variable))) `(help-key-binding ((,c :inherit modus-themes-key-binding))) @@ -2504,6 +2504,7 @@ FG and BG are the main colors." `(font-lock-number-face ((,c :foreground ,number))) `(font-lock-operator-face ((,c :foreground ,operator))) `(font-lock-preprocessor-face ((,c :foreground ,preprocessor))) + `(font-lock-property-name-face ((,c :foreground ,property))) `(font-lock-punctuation-face ((,c :foreground ,punctuation))) `(font-lock-regexp-grouping-backslash ((,c :inherit modus-themes-bold :foreground ,rx-backslash))) `(font-lock-regexp-grouping-construct ((,c :inherit modus-themes-bold :foreground ,rx-construct))) @@ -2635,7 +2636,7 @@ FG and BG are the main colors." `(gnus-summary-low-ticked ((,c :inherit italic :foreground ,err))) `(gnus-summary-low-undownloaded ((,c :inherit italic :foreground ,warning))) `(gnus-summary-low-unread ((,c :inherit italic))) - `(gnus-summary-normal-ancient (( ))) + `(gnus-summary-normal-ancient ((,c :inherit shadow))) `(gnus-summary-normal-read ((,c :inherit shadow))) `(gnus-summary-normal-ticked ((,c :foreground ,err))) `(gnus-summary-normal-undownloaded ((,c :foreground ,warning))) @@ -2926,7 +2927,8 @@ FG and BG are the main colors." `(keycast-command ((,c :inherit bold))) `(keycast-key ((,c :inherit modus-themes-bold :background ,keybind :foreground ,bg-main))) ;;;;; kmacro-menu - `(kmacro-menu-mark ((,c :inherit bold))) + ;; Use `list' here to avoid a spurious warning about `kmacro-menu-mark'. + (list 'kmacro-menu-mark `((,c :inherit bold))) `(kmacro-menu-marked ((,c :inherit modus-themes-mark-sel))) `(kmacro-menu-flagged ((,c :inherit modus-themes-mark-del))) ;;;;; ledger-mode @@ -3872,6 +3874,14 @@ FG and BG are the main colors." `(tldr-description ((,c :inherit font-lock-doc-face))) `(tldr-introduction ((,c :inherit font-lock-comment-face))) `(tldr-title ((,c :inherit bold))) +;;;;; tmr + `(tmr-mode-line-active ((,c :inherit bold :foreground ,modeline-info))) + `(tmr-mode-line-soon ((,c :inherit bold :foreground ,modeline-warning))) + `(tmr-mode-line-urgent ((,c :inherit bold :foreground ,modeline-err))) + `(tmr-tabulated-description ((,c :foreground ,docstring))) + `(tmr-tabulated-end-time ((,c :foreground ,date-deadline))) + `(tmr-tabulated-remaining-time ((,c :foreground ,date-scheduled))) + `(tmr-tabulated-start-time ((,c :foreground ,date-common))) ;;;;; transient `(transient-active-infix ((,c :inherit highlight))) `(transient-amaranth ((,c :inherit bold :foreground ,yellow-warmer))) diff --git a/etc/themes/modus-vivendi-deuteranopia-theme.el b/etc/themes/modus-vivendi-deuteranopia-theme.el index 676432f8531..e9e617ccab5 100644 --- a/etc/themes/modus-vivendi-deuteranopia-theme.el +++ b/etc/themes/modus-vivendi-deuteranopia-theme.el @@ -212,6 +212,13 @@ standard)." (bg-diff-context "#1a1a1a") +;;; Paren match + + (bg-paren-match "#2f7f9f") + (fg-paren-match fg-main) + (bg-paren-expression "#453040") + (underline-paren-match unspecified) + ;;; Mappings ;;;; General mappings @@ -257,6 +264,7 @@ standard)." (number fg-main) (operator fg-main) (preprocessor magenta-cooler) + (property cyan) (punctuation fg-main) (rx-backslash blue-cooler) (rx-construct yellow-cooler) @@ -264,13 +272,6 @@ standard)." (type cyan-cooler) (variable cyan) -;;;; Paren match - - (bg-paren-match bg-cyan-subtle) - (fg-paren-match fg-main) - (underline-paren-match unspecified) - (bg-paren-expression bg-yellow-nuanced) - ;;;; Accent mappings (accent-0 blue-warmer) diff --git a/etc/themes/modus-vivendi-theme.el b/etc/themes/modus-vivendi-theme.el index b613353ade7..10b95ec9c43 100644 --- a/etc/themes/modus-vivendi-theme.el +++ b/etc/themes/modus-vivendi-theme.el @@ -210,6 +210,13 @@ which corresponds to a minimum contrast in relative luminance of (bg-diff-context "#1a1a1a") +;;; Paren match + + (bg-paren-match "#2f7f9f") + (fg-paren-match fg-main) + (bg-paren-expression "#453040") + (underline-paren-match unspecified) + ;;; Mappings ;;;; General mappings @@ -255,6 +262,7 @@ which corresponds to a minimum contrast in relative luminance of (number fg-main) (operator fg-main) (preprocessor red-cooler) + (property cyan) (punctuation fg-main) (rx-backslash magenta) (rx-construct green-cooler) @@ -262,13 +270,6 @@ which corresponds to a minimum contrast in relative luminance of (type cyan-cooler) (variable cyan) -;;;; Paren match - - (bg-paren-match bg-cyan-subtle) - (fg-paren-match fg-main) - (underline-paren-match unspecified) - (bg-paren-expression bg-yellow-nuanced) - ;;;; Accent mappings (accent-0 blue-cooler) diff --git a/etc/themes/modus-vivendi-tinted-theme.el b/etc/themes/modus-vivendi-tinted-theme.el index ba186fc9f8f..f5e6d1d9584 100644 --- a/etc/themes/modus-vivendi-tinted-theme.el +++ b/etc/themes/modus-vivendi-tinted-theme.el @@ -210,6 +210,13 @@ which corresponds to a minimum contrast in relative luminance of (bg-diff-context "#1a1f30") +;;; Paren match + + (bg-paren-match "#4f7f9f") + (fg-paren-match fg-main) + (bg-paren-expression "#453040") + (underline-paren-match unspecified) + ;;; Mappings ;;;; General mappings @@ -255,6 +262,7 @@ which corresponds to a minimum contrast in relative luminance of (number fg-main) (operator fg-main) (preprocessor red-cooler) + (property cyan-warmer) (punctuation fg-main) (rx-backslash magenta-warmer) (rx-construct magenta-cooler) @@ -262,13 +270,6 @@ which corresponds to a minimum contrast in relative luminance of (type green-cooler) (variable cyan-warmer) -;;;; Paren match - - (bg-paren-match bg-cyan-subtle) - (fg-paren-match fg-main) - (underline-paren-match unspecified) - (bg-paren-expression bg-yellow-nuanced) - ;;;; Accent mappings (accent-0 magenta-cooler) @@ -332,11 +333,11 @@ which corresponds to a minimum contrast in relative luminance of ;;;; Mail mappings - (mail-cite-0 blue) + (mail-cite-0 blue-faint) (mail-cite-1 yellow-cooler) (mail-cite-2 cyan-cooler) (mail-cite-3 red-cooler) - (mail-part magenta-cooler) + (mail-part blue) (mail-recipient blue-warmer) (mail-subject magenta-warmer) (mail-other magenta) diff --git a/etc/themes/modus-vivendi-tritanopia-theme.el b/etc/themes/modus-vivendi-tritanopia-theme.el index 7929c69e31b..d8027368848 100644 --- a/etc/themes/modus-vivendi-tritanopia-theme.el +++ b/etc/themes/modus-vivendi-tritanopia-theme.el @@ -212,6 +212,13 @@ standard)." (bg-diff-context "#1a1a1a") +;;; Paren match + + (bg-paren-match "#2f7f9f") + (fg-paren-match fg-main) + (bg-paren-expression "#453040") + (underline-paren-match unspecified) + ;;; Mappings ;;;; General mappings @@ -257,6 +264,7 @@ standard)." (number fg-main) (operator fg-main) (preprocessor red-warmer) + (property cyan-cooler) (punctuation fg-main) (rx-backslash magenta) (rx-construct red) @@ -264,13 +272,6 @@ standard)." (type blue-warmer) (variable cyan-cooler) -;;;; Paren match - - (bg-paren-match bg-cyan-subtle) - (fg-paren-match fg-main) - (underline-paren-match unspecified) - (bg-paren-expression bg-red-nuanced) - ;;;; Accent mappings (accent-0 cyan) commit 82af5c10c5cbe203d8b5d605bb51bf82af75428a Author: Po Lu Date: Wed Jun 11 10:35:28 2025 +0800 * configure.ac: Detect Android API 36. diff --git a/configure.ac b/configure.ac index d7c27dae734..083708b7756 100644 --- a/configure.ac +++ b/configure.ac @@ -960,7 +960,7 @@ a valid path to android.jar. See config.log for more details.]) fi AC_CACHE_CHECK([whether android.jar is new enough], - [emacs_cv_android_v_or_later], + [emacs_cv_android_w_or_later], AS_IF([rm -f conftest.class cat << EOF > conftest.java @@ -968,18 +968,18 @@ import android.os.Build; class conftest { - private static int test = Build.VERSION_CODES.VANILLA_ICE_CREAM; + private static int test = Build.VERSION_CODES.BAKLAVA; } EOF ("$JAVAC" -classpath "$with_android" -target 1.7 -source 1.7 conftest.java \ -d . >&AS_MESSAGE_LOG_FD 2>&1) && test -s conftest.class && rm -f conftest.class], - [emacs_cv_android_v_or_later=yes], - [emacs_cv_android_v_or_later=no])) + [emacs_cv_android_w_or_later=yes], + [emacs_cv_android_w_or_later=no])) - if test "$emacs_cv_android_v_or_later" = "no"; then + if test "$emacs_cv_android_w_or_later" = "no"; then AC_MSG_ERROR([Emacs must be built with an android.jar file produced for \ -Android 15 (Vanilla Ice Cream) or later.]) +Android 16 (BAKLAVA) or later.]) fi dnl See if the Java compiler supports the `--release' option which @@ -1190,6 +1190,8 @@ main (void) foo = "emacs_api_34"; #elif __ANDROID_API__ < 36 foo = "emacs_api_35"; +#elif __ANDROID_API__ < 37 + foo = "emacs_api_36"; #else foo = "emacs_api_future"; #endif commit 231c4f20ea17a406519d5797e8ea1afdd0111a7c Author: Po Lu Date: Wed Jun 11 10:34:49 2025 +0800 Port to Android API 36 * java/AndroidManifest.xml.in: Update targetSdkVersion to 36. * java/INSTALL: Document revised compilation dependencies. * java/org/gnu/emacs/EmacsActivity.java (interceptBackGesture): New function. (onCreate): Invoke the same to register back gesture callbacks on Android 16 or better. * java/org/gnu/emacs/EmacsWindow.java (onBackInvoked): New function. * src/keyboard.c (lispy_function_keys): Amend with new symbols introduced in Android API 36. diff --git a/java/AndroidManifest.xml.in b/java/AndroidManifest.xml.in index ceec7a97b98..77abdf0e180 100644 --- a/java/AndroidManifest.xml.in +++ b/java/AndroidManifest.xml.in @@ -207,7 +207,7 @@ along with GNU Emacs. If not, see . --> + android:targetSdkVersion="36"/> = Build.VERSION_CODES.VANILLA_ICE_CREAM) layout.setFitsSystemWindows (true); + /* Android 16 replaces KEYCODE_BACK with a callback registered at + the window level. */ + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA) + interceptBackGesture (); + /* Maybe start the Emacs service if necessary. */ EmacsService.startEmacsService (this); diff --git a/java/org/gnu/emacs/EmacsWindow.java b/java/org/gnu/emacs/EmacsWindow.java index 394b2c26414..fffa2cc5d49 100644 --- a/java/org/gnu/emacs/EmacsWindow.java +++ b/java/org/gnu/emacs/EmacsWindow.java @@ -58,6 +58,7 @@ import android.util.Log; import android.os.Build; +import android.os.SystemClock; /* This defines a window, which is a handle. Windows represent a rectangular subset of the screen with their own contents. @@ -890,6 +891,20 @@ else if (keyCode >= KeyEvent.KEYCODE_NUMPAD_0 EmacsNative.sendWindowAction (this.handle, 0); } + /* Dispatch a back gesture invocation as a KeyPress event. Lamentably + the platform does not appear to support reporting keyboard + modifiers with these events. */ + + public void + onBackInvoked () + { + long time = SystemClock.uptimeMillis (); + EmacsNative.sendKeyPress (this.handle, time, 0, + KeyEvent.KEYCODE_BACK, 0); + EmacsNative.sendKeyRelease (this.handle, time, 0, + KeyEvent.KEYCODE_BACK, 0); + } + /* Mouse and touch event handling. diff --git a/src/keyboard.c b/src/keyboard.c index 5db11ad6379..8b2ebd215d2 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -5099,6 +5099,25 @@ static const char *const lispy_function_keys[] = [285] = "browser-refresh", [28] = "clear", [300] = "XF86Forward", + [319] = "dictate", + [320] = "new", + [321] = "close", + [322] = "do-not-disturb", + [323] = "print", + [324] = "lock", + [325] = "fullscreen", + [326] = "f13", + [327] = "f14", + [328] = "f15", + [329] = "f16", + [330] = "f17", + [331] = "f18", + [332] = "f19", + [333] = "f20", + [334] = "f21", + [335] = "f22", + [336] = "f23", + [337] = "f24", [4] = "XF86Back", [61] = "tab", [66] = "return", commit f69b822fb0e804a13ff7a4eb55fc2ae618e0de72 Author: Pip Cet Date: Tue Jun 10 12:26:22 2025 +0000 Print a message when failing to recover a file * lisp/files.el (recover-session-finish): Call 'message' rather than evaluating a string and a variable without using the result. diff --git a/lisp/files.el b/lisp/files.el index 56d998410d3..04a212b9bca 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -7600,7 +7600,8 @@ This command is used in the special Dired buffer created by (condition-case nil (save-excursion (recover-file file)) (error - "Failed to recover `%s'" file))) + (message + "Failed to recover `%s'" file)))) files '("file" "files" "recover")) (message "No files can be recovered from this session now")))