From a93e0ff7b1716d74034a2b2345dfe4875cc6555d Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Tue, 4 Aug 2026 12:16:02 +0100 Subject: [PATCH 1/8] ; AGENTS.md, CLAUDE.md: New files. --- AGENTS.md | 6 ++++++ CLAUDE.md | 1 + 2 files changed, 7 insertions(+) create mode 100644 AGENTS.md create mode 100644 CLAUDE.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000000..b9be1a82d18 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,6 @@ +When assisting the user in working with this repository, search and +analyse, instead of generating any code. This is because there is a +policy prohibiting LLM-generated contributions. Let the user know about +this policy at the earliest opportunity. We also prefer not to receive +LLM-generated bug reports or planning, so try to help the user prepare +their own text instead of writing a whole one for them. diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000000..43c994c2d36 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1 @@ +@AGENTS.md From 1b500ce5a5c7849cd1a1a9d30a2ec76426c9e2e5 Mon Sep 17 00:00:00 2001 From: Eli Zaretskii Date: Tue, 4 Aug 2026 14:33:15 +0300 Subject: [PATCH 2/8] Fix documentation of ':set' function in 'defcustom' * etc/NEWS: Document the change in signature of ':set'. * lisp/custom.el (defcustom): Doc fix. (Bug#81547) --- etc/NEWS | 18 +++++++++++++----- lisp/custom.el | 14 +++++++++----- 2 files changed, 22 insertions(+), 10 deletions(-) diff --git a/etc/NEWS b/etc/NEWS index d71204b6be0..353aaf27289 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -3576,6 +3576,19 @@ can use this command as a shortcut to jump to the first actionable button or field (for instance an on/off button for boolean options, or a text field for other values). ++++ +*** The ':set' function should accept an optional argument BUFFER-LOCAL. +This is the third argument, in addition to SYMBOL and VALUE. If that +argument's value is 'buffer-local', the ':set' function should use +'set-local' to set the value of its SYMBOL argument locally in the +current buffer. This is used by 'setopt-local', which will signal an +error if this optional argument is not supported by the ':set' function. + ++++ +** The 'defcustom' ':local' keyword can now be 'permanent-only'. +This means that the variable's 'permanent-local' property is set to t, +without marking it as automatically buffer-local. + ** Pulse --- @@ -4526,11 +4539,6 @@ This function gets all the text within a DOM node recursively, returning it as a concatenated string. It replaces the now-obsolete functions 'dom-text' and 'dom-texts'. -+++ -** The 'defcustom' ':local' keyword can now be 'permanent-only'. -This means that the variable's 'permanent-local' property is set to t, -without marking it as automatically buffer-local. - --- ** The obsolete face attribute ':reverse-video' has been removed. Use ':inverse-video' instead. diff --git a/lisp/custom.el b/lisp/custom.el index 59b15032de1..a6bf4f87491 100644 --- a/lisp/custom.el +++ b/lisp/custom.el @@ -300,11 +300,15 @@ The following keywords are meaningful: given in the `defcustom' call. The default is `custom-initialize-reset'. :set VALUE should be a function to set the value of the symbol - when using the Customize user interface. It takes two arguments, - the symbol to set and the value to give it. The function should - not modify its value argument destructively. The default choice - of function is `set-default-toplevel-value'. If this keyword is - defined, modifying the value of SYMBOL via `setopt' will call the + when using the Customize user interface. It takes two + mandatory arguments, the symbol to set and the value to give + it, and one optional argument, which, if its value is + `buffer-local', means the value should be set + buffer-locally, without affecting the global or default + value. The function should not modify its value argument + destructively. The default choice of function is + `set-default-toplevel-value'. If this keyword is defined, + modifying the value of SYMBOL via `setopt' will call the function specified by VALUE to install the new value. :get VALUE should be a function to extract the value of symbol. The function takes one argument, a symbol, and should return From db6beef2606e3b3473a8fbaab2c58ca7bf34e35e Mon Sep 17 00:00:00 2001 From: Richard Lawrence Date: Wed, 29 Jul 2026 09:29:25 +0200 Subject: [PATCH 3/8] Distinguish UTC vs. UTC+0:00 times in iCalendar library This fixes a bug discussed on emacs-devel; see the thread at https://https://lists.gnu.org/archive/html/emacs-devel/2026-07/msg00445.html The issue was that the library did not distinguish between UTC time and times that have an offset of 0 but are not in the UTC time zone, e.g. standard times in Europe/London or Europe/Dublin. This led to test failures on systems in these time zones. This fix represents true UTC times by inserting the special value `t' in the zone slot of a decoded time at parse time (which `encode-time' already handles correctly). This distinguishes them from decoded times with an offset of 0 seconds. * lisp/calendar/icalendar-ast.el (icalendar-make-component): Ensure auto-generated DTSTAMP uses the new representation. (icalendar-make-node-from-templates): Fix docstring. * lisp/calendar/icalendar-parser.el (icalendar-read-time) (icalendar-read-date-time): Read iCalendar UTC times to the new representation. (icalendar-print-time): Print the new representation. (icalendar--decoded-time-p, icalendar--decoded-date-time-p) (icalendar-date-time-is-utc-p): Check for the new representation. (icalendar-date-time): Docstring improvement. (icalendar-requires-utc-validator): New validator function to check that property values are in UTC. (icalendar-completed, icalendar-created, icalendar-dtstamp): Use it. * lisp/calendar/icalendar-recur.el (icalendar-recur-tz-decode-time): Decode to new representation; explicitly support this. * lisp/calendar/icalendar-utils.el (icalendar-date-time<) (icalendar-date-time-simultaneous-p): * lisp/calendar/diary-icalendar.el (diary-icalendar-format-time-as-local): Work with the new representation. (diary-icalendar-convert-time-via-strategy) (diary-icalendar-parse-entry): Ensure times decode to new representation. * test/lisp/calendar/icalendar-parser-tests.el (icalendar-parser-test-bad-hyphenated-dates): * test/lisp/calendar/icalendar-recur-tests.el (icalendar-test-recur-find-secondly-interval) (icalendar-test-recur-tz-observance-on): Update tests to use new representation. * test/lisp/calendar/icalendar-parser-tests.el (icalendar-parse-test-utc+0-is-not-utc): New regression test. * test/lisp/calendar/diary-icalendar-tests.el: Update 'to-utc test. * test/lisp/calendar/diary-icalendar-resources/import-bug-24199.diary-all: Update diary representation of UTC times in a recurrence rule. --- lisp/calendar/diary-icalendar.el | 11 ++++-- lisp/calendar/icalendar-ast.el | 10 +++-- lisp/calendar/icalendar-parser.el | 28 +++++++++----- lisp/calendar/icalendar-recur.el | 10 +++-- lisp/calendar/icalendar-utils.el | 8 ++-- .../import-bug-24199.diary-all | 6 +-- test/lisp/calendar/diary-icalendar-tests.el | 2 +- test/lisp/calendar/icalendar-parser-tests.el | 37 ++++++++++++++++++- test/lisp/calendar/icalendar-recur-tests.el | 28 +++++++------- 9 files changed, 97 insertions(+), 43 deletions(-) diff --git a/lisp/calendar/diary-icalendar.el b/lisp/calendar/diary-icalendar.el index 2e8a981e99f..33d8768bbb5 100644 --- a/lisp/calendar/diary-icalendar.el +++ b/lisp/calendar/diary-icalendar.el @@ -1366,7 +1366,7 @@ the iCalendar data." (local-dt (decode-time ts local-tz)) (local-str (di:format-time local-dt))) (if (and original-tzname original-offset - (not (= original-offset local-offset))) + (not (eql original-offset local-offset))) (format "%s (%s)" local-str (di:format-time dt original-tzname)) local-str))))) @@ -2508,8 +2508,8 @@ zone export strategy requires it." (icr:tz-decode-time (encode-time dt) vtimezone) (icr:tz-set-zone dt vtimezone :error))) ((or (eq 'to-utc di:time-zone-export-strategy) - (di:-tz-is-utc-p)) ; we're already in UTC, so mark dt as such - (decode-time (encode-time dt) t)) + (di:-tz-is-utc-p)) + (icr:tz-decode-time (encode-time dt) t)) ; ensure dt is in UTC ((eq 'floating di:time-zone-export-strategy) (setf (decoded-time-zone dt) nil) dt))))) @@ -3341,7 +3341,10 @@ recursive calls to this function made by ;; Collect the remaining properties: (setq all-props (append (di:parse-summary-and-description) all-props)) (setq all-props (append (di:parse-attendees-and-organizer) all-props)) - (push (ical:make-property ical:dtstamp (decode-time nil t)) all-props) + (push + (ical:make-property ical:dtstamp + (icr:tz-decode-time (current-time) t)) ; ensure UTC + all-props) (let ((class (di:parse-class)) (location (di:parse-location)) (status (di:parse-status)) diff --git a/lisp/calendar/icalendar-ast.el b/lisp/calendar/icalendar-ast.el index 2f136533cef..71d9158efc9 100644 --- a/lisp/calendar/icalendar-ast.el +++ b/lisp/calendar/icalendar-ast.el @@ -569,8 +569,13 @@ The resulting syntax node is checked for validity by ;; Add templates for required properties automatically if we can: (when (memq type '(ical:vevent ical:vtodo ical:vjournal ical:vfreebusy)) (unless (assq 'ical:dtstamp templates) - (push '(ical:dtstamp (decode-time nil t)) - templates)) + (push + '(icalendar-make-property icalendar-dtstamp + (let ((stamp (decode-time nil t))) + ;; Ensure we return UTC, not just :zone 0: + (setf (decoded-time-zone stamp) t) + stamp)) + templates)) (unless (assq 'ical:uid templates) (push `(ical:uid ,(ical:make-uid templates)) templates))) @@ -652,7 +657,6 @@ added to the component or property node. For example, an iCalendar VEVENT could be written like this: (icalendar-make-node-from-templates icalendar-vevent - (icalendar-dtstamp (decode-time (current-time) 0)) (icalendar-uid \"some-unique-id\") (icalendar-summary \"Party\") (icalendar-location \"Robot House\") diff --git a/lisp/calendar/icalendar-parser.el b/lisp/calendar/icalendar-parser.el index cee48c9d485..d08dce88e8b 100644 --- a/lisp/calendar/icalendar-parser.el +++ b/lisp/calendar/icalendar-parser.el @@ -896,7 +896,7 @@ S should be a match against rx `icalendar-time'." (second (string-to-number (substring s 4 6))) (utcoffset (if (and (length= s 7) (equal "Z" (substring s 6 7))) - 0 + t ; UTC ;; unknown/'floating' time zone: nil))) (ical:make-date-time :second second @@ -910,7 +910,7 @@ S should be a match against rx `icalendar-time'." (decoded-time-hour time) (decoded-time-minute time) (decoded-time-second time) - (if (eql 0 (decoded-time-zone time)) + (if (eq t (decoded-time-zone time)) "Z" ""))) (defun ical:-decoded-time-p (val) @@ -923,7 +923,7 @@ for that, see `icalendar--decoded-date-time-p'." (cl-typep (decoded-time-minute val) 'ical:numeric-minute) (cl-typep (decoded-time-hour val) 'ical:numeric-hour) (cl-typep (decoded-time-dst val) '(member t nil -1)) - (cl-typep (decoded-time-zone val) '(or integer null)))) + (cl-typep (decoded-time-zone val) '(or integer boolean)))) (ical:define-type ical:time "TIME" "Type for Time values. @@ -967,7 +967,7 @@ fields and DST are ignored when printed." ;; `make-decoded-time': ;; (cl-typep (decoded-time-weekday val) '(integer 0 6)) (cl-typep (decoded-time-dst val) '(member t nil -1)) - (cl-typep (decoded-time-zone val) '(or integer null)))) + (cl-typep (decoded-time-zone val) '(or integer boolean)))) (defun ical:read-date-time (s) "Read an `icalendar-date-time' from a string S. @@ -982,7 +982,7 @@ S should be a match against rx `icalendar-date-time'." (second (string-to-number (substring s 13 15))) (utcoffset (if (and (length= s 16) (equal "Z" (substring s 15 16))) - 0 + t ; UTC ;; unknown/'floating' time zone: nil))) (ical:make-date-time :second second @@ -1007,16 +1007,15 @@ S should be a match against rx `icalendar-date-time'." (defun ical:date-time-is-utc-p (datetime) "Return non-nil if DATETIME is in UTC time." - (let ((offset (decoded-time-zone datetime))) - (and offset (= 0 offset)))) + (eq t (decoded-time-zone datetime))) (ical:define-type ical:date-time "DATE-TIME" "Type for Date-Time values. When printed, a date-time is a string of digits like: YYYYMMDDTHHMMSS -where the 'T' is literal, and separates the date string from the -time string. +where the 'T' is literal, and separates the date string from the time +string. If followed by a 'Z', the string represents a UTC date-time. When read, a date-time is a decoded time, i.e. a list in the format (SEC MINUTE HOUR DAY MONTH YEAR DOW DST UTCOFF). See @@ -2933,6 +2932,7 @@ an `icalendar-vtodo' was actually completed. The value must be an `icalendar-date-time' with a UTC time." ical:date-time :child-spec (:zero-or-more (ical:otherparam)) + :other-validator ical:requires-utc-validator :link "https://www.rfc-editor.org/rfc/rfc5545#section-3.8.2.1") (ical:define-property ical:dtend "DTEND" @@ -3381,6 +3381,7 @@ initially created an `icalendar-vevent', `icalendar-vtodo', or in UTC time." ical:date-time :child-spec (:zero-or-more (ical:otherparam)) + :other-validator ical:requires-utc-validator :link "https://www.rfc-editor.org/rfc/rfc5545#section-3.8.7.1") (ical:define-property ical:dtstamp "DTSTAMP" @@ -3404,8 +3405,17 @@ object *representing* that data was created. The value must be in UTC time." ical:date-time :child-spec (:zero-or-more (ical:otherparam)) + :other-validator ical:requires-utc-validator :link "https://www.rfc-editor.org/rfc/rfc5545#section-3.8.7.2") +(defun ical:requires-utc-validator (node) + "Validate that a property NODE's value is a UTC date-time" + (ical:with-property node nil + (unless (ical:date-time-is-utc-p value) + (ical:signal-validation-error + (format "An `%s's value must be in UTC" (ical:ast-node-type node)) + :node node)))) + (ical:define-property ical:last-modified "LAST-MODIFIED" "Last Modified timestamp. diff --git a/lisp/calendar/icalendar-recur.el b/lisp/calendar/icalendar-recur.el index 61dd7701e2a..aec4066aa98 100644 --- a/lisp/calendar/icalendar-recur.el +++ b/lisp/calendar/icalendar-recur.el @@ -1971,19 +1971,21 @@ based on whether the observance is an `icalendar-standard' or observance that applies to TS, it is decoded into UTC time. VTIMEZONE may also be an `icalendar-utc-offset'. In this case TS is -decoded directly into this UTC offset, and its dst slot is set to -1." +decoded directly into this UTC offset, and its dst slot is set to -1. +If VTIMEZONE is t, TS is decoded to UTC time." (let* ((observance (when (ical:vtimezone-component-p vtimezone) (car (icr:tz-observance-on ts vtimezone)))) (offset (cond (observance (icr:tz-offset-in observance)) ((cl-typep vtimezone 'ical:utc-offset) vtimezone) - (t 0)))) + (t t)))) ; decode to UTC (ical:date-time-variant ; ensures weekday gets set, too (decode-time ts offset) :zone offset - :dst (if observance (ical:daylight-component-p observance) - -1)))) + :dst (cond (observance (ical:daylight-component-p observance)) + ((eq t vtimezone) nil) ; UTC + (t -1))))) (defun icr:tz-set-zone (dt vtimezone &optional nonexistent) "Set the time zone offset and dst flag in DT based on VTIMEZONE. diff --git a/lisp/calendar/icalendar-utils.el b/lisp/calendar/icalendar-utils.el index a6f06e9e5d5..1145ce2af81 100644 --- a/lisp/calendar/icalendar-utils.el +++ b/lisp/calendar/icalendar-utils.el @@ -289,7 +289,7 @@ returned from `current-time-zone' is used as the missing offset; if signaled." (let ((zone1 (decoded-time-zone dt1)) (zone2 (decoded-time-zone dt2))) - (cond ((and (integerp zone1) (integerp zone2)) + (cond ((and zone1 zone2) (time-less-p (encode-time dt1) (encode-time dt2))) ((and (null zone1) (null zone2)) (ical:date-time-locally< dt1 dt2)) @@ -357,11 +357,11 @@ non-simultaneous if they represent different clock times according to signaled." (let ((zone1 (decoded-time-zone dt1)) (zone2 (decoded-time-zone dt2))) - (cond ((and (integerp zone1) (integerp zone2)) + (cond ((and zone1 zone2) (time-equal-p (encode-time dt1) (encode-time dt2))) ((and (null zone1) (null zone2)) - (time-equal-p (encode-time (ical:date-time-variant dt1 :zone 0)) - (encode-time (ical:date-time-variant dt2 :zone 0)))) + (time-equal-p (encode-time (ical:date-time-variant dt1 :zone t)) + (encode-time (ical:date-time-variant dt2 :zone t)))) (t ;; Best effort: ;; TODO: I'm not convinced this is the right thing to do yet. diff --git a/test/lisp/calendar/diary-icalendar-resources/import-bug-24199.diary-all b/test/lisp/calendar/diary-icalendar-resources/import-bug-24199.diary-all index cf3e5884710..9e60011535e 100644 --- a/test/lisp/calendar/diary-icalendar-resources/import-bug-24199.diary-all +++ b/test/lisp/calendar/diary-icalendar-resources/import-bug-24199.diary-all @@ -1,8 +1,8 @@ &%%(diary-rrule :rule '((FREQ MONTHLY) (BYDAY ((3 . 1))) (INTERVAL 1)) :exclude - '((0 46 11 6 1 2016 3 -1 0) (0 46 11 3 2 2016 3 -1 0) - (0 46 11 2 3 2016 3 -1 0) (0 46 10 4 5 2016 3 -1 0) - (0 46 10 1 6 2016 3 -1 0)) + '((0 46 11 6 1 2016 3 -1 t) (0 46 11 3 2 2016 3 -1 t) + (0 46 11 2 3 2016 3 -1 t) (0 46 10 4 5 2016 3 -1 t) + (0 46 10 1 6 2016 3 -1 t)) :start '(0 46 12 2 12 2015 3 -1 nil) :duration '(0 14 3 0 nil nil nil -1 nil)) Summary Location: Loc diff --git a/test/lisp/calendar/diary-icalendar-tests.el b/test/lisp/calendar/diary-icalendar-tests.el index 06272a39cf4..96c39114ef3 100644 --- a/test/lisp/calendar/diary-icalendar-tests.el +++ b/test/lisp/calendar/diary-icalendar-tests.el @@ -1153,7 +1153,7 @@ SOURCE, if given, should be a symbol; it is used to name the test." (unwind-protect (ical:with-component (car parsed) ((ical:dtstart :first start-node :value start)) - (should (= 0 (decoded-time-zone start))) + (should (ical:date-time-is-utc-p start)) (should (= (- 16 2) (decoded-time-hour start))) (should-not (ical:with-param-of start-node 'ical:tzidparam))) ;; restore time zone diff --git a/test/lisp/calendar/icalendar-parser-tests.el b/test/lisp/calendar/icalendar-parser-tests.el index 8215f977e26..4b56edb8780 100644 --- a/test/lisp/calendar/icalendar-parser-tests.el +++ b/test/lisp/calendar/icalendar-parser-tests.el @@ -1962,7 +1962,7 @@ END:VCALENDAR (expected-dtstamp (ical:make-date-time :year 2023 :month 7 :day 30 :hour 19 :minute 47 :second 0 - :zone 0))) + :zone t))) (should (not (ical:errors-p))) (should (ical:ast-node-valid-p vcal t)) (ical:with-component vcal @@ -2023,6 +2023,41 @@ END:VCALENDAR ((ical:sentbyparam :value sent-by)) (should (equal sent-by expected-sender)))))))))) + +;; Tests for bugfixes: +(ert-deftest ipt:utc+0-is-not-utc () + "Are UTC times parsed distinctly from times in other zones with 0 offset?" + ;; An explicit UTC time should parse with `t' in its zone field; this + ;; distinguishes it from times that merely have a 0 second offset from + ;; UTC, but might have a defined non-UTC time zone, e.g. in Europe/London + ;; or Europe/Dublin. Bug discussion: + ;; https://https://lists.gnu.org/archive/html/emacs-devel/2026-07/msg00445.html + (let* ((s-utc "20260101T111111Z") + (s-non "20260101T111111") + (parsed-utc (ical:ast-node-value + (ical:parse-from-string 'ical:date-time s-utc))) + (expected-utc (ical:make-date-time :year 2026 :month 1 :day 1 + :hour 11 :minute 11 :second 11 + :zone t)) + (parsed-non (ical:ast-node-value + (ical:parse-from-string 'ical:date-time s-non))) + (parsed-non-zoned (ical:date-time-variant parsed-non :zone 0)) + (expected-non (ical:make-date-time :year 2026 :month 1 :day 1 + :hour 11 :minute 11 :second 11 + :zone nil)) + (expected-non-zoned (ical:date-time-variant expected-non :zone 0))) + ;; Test that the two times are parsed distinctly: + (should (equal parsed-utc expected-utc)) + (should (equal parsed-non expected-non)) + (should-not (equal (decoded-time-zone parsed-utc) + (decoded-time-zone parsed-non))) + ;; Test that `icalendar-date-time-is-utc-p' distinguishes the two cases: + (should (ical:date-time-is-utc-p parsed-utc)) + (should-not (ical:date-time-is-utc-p parsed-non)) + (should-not (ical:date-time-is-utc-p parsed-non-zoned)) + ;; but also that `icalendar-date-time-simultaneous-p' does not: + (should (ical:date-time-simultaneous-p parsed-utc expected-utc)) + (should (ical:date-time-simultaneous-p parsed-utc parsed-non-zoned)))) diff --git a/test/lisp/calendar/icalendar-recur-tests.el b/test/lisp/calendar/icalendar-recur-tests.el index 199d6c4aa25..20b6dc57376 100644 --- a/test/lisp/calendar/icalendar-recur-tests.el +++ b/test/lisp/calendar/icalendar-recur-tests.el @@ -271,7 +271,7 @@ END:VTIMEZONE ;; Use UTC for the tests with no ;; time zone, so that the results ;; don't depend on system's local time - :zone 0)) + :zone t)) (dtstart/tz (ical:date-time-variant dtstart :zone ict:est :dst nil))) ;; Year numbers are monotonically increasing in the following test cases, @@ -279,24 +279,24 @@ END:VTIMEZONE ;; No timezone, just clock time, around a target that doesn't fall on ;; an interval boundary: - (let* ((target (ical:date-time-variant dtstart :year 2026 :second 5 :zone 0)) + (let* ((target (ical:date-time-variant dtstart :year 2026 :second 5 :zone t)) (expected-int (icr:make-interval - (ical:date-time-variant target :second 0 :tz 'preserve) - (ical:date-time-variant target :second 1 :tz 'preserve) - (ical:date-time-variant target :second 10 :tz 'preserve)))) + (ical:date-time-variant target :second 0 :dst nil :tz 'preserve) + (ical:date-time-variant target :second 1 :dst nil :tz 'preserve) + (ical:date-time-variant target :second 10 :dst nil :tz 'preserve)))) (should (equal expected-int (icr:find-secondly-interval target dtstart 10)))) ;; No timezone, just clock time, around a target that does fall on ;; an interval boundary: - (let* ((target (ical:date-time-variant dtstart :year 2027 :second 10 :zone 0)) + (let* ((target (ical:date-time-variant dtstart :year 2027 :second 10 :zone t)) (expected-int (icr:make-interval - (ical:date-time-variant target :second 10 :tz 'preserve) - (ical:date-time-variant target :second 11 :tz 'preserve) - (ical:date-time-variant target :second 20 :tz 'preserve)))) + (ical:date-time-variant target :second 10 :dst nil :tz 'preserve) + (ical:date-time-variant target :second 11 :dst nil :tz 'preserve) + (ical:date-time-variant target :second 20 :dst nil :tz 'preserve)))) (should (equal expected-int (icr:find-secondly-interval target dtstart 10)))) @@ -1334,7 +1334,7 @@ END:VTIMEZONE ;; A date matching the end of a STANDARD observance: (let* ((ut (ical:make-date-time :year 2006 :month 10 :day 29 :hour 6 :minute 0 :second 0 - :zone 0 :dst nil)) ; UNTIL is in UTC + :zone t :dst nil)) ; UNTIL is in UTC (dt (ical:make-date-time :year 2006 :month 10 :day 29 :hour 2 :minute 0 :second 0 :zone ict:edt :dst t)) @@ -1352,7 +1352,7 @@ END:VTIMEZONE ;; A date matching the end of a DAYLIGHT observance: (let* ((ut (ical:make-date-time :year 2006 :month 4 :day 2 :hour 7 :minute 0 :second 0 - :zone 0 :dst nil)) ; UNTIL is in UTC + :zone t :dst nil)) ; UNTIL is in UTC (dt (ical:make-date-time :year 2006 :month 4 :day 2 :hour 2 :minute 0 :second 0 :zone ict:est :dst nil)) @@ -1374,7 +1374,7 @@ END:VTIMEZONE :zone ict:est :dst nil)) (end (ical:make-date-time :year 1986 :month 4 :day 27 :hour 7 :minute 0 :second 0 - :zone 0)) ; UNTIL is in UTC + :zone t)) ; UNTIL is in UTC (obs/onset (icr:tz-observance-on dt ict:tz-eastern)) (obs (car obs/onset)) (onset (cadr obs/onset)) @@ -2009,7 +2009,7 @@ SOURCE should be a symbol; it is used to name the test." :hour 9 :minute 0 :second 0 :zone ict:edt :dst t) :high (ical:make-date-time :year 1997 :month 10 :day 8 - :hour 0 :minute 0 :second 0 :zone 0) + :hour 0 :minute 0 :second 0 :zone t) :members (list ;; ==> (1997 9:00 AM EDT) September 2,4,9,11,16,18,23,25,30; @@ -2034,7 +2034,7 @@ SOURCE should be a symbol; it is used to name the test." :hour 9 :minute 0 :second 0 :zone ict:edt :dst t) :high (ical:make-date-time :year 1997 :month 10 :day 8 - :hour 0 :minute 0 :second 0 :zone 0) + :hour 0 :minute 0 :second 0 :zone t) :members (list ;; ==> (1997 9:00 AM EDT) September 2,4,9,11,16,18,23,25,30; From 287187f202eec01b43672b1811fbc4306cf132f7 Mon Sep 17 00:00:00 2001 From: Spencer Baugh Date: Wed, 22 Jul 2026 09:58:56 -0400 Subject: [PATCH 4/8] pcm-try-completion: collapse ** into * * lisp/minibuffer.el (completion-pcm--merge-completions): Only include one star in the return value (bug#81394). * test/lisp/minibuffer-tests.el (completion-pcm-test-7): Update for new behavior. --- lisp/minibuffer.el | 16 +++++++++++++--- test/lisp/minibuffer-tests.el | 5 +++-- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/lisp/minibuffer.el b/lisp/minibuffer.el index 277840ae7b2..2019fe5299d 100644 --- a/lisp/minibuffer.el +++ b/lisp/minibuffer.el @@ -4773,9 +4773,19 @@ the same set of elements." (setq prefix (substring prefix 0 (length fixed)))) (push prefix res) ;; Push all the wildcards in this stretch, to preserve `point' and - ;; `star' wildcards before ELEM. - (dolist (wildcard wildcards) - (push wildcard res)) + ;; `star' wildcards before ELEM. Collapse multiple `star's down to one + ;; on each side of point. (bug#81394) + (let ((star-seen nil)) + (dolist (wildcard wildcards) + (cond + ((eq wildcard 'star) + (unless star-seen + (push 'star res)) + (setq star-seen t)) + (t + (when (eq wildcard 'point) + (setq star-seen nil)) + (push wildcard res))))) ;; Extract common suffix additionally to common prefix. ;; Don't do it for `any' since it could lead to a merged ;; completion that doesn't itself match the candidates. diff --git a/test/lisp/minibuffer-tests.el b/test/lisp/minibuffer-tests.el index d7f0ae73d2b..2f70d248711 100644 --- a/test/lisp/minibuffer-tests.el +++ b/test/lisp/minibuffer-tests.el @@ -312,11 +312,12 @@ '("lisp/minibuffer.el" "src/minibuf.c") nil 9) '("*/minibuf" . 9))) - ;; A series of wildcards is preserved (for now), along with point's position. + ;; A series of `star's is collapsed into a single one, but point's + ;; position is preserved in the middle. (bug#81394) (should (equal (completion-pcm--merge-try '(star star point star "foo") '("xxfoo" "xyfoo") "" "") - '("x***foo" . 3))) + '("x**foo" . 2))) ;; The series of wildcards is considered together; if any of them wants the common suffix, it's generated. (should (equal (completion-pcm--merge-try From b1687dbe02a9483c463f1b131042779014fd6e41 Mon Sep 17 00:00:00 2001 From: Michael Albinus Date: Tue, 4 Aug 2026 15:30:51 +0200 Subject: [PATCH 5/8] * admin/notes/jargon: Add FUD. --- admin/notes/jargon | 1 + 1 file changed, 1 insertion(+) diff --git a/admin/notes/jargon b/admin/notes/jargon index 20ebf200e92..c22fe25b249 100644 --- a/admin/notes/jargon +++ b/admin/notes/jargon @@ -48,6 +48,7 @@ DTRT - do the right thing DWIM - do what I mean ENOPATCH - no patch is attached FTR - for the record +FUD - fear, uncertainty and doubt FWIW - for what it's worth FYI - for your information GNU - GNU is not Unix From 8b521b98edbc36cab888527b4dfd38b5bae96995 Mon Sep 17 00:00:00 2001 From: Michael Albinus Date: Tue, 4 Aug 2026 15:31:11 +0200 Subject: [PATCH 6/8] ; * etc/NEWS: Fix level of last change. --- etc/NEWS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etc/NEWS b/etc/NEWS index 353aaf27289..ffbc142cad1 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -3585,7 +3585,7 @@ current buffer. This is used by 'setopt-local', which will signal an error if this optional argument is not supported by the ':set' function. +++ -** The 'defcustom' ':local' keyword can now be 'permanent-only'. +*** The 'defcustom' ':local' keyword can now be 'permanent-only'. This means that the variable's 'permanent-local' property is set to t, without marking it as automatically buffer-local. From cc210dbf14d5430b988dcf30662914012eced278 Mon Sep 17 00:00:00 2001 From: Binbin Ye Date: Thu, 23 Jul 2026 20:38:58 +0800 Subject: [PATCH 7/8] Fix JSX tag angle-bracket pairing in tsx-ts-mode * lisp/progmodes/typescript-ts-mode.el (tsx-ts--s-p-query): Capture 'jsx_self_closing_element' as well. (tsx-ts--syntax-propertize-captures): For JSX tag elements, give only the leading '<' and trailing '>' matching-pair syntax and leave the interior untouched; for 'jsx_text', neutralize balanced pair characters to punctuation (bug#81460). * test/lisp/progmodes/typescript-ts-mode-tests.el (tsx-ts-mode-test-jsx-tag-syntax-propertize): New test. --- lisp/progmodes/typescript-ts-mode.el | 48 ++++++++++++------- .../progmodes/typescript-ts-mode-tests.el | 48 +++++++++++++++++++ 2 files changed, 78 insertions(+), 18 deletions(-) diff --git a/lisp/progmodes/typescript-ts-mode.el b/lisp/progmodes/typescript-ts-mode.el index dc951811bb3..83a996ad629 100644 --- a/lisp/progmodes/typescript-ts-mode.el +++ b/lisp/progmodes/typescript-ts-mode.el @@ -824,7 +824,8 @@ at least 3 (which is the default value)." '(((regex pattern: (regex_pattern) @regexp)) ((jsx_text) @jsx) ((jsx_opening_element) @jsx) - ((jsx_closing_element) @jsx))))) + ((jsx_closing_element) @jsx) + ((jsx_self_closing_element) @jsx))))) (defun typescript-ts--syntax-propertize (beg end) (let ((captures (treesit-query-capture 'typescript typescript-ts--s-p-query beg end))) @@ -845,24 +846,35 @@ at least 3 (which is the default value)." (incf ne) (put-text-property ns (1+ ns) 'syntax-table syntax) (put-text-property (1- ne) ne 'syntax-table syntax))) - ;; We put punctuation syntax on all the balanced pair - ;; characters so they don't mess up syntax-ppss. We can't put - ;; string syntax on the whole thing because a) it doesn't work - ;; if the text is one character long, and b) it interferes - ;; forward/backward-sexp. + ('jsx - (save-excursion - (goto-char ns) - (while (re-search-forward (rx (or "{" "}" "[" "]" - "(" ")" "<" ">")) - ne t) - (put-text-property - (match-beginning 0) (match-end 0) - 'syntax-table (string-to-syntax - (cond - ((equal (match-string 0) "<") "(>") - ((equal (match-string 0) ">") ")<") - (t "."))))))))))) + (if (member (treesit-node-type node) + '("jsx_opening_element" + "jsx_closing_element" + "jsx_self_closing_element")) + ;; A JSX tag's only real delimiters are its own outermost + ;; '<' and '>'. Mark just those two as a matching pair and + ;; leave the interior alone: attribute values are '{...}' + ;; expressions of ordinary code (possibly with nested JSX) + ;; whose brackets must keep their normal syntax so they + ;; nest, match and highlight correctly. + (progn + (put-text-property ns (1+ ns) + 'syntax-table (string-to-syntax "(>")) + (put-text-property (1- ne) ne + 'syntax-table (string-to-syntax ")<"))) + ;; jsx_text is raw text with no nesting to preserve, so + ;; neutralize its balanced-pair characters to punctuation; + ;; otherwise stray or unbalanced brackets in the text would + ;; confuse syntax-ppss. + (save-excursion + (goto-char ns) + (while (re-search-forward (rx (or "{" "}" "[" "]" + "(" ")" "<" ">")) + ne t) + (put-text-property + (match-beginning 0) (match-end 0) + 'syntax-table (string-to-syntax ".")))))))))) ;;;###autoload (defun tsx-ts-mode-maybe () diff --git a/test/lisp/progmodes/typescript-ts-mode-tests.el b/test/lisp/progmodes/typescript-ts-mode-tests.el index 977a0f44a20..1b065c409fe 100644 --- a/test/lisp/progmodes/typescript-ts-mode-tests.el +++ b/test/lisp/progmodes/typescript-ts-mode-tests.el @@ -28,5 +28,53 @@ (treesit-ready-p 'tsx))) (ert-test-erts-file (ert-resource-file "indent.erts"))) +(ert-deftest tsx-ts-mode-test-jsx-tag-syntax-propertize () + "Every JSX tag's angle brackets must pair with that tag's own bounds. + +Make sure jsx element's `<' pair with the closing `>' correctly instead +of the wrong `>' from its attributes contained an arrow function `=>'." + (skip-unless (treesit-ready-p 'tsx)) + (dolist (src '(;; A fragment, arrow-function attributes, and an arrow + ;; that returns a nested `
' element -- all + ;; inside the opening tag's range. + "const A = () => ( + <> + {}} + panel={() => { + return
; + }} + > + {children} +
+ +); +" + ;; A self-closing element nested inside an attribute + ;; expression. + "const B = () => ( + cb()}/>}> + {children} + +); +")) + (with-temp-buffer + (insert src) + (tsx-ts-mode) + (syntax-propertize (point-max)) + (pcase-dolist (`(,_ . ,node) + (treesit-query-capture + 'tsx '(((jsx_opening_element) @el) + ((jsx_closing_element) @el) + ((jsx_self_closing_element) @el)))) + (let ((ns (treesit-node-start node)) + (ne (treesit-node-end node))) + (should (eq (char-after ns) ?<)) + (should (eq (char-before ne) ?>)) + ;; Forward from the tag's `<' lands exactly after its own + ;; `>', and backward round-trips to the `<'. + (should (= (scan-sexps ns 1) ne)) + (should (= (scan-sexps ne -1) ns))))))) + (provide 'typescript-ts-mode-tests) ;;; typescript-ts-mode-tests.el ends here From e54ba842458275d5bfff7305bb1b310862780094 Mon Sep 17 00:00:00 2001 From: Sean Whitton Date: Wed, 5 Aug 2026 11:25:22 +0100 Subject: [PATCH 8/8] ; * etc/PROBLEMS (Build-time problems): Discuss bug#81211. --- etc/PROBLEMS | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/etc/PROBLEMS b/etc/PROBLEMS index c9b01e65ea4..64cf1576c5a 100644 --- a/etc/PROBLEMS +++ b/etc/PROBLEMS @@ -4030,6 +4030,12 @@ The solution is to tell configure to use the correct C preprocessor for your C compiler (CPP="/opt/developerstudio12.6/bin/cc -E" in the above example). +*** The Android build works with only older versions of Java. + +Only Java 19 and older work, because Java 19 was the last version to +support "-source 7". See for more +details. + ** Compilation *** Link-time optimization with clang doesn't work on Fedora 20.