Merge from origin/emacs-29

3204825f56 Fix mangled Subject header field when forwarding (Bug#67360)
7591acfe38 Update to Org 9.6.15
240b4594f1 ; * etc/TODO: Add an item about 'Info-hide-note-references'.
01be4fe39d * doc/emacs/custom.texi (Modifier Keys): Fix markup (bug#...
55555a6a0d org-protocol: Minor copy-edits to Commentary
4696869d3d Improve syntax highlighting for python-ts-mode
This commit is contained in:
Po Lu 2024-01-02 10:19:48 +08:00
commit 083e90dd80
13 changed files with 536 additions and 93 deletions

View file

@ -2065,7 +2065,7 @@ C-a} is a way to enter @kbd{Hyper-Control-a}. (Unfortunately, there
is no way to add two modifiers by using @kbd{C-x @@} twice for the is no way to add two modifiers by using @kbd{C-x @@} twice for the
same character, because the first one goes to work on the @kbd{C-x}.) same character, because the first one goes to work on the @kbd{C-x}.)
You can similarly enter the Shift, Control, and Meta modifiers by You can similarly enter the Shift, Control, and Meta modifiers by
using @kbd{C-x @ S}, @kbd{C-x @ c}, and @kbd{C-x @ m}, respectively, using @kbd{C-x @@ S}, @kbd{C-x @@ c}, and @kbd{C-x @@ m}, respectively,
although this is rarely needed. although this is rarely needed.
@node Function Keys @node Function Keys

View file

@ -4606,7 +4606,7 @@ checked.
#+cindex: statistics, for checkboxes #+cindex: statistics, for checkboxes
#+cindex: checkbox statistics #+cindex: checkbox statistics
#+cindex: @samp{COOKIE_DATA}, property #+cindex: @samp{COOKIE_DATA}, property
#+vindex: org-hierarchical-checkbox-statistics #+vindex: org-checkbox-hierarchical-statistics
The =[2/4]= and =[1/3]= in the first and second line are cookies The =[2/4]= and =[1/3]= in the first and second line are cookies
indicating how many checkboxes present in this entry have been checked indicating how many checkboxes present in this entry have been checked
off, and the total number of checkboxes present. This can give you an off, and the total number of checkboxes present. This can give you an
@ -4614,7 +4614,7 @@ idea on how many checkboxes remain, even without opening a folded
entry. The cookies can be placed into a headline or into (the first entry. The cookies can be placed into a headline or into (the first
line of) a plain list item. Each cookie covers checkboxes of direct line of) a plain list item. Each cookie covers checkboxes of direct
children structurally below the headline/item on which the cookie children structurally below the headline/item on which the cookie
appears[fn:: Set the variable ~org-hierarchical-checkbox-statistics~ appears[fn:: Set the variable ~org-checkbox-hierarchical-statistics~
if you want such cookies to count all checkboxes below the cookie, not if you want such cookies to count all checkboxes below the cookie, not
just those belonging to direct children.]. You have to insert the just those belonging to direct children.]. You have to insert the
cookie yourself by typing either =[/]= or =[%]=. With =[/]= you get cookie yourself by typing either =[/]= or =[%]=. With =[/]= you get

View file

@ -133,6 +133,20 @@ This should use a heuristic of some kind?
** In Emacs Info, examples of using Customize should be clickable ** In Emacs Info, examples of using Customize should be clickable
They should create Custom buffers when clicked. They should create Custom buffers when clicked.
** Replacements under 'Info-hide-note-references' should be language-sensitive
Currently, we replace the "*note" cross-reference indicators with a
hard-coded "see", which is English-centric and doesn't look well in
manuals written in languages other than English. To fix this, we need
a change in the Texinfo's 'makeinfo' program so that it records the
document's language (specified via the @documentlanguage directive in
Texinfo) in a variable in the Local Variables section of the produced
Info file. Then 'Info-fontify-node' should be modified to look up the
translation of "see" to that language in a database (which should be
added), and should use that translation instead of "see". See this
discussion on the Texinfo mailing list for more details:
https://lists.gnu.org/archive/html/help-texinfo/2023-12/msg00011.html
** Add function to redraw the tool bar ** Add function to redraw the tool bar
** Redesign the load-history data structure ** Redesign the load-history data structure

View file

@ -1,5 +1,5 @@
% Reference Card for Org Mode % Reference Card for Org Mode
\def\orgversionnumber{9.6.13} \def\orgversionnumber{9.6.15}
\def\versionyear{2023} % latest update \def\versionyear{2023} % latest update
\input emacsver.tex \input emacsver.tex

View file

@ -574,7 +574,7 @@ See also `mh-compose-forward-as-mime-flag',
(setq orig-subject (mh-get-header-field "Subject:"))) (setq orig-subject (mh-get-header-field "Subject:")))
(let ((forw-subject (let ((forw-subject
(mh-forwarded-letter-subject orig-from orig-subject))) (mh-forwarded-letter-subject orig-from orig-subject)))
(mh-insert-fields "Subject:" forw-subject) (mh-modify-header-field "Subject" forw-subject t)
(goto-char (point-min)) (goto-char (point-min))
;; Set the local value of mh-mail-header-separator according to what is ;; Set the local value of mh-mail-header-separator according to what is
;; present in the buffer... ;; present in the buffer...

View file

@ -41,14 +41,19 @@
(defun org-entities--user-safe-p (v) (defun org-entities--user-safe-p (v)
"Non-nil if V is a safe value for `org-entities-user'." "Non-nil if V is a safe value for `org-entities-user'."
(pcase v (cond
(`nil t) ((not v) t)
(`(,(and (pred stringp) ((listp v)
(pred (string-match-p "\\`[a-zA-Z][a-zA-Z0-9]*\\'"))) (seq-every-p
,(pred stringp) ,(pred booleanp) ,(pred stringp) (lambda (e)
,(pred stringp) ,(pred stringp) ,(pred stringp)) (pcase e
t) (`(,(and (pred stringp)
(_ nil))) (pred (string-match-p "\\`[a-zA-Z][a-zA-Z0-9]*\\'")))
,(pred stringp) ,(pred booleanp) ,(pred stringp)
,(pred stringp) ,(pred stringp) ,(pred stringp))
t)
(_ nil)))
v))))
(defcustom org-entities-user nil (defcustom org-entities-user nil
"User-defined entities used in Org to produce special characters. "User-defined entities used in Org to produce special characters.

View file

@ -56,8 +56,8 @@ by `package-activate-all').")
;; `org-assert-version' calls would fail using strict ;; `org-assert-version' calls would fail using strict
;; `org-git-version' check because the generated Org version strings ;; `org-git-version' check because the generated Org version strings
;; will not match. ;; will not match.
`(unless (or org--inhibit-version-check (equal (org-release) ,(org-release))) `(unless (or ,org--inhibit-version-check (equal (org-release) ,(org-release)))
(warn "Org version mismatch. Org loading aborted. (warn "Org version mismatch.
This warning usually appears when a built-in Org version is loaded This warning usually appears when a built-in Org version is loaded
prior to the more recent Org version. prior to the more recent Org version.
@ -91,10 +91,15 @@ Version mismatch is commonly encountered in the following situations:
early in the config. Ideally, right after the straight.el early in the config. Ideally, right after the straight.el
bootstrap. Moving `use-package' :straight declaration may not be bootstrap. Moving `use-package' :straight declaration may not be
sufficient if the corresponding `use-package' statement is sufficient if the corresponding `use-package' statement is
deferring the loading." deferring the loading.
4. A new Org version is synchronized with Emacs git repository and
stale .elc files are still left from the previous build.
It is recommended to remove .elc files from lisp/org directory and
re-compile."
;; Avoid `warn' replacing "'" with "" (see `format-message'). ;; Avoid `warn' replacing "'" with "" (see `format-message').
"(straight-use-package 'org)") "(straight-use-package 'org)")))
(error "Org version mismatch. Make sure that correct `load-path' is set early in init.el")))
;; We rely on org-macs when generating Org version. Checking Org ;; We rely on org-macs when generating Org version. Checking Org
;; version here will interfere with Org build process. ;; version here will interfere with Org build process.

View file

@ -34,7 +34,10 @@
;; `org-protocol-protocol-alist' and `org-protocol-protocol-alist-default'. ;; `org-protocol-protocol-alist' and `org-protocol-protocol-alist-default'.
;; ;;
;; Any application that supports calling external programs with an URL ;; Any application that supports calling external programs with an URL
;; as argument may be used with this functionality. ;; as argument could use this functionality. For example, you can
;; configure bookmarks in your web browser to send a link to the
;; current page to Org and create a note from it using `org-capture'.
;; See Info node `(org) Protocols' for more information.
;; ;;
;; ;;
;; Usage: ;; Usage:
@ -44,13 +47,13 @@
;; ;;
;; (require 'org-protocol) ;; (require 'org-protocol)
;; ;;
;; 3.) Ensure emacs-server is up and running. ;; 2.) Ensure emacs-server is up and running.
;; 4.) Try this from the command line (adjust the URL as needed): ;; 3.) Try this from the command line (adjust the URL as needed):
;; ;;
;; $ emacsclient \ ;; $ emacsclient \
;; "org-protocol://store-link?url=http:%2F%2Flocalhost%2Findex.html&title=The%20title" ;; "org-protocol://store-link?url=http:%2F%2Flocalhost%2Findex.html&title=The%20title"
;; ;;
;; 5.) Optionally add custom sub-protocols and handlers: ;; 4.) Optionally, add custom sub-protocols and handlers:
;; ;;
;; (setq org-protocol-protocol-alist ;; (setq org-protocol-protocol-alist
;; '(("my-protocol" ;; '(("my-protocol"
@ -64,10 +67,11 @@
;; If it works, you can now setup other applications for using this feature. ;; If it works, you can now setup other applications for using this feature.
;; ;;
;; ;;
;; As of March 2009 Firefox users follow the steps documented on ;; Firefox users follow the steps documented on
;; https://kb.mozillazine.org/Register_protocol, Opera setup is described here: ;; https://kb.mozillazine.org/Register_protocol, Opera setup is
;; http://www.opera.com/support/kb/view/535/ ;; described here: http://www.opera.com/support/kb/view/535/
;; ;;
;; See also: https://orgmode.org/worg/org-contrib/org-protocol.html
;; ;;
;; Documentation ;; Documentation
;; ------------- ;; -------------
@ -123,9 +127,6 @@
;; Note that using double slashes is optional from org-protocol.el's point of ;; Note that using double slashes is optional from org-protocol.el's point of
;; view because emacsclient squashes the slashes to one. ;; view because emacsclient squashes the slashes to one.
;; ;;
;;
;; provides: 'org-protocol
;;
;;; Code: ;;; Code:
(require 'org-macs) (require 'org-macs)

View file

@ -5,13 +5,13 @@
(defun org-release () (defun org-release ()
"The release version of Org. "The release version of Org.
Inserted by installing Org mode or when a release is made." Inserted by installing Org mode or when a release is made."
(let ((org-release "9.6.13")) (let ((org-release "9.6.15"))
org-release)) org-release))
;;;###autoload ;;;###autoload
(defun org-git-version () (defun org-git-version ()
"The Git version of Org mode. "The Git version of Org mode.
Inserted by installing Org or when a release is made." Inserted by installing Org or when a release is made."
(let ((org-git-version "release_9.6.13")) (let ((org-git-version "release_9.6.15"))
org-git-version)) org-git-version))
(provide 'org-version) (provide 'org-version)

View file

@ -9,7 +9,7 @@
;; URL: https://orgmode.org ;; URL: https://orgmode.org
;; Package-Requires: ((emacs "26.1")) ;; Package-Requires: ((emacs "26.1"))
;; Version: 9.6.13 ;; Version: 9.6.15
;; This file is part of GNU Emacs. ;; This file is part of GNU Emacs.
;; ;;

View file

@ -305,7 +305,7 @@ INFO is a plist used as a communication channel."
(section-title (org-html--translate "Footnotes" info))) (section-title (org-html--translate "Footnotes" info)))
(when fn-alist (when fn-alist
(format (plist-get info :md-footnotes-section) (format (plist-get info :md-footnotes-section)
(org-md--headline-title headline-style 1 section-title) (org-md--headline-title headline-style (plist-get info :md-toplevel-hlevel) section-title)
(mapconcat (lambda (fn) (org-md--footnote-formatted fn info)) (mapconcat (lambda (fn) (org-md--footnote-formatted fn info))
fn-alist fn-alist
"\n"))))) "\n")))))

View file

@ -979,19 +979,30 @@ It makes underscores and dots word constituent chars.")
"raise" "return" "try" "while" "with" "yield" "raise" "return" "try" "while" "with" "yield"
;; These are technically operators, but we fontify them as ;; These are technically operators, but we fontify them as
;; keywords. ;; keywords.
"and" "in" "is" "not" "or" "not in")) "and" "in" "is" "not" "or" "not in" "is not"))
(defvar python--treesit-builtin-types
'("int" "float" "complex" "bool" "list" "tuple" "range" "str"
"bytes" "bytearray" "memoryview" "set" "frozenset" "dict"))
(defvar python--treesit-type-regex
(rx-to-string `(seq bol (or
,@python--treesit-builtin-types
(seq (? "_") (any "A-Z") (+ (any "a-zA-Z_0-9"))))
eol)))
(defvar python--treesit-builtins (defvar python--treesit-builtins
'("abs" "all" "any" "ascii" "bin" "bool" "breakpoint" "bytearray" (append python--treesit-builtin-types
"bytes" "callable" "chr" "classmethod" "compile" "complex" '("abs" "all" "any" "ascii" "bin" "breakpoint"
"delattr" "dict" "dir" "divmod" "enumerate" "eval" "exec" "callable" "chr" "classmethod" "compile"
"filter" "float" "format" "frozenset" "getattr" "globals" "delattr" "dir" "divmod" "enumerate" "eval" "exec"
"hasattr" "hash" "help" "hex" "id" "input" "int" "isinstance" "filter" "format" "getattr" "globals"
"issubclass" "iter" "len" "list" "locals" "map" "max" "hasattr" "hash" "help" "hex" "id" "input" "isinstance"
"memoryview" "min" "next" "object" "oct" "open" "ord" "pow" "issubclass" "iter" "len" "locals" "map" "max"
"print" "property" "range" "repr" "reversed" "round" "set" "min" "next" "object" "oct" "open" "ord" "pow"
"setattr" "slice" "sorted" "staticmethod" "str" "sum" "super" "print" "property" "repr" "reversed" "round"
"tuple" "type" "vars" "zip" "__import__")) "setattr" "slice" "sorted" "staticmethod" "sum" "super"
"type" "vars" "zip" "__import__")))
(defvar python--treesit-constants (defvar python--treesit-constants
'("Ellipsis" "False" "None" "NotImplemented" "True" "__debug__" '("Ellipsis" "False" "None" "NotImplemented" "True" "__debug__"
@ -1042,9 +1053,7 @@ NODE is the string node. Do not fontify the initial f for
f-strings. OVERRIDE is the override flag described in f-strings. OVERRIDE is the override flag described in
`treesit-font-lock-rules'. START and END mark the region to be `treesit-font-lock-rules'. START and END mark the region to be
fontified." fontified."
(let* ((string-beg (treesit-node-start node)) (let* ((maybe-expression (treesit-node-parent node))
(string-end (treesit-node-end node))
(maybe-expression (treesit-node-parent node))
(grandparent (treesit-node-parent (grandparent (treesit-node-parent
(treesit-node-parent (treesit-node-parent
maybe-expression))) maybe-expression)))
@ -1072,28 +1081,92 @@ fontified."
(equal (treesit-node-type maybe-expression) (equal (treesit-node-type maybe-expression)
"expression_statement")) "expression_statement"))
'font-lock-doc-face 'font-lock-doc-face
'font-lock-string-face))) 'font-lock-string-face))
;; Don't highlight string prefixes like f/r/b.
(save-excursion
(goto-char string-beg)
(when (re-search-forward "[\"']" string-end t)
(setq string-beg (match-beginning 0))))
(treesit-fontify-with-override
string-beg string-end face override start end)))
(defun python--treesit-fontify-string-interpolation (ignore-interpolation (not
(node _ start end &rest _) (seq-some
"Fontify string interpolation. (lambda (feats) (memq 'string-interpolation feats))
NODE is the string node. Do not fontify the initial f for (seq-take treesit-font-lock-feature-list treesit-font-lock-level))))
f-strings. START and END mark the region to be ;; If interpolation is enabled, highlight only
;; string_start/string_content/string_end children. Do not
;; touch interpolation node that can occur inside of the
;; string.
(string-nodes (if ignore-interpolation
(list node)
(treesit-filter-child
node
(lambda (ch) (member (treesit-node-type ch)
'("string_start"
"string_content"
"string_end")))
t))))
(dolist (string-node string-nodes)
(let ((string-beg (treesit-node-start string-node))
(string-end (treesit-node-end string-node)))
(when (or ignore-interpolation
(equal (treesit-node-type string-node) "string_start"))
;; Don't highlight string prefixes like f/r/b.
(save-excursion
(goto-char string-beg)
(when (re-search-forward "[\"']" string-end t)
(setq string-beg (match-beginning 0)))))
(treesit-fontify-with-override
string-beg string-end face override start end)))))
(defun python--treesit-fontify-union-types (node override start end &optional type-regex &rest _)
"Fontify nested union types in the type hints.
For examlpe, Lvl1 | Lvl2[Lvl3[Lvl4[Lvl5 | None]], Lvl2]. This
structure is represented via nesting binary_operator and
subscript nodes. This function iterates over all levels and
highlight identifier nodes. If TYPE-REGEX is not nil fontify type
identifier only if it matches against TYPE-REGEX. NODE is the
binary_operator node. OVERRIDE is the override flag described in
`treesit-font-lock-rules'. START and END mark the region to be
fontified." fontified."
;; This is kind of a hack, it basically removes the face applied by (dolist (child (treesit-node-children node t))
;; the string feature, so that following features can apply their (let (font-node)
;; face. (pcase (treesit-node-type child)
(let ((n-start (treesit-node-start node)) ((or "identifier" "none")
(n-end (treesit-node-end node))) (setq font-node child))
(remove-text-properties ("attribute"
(max start n-start) (min end n-end) '(face)))) (when-let ((type-node (treesit-node-child-by-field-name child "attribute")))
(setq font-node type-node)))
((or "binary_operator" "subscript")
(python--treesit-fontify-union-types child override start end type-regex)))
(when (and font-node
(or (null type-regex)
(let ((case-fold-search nil))
(string-match-p type-regex (treesit-node-text font-node)))))
(treesit-fontify-with-override
(treesit-node-start font-node) (treesit-node-end font-node)
'font-lock-type-face override start end)))))
(defun python--treesit-fontify-union-types-strict (node override start end &rest _)
"Fontify nested union types.
Same as `python--treesit-fontify-union-types' but type identifier
should match against `python--treesit-type-regex'. For NODE,
OVERRIDE, START and END description see
`python--treesit-fontify-union-types'."
(python--treesit-fontify-union-types node override start end python--treesit-type-regex))
(defun python--treesit-fontify-dotted-decorator (node override start end &rest _)
"Fontify dotted decorators.
For example @pytes.mark.skip. Iterate over all nested attribute
nodes and highlight identifier nodes. NODE is the first attribute
node. OVERRIDE is the override flag described in
`treesit-font-lock-rules'. START and END mark the region to be
fontified."
(dolist (child (treesit-node-children node t))
(pcase (treesit-node-type child)
("identifier"
(treesit-fontify-with-override
(treesit-node-start child) (treesit-node-end child)
'font-lock-type-face override start end))
("attribute"
(python--treesit-fontify-dotted-decorator child override start end)))))
(defvar python--treesit-settings (defvar python--treesit-settings
(treesit-font-lock-rules (treesit-font-lock-rules
@ -1103,14 +1176,9 @@ fontified."
:feature 'string :feature 'string
:language 'python :language 'python
'((string) @python--treesit-fontify-string) '((string) @python--treesit-fontify-string
(interpolation ["{" "}"] @font-lock-misc-punctuation-face))
;; HACK: This feature must come after the string feature and before
;; other features. Maybe we should make string-interpolation an
;; option rather than a feature.
:feature 'string-interpolation
:language 'python
'((interpolation) @python--treesit-fontify-string-interpolation)
:feature 'keyword :feature 'keyword
:language 'python :language 'python
@ -1127,12 +1195,6 @@ fontified."
(parameters (identifier) @font-lock-variable-name-face) (parameters (identifier) @font-lock-variable-name-face)
(parameters (default_parameter name: (identifier) @font-lock-variable-name-face))) (parameters (default_parameter name: (identifier) @font-lock-variable-name-face)))
:feature 'function
:language 'python
'((call function: (identifier) @font-lock-function-call-face)
(call function: (attribute
attribute: (identifier) @font-lock-function-call-face)))
:feature 'builtin :feature 'builtin
:language 'python :language 'python
`(((identifier) @font-lock-builtin-face `(((identifier) @font-lock-builtin-face
@ -1143,6 +1205,19 @@ fontified."
eol)) eol))
@font-lock-builtin-face))) @font-lock-builtin-face)))
:feature 'decorator
:language 'python
'((decorator "@" @font-lock-type-face)
(decorator (call function: (identifier) @font-lock-type-face))
(decorator (identifier) @font-lock-type-face)
(decorator [(attribute) (call (attribute))] @python--treesit-fontify-dotted-decorator))
:feature 'function
:language 'python
'((call function: (identifier) @font-lock-function-call-face)
(call function: (attribute
attribute: (identifier) @font-lock-function-call-face)))
:feature 'constant :feature 'constant
:language 'python :language 'python
'([(true) (false) (none)] @font-lock-constant-face) '([(true) (false) (none)] @font-lock-constant-face)
@ -1154,30 +1229,71 @@ fontified."
@font-lock-variable-name-face) @font-lock-variable-name-face)
(assignment left: (attribute (assignment left: (attribute
attribute: (identifier) attribute: (identifier)
@font-lock-property-use-face)) @font-lock-variable-name-face))
(pattern_list (identifier) (augmented_assignment left: (identifier)
@font-lock-variable-name-face)
(named_expression name: (identifier)
@font-lock-variable-name-face)
(pattern_list [(identifier)
(list_splat_pattern (identifier))]
@font-lock-variable-name-face) @font-lock-variable-name-face)
(tuple_pattern (identifier) (tuple_pattern [(identifier)
(list_splat_pattern (identifier))]
@font-lock-variable-name-face) @font-lock-variable-name-face)
(list_pattern (identifier) (list_pattern [(identifier)
@font-lock-variable-name-face) (list_splat_pattern (identifier))]
(list_splat_pattern (identifier) @font-lock-variable-name-face))
@font-lock-variable-name-face))
:feature 'decorator
:language 'python
'((decorator "@" @font-lock-type-face)
(decorator (call function: (identifier) @font-lock-type-face))
(decorator (identifier) @font-lock-type-face))
:feature 'type :feature 'type
:language 'python :language 'python
;; Override built-in faces when dict/list are used for type hints.
:override t
`(((identifier) @font-lock-type-face `(((identifier) @font-lock-type-face
(:match ,(rx-to-string (:match ,(rx-to-string
`(seq bol (or ,@python--treesit-exceptions) `(seq bol (or ,@python--treesit-exceptions)
eol)) eol))
@font-lock-type-face)) @font-lock-type-face))
(type (identifier) @font-lock-type-face)) (type [(identifier) (none)] @font-lock-type-face)
(type (attribute attribute: (identifier) @font-lock-type-face))
;; We don't want to highlight a package of the type
;; (e.g. pack.ClassName). So explicitly exclude patterns with
;; attribute, since we handle dotted type name in the previous
;; rule. The following rule handle
;; generic_type/list/tuple/splat_type nodes.
(type (_ !attribute [[(identifier) (none)] @font-lock-type-face
(attribute attribute: (identifier) @font-lock-type-face) ]))
;; collections.abc.Iterator[T] case.
(type (subscript (attribute attribute: (identifier) @font-lock-type-face)))
;; Nested optional type hints, e.g. val: Lvl1 | Lvl2[Lvl3[Lvl4]].
(type (binary_operator) @python--treesit-fontify-union-types)
;;class Type(Base1, Sequence[T]).
(class_definition
superclasses:
(argument_list [(identifier) @font-lock-type-face
(attribute attribute: (identifier) @font-lock-type-face)
(subscript (identifier) @font-lock-type-face)
(subscript (attribute attribute: (identifier) @font-lock-type-face))]))
;; Patern matching: case [str(), pack0.Type0()]. Take only the
;; last identifier.
(class_pattern (dotted_name (identifier) @font-lock-type-face :anchor))
;; Highlight the second argument as a type in isinstance/issubclass.
((call function: (identifier) @func-name
(argument_list :anchor (_)
[(identifier) @font-lock-type-face
(attribute attribute: (identifier) @font-lock-type-face)
(tuple (identifier) @font-lock-type-face)
(tuple (attribute attribute: (identifier) @font-lock-type-face))]
(:match ,python--treesit-type-regex @font-lock-type-face)))
(:match "^is\\(?:instance\\|subclass\\)$" @func-name))
;; isinstance(t, int|float).
((call function: (identifier) @func-name
(argument_list :anchor (_)
(binary_operator) @python--treesit-fontify-union-types-strict))
(:match "^is\\(?:instance\\|subclass\\)$" @func-name)))
:feature 'escape-sequence :feature 'escape-sequence
:language 'python :language 'python

View file

@ -7299,6 +7299,308 @@ buffer with overlapping strings."
"Unused import a.b.c (unused-import)" "Unused import a.b.c (unused-import)"
"W0611: Unused import a.b.c (unused-import)")))))) "W0611: Unused import a.b.c (unused-import)"))))))
;;; python-ts-mode font-lock tests
(defmacro python-ts-tests-with-temp-buffer (contents &rest body)
"Create a `python-ts-mode' enabled temp buffer with CONTENTS.
BODY is code to be executed within the temp buffer. Point is
always located at the beginning of buffer."
(declare (indent 1) (debug t))
`(with-temp-buffer
(skip-unless (treesit-ready-p 'python))
(require 'python)
(let ((python-indent-guess-indent-offset nil))
(python-ts-mode)
(setopt treesit-font-lock-level 3)
(insert ,contents)
(font-lock-ensure)
(goto-char (point-min))
,@body)))
(ert-deftest python-ts-mode-compound-keywords-face ()
(dolist (test '("is not" "not in"))
(python-ts-tests-with-temp-buffer
(concat "t " test " t")
(forward-to-word 1)
(should (eq (face-at-point) font-lock-keyword-face))
(forward-to-word 1)
(should (eq (face-at-point) font-lock-keyword-face)))))
(ert-deftest python-ts-mode-named-assignement-face-1 ()
(python-ts-tests-with-temp-buffer
"var := 3"
(should (eq (face-at-point) font-lock-variable-name-face))))
(ert-deftest python-ts-mode-assignement-face-2 ()
(python-ts-tests-with-temp-buffer
"var, *rest = call()"
(dolist (test '("var" "rest"))
(search-forward test)
(goto-char (match-beginning 0))
(should (eq (face-at-point) font-lock-variable-name-face))))
(python-ts-tests-with-temp-buffer
"def func(*args):"
(dolist (test '("args"))
(search-forward test)
(goto-char (match-beginning 0))
(should (not (eq (face-at-point) font-lock-variable-name-face))))))
(ert-deftest python-ts-mode-nested-types-face-1 ()
(python-ts-tests-with-temp-buffer
"def func(v:dict[ list[ tuple[str] ], int | None] | None):"
(dolist (test '("dict" "list" "tuple" "str" "int" "None" "None"))
(search-forward test)
(goto-char (match-beginning 0))
(should (eq (face-at-point) font-lock-type-face)))))
(ert-deftest python-ts-mode-union-types-face-1 ()
(python-ts-tests-with-temp-buffer
"def f(val: tuple[tuple, list[Lvl1 | Lvl2[Lvl3[Lvl4[Lvl5 | None]], Lvl2]]]):"
(dolist (test '("tuple" "tuple" "list" "Lvl1" "Lvl2" "Lvl3" "Lvl4" "Lvl5" "None" "Lvl2"))
(search-forward test)
(goto-char (match-beginning 0))
(should (eq (face-at-point) font-lock-type-face)))))
(ert-deftest python-ts-mode-union-types-face-2 ()
(python-ts-tests-with-temp-buffer
"def f(val: Type0 | Type1[Type2, pack0.Type3] | pack1.pack2.Type4 | None):"
(dolist (test '("Type0" "Type1" "Type2" "Type3" "Type4" "None"))
(search-forward test)
(goto-char (match-beginning 0))
(should (eq (face-at-point) font-lock-type-face)))
(goto-char (point-min))
(dolist (test '("pack0" "pack1" "pack2"))
(search-forward test)
(goto-char (match-beginning 0))
(should (not (eq (face-at-point) font-lock-type-face))))))
(ert-deftest python-ts-mode-types-face-1 ()
(python-ts-tests-with-temp-buffer
"def f(val: Callable[[Type0], (Type1, Type2)]):"
(dolist (test '("Callable" "Type0" "Type1" "Type2"))
(search-forward test)
(goto-char (match-beginning 0))
(should (eq (face-at-point) font-lock-type-face)))))
(ert-deftest python-ts-mode-types-face-2 ()
(python-ts-tests-with-temp-buffer
"def annot3(val:pack0.Type0)->pack1.pack2.pack3.Type1:"
(dolist (test '("Type0" "Type1"))
(search-forward test)
(goto-char (match-beginning 0))
(should (eq (face-at-point) font-lock-type-face)))
(goto-char (point-min))
(dolist (test '("pack0" "pack1" "pack2" "pack3"))
(search-forward test)
(goto-char (match-beginning 0))
(should (not (eq (face-at-point) font-lock-type-face))))))
(ert-deftest python-ts-mode-types-face-3 ()
(python-ts-tests-with-temp-buffer
"def annot3(val:collections.abc.Iterator[Type0]):"
(dolist (test '("Iterator" "Type0"))
(search-forward test)
(goto-char (match-beginning 0))
(should (eq (face-at-point) font-lock-type-face)))
(goto-char (point-min))
(dolist (test '("collections" "abc"))
(search-forward test)
(goto-char (match-beginning 0))
(should (not (eq (face-at-point) font-lock-type-face))))))
(ert-deftest python-ts-mode-isinstance-type-face-1 ()
(python-ts-tests-with-temp-buffer
"isinstance(var1, pkg.Type0)
isinstance(var2, (str, dict, Type1, type(None)))
isinstance(var3, my_type())"
(dolist (test '("var1" "pkg" "var2" "type" "None" "var3" "my_type"))
(let ((case-fold-search nil))
(search-forward test))
(goto-char (match-beginning 0))
(should (not (eq (face-at-point) font-lock-type-face))))
(goto-char (point-min))
(dolist (test '("Type0" "str" "dict" "Type1"))
(search-forward test)
(goto-char (match-beginning 0))
(should (eq (face-at-point) font-lock-type-face)))))
(ert-deftest python-ts-mode-isinstance-type-face-2 ()
(python-ts-tests-with-temp-buffer
"issubclass(mytype, int|list|collections.abc.Iterable)"
(dolist (test '("int" "list" "Iterable"))
(search-forward test)
(goto-char (match-beginning 0))
(should (eq (face-at-point) font-lock-type-face)))))
(ert-deftest python-ts-mode-isinstance-type-face-3 ()
(python-ts-tests-with-temp-buffer
"issubclass(mytype, typevar1)
isinstance(mytype, (Type1, typevar2, tuple, abc.Coll))
isinstance(mytype, pkg0.Type2|self.typevar3|typevar4)"
(dolist (test '("typevar1" "typevar2" "pkg0" "self" "typevar3" "typevar4"))
(search-forward test)
(goto-char (match-beginning 0))
(should (not (eq (face-at-point) font-lock-type-face))))
(goto-char (point-min))
(dolist (test '("Type1" "tuple" "Coll" "Type2"))
(search-forward test)
(goto-char (match-beginning 0))
(should (eq (face-at-point) font-lock-type-face)))))
(ert-deftest python-ts-mode-superclass-type-face ()
(python-ts-tests-with-temp-buffer
"class Temp(Base1, pack0.Base2, Sequence[T1, T2]):"
(dolist (test '("Base1" "Base2" "Sequence" "T1" "T2"))
(search-forward test)
(goto-char (match-beginning 0))
(should (eq (face-at-point) font-lock-type-face)))
(goto-char (point-min))
(dolist (test '("pack0"))
(search-forward test)
(goto-char (match-beginning 0))
(should (not (eq (face-at-point) font-lock-type-face))))))
(ert-deftest python-ts-mode-class-patterns-face ()
(python-ts-tests-with-temp-buffer
"match tt:
case str():
pass
case [Type0() | bytes(b) | pack0.pack1.Type1()]:
pass
case {'i': int(i), 'f': float() as f}:
pass"
(dolist (test '("str" "Type0" "bytes" "Type1" "int" "float"))
(search-forward test)
(goto-char (match-beginning 0))
(should (eq (face-at-point) font-lock-type-face)))
(goto-char (point-min))
(dolist (test '("pack0" "pack1"))
(search-forward test)
(goto-char (match-beginning 0))
(should (not (eq (face-at-point) font-lock-type-face))))))
(ert-deftest python-ts-mode-dotted-decorator-face-1 ()
(python-ts-tests-with-temp-buffer
"@pytest.mark.skip
@pytest.mark.skip(reason='msg')
def test():"
(dolist (test '("pytest" "mark" "skip" "pytest" "mark" "skip"))
(search-forward test)
(goto-char (match-beginning 0))
(should (eq (face-at-point) font-lock-type-face)))))
(ert-deftest python-ts-mode-dotted-decorator-face-2 ()
(python-ts-tests-with-temp-buffer
"@pytest.mark.skip(reason='msg')
def test():"
(setopt treesit-font-lock-level 4)
(dolist (test '("pytest" "mark" "skip"))
(search-forward test)
(goto-char (match-beginning 0))
(should (eq (face-at-point) font-lock-type-face)))))
(ert-deftest python-ts-mode-builtin-call-face ()
(python-ts-tests-with-temp-buffer
"all()"
;; enable 'function' feature from 4th level
(setopt treesit-font-lock-level 4)
(should (eq (face-at-point) font-lock-builtin-face))))
(ert-deftest python-ts-mode-interpolation-nested-string ()
(python-ts-tests-with-temp-buffer
"t = f\"beg {True + 'string'}\""
(search-forward "True")
(goto-char (match-beginning 0))
(should (eq (face-at-point) font-lock-constant-face))
(goto-char (point-min))
(dolist (test '("f" "{" "+" "}"))
(search-forward test)
(goto-char (match-beginning 0))
(should (not (eq (face-at-point) font-lock-string-face))))
(goto-char (point-min))
(dolist (test '("beg" "'string'" "\""))
(search-forward test)
(goto-char (match-beginning 0))
(should (eq (face-at-point) font-lock-string-face)))))
(ert-deftest python-ts-mode-level-fontification-wo-interpolation ()
(python-ts-tests-with-temp-buffer
"t = f\"beg {True + var}\""
(setopt treesit-font-lock-level 2)
(search-forward "f")
(goto-char (match-beginning 0))
(should (not (eq (face-at-point) font-lock-string-face)))
(dolist (test '("\"" "beg" "{" "True" "var" "}" "\""))
(search-forward test)
(goto-char (match-beginning 0))
(should (eq (face-at-point) font-lock-string-face)))))
(ert-deftest python-ts-mode-disabled-string-interpolation ()
(python-ts-tests-with-temp-buffer
"t = f\"beg {True + var}\""
(unwind-protect
(progn
(setf (nth 2 treesit-font-lock-feature-list)
(remq 'string-interpolation (nth 2 treesit-font-lock-feature-list)))
(setopt treesit-font-lock-level 3)
(search-forward "f")
(goto-char (match-beginning 0))
(should (not (eq (face-at-point) font-lock-string-face)))
(dolist (test '("\"" "beg" "{" "True" "var" "}" "\""))
(search-forward test)
(goto-char (match-beginning 0))
(should (eq (face-at-point) font-lock-string-face))))
(setf (nth 2 treesit-font-lock-feature-list)
(append (nth 2 treesit-font-lock-feature-list) '(string-interpolation))))))
(ert-deftest python-ts-mode-interpolation-doc-string ()
(python-ts-tests-with-temp-buffer
"f\"\"\"beg {'s1' + True + 's2'} end\"\"\""
(search-forward "True")
(goto-char (match-beginning 0))
(should (eq (face-at-point) font-lock-constant-face))
(goto-char (point-min))
(dolist (test '("f" "{" "+" "}"))
(search-forward test)
(goto-char (match-beginning 0))
(should (not (eq (face-at-point) font-lock-string-face))))
(goto-char (point-min))
(dolist (test '("\"\"\"" "beg" "end" "\"\"\""))
(search-forward test)
(goto-char (match-beginning 0))
(should (eq (face-at-point) font-lock-doc-face)))
(goto-char (point-min))
(dolist (test '("'s1'" "'s2'"))
(search-forward test)
(goto-char (match-beginning 0))
(should (eq (face-at-point) font-lock-string-face)))))
(provide 'python-tests) (provide 'python-tests)
;;; python-tests.el ends here ;;; python-tests.el ends here