[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
master 970f94a2dd8 5/5: Merge from origin/emacs-29
From: |
Eli Zaretskii |
Subject: |
master 970f94a2dd8 5/5: Merge from origin/emacs-29 |
Date: |
Sat, 3 Jun 2023 05:51:57 -0400 (EDT) |
branch: master
commit 970f94a2dd8bc4be4d71f1075421093ca6f87d28
Merge: 68b87288aa5 ede3535051a
Author: Eli Zaretskii <eliz@gnu.org>
Commit: Eli Zaretskii <eliz@gnu.org>
Merge from origin/emacs-29
ede3535051a ; Fix last change
8ec786349e1 Fix apostrophe handling in rust-ts-mode and go-ts-mode (B...
0eba9cf6511 * test/infra/Dockerfile.emba (emacs-base): Install also g...
4897c98b6c4 Fix 'python-util-clone-local-variables'
6b2c8dc9050 Revert "Enhance Python font-lock to support multilines"
348e4504c6d Fix typo in calc.texi
03663b8798a Update to Transient v0.4.1
dc7acb1aafe Avoid errors in 'delete-forward-char' deleting static com...
2f94f6de9d6 Make VS-15 and VS-16 compositions work correctly
753f8aa1f14 Fix project-name for vc-aware backend in non-file buffers
17c7915ab94 ; Fix 'package-install-upgrade-built-in' check for packag...
e252ce26eab Add type_predicate 'is' as keyword in typescript-ts-mode ...
0a354d65784 Fix infloop in info-look.el
83b22139e4c Fix several todo-mode.el item editing bugs (bug#63811)
ed4cd3eddf7 dockerfile-ts-mode: Prevent empty categories in imenu (Bu...
2e20e318da2 Brush up doc strings and terminology in plstore.el
372bc1278c2 Add internal documentation on plstore.el
23a14e7b902 Add compact_constructor_declaration font-locking to java-...
500abc4dc37 * lisp/tmm.el (tmm-completion-delete-prompt): Add more ch...
afc1f329356 Allow to disable the DWIMish behavior of 'x' in package menu
08104c01504 Allow dired to invoke secondary browser
a3063f0bc87 Add a binding for enriched-toggle-markup
d8ba28fa394 Fix order of tmm-menubar when 'tmm-mid-prompt' is nil
# Conflicts:
# etc/NEWS
---
admin/unidata/emoji-zwj.awk | 3 +-
doc/misc/calc.texi | 2 +-
doc/misc/transient.texi | 6 +-
etc/NEWS.29 | 13 +-
lisp/calendar/todo-mode.el | 298 ++++++++++++++++++++---------------
lisp/composite.el | 2 +-
lisp/emacs-lisp/package.el | 19 ++-
lisp/info-look.el | 6 +-
lisp/net/browse-url.el | 13 +-
lisp/plstore.el | 203 +++++++++++++++++++++---
lisp/progmodes/dockerfile-ts-mode.el | 5 +-
lisp/progmodes/go-ts-mode.el | 1 +
lisp/progmodes/java-ts-mode.el | 3 +
lisp/progmodes/project.el | 4 +-
lisp/progmodes/python.el | 52 ++----
lisp/progmodes/rust-ts-mode.el | 16 +-
lisp/progmodes/typescript-ts-mode.el | 2 +-
lisp/simple.el | 3 +-
lisp/textmodes/enriched.el | 1 +
lisp/tmm.el | 11 +-
lisp/transient.el | 19 ++-
test/infra/Dockerfile.emba | 2 +-
test/lisp/progmodes/python-tests.el | 144 -----------------
23 files changed, 465 insertions(+), 363 deletions(-)
diff --git a/admin/unidata/emoji-zwj.awk b/admin/unidata/emoji-zwj.awk
index 7d2ff6cb900..f13f796bcac 100644
--- a/admin/unidata/emoji-zwj.awk
+++ b/admin/unidata/emoji-zwj.awk
@@ -106,7 +106,8 @@ END {
for (elt in ch)
{
- printf("(#x%s .\n,(eval-when-compile (regexp-opt\n'(\n%s\n))))\n",
elt, vec[elt])
+ entries =
sprintf("%s\n\"\\N{U+%s}\\N{U+FE0E}\"\n\"\\N{U+%s}\\N{U+FE0F}\"", vec[elt],
elt, elt)
+ printf("(#x%s .\n,(eval-when-compile (regexp-opt\n'(\n%s\n))))\n",
elt, entries)
}
print "))"
print " (set-char-table-range composition-function-table"
diff --git a/doc/misc/calc.texi b/doc/misc/calc.texi
index aacc0d0b0ec..f5f4dff0e18 100644
--- a/doc/misc/calc.texi
+++ b/doc/misc/calc.texi
@@ -6217,7 +6217,7 @@ method when it is able. @xref{Programming Answer 8, 8}.
(@bullet{})
@cindex Gamma constant, Euler's
@cindex Euler's gamma constant
(@bullet{}) @strong{Exercise 9.} The @dfn{digamma} function
-@texline @math{\psi(z) (``psi'')}
+@texline @math{\psi(z)} (``psi'')
@infoline @expr{psi(z)}
is defined as the derivative of
@texline @math{\ln \Gamma(z)}.
diff --git a/doc/misc/transient.texi b/doc/misc/transient.texi
index 330fdd1e3a2..be9e8698ab4 100644
--- a/doc/misc/transient.texi
+++ b/doc/misc/transient.texi
@@ -31,7 +31,7 @@ General Public License for more details.
@finalout
@titlepage
@title Transient User and Developer Manual
-@subtitle for version 0.4.0
+@subtitle for version 0.4.1
@author Jonas Bernoulli
@page
@vskip 0pt plus 1filll
@@ -74,7 +74,7 @@ that hurdle is Psionic K's interactive tutorial, available at
@end quotation
@noindent
-This manual is for Transient version 0.4.0.
+This manual is for Transient version 0.4.1.
@insertcopying
@end ifnottex
@@ -2150,7 +2150,7 @@ default value of a prefix using the same format as
returned by
@item
@code{always-read} For options, whether to read a value on every invocation.
-If this is nil, then options that have a value are simply unset and
+If this is @code{nil}, then options that have a value are simply unset and
have to be invoked a second time to set a new value.
@item
diff --git a/etc/NEWS.29 b/etc/NEWS.29
index a3457d70340..60aa64b5ede 100644
--- a/etc/NEWS.29
+++ b/etc/NEWS.29
@@ -1692,6 +1692,13 @@ the following to your Init file:
*** New command 'dired-do-eww'.
This command visits the file on the current line with EWW.
+---
+*** 'browse-url-of-dired-file' can now call the secondary browser.
+When invoked with a prefix arg, this will now call
+'browse-url-secondary-browser-function' instead of the default
+browser. 'browse-url-of-dired-file' is bound to 'W' by default in
+dired mode.
+
---
*** New user option 'dired-omit-lines'.
This is used by 'dired-omit-mode', and now allows you to hide based on
@@ -1769,7 +1776,7 @@ or can take a long time to render.
+++
*** New command 'enriched-toggle-markup'.
This allows you to see the markup in 'enriched-mode' buffers (e.g.,
-the "HELLO" file).
+the "HELLO" file). Bound to 'M-o m' by default.
** Shell Script Mode
@@ -1879,7 +1886,9 @@ These commands can be useful if the ".elc" files are out
of date
+++
*** New DWIM action on 'x' in "*Packages*" buffer.
If no packages are marked, 'x' will install the package under point if
-it isn't already, and remove it if it is installed.
+it isn't already, and remove it if it is installed. Customize the new
+option 'package-menu-use-current-if-no-marks' to the nil value to get
+back the old behavior of signaling an error in that case.
+++
*** New command 'package-vc-install'.
diff --git a/lisp/calendar/todo-mode.el b/lisp/calendar/todo-mode.el
index 671210f3ee8..35cac5d7310 100644
--- a/lisp/calendar/todo-mode.el
+++ b/lisp/calendar/todo-mode.el
@@ -1985,7 +1985,13 @@ their associated keys and their effects."
(setq done-only t)
(todo-toggle-view-done-only))
(if here
- (todo-insert-with-overlays new-item)
+ (progn
+ ;; Ensure item is inserted where command was invoked.
+ (unless (= (point) opoint)
+ (todo-category-number ocat)
+ (todo-category-select)
+ (goto-char opoint))
+ (todo-insert-with-overlays new-item))
(todo-set-item-priority new-item cat t))
(setq item-added t))
;; If user cancels before setting priority, restore
@@ -2119,6 +2125,9 @@ the item at point."
((or marked (todo-item-string))
(todo-edit-item--next-key 'todo arg)))))
+(defvar todo-edit-item--cat nil)
+(defvar todo-edit-item--pos nil)
+
(defun todo-edit-item--text (&optional arg)
"Function providing the text editing facilities of `todo-edit-item'."
(let ((full-item (todo-item-string)))
@@ -2127,6 +2136,7 @@ the item at point."
;; 1+ signals an error, so just make this a noop.
(when full-item
(let* ((opoint (point))
+ (ocat (todo-current-category))
(start (todo-item-start))
(end (save-excursion (todo-item-end)))
(item-beg (progn
@@ -2151,8 +2161,7 @@ the item at point."
(concat " \\[" (regexp-quote todo-comment-string)
": \\([^]]+\\)\\]")
end t)))
- (prompt (if comment "Edit comment: " "Enter a comment: "))
- (buffer-read-only nil))
+ (prompt (if comment "Edit comment: " "Enter a comment: ")))
;; When there are marked items, user can invoke todo-edit-item
;; even if point is not on an item, but text editing only
;; applies to the item at point.
@@ -2170,22 +2179,43 @@ the item at point."
end t)
(if comment-delete
(when (todo-y-or-n-p "Delete comment? ")
- (delete-region (match-beginning 0) (match-end 0)))
- (replace-match (save-match-data
- (read-string prompt
- (cons (match-string 1) 1)))
- nil nil nil 1))
+ (let ((buffer-read-only nil))
+ (delete-region (match-beginning 0) (match-end 0))))
+ (let ((buffer-read-only nil))
+ (replace-match (save-match-data
+ (prog1 (let ((buffer-read-only t))
+ (read-string
+ prompt
+ (cons (match-string 1) 1)))
+ ;; If user moved point while editing
+ ;; a comment, restore it and ensure
+ ;; done items section is displayed.
+ (unless (= (point) opoint)
+ (todo-category-number ocat)
+ (let ((todo-show-with-done t))
+ (todo-category-select)
+ (goto-char opoint)))))
+ nil nil nil 1)))
(if comment-delete
(user-error "There is no comment to delete")
- (insert " [" todo-comment-string ": "
- (prog1 (read-string prompt)
- ;; If user moved point during editing,
- ;; make sure it moves back.
- (goto-char opoint)
- (todo-item-end))
- "]")))))
+ (let ((buffer-read-only nil))
+ (insert " [" todo-comment-string ": "
+ (prog1 (let ((buffer-read-only t))
+ (read-string prompt))
+ ;; If user moved point while inserting a
+ ;; comment, restore it and ensure done items
+ ;; section is displayed.
+ (unless (= (point) opoint)
+ (todo-category-number ocat)
+ (let ((todo-show-with-done t))
+ (todo-category-select)
+ (goto-char opoint)))
+ (todo-item-end))
+ "]"))))))
(multiline
(let ((buf todo-edit-buffer))
+ (setq todo-edit-item--cat ocat)
+ (setq todo-edit-item--pos opoint)
(set-window-buffer (selected-window)
(set-buffer (make-indirect-buffer
(buffer-name) buf)))
@@ -2208,10 +2238,14 @@ the item at point."
;; Ensure lines following hard newlines are indented.
(setq new (replace-regexp-in-string "\\(\n\\)[^[:blank:]]"
"\n\t" new nil nil 1))
- ;; If user moved point during editing, make sure it moves back.
- (goto-char opoint)
- (todo-remove-item)
- (todo-insert-with-overlays new)
+ ;; If user moved point while editing item, restore it.
+ (unless (= (point) opoint)
+ (todo-category-number ocat)
+ (todo-category-select)
+ (goto-char opoint))
+ (let ((buffer-read-only nil))
+ (todo-remove-item)
+ (todo-insert-with-overlays new))
(move-to-column item-beg)))))))))
(defun todo-edit-quit ()
@@ -2243,6 +2277,9 @@ made in the number or names of categories."
(kill-buffer)
(unless (eq (current-buffer) buf)
(set-window-buffer (selected-window) (set-buffer buf)))
+ (todo-category-number todo-edit-item--cat)
+ (todo-category-select)
+ (goto-char todo-edit-item--pos)
(if transient-mark-mode (deactivate-mark)))
;; We got here via `F e'.
(when (todo-check-format)
@@ -2315,117 +2352,118 @@ made in the number or names of categories."
;; If there are marked items, use only the first to set
;; header changes, and apply these to all marked items.
(when first
- (cond
- ((eq what 'date)
- (setq ndate (todo-read-date)))
- ((eq what 'calendar)
- (setq ndate (save-match-data (todo-set-date-from-calendar))))
- ((eq what 'today)
- (setq ndate (calendar-date-string (calendar-current-date) t t)))
- ((eq what 'dayname)
- (setq ndate (todo-read-dayname)))
- ((eq what 'time)
- (setq ntime (save-match-data (todo-read-time)))
- (when (> (length ntime) 0)
- (setq ntime (concat " " ntime))))
- ;; When date string consists only of a day name,
- ;; passing other date components is a noop.
- ((and odayname (memq what '(year month day))))
- ((eq what 'year)
- (setq day oday
- monthname omonthname
- month omonth
- year (cond ((not current-prefix-arg)
- (todo-read-date 'year))
- ((string= oyear "*")
- (user-error "Cannot increment *"))
- (t
- (number-to-string (+ yy inc))))))
- ((eq what 'month)
- (setf day oday
- year oyear
- (if (memq 'month calendar-date-display-form)
- month
- monthname)
- (cond ((not current-prefix-arg)
- (todo-read-date 'month))
- ((or (string= omonth "*") (= mm 13))
- (user-error "Cannot increment *"))
- (t
- (let* ((mmo mm)
- ;; Change by 12 or more months?
- (bigincp (>= (abs inc) 12))
- ;; Month number is in range 1..12.
- (mminc (+ mm (% inc 12)))
- (mm (% (+ mminc 12) 12))
- ;; 12n mod 12 = 0, so 0 is December.
- (mm (if (= mm 0) 12 mm))
- ;; Does change in month cross year?
- (mmcmp (cond ((< inc 0) (> mm mmo))
- ((> inc 0) (< mm mmo))))
- (yyadjust (if bigincp
- (+ (abs (/ inc 12))
- (if mmcmp 1 0))
- 1)))
- ;; Adjust year if necessary.
- (setq yy (cond ((and (< inc 0)
- (or mmcmp bigincp))
- (- yy yyadjust))
- ((and (> inc 0)
- (or mmcmp bigincp))
- (+ yy yyadjust))
- (t yy)))
- (setq year (number-to-string yy))
- ;; Return the changed numerical month as
- ;; a string or the corresponding month name.
- (if omonth
- (number-to-string mm)
- (aref tma-array (1- mm)))))))
- ;; Since the number corresponding to the arbitrary
- ;; month name "*" is out of the range of
- ;; calendar-last-day-of-month, set it to 1
- ;; (corresponding to January) to allow 31 days.
- (let ((mm (if (= mm 13) 1 mm)))
- (if (> (string-to-number day)
- (calendar-last-day-of-month mm yy))
- (user-error "%s %s does not have %s days"
- (aref tmn-array (1- mm))
- (if (= mm 2) yy "") day))))
- ((eq what 'day)
- (setq year oyear
- month omonth
- monthname omonthname
- day (cond
- ((not current-prefix-arg)
- (todo-read-date 'day mm yy))
- ((string= oday "*")
- (user-error "Cannot increment *"))
- ((or (string= omonth "*") (string= omonthname "*"))
- (setq dd (+ dd inc))
- (if (> dd 31)
- (user-error
- "A month cannot have more than 31 days")
- (number-to-string dd)))
- ;; Increment or decrement day by INC,
- ;; adjusting month and year if necessary
- ;; (if year is "*" assume current year to
- ;; calculate adjustment).
- (t
- (let* ((yy (or yy (calendar-extract-year
- (calendar-current-date))))
- (date (calendar-gregorian-from-absolute
- (+ (calendar-absolute-from-gregorian
- (list mm dd yy))
- inc)))
- (adjmm (nth 0 date)))
- ;; Set year and month(name) to adjusted values.
- (unless (string= year "*")
- (setq year (number-to-string (nth 2 date))))
- (if month
- (setq month (number-to-string adjmm))
- (setq monthname (aref tma-array (1- adjmm))))
- ;; Return changed numerical day as a string.
- (number-to-string (nth 1 date)))))))))
+ (save-match-data
+ (cond
+ ((eq what 'date)
+ (setq ndate (todo-read-date)))
+ ((eq what 'calendar)
+ (setq ndate (todo-set-date-from-calendar)))
+ ((eq what 'today)
+ (setq ndate (calendar-date-string (calendar-current-date) t
t)))
+ ((eq what 'dayname)
+ (setq ndate (todo-read-dayname)))
+ ((eq what 'time)
+ (setq ntime (todo-read-time))
+ (when (> (length ntime) 0)
+ (setq ntime (concat " " ntime))))
+ ;; When date string consists only of a day name,
+ ;; passing other date components is a noop.
+ ((and odayname (memq what '(year month day))))
+ ((eq what 'year)
+ (setq day oday
+ monthname omonthname
+ month omonth
+ year (cond ((not current-prefix-arg)
+ (todo-read-date 'year))
+ ((string= oyear "*")
+ (user-error "Cannot increment *"))
+ (t
+ (number-to-string (+ yy inc))))))
+ ((eq what 'month)
+ (setf day oday
+ year oyear
+ (if (memq 'month calendar-date-display-form)
+ month
+ monthname)
+ (cond ((not current-prefix-arg)
+ (todo-read-date 'month))
+ ((or (string= omonth "*") (= mm 13))
+ (user-error "Cannot increment *"))
+ (t
+ (let* ((mmo mm)
+ ;; Change by 12 or more months?
+ (bigincp (>= (abs inc) 12))
+ ;; Month number is in range 1..12.
+ (mminc (+ mm (% inc 12)))
+ (mm (% (+ mminc 12) 12))
+ ;; 12n mod 12 = 0, so 0 is December.
+ (mm (if (= mm 0) 12 mm))
+ ;; Does change in month cross year?
+ (mmcmp (cond ((< inc 0) (> mm mmo))
+ ((> inc 0) (< mm mmo))))
+ (yyadjust (if bigincp
+ (+ (abs (/ inc 12))
+ (if mmcmp 1 0))
+ 1)))
+ ;; Adjust year if necessary.
+ (setq yy (cond ((and (< inc 0)
+ (or mmcmp bigincp))
+ (- yy yyadjust))
+ ((and (> inc 0)
+ (or mmcmp bigincp))
+ (+ yy yyadjust))
+ (t yy)))
+ (setq year (number-to-string yy))
+ ;; Return the changed numerical month as
+ ;; a string or the corresponding month name.
+ (if omonth
+ (number-to-string mm)
+ (aref tma-array (1- mm)))))))
+ ;; Since the number corresponding to the arbitrary
+ ;; month name "*" is out of the range of
+ ;; calendar-last-day-of-month, set it to 1
+ ;; (corresponding to January) to allow 31 days.
+ (let ((mm (if (= mm 13) 1 mm)))
+ (if (> (string-to-number day)
+ (calendar-last-day-of-month mm yy))
+ (user-error "%s %s does not have %s days"
+ (aref tmn-array (1- mm))
+ (if (= mm 2) yy "") day))))
+ ((eq what 'day)
+ (setq year oyear
+ month omonth
+ monthname omonthname
+ day (cond
+ ((not current-prefix-arg)
+ (todo-read-date 'day mm yy))
+ ((string= oday "*")
+ (user-error "Cannot increment *"))
+ ((or (string= omonth "*") (string= omonthname "*"))
+ (setq dd (+ dd inc))
+ (if (> dd 31)
+ (user-error
+ "A month cannot have more than 31 days")
+ (number-to-string dd)))
+ ;; Increment or decrement day by INC,
+ ;; adjusting month and year if necessary
+ ;; (if year is "*" assume current year to
+ ;; calculate adjustment).
+ (t
+ (let* ((yy (or yy (calendar-extract-year
+ (calendar-current-date))))
+ (date (calendar-gregorian-from-absolute
+ (+ (calendar-absolute-from-gregorian
+ (list mm dd yy))
+ inc)))
+ (adjmm (nth 0 date)))
+ ;; Set year and month(name) to adjusted values.
+ (unless (string= year "*")
+ (setq year (number-to-string (nth 2 date))))
+ (if month
+ (setq month (number-to-string adjmm))
+ (setq monthname (aref tma-array (1- adjmm))))
+ ;; Return changed numerical day as a string.
+ (number-to-string (nth 1 date))))))))))
(unless odayname
;; If year, month or day date string components were
;; changed, rebuild the date string.
diff --git a/lisp/composite.el b/lisp/composite.el
index 06c7c174163..3ae3e64d5b9 100644
--- a/lisp/composite.el
+++ b/lisp/composite.el
@@ -861,7 +861,7 @@ and the second is a glyph for a variation selector."
;; handled in font_range, we end up choosing the Emoji presentation
;; rather than the Text presentation.
(let ((elt '([".." 1 compose-gstring-for-variation-glyph])))
- (set-char-table-range composition-function-table '(#xFE00 . #xFE0E) elt)
+ (set-char-table-range composition-function-table '(#xFE00 . #xFE0D) elt)
(set-char-table-range composition-function-table '(#xE0100 . #xE01EF) elt))
(defun auto-compose-chars (func from to font-object string direction)
diff --git a/lisp/emacs-lisp/package.el b/lisp/emacs-lisp/package.el
index 69595601bc8..ba0e3618f28 100644
--- a/lisp/emacs-lisp/package.el
+++ b/lisp/emacs-lisp/package.el
@@ -3317,6 +3317,18 @@ Values can be interactively added to this list by typing
:version "25.1"
:type '(repeat (regexp :tag "Hide packages with name matching")))
+(defcustom package-menu-use-current-if-no-marks t
+ "Whether \\<package-menu-mode-map>\\[package-menu-execute] in package menu
operates on current package if none are marked.
+
+If non-nil, and no packages are marked for installation or
+deletion, \\<package-menu-mode-map>\\[package-menu-execute] will operate on
the current package at point,
+see `package-menu-execute' for details.
+The default is t. Set to nil to get back the original behavior
+of having `package-menu-execute' signal an error when no packages
+are marked for installation or deletion."
+ :version "29.1"
+ :type 'boolean)
+
(defun package-menu--refresh (&optional packages keywords)
"Re-populate the `tabulated-list-entries'.
PACKAGES should be nil or t, which means to display all known packages.
@@ -3760,8 +3772,8 @@ object corresponding to the newer version."
(and avail-pkg
(version-list-< (package-desc-priority-version pkg-desc)
(package-desc-priority-version avail-pkg))
- (xor (not package-install-upgrade-built-in)
- (package--active-built-in-p pkg-desc))
+ (or (not (package--active-built-in-p pkg-desc))
+ package-install-upgrade-built-in)
(push (cons name avail-pkg) upgrades))))
upgrades))
@@ -3946,7 +3958,8 @@ invocations."
;; Nothing marked.
(unless (or delete-list install-list)
;; Not on a package line.
- (unless (tabulated-list-get-id)
+ (unless (and (tabulated-list-get-id)
+ package-menu-use-current-if-no-marks)
(user-error "No operations specified"))
(let* ((id (tabulated-list-get-id))
(status (package-menu-get-status)))
diff --git a/lisp/info-look.el b/lisp/info-look.el
index 7858ed58774..da45e30cd36 100644
--- a/lisp/info-look.el
+++ b/lisp/info-look.el
@@ -733,7 +733,11 @@ Return nil if there is nothing appropriate in the buffer
near point."
(let ((str (string-join str-list " ")))
(when (assoc str completions)
(throw 'result str))
- (nbutlast str-list)))))))
+ ;; 'nbutlast' will not destructively set its argument
+ ;; to nil when the argument is a list of 1 element.
+ (if (= (length str-list) 1)
+ (setq str-list nil)
+ (nbutlast str-list))))))))
(error nil)))
;;;###autoload
diff --git a/lisp/net/browse-url.el b/lisp/net/browse-url.el
index 0177d12f236..39513b8f602 100644
--- a/lisp/net/browse-url.el
+++ b/lisp/net/browse-url.el
@@ -825,10 +825,17 @@ If optional arg TEMP-FILE-NAME is non-nil, delete it
instead."
(&optional localp no-error-if-not-filep))
;;;###autoload
-(defun browse-url-of-dired-file ()
- "In Dired, ask a WWW browser to display the file named on this line."
- (interactive)
+(defun browse-url-of-dired-file (&optional secondary)
+ "In Dired, ask a WWW browser to display the file named on this line.
+With prefix arg, use the secondary browser instead (e.g. EWW if
+`browse-url-secondary-browser-function' is set to
+`eww-browse-url'."
+ (interactive "P")
(let ((tem (dired-get-filename t t))
+ (browse-url-browser-function
+ (if secondary
+ browse-url-secondary-browser-function
+ browse-url-browser-function))
;; Some URL handlers open files in Emacs. We want to always
;; open in a browser, so disable those.
(browse-url-default-handlers nil))
diff --git a/lisp/plstore.el b/lisp/plstore.el
index 0276a752a0f..d18d461d7d1 100644
--- a/lisp/plstore.el
+++ b/lisp/plstore.el
@@ -24,6 +24,14 @@
;; Plist based data store providing search and partial encryption.
;;
+;; By default, this package uses symmetric encryption, which means
+;; that you have to enter the password protecting your store more
+;; often than you probably expect to. To use public key encryption
+;; with this package, create a GnuPG key and customize user option
+;; `plstore-encrypt-to' to use it. You can then configure the GnuPG
+;; agent to adjust caching and expiration of the passphrase for your
+;; store.
+;;
;; Creating:
;;
;; ;; Open a new store associated with ~/.emacs.d/auth.plist.
@@ -43,12 +51,16 @@
;; ;; Kill the buffer visiting ~/.emacs.d/auth.plist.
;; (plstore-close store)
;;
+;; Avoid marking one property both as public *and* secret, as the
+;; behavior of this package with respect to such duplicate properties
+;; is not (yet) defined.
+;;
;; Searching:
;;
;; (setq store (plstore-open (expand-file-name "~/.emacs.d/auth.plist")))
;;
;; ;; As the entry "foo" associated with "foo.example.org" has no
-;; ;; secret properties, no need to decryption.
+;; ;; secret properties, no need for decryption.
;; (plstore-find store '(:host ("foo.example.org")))
;;
;; ;; As the entry "bar" associated with "bar.example.org" has a
@@ -66,17 +78,119 @@
;; Editing:
;;
;; This file also provides `plstore-mode', a major mode for editing
-;; the PLSTORE format file. Visit a non-existing file and put the
+;; the plstore format file. Visit a non-existing file and put the
;; following line:
;;
;; (("foo" :host "foo.example.org" :secret-user "user"))
;;
;; where the prefixing `:secret-' means the property (without
;; `:secret-' prefix) is marked as secret. Thus, when you save the
-;; buffer, the `:secret-user' property is encrypted as `:user'.
+;; buffer, the `:secret-user' property is encrypted as `:user'. Do
+;; not use a property consisting solely of the prefix, as the behavior
+;; of this package with respect to such properties is not (yet)
+;; defined.
;;
;; You can toggle the view between encrypted form and the decrypted
;; form with C-c C-c.
+;;
+;; If you have opened a plstore with `plstore-open' you should not
+;; edit its underlying buffer in `plstore-mode' or in any other way at
+;; the same time, since your manual changes will be overwritten when
+;; `plstore-save' is called on that plstore.
+;;
+;; Internals:
+;;
+;; This is information on the internal data structure and functions of
+;; this package. None of it should be necessary to actually use it.
+;; For easier reading, we usually do not distinguish in this internal
+;; documentation between a Lisp object and its printed representation.
+;;
+;; A plstore corresponds to an alist mapping strings to property
+;; lists. Internally, that alist is organized as two alists, one
+;; mapping to the non-secret properties and placeholders for the
+;; secret properties (called "template alist" with identifier ALIST)
+;; and one mapping to the secret properties ("secret alist",
+;; SECRET-ALIST). The secret alist is read from and written to file
+;; as pgp-encrypted printed representation of the alist ("encrypted
+;; data", ENCRYPTED-DATA).
+;;
+;; During the lifetime of a plstore, a third type of alist may pop up,
+;; which maps to the merged non-secret properties and plain-text
+;; secret properties ("merged alist", MERGED-ALIST).
+;;
+;; After executing the "foo", "bar", "baz" example from above the
+;; alists described above look like the following:
+;;
+;; Template Alist:
+;;
+;; (("foo" :host "foo.example.org" :port 80)
+;; ("bar" :secret-user t :host "bar.example.org")
+;; ("baz" :secret-password t :host "baz.example.org"))
+;;
+;; Secret Alist:
+;;
+;; (("bar" :user "test")
+;; ("baz" :password "test"))
+;;
+;; Merged Alist:
+;;
+;; (("foo" :host "foo.example.org" :port 80)
+;; ("bar" :user "test" :host "bar.example.org")
+;; ("baz" :password "test" :host "baz.example.org"))
+;;
+;; Finally, a plstore requires a buffer ("plstore buffer", BUFFER) for
+;; conversion between its Lisp objects and its file representation.
+;; It is important to note that this buffer is *not* continuously
+;; synchronized as the plstore changes. During the lifetime of a
+;; plstore, its buffer is read from in function `plstore-open' and
+;; (destructively) written to in `plstore-save', but not touched
+;; otherwise. We call the file visited by the plstore buffer the
+;; associated file of the plstore.
+;;
+;; With the identifiers defined above a plstore is a vector with the
+;; following elements and accessor functions:
+;;
+;; [
+;; BUFFER ; plstore--get/set-buffer
+;; ALIST ; plstore--get/set-alist
+;; ENCRYPTED-DATA ; plstore--get/set-encrypted-data
+;; SECRET-ALIST ; plstore--get/set-secret-alist
+;; MERGED-ALIST ; plstore--get/set-merged-alist
+;; ]
+;;
+;; When a plstore is created through `plstore-open', its ALIST and
+;; ENCRYPTED-DATA are initialized from the contents of BUFFER without
+;; any decryption taking place, and MERGED-ALIST is initialized as a
+;; copy of ALIST. (Which means that at that stage the merged alist
+;; still contains the secret property placeholders!)
+;;
+;; During on-demand decryption of a plstore through function
+;; `plstore--decrypt', SECRET-ALIST is populated from ENCRYPTED-DATA,
+;; which is in turn replaced by value nil. (Which further serves as
+;; an indicator that the plstore has been decrypted already.) In
+;; addition, MERGED-ALIST is recomputed by function
+;; `plstore--merge-secret' to replace the secret property placeholders
+;; by their plain-text secret property equivalents.
+;;
+;; The file representation of a plstore consists of two Lisp forms plus
+;; markers to introduce them:
+;;
+;; ;;; public entries
+;; ALIST
+;; ;;; secret entries
+;; ENCRYPTED-DATA
+;;
+;; Both of these are optional, but the first section must be present
+;; if the second one is. If both sections are missing, the plstore is
+;; empty. If the second section is missing, it contains only
+;; non-secret data. If present, the printed representation of the
+;; encrypted data includes the delimiting double quotes.
+;;
+;; The plstore API (`plstore-open', `plstore-put', etc.) and the
+;; plstore mode implemented by `plstore-mode' are orthogonal to each
+;; other and should not be mixed up. In particular, encoding and
+;; decoding a plstore mode buffer with `plstore-mode-toggle-display'
+;; is not related in any way to the state of the plstore buffer.
;;; Code:
@@ -121,10 +235,13 @@ symmetric encryption will be used."
(put 'plstore-encrypt-to 'permanent-local t)
-(defvar plstore-encoded nil)
+(defvar plstore-encoded nil
+ "Non-nil if the current buffer shows the decoded alist.") ; [sic!]
(put 'plstore-encoded 'permanent-local t)
+;;; EasyPG callback functions.
+
(defvar plstore-cache-passphrase-for-symmetric-encryption nil)
(defvar plstore-passphrase-alist nil)
@@ -141,11 +258,11 @@ symmetric encryption will be used."
(cons entry
plstore-passphrase-alist)))
(setq passphrase
- (read-passwd (format "Passphrase for PLSTORE %s: "
+ (read-passwd (format "Passphrase for plstore %s: "
(plstore--get-buffer plstore))))
(setcdr entry (copy-sequence passphrase))
passphrase)))
- (read-passwd (format "Passphrase for PLSTORE %s: "
+ (read-passwd (format "Passphrase for plstore %s: "
(plstore--get-buffer plstore)))))
(defun plstore-progress-callback-function (_context _what _char current total
@@ -155,6 +272,8 @@ symmetric encryption will be used."
(message "%s...%d%%" handback
(if (> total 0) (floor (* (/ current (float total)) 100)) 0))))
+;;; Core functions.
+
(defun plstore--get-buffer (arg)
(aref arg 0))
@@ -193,6 +312,7 @@ symmetric encryption will be used."
(vector buffer alist encrypted-data secret-alist merged-alist))
(defun plstore--init-from-buffer (plstore)
+ "Parse current buffer and initialize PLSTORE from it."
(goto-char (point-min))
(when (looking-at ";;; public entries")
(forward-line)
@@ -223,16 +343,20 @@ symmetric encryption will be used."
store)))
(defun plstore-revert (plstore)
- "Replace current data in PLSTORE with the file on disk."
+ "Replace current data in PLSTORE from its associated file."
(with-current-buffer (plstore--get-buffer plstore)
(revert-buffer t t)
(plstore--init-from-buffer plstore)))
(defun plstore-close (plstore)
- "Destroy a plstore instance PLSTORE."
+ "Destroy plstore instance PLSTORE."
(kill-buffer (plstore--get-buffer plstore)))
(defun plstore--merge-secret (plstore)
+ "Determine the merged alist of PLSTORE.
+Create the merged alist as a copy of the template alist with all
+placeholder properties that have corresponding properties in the
+secret alist replaced by their plain-text secret properties."
(let ((alist (plstore--get-secret-alist plstore))
modified-alist
modified-plist
@@ -251,19 +375,26 @@ symmetric encryption will be used."
modified-entry (assoc (car entry) modified-alist)
modified-plist (cdr modified-entry))
(while plist
+ ;; Search for a placeholder property in the merged alist
+ ;; corresponding to the current secret property.
(setq placeholder
(plist-member
modified-plist
(intern (concat ":secret-"
(substring (symbol-name (car plist)) 1)))))
+ ;; Replace its name with the real, secret property name.
(if placeholder
(setcar placeholder (car plist)))
+ ;; Update its value to the plain-text secret property value.
(setq modified-plist
(plist-put modified-plist (car plist) (car (cdr plist))))
(setq plist (nthcdr 2 plist)))
(setcdr modified-entry modified-plist))))
(defun plstore--decrypt (plstore)
+ "Decrypt the encrypted data of PLSTORE.
+Update its internal alists and other data structures
+accordingly."
(if (plstore--get-encrypted-data plstore)
(let ((context (epg-make-context 'OpenPGP))
plain)
@@ -290,6 +421,11 @@ symmetric encryption will be used."
(plstore--set-encrypted-data plstore nil))))
(defun plstore--match (entry keys skip-if-secret-found)
+ "Return whether plist KEYS matches ENTRY.
+ENTRY should be a key of the merged alist of a PLSTORE. This
+function returns nil if KEYS do not match ENTRY, t if they match,
+and symbol `secret' if the secret alist needs to be consulted to
+perform a match."
(let ((result t) key-name key-value prop-value secret-name)
(while keys
(setq key-name (car keys)
@@ -311,11 +447,10 @@ symmetric encryption will be used."
result))
(defun plstore-find (plstore keys)
- "Perform search on PLSTORE with KEYS.
-KEYS is a plist."
+ "Return all PLSTORE entries matching plist KEYS."
(let (entries alist entry match decrypt plist)
- ;; First, go through the merged plist alist and collect entries
- ;; matched with keys.
+ ;; First, go through the merged alist and collect entries matched
+ ;; by the keys.
(setq alist (plstore--get-merged-alist plstore))
(while alist
(setq entry (car alist)
@@ -331,7 +466,7 @@ KEYS is a plist."
plist nil))
(setq plist (nthcdr 2 plist)))
(setq entries (cons entry entries)))))
- ;; Second, decrypt the encrypted plist and try again.
+ ;; Second, decrypt the plstore and try again.
(when decrypt
(setq entries nil)
(plstore--decrypt plstore)
@@ -345,7 +480,8 @@ KEYS is a plist."
(nreverse entries)))
(defun plstore-get (plstore name)
- "Get an entry with NAME in PLSTORE."
+ "Return the entry named NAME in PLSTORE.
+Return nil if there is none."
(let ((entry (assoc name (plstore--get-merged-alist plstore)))
plist)
(setq plist (cdr entry))
@@ -359,7 +495,7 @@ KEYS is a plist."
entry))
(defun plstore-put (plstore name keys secret-keys)
- "Put an entry with NAME in PLSTORE.
+ "Put an entry named NAME in PLSTORE.
KEYS is a plist containing non-secret data.
SECRET-KEYS is a plist containing secret data."
(let (entry
@@ -398,7 +534,7 @@ SECRET-KEYS is a plist containing secret data."
(plstore--merge-secret plstore)))
(defun plstore-delete (plstore name)
- "Delete an entry with NAME from PLSTORE."
+ "Delete the first entry named NAME from PLSTORE."
(let ((entry (assoc name (plstore--get-alist plstore))))
(if entry
(plstore--set-alist
@@ -417,6 +553,8 @@ SECRET-KEYS is a plist containing secret data."
(defvar pp-escape-newlines)
(defun plstore--insert-buffer (plstore)
+ "Insert the file representation of PLSTORE at point.
+Assumes that PLSTORE has been decrypted."
(insert ";;; public entries -*- mode: plstore -*- \n"
(pp-to-string (plstore--get-alist plstore)))
(if (plstore--get-secret-alist plstore)
@@ -451,13 +589,31 @@ If no one is selected, symmetric encryption will be
performed. "
(insert ";;; secret entries\n" (pp-to-string cipher)))))
(defun plstore-save (plstore)
- "Save the contents of PLSTORE associated with a FILE."
+ "Save PLSTORE to its associated file."
(with-current-buffer (plstore--get-buffer plstore)
(erase-buffer)
(plstore--insert-buffer plstore)
(save-buffer)))
+;;; plstore mode.
+
+;; The functions related to plstore mode unfortunately introduce yet
+;; another alist format ("decoded alist"). After executing the "foo",
+;; "bar", "baz" example from above the decoded alist of the plstore
+;; would look like the following:
+;;
+;; (("foo" :host "foo.example.org" :port 80)
+;; ("bar" :secret-user "test" :host "bar.example.org")
+;; ("baz" :secret-password "test" :host "baz.example.org"))
+;;
+;; Even more unfortunately, variable and function names of the
+;; following are a bit mixed up IMHO: With the current names, the
+;; result of function `plstore--encode' is used to create what is
+;; presented as "decoded form of a plstore" to the user. And variable
+;; `plstore-encoded' is non-nil if a buffer shows the decoded form.
+
(defun plstore--encode (plstore)
+ "Return the printed representation of the decoded alist of PLSTORE."
(plstore--decrypt plstore)
(let ((merged-alist (plstore--get-merged-alist plstore)))
(concat "("
@@ -482,6 +638,9 @@ If no one is selected, symmetric encryption will be
performed. "
")")))
(defun plstore--decode (string)
+ "Create a plstore instance from STRING.
+STRING should be the printed representation of a decoded alist of
+some plstore."
(let* ((alist (car (read-from-string string)))
(pointer alist)
secret-alist
@@ -489,7 +648,7 @@ If no one is selected, symmetric encryption will be
performed. "
entry)
(while pointer
(unless (stringp (car (car pointer)))
- (error "Invalid PLSTORE format %s" string))
+ (error "Invalid plstore format %s" string))
(setq plist (cdr (car pointer)))
(while plist
(when (string-match "\\`:secret-" (symbol-name (car plist)))
@@ -509,6 +668,10 @@ If no one is selected, symmetric encryption will be
performed. "
(plstore--make nil alist nil secret-alist)))
(defun plstore--write-contents-functions ()
+ "Convert the decoded form of a plstore in the current buffer.
+Convert it to the regular file representation of a plstore if
+needed. This function is used on hook `write-contents-functions'
+in plstore mode buffers."
(when plstore-encoded
(let ((store (plstore--decode (buffer-string)))
(file (buffer-file-name)))
@@ -546,7 +709,7 @@ If no one is selected, symmetric encryption will be
performed. "
(erase-buffer)
(insert
(substitute-command-keys "\
-;;; You are looking at the decoded form of the PLSTORE file.\n\
+;;; You are looking at the decoded form of the plstore file.\n\
;;; To see the original form content, do \\[plstore-mode-toggle-display]\n\n"))
(insert (plstore--encode store))
(set-buffer-modified-p nil)
@@ -561,7 +724,7 @@ If no one is selected, symmetric encryption will be
performed. "
;;;###autoload
(define-derived-mode plstore-mode emacs-lisp-mode "PLSTORE"
- "Major mode for editing PLSTORE files."
+ "Major mode for editing plstore files."
(make-local-variable 'plstore-encoded)
(add-hook 'write-contents-functions #'plstore--write-contents-functions)
(define-key plstore-mode-map "\C-c\C-c" #'plstore-mode-toggle-display)
diff --git a/lisp/progmodes/dockerfile-ts-mode.el
b/lisp/progmodes/dockerfile-ts-mode.el
index c9125bc6cbd..333158e20f6 100644
--- a/lisp/progmodes/dockerfile-ts-mode.el
+++ b/lisp/progmodes/dockerfile-ts-mode.el
@@ -123,8 +123,9 @@ continuation to the previous entry."
(let* ((node (treesit-buffer-root-node))
(stage-tree (treesit-induce-sparse-tree
node "from_instruction"
- nil 1000)))
- `(("Stage" . ,(dockerfile-ts-mode--imenu-1 stage-tree)))))
+ nil 1000))
+ (stage-index (dockerfile-ts-mode--imenu-1 stage-tree)))
+ (when stage-index `(("Stage" . ,stage-index)))))
(defun dockerfile-ts-mode--imenu-1 (node)
"Helper for `dockerfile-ts-mode--imenu'.
diff --git a/lisp/progmodes/go-ts-mode.el b/lisp/progmodes/go-ts-mode.el
index b8705ecc4d0..32d86f44235 100644
--- a/lisp/progmodes/go-ts-mode.el
+++ b/lisp/progmodes/go-ts-mode.el
@@ -59,6 +59,7 @@
(modify-syntax-entry ?< "." table)
(modify-syntax-entry ?> "." table)
(modify-syntax-entry ?\\ "\\" table)
+ (modify-syntax-entry ?\' "\"" table)
(modify-syntax-entry ?/ ". 124b" table)
(modify-syntax-entry ?* ". 23" table)
(modify-syntax-entry ?\n "> b" table)
diff --git a/lisp/progmodes/java-ts-mode.el b/lisp/progmodes/java-ts-mode.el
index 47d87112ffb..8a396a8c977 100644
--- a/lisp/progmodes/java-ts-mode.el
+++ b/lisp/progmodes/java-ts-mode.el
@@ -226,6 +226,9 @@ the available version of Tree-sitter for java."
(constructor_declaration
name: (identifier) @font-lock-type-face)
+ (compact_constructor_declaration
+ name: (identifier) @font-lock-type-face)
+
(field_access
object: (identifier) @font-lock-type-face)
diff --git a/lisp/progmodes/project.el b/lisp/progmodes/project.el
index 41a5c976629..56c524bcab5 100644
--- a/lisp/progmodes/project.el
+++ b/lisp/progmodes/project.el
@@ -816,8 +816,8 @@ DIRS must contain directory names."
(push buf bufs)))
(nreverse bufs)))
-(cl-defmethod project-name ((_project (head vc)))
- (or project-vc-name
+(cl-defmethod project-name ((project (head vc)))
+ (or (project--value-in-dir 'project-vc-name (project-root project))
(cl-call-next-method)))
diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el
index 8c716ffb313..86bbf51dab7 100644
--- a/lisp/progmodes/python.el
+++ b/lisp/progmodes/python.el
@@ -415,7 +415,6 @@ instead."
"Python mode specialized rx macro.
This variant of `rx' supports common Python named REGEXPS."
`(rx-let ((sp-bsnl (or space (and ?\\ ?\n)))
- (sp-nl (or space (and (? ?\\) ?\n)))
(block-start (seq symbol-start
(or "def" "class" "if" "elif" "else" "try"
"except" "finally" "for" "while" "with"
@@ -650,9 +649,9 @@ the {...} holes that appear within f-strings."
finally return (and result-valid result))))
(defvar python-font-lock-keywords-level-1
- `((,(python-rx symbol-start "def" (1+ sp-bsnl) (group symbol-name))
+ `((,(python-rx symbol-start "def" (1+ space) (group symbol-name))
(1 font-lock-function-name-face))
- (,(python-rx symbol-start "class" (1+ sp-bsnl) (group symbol-name))
+ (,(python-rx symbol-start "class" (1+ space) (group symbol-name))
(1 font-lock-type-face)))
"Font lock keywords to use in `python-mode' for level 1 decoration.
@@ -792,12 +791,12 @@ sign in chained assignment."
;; [*a] = 5, 6
;; are handled separately below
(,(python-font-lock-assignment-matcher
- (python-rx (? (or "[" "(") (* sp-nl))
- grouped-assignment-target (* sp-nl) ?, (* sp-nl)
- (* assignment-target (* sp-nl) ?, (* sp-nl))
- (? assignment-target (* sp-nl))
- (? ?, (* sp-nl))
- (? (or ")" "]") (* sp-bsnl))
+ (python-rx (? (or "[" "(") (* space))
+ grouped-assignment-target (* space) ?, (* space)
+ (* assignment-target (* space) ?, (* space))
+ (? assignment-target (* space))
+ (? ?, (* space))
+ (? (or ")" "]") (* space))
(group assignment-operator)))
(1 font-lock-variable-name-face)
(2 'font-lock-operator-face)
@@ -813,9 +812,9 @@ sign in chained assignment."
;; c: Collection = {1, 2, 3}
;; d: Mapping[int, str] = {1: 'bar', 2: 'baz'}
(,(python-font-lock-assignment-matcher
- (python-rx (or line-start ?\;) (* sp-bsnl)
- grouped-assignment-target (* sp-bsnl)
- (? ?: (* sp-bsnl) (+ not-simple-operator) (* sp-bsnl))
+ (python-rx (or line-start ?\;) (* space)
+ grouped-assignment-target (* space)
+ (? ?: (* space) (+ not-simple-operator) (* space))
(group assignment-operator)))
(1 font-lock-variable-name-face)
(2 'font-lock-operator-face))
@@ -824,10 +823,10 @@ sign in chained assignment."
;; [a] = 5,
;; [*a] = 5, 6
(,(python-font-lock-assignment-matcher
- (python-rx (or line-start ?\; ?=) (* sp-bsnl)
- (or "[" "(") (* sp-nl)
- grouped-assignment-target (* sp-nl)
- (or ")" "]") (* sp-bsnl)
+ (python-rx (or line-start ?\; ?=) (* space)
+ (or "[" "(") (* space)
+ grouped-assignment-target (* space)
+ (or ")" "]") (* space)
(group assignment-operator)))
(1 font-lock-variable-name-face)
(2 'font-lock-operator-face))
@@ -869,22 +868,6 @@ decorators, exceptions, and assignments.")
Which one will be chosen depends on the value of
`font-lock-maximum-decoration'.")
-(defvar font-lock-beg)
-(defvar font-lock-end)
-(defun python-font-lock-extend-region ()
- "Extend font-lock region to statement boundaries."
- (let ((beg font-lock-beg)
- (end font-lock-end))
- (goto-char beg)
- (python-nav-beginning-of-statement)
- (beginning-of-line)
- (when (< (point) beg)
- (setq font-lock-beg (point)))
- (goto-char end)
- (python-nav-end-of-statement)
- (when (< end (point))
- (setq font-lock-end (point)))
- (or (/= beg font-lock-beg) (/= end font-lock-end))))
(defconst python-syntax-propertize-function
(syntax-propertize-rules
@@ -6171,7 +6154,8 @@ Optional argument REGEXP selects variables to clone and
defaults
to \"^python-\"."
(mapc
(lambda (pair)
- (and (symbolp (car pair))
+ (and (consp pair)
+ (symbolp (car pair))
(string-match (or regexp "^python-")
(symbol-name (car pair)))
(set (make-local-variable (car pair))
@@ -6769,8 +6753,6 @@ implementations: `python-mode' and `python-ts-mode'."
nil nil nil nil
(font-lock-syntactic-face-function
. python-font-lock-syntactic-face-function)))
- (add-hook 'font-lock-extend-region-functions
- #'python-font-lock-extend-region nil t)
(setq-local syntax-propertize-function
python-syntax-propertize-function)
(setq-local imenu-create-index-function
diff --git a/lisp/progmodes/rust-ts-mode.el b/lisp/progmodes/rust-ts-mode.el
index be06acde3e3..c3cf8d0cf44 100644
--- a/lisp/progmodes/rust-ts-mode.el
+++ b/lisp/progmodes/rust-ts-mode.el
@@ -350,7 +350,12 @@ Return nil if there is no name or if NODE is not a defun
node."
(treesit-node-child-by-field-name node "name") t))))
(defun rust-ts-mode--syntax-propertize (beg end)
- "Apply syntax text property to template delimiters between BEG and END.
+ "Apply syntax properties to special characters between BEG and END.
+
+Apply syntax properties to various special characters with
+contextual meaning between BEG and END.
+
+The apostrophe \\=' should be treated as string when used for char literals.
< and > are usually punctuation, e.g., as greater/less-than. But
when used for types, they should be considered pairs.
@@ -359,11 +364,18 @@ This function checks for < and > in the changed RANGES
and apply
appropriate text property to alter the syntax of template
delimiters < and >'s."
(goto-char beg)
+ (while (search-forward "'" end t)
+ (when (string-equal "char_literal"
+ (treesit-node-type
+ (treesit-node-at (match-beginning 0))))
+ (put-text-property (match-beginning 0) (match-end 0)
+ 'syntax-table (string-to-syntax "\""))))
+ (goto-char beg)
(while (re-search-forward (rx (or "<" ">")) end t)
(pcase (treesit-node-type
(treesit-node-parent
(treesit-node-at (match-beginning 0))))
- ("type_arguments"
+ ((or "type_arguments" "type_parameters")
(put-text-property (match-beginning 0)
(match-end 0)
'syntax-table
diff --git a/lisp/progmodes/typescript-ts-mode.el
b/lisp/progmodes/typescript-ts-mode.el
index 3f198e9f180..8255e82a99e 100644
--- a/lisp/progmodes/typescript-ts-mode.el
+++ b/lisp/progmodes/typescript-ts-mode.el
@@ -128,7 +128,7 @@ Argument LANGUAGE is either `typescript' or `tsx'."
"case" "catch" "class" "const" "continue" "debugger"
"declare" "default" "delete" "do" "else" "enum"
"export" "extends" "finally" "for" "from" "function"
- "get" "if" "implements" "import" "in" "instanceof" "interface"
+ "get" "if" "implements" "import" "in" "instanceof" "interface" "is"
"keyof" "let" "namespace" "new" "of" "private" "protected"
"public" "readonly" "return" "set" "static" "switch"
"target" "throw" "try" "type" "typeof" "var" "void"
diff --git a/lisp/simple.el b/lisp/simple.el
index e2bec58a7b0..e08bf4fdd64 100644
--- a/lisp/simple.el
+++ b/lisp/simple.el
@@ -1520,7 +1520,8 @@ the actual saved text might be different from what was
killed."
(let ((from (car cmp))
(to (cadr cmp)))
(cond
- ((= (length cmp) 2) ; static composition
+ ((and (= (length cmp) 3) ; static composition
+ (booleanp (nth 2 cmp)))
to)
;; TO can be at POS, in which case we want
;; to make sure we advance at least by 1
diff --git a/lisp/textmodes/enriched.el b/lisp/textmodes/enriched.el
index ec3046decd4..92be5a52bf2 100644
--- a/lisp/textmodes/enriched.el
+++ b/lisp/textmodes/enriched.el
@@ -192,6 +192,7 @@ The value is a list of \(VAR VALUE VAR VALUE...).")
(define-key map "\C-c[" #'set-left-margin)
(define-key map "\C-c]" #'set-right-margin)
(define-key map "\M-o" #'facemenu-keymap)
+ (define-key map "\M-om" #'enriched-toggle-markup)
map)
"Keymap for Enriched mode.")
diff --git a/lisp/tmm.el b/lisp/tmm.el
index 6088961fa4c..a4058594622 100644
--- a/lisp/tmm.el
+++ b/lisp/tmm.el
@@ -170,9 +170,11 @@ instead of executing it."
(error "Empty menu reached"))
(and tmm-km-list
(let ((index-of-default 0))
- (if tmm-mid-prompt
- (setq tmm-km-list (tmm-add-shortcuts tmm-km-list))
- t)
+ (setq tmm-km-list
+ (if tmm-mid-prompt
+ (tmm-add-shortcuts tmm-km-list)
+ ;; tmm-add-shortcuts reverses tmm-km-list internally.
+ (reverse tmm-km-list)))
;; Find the default item's index within the menu bar.
;; We use this to decide the initial minibuffer contents
;; and initial history position.
@@ -327,7 +329,8 @@ Stores a list of all the shortcuts in the free variable
`tmm-short-cuts'."
(with-current-buffer standard-output
(goto-char (point-min))
(let* (;; First candidate: first string with mouse-face
- (menu-start-1 (next-single-char-property-change (point)
'mouse-face))
+ (menu-start-1 (or (and (get-text-property (point) 'mouse-face)
(point))
+ (next-single-char-property-change (point)
'mouse-face)))
;; Second candidate: an inactive menu item with tmm-inactive face
(tps-result (save-excursion
(text-property-search-forward 'face 'tmm-inactive t)))
diff --git a/lisp/transient.el b/lisp/transient.el
index 1d763c4ddeb..048554eee13 100644
--- a/lisp/transient.el
+++ b/lisp/transient.el
@@ -6,7 +6,7 @@
;; URL: https://github.com/magit/transient
;; Keywords: extensions
-;; Package-Version: 0.4.0
+;; Package-Version: 0.4.1
;; Package-Requires: ((emacs "26.1"))
;; SPDX-License-Identifier: GPL-3.0-or-later
@@ -1643,6 +1643,7 @@ of the corresponding object."
"<transient-history-prev>" #'transient--do-stay
"<transient-history-next>" #'transient--do-stay
"<universal-argument>" #'transient--do-stay
+ "<universal-argument-more>" #'transient--do-stay
"<negative-argument>" #'transient--do-minus
"<digit-argument>" #'transient--do-stay
"<top-level>" #'transient--do-quit-all
@@ -3043,10 +3044,12 @@ prompt."
(progn
(cl-call-next-method obj value)
(dolist (arg incomp)
- (when-let ((obj (cl-find-if (lambda (obj)
- (and (slot-boundp obj 'argument)
- (equal (oref obj argument)
arg)))
- transient--suffixes)))
+ (when-let ((obj (cl-find-if
+ (lambda (obj)
+ (and (slot-exists-p obj 'argument)
+ (slot-boundp obj 'argument)
+ (equal (oref obj argument) arg)))
+ transient--suffixes)))
(let ((transient--unset-incompatible nil))
(transient-infix-set obj nil)))))
(cl-call-next-method obj value))))
@@ -3253,6 +3256,8 @@ have a history of their own.")
(with-current-buffer buf
(when transient-enable-popup-navigation
(setq focus (or (button-get (point) 'command)
+ (and (not (bobp))
+ (button-get (1- (point)) 'command))
(transient--heading-at-point))))
(erase-buffer)
(setq window-size-fixed t)
@@ -3384,7 +3389,9 @@ have a history of their own.")
(insert ?\n)
(insert (propertize " " 'display
`(space :align-to (,(nth (1+ c) cc)))))))
- (insert (make-string (max 1 (- (nth c cc) (current-column))) ?\s))
+ (when (> c 0)
+ (insert (make-string (max 1 (- (nth c cc) (current-column)))
+ ?\s)))
(when-let ((cell (nth r (nth c columns))))
(insert cell))
(when (= c (1- cs))
diff --git a/test/infra/Dockerfile.emba b/test/infra/Dockerfile.emba
index c2e3afadb05..325cff6bd26 100644
--- a/test/infra/Dockerfile.emba
+++ b/test/infra/Dockerfile.emba
@@ -29,7 +29,7 @@ FROM debian:bullseye as emacs-base
RUN apt-get update && \
apt-get install -y --no-install-recommends -o=Dpkg::Use-Pty=0 \
libc-dev gcc g++ make autoconf automake libncurses-dev gnutls-dev \
- libdbus-1-dev libacl1-dev acl git texinfo gdb \
+ libdbus-1-dev libacl1-dev acl git texinfo gawk gdb \
&& rm -rf /var/lib/apt/lists/*
FROM emacs-base as emacs-inotify
diff --git a/test/lisp/progmodes/python-tests.el
b/test/lisp/progmodes/python-tests.el
index b916232c4be..9323f72f384 100644
--- a/test/lisp/progmodes/python-tests.el
+++ b/test/lisp/progmodes/python-tests.el
@@ -136,20 +136,6 @@ STRING, it is skipped so the next STRING occurrence is
selected."
while pos
collect (cons pos (get-text-property pos 'face))))
-(defun python-tests-assert-faces-after-change (content faces search replace)
- "Assert that font faces for CONTENT are equal to FACES after change.
-All occurrences of SEARCH are changed to REPLACE."
- (python-tests-with-temp-buffer
- content
- ;; Force enable font-lock mode without jit-lock.
- (rename-buffer "*python-font-lock-test*" t)
- (let (noninteractive font-lock-support-mode)
- (font-lock-mode))
- (while
- (re-search-forward search nil t)
- (replace-match replace))
- (should (equal faces (python-tests-get-buffer-faces)))))
-
(defun python-tests-self-insert (char-or-str)
"Call `self-insert-command' for chars in CHAR-OR-STR."
(let ((chars
@@ -297,13 +283,6 @@ p = (1 + 2)
"def 1func():"
'((1 . font-lock-keyword-face) (4))))
-(ert-deftest python-font-lock-keywords-level-1-3 ()
- (python-tests-assert-faces
- "def \\
- func():"
- '((1 . font-lock-keyword-face) (4)
- (15 . font-lock-function-name-face) (19))))
-
(ert-deftest python-font-lock-assignment-statement-1 ()
(python-tests-assert-faces
"a, b, c = 1, 2, 3"
@@ -495,129 +474,6 @@ def f(x: CustomInt) -> CustomInt:
(136 . font-lock-operator-face) (137)
(144 . font-lock-keyword-face) (150))))
-(ert-deftest python-font-lock-assignment-statement-multiline-1 ()
- (python-tests-assert-faces-after-change
- "
-[
- a,
- b
-] # (
- 1,
- 2
-)
-"
- '((1)
- (8 . font-lock-variable-name-face) (9)
- (15 . font-lock-variable-name-face) (16)
- (19 . font-lock-operator-face) (20))
- "#" "="))
-
-(ert-deftest python-font-lock-assignment-statement-multiline-2 ()
- (python-tests-assert-faces-after-change
- "
-[
- *a
-] # 5, 6
-"
- '((1)
- (8 . font-lock-operator-face)
- (9 . font-lock-variable-name-face) (10)
- (13 . font-lock-operator-face) (14))
- "#" "="))
-
-(ert-deftest python-font-lock-assignment-statement-multiline-3 ()
- (python-tests-assert-faces-after-change
- "a\\
- ,\\
- b\\
- ,\\
- c\\
- #\\
- 1\\
- ,\\
- 2\\
- ,\\
- 3"
- '((1 . font-lock-variable-name-face) (2)
- (15 . font-lock-variable-name-face) (16)
- (29 . font-lock-variable-name-face) (30)
- (36 . font-lock-operator-face) (37))
- "#" "="))
-
-(ert-deftest python-font-lock-assignment-statement-multiline-4 ()
- (python-tests-assert-faces-after-change
- "a\\
- :\\
- int\\
- #\\
- 5"
- '((1 . font-lock-variable-name-face) (2)
- (15 . font-lock-builtin-face) (18)
- (24 . font-lock-operator-face) (25))
- "#" "="))
-
-(ert-deftest python-font-lock-assignment-statement-multiline-5 ()
- (python-tests-assert-faces-after-change
- "(\\
- a\\
-)\\
- #\\
- 5\\
- ;\\
- (\\
- b\\
- )\\
- #\\
- 6"
- '((1)
- (8 . font-lock-variable-name-face) (9)
- (18 . font-lock-operator-face) (19)
- (46 . font-lock-variable-name-face) (47)
- (60 . font-lock-operator-face) (61))
- "#" "="))
-
-(ert-deftest python-font-lock-assignment-statement-multiline-6 ()
- (python-tests-assert-faces-after-change
- "(
- a
-)\\
- #\\
- 5\\
- ;\\
- (
- b
- )\\
- #\\
- 6"
- '((1)
- (7 . font-lock-variable-name-face) (8)
- (16 . font-lock-operator-face) (17)
- (43 . font-lock-variable-name-face) (44)
- (56 . font-lock-operator-face) (57))
- "#" "="))
-
-(ert-deftest python-font-lock-operator-1 ()
- (python-tests-assert-faces
- "1 << 2 ** 3 == +4%-5|~6&7^8%9"
- '((1)
- (3 . font-lock-operator-face) (5)
- (8 . font-lock-operator-face) (10)
- (13 . font-lock-operator-face) (15)
- (16 . font-lock-operator-face) (17)
- (18 . font-lock-operator-face) (20)
- (21 . font-lock-operator-face) (23)
- (24 . font-lock-operator-face) (25)
- (26 . font-lock-operator-face) (27)
- (28 . font-lock-operator-face) (29))))
-
-(ert-deftest python-font-lock-operator-2 ()
- "Keyword operators are font-locked as keywords."
- (python-tests-assert-faces
- "is_ is None"
- '((1)
- (5 . font-lock-keyword-face) (7)
- (8 . font-lock-constant-face))))
-
(ert-deftest python-font-lock-escape-sequence-string-newline ()
(python-tests-assert-faces
"'\\n'