aboutsummaryrefslogtreecommitdiffstats
path: root/lisp
diff options
context:
space:
mode:
authorPo Lu2023-01-30 21:19:55 +0800
committerPo Lu2023-01-30 21:19:55 +0800
commit46e8ab23eaeb5e453042f430fc016cf9ffc2ac37 (patch)
treee01b8db7b92c7e823b0957f4c3cd5db7a68fef23 /lisp
parentf69583941c873506b017bd5f5a81a3dfe15d7e22 (diff)
parent3f069bd796b0024033640051b5f74ba9834985f8 (diff)
downloademacs-46e8ab23eaeb5e453042f430fc016cf9ffc2ac37.tar.gz
emacs-46e8ab23eaeb5e453042f430fc016cf9ffc2ac37.zip
Merge remote-tracking branch 'origin/master' into feature/android
Diffstat (limited to 'lisp')
-rw-r--r--lisp/emacs-lisp/byte-opt.el44
-rw-r--r--lisp/emacs-lisp/bytecomp.el50
-rw-r--r--lisp/emacs-lisp/comp.el7
-rw-r--r--lisp/net/tramp.el4
-rw-r--r--lisp/org/org-agenda.el8
-rw-r--r--lisp/org/org-macs.el2
-rw-r--r--lisp/org/org-version.el2
-rw-r--r--lisp/org/org.el1
-rw-r--r--lisp/org/ox.el4
-rw-r--r--lisp/progmodes/c-ts-common.el118
-rw-r--r--lisp/progmodes/c-ts-mode.el204
-rw-r--r--lisp/progmodes/dockerfile-ts-mode.el20
-rw-r--r--lisp/progmodes/java-ts-mode.el1
-rw-r--r--lisp/progmodes/python.el4
-rw-r--r--lisp/progmodes/rust-ts-mode.el26
-rw-r--r--lisp/treesit.el107
16 files changed, 397 insertions, 205 deletions
diff --git a/lisp/emacs-lisp/byte-opt.el b/lisp/emacs-lisp/byte-opt.el
index b1a46d520e6..4d39e28fc8e 100644
--- a/lisp/emacs-lisp/byte-opt.el
+++ b/lisp/emacs-lisp/byte-opt.el
@@ -975,6 +975,43 @@ for speeding up processing.")
975 (t ;; Moving the constant to the end can enable some lapcode optimizations. 975 (t ;; Moving the constant to the end can enable some lapcode optimizations.
976 (list (car form) (nth 2 form) (nth 1 form))))) 976 (list (car form) (nth 2 form) (nth 1 form)))))
977 977
978(defun byte-opt--nary-comparison (form)
979 "Optimise n-ary comparisons such as `=', `<' etc."
980 (let ((nargs (length (cdr form))))
981 (cond
982 ((= nargs 1)
983 `(progn (cadr form) t))
984 ((>= nargs 3)
985 ;; At least 3 arguments: transform to N-1 binary comparisons,
986 ;; since those have their own byte-ops which are particularly
987 ;; fast for fixnums.
988 (let* ((op (car form))
989 (bindings nil)
990 (rev-args nil))
991 (if (memq nil (mapcar #'macroexp-copyable-p (cddr form)))
992 ;; At least one arg beyond the first is non-constant non-variable:
993 ;; create temporaries for all args to guard against side-effects.
994 ;; The optimiser will eliminate trivial bindings later.
995 (let ((i 1))
996 (dolist (arg (cdr form))
997 (let ((var (make-symbol (format "arg%d" i))))
998 (push var rev-args)
999 (push (list var arg) bindings)
1000 (setq i (1+ i)))))
1001 ;; All args beyond the first are copyable: no temporary variables
1002 ;; required.
1003 (setq rev-args (reverse (cdr form))))
1004 (let ((prev (car rev-args))
1005 (exprs nil))
1006 (dolist (arg (cdr rev-args))
1007 (push (list op arg prev) exprs)
1008 (setq prev arg))
1009 (let ((and-expr (cons 'and exprs)))
1010 (if bindings
1011 (list 'let (nreverse bindings) and-expr)
1012 and-expr)))))
1013 (t form))))
1014
978(defun byte-optimize-constant-args (form) 1015(defun byte-optimize-constant-args (form)
979 (let ((ok t) 1016 (let ((ok t)
980 (rest (cdr form))) 1017 (rest (cdr form)))
@@ -1130,13 +1167,18 @@ See Info node `(elisp) Integer Basics'."
1130(put 'max 'byte-optimizer #'byte-optimize-min-max) 1167(put 'max 'byte-optimizer #'byte-optimize-min-max)
1131(put 'min 'byte-optimizer #'byte-optimize-min-max) 1168(put 'min 'byte-optimizer #'byte-optimize-min-max)
1132 1169
1133(put '= 'byte-optimizer #'byte-optimize-binary-predicate)
1134(put 'eq 'byte-optimizer #'byte-optimize-eq) 1170(put 'eq 'byte-optimizer #'byte-optimize-eq)
1135(put 'eql 'byte-optimizer #'byte-optimize-equal) 1171(put 'eql 'byte-optimizer #'byte-optimize-equal)
1136(put 'equal 'byte-optimizer #'byte-optimize-equal) 1172(put 'equal 'byte-optimizer #'byte-optimize-equal)
1137(put 'string= 'byte-optimizer #'byte-optimize-binary-predicate) 1173(put 'string= 'byte-optimizer #'byte-optimize-binary-predicate)
1138(put 'string-equal 'byte-optimizer #'byte-optimize-binary-predicate) 1174(put 'string-equal 'byte-optimizer #'byte-optimize-binary-predicate)
1139 1175
1176(put '= 'byte-optimizer #'byte-opt--nary-comparison)
1177(put '< 'byte-optimizer #'byte-opt--nary-comparison)
1178(put '<= 'byte-optimizer #'byte-opt--nary-comparison)
1179(put '> 'byte-optimizer #'byte-opt--nary-comparison)
1180(put '>= 'byte-optimizer #'byte-opt--nary-comparison)
1181
1140(put 'string-greaterp 'byte-optimizer #'byte-optimize-string-greaterp) 1182(put 'string-greaterp 'byte-optimizer #'byte-optimize-string-greaterp)
1141(put 'string> 'byte-optimizer #'byte-optimize-string-greaterp) 1183(put 'string> 'byte-optimizer #'byte-optimize-string-greaterp)
1142 1184
diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el
index aa9521e5a65..e8a8fe37756 100644
--- a/lisp/emacs-lisp/bytecomp.el
+++ b/lisp/emacs-lisp/bytecomp.el
@@ -3748,7 +3748,7 @@ If it is nil, then the handler is \"byte-compile-SYMBOL.\""
3748 '((0 . byte-compile-no-args) 3748 '((0 . byte-compile-no-args)
3749 (1 . byte-compile-one-arg) 3749 (1 . byte-compile-one-arg)
3750 (2 . byte-compile-two-args) 3750 (2 . byte-compile-two-args)
3751 (2-and . byte-compile-and-folded) 3751 (2-cmp . byte-compile-cmp)
3752 (3 . byte-compile-three-args) 3752 (3 . byte-compile-three-args)
3753 (0-1 . byte-compile-zero-or-one-arg) 3753 (0-1 . byte-compile-zero-or-one-arg)
3754 (1-2 . byte-compile-one-or-two-args) 3754 (1-2 . byte-compile-one-or-two-args)
@@ -3827,11 +3827,11 @@ If it is nil, then the handler is \"byte-compile-SYMBOL.\""
3827(byte-defop-compiler cons 2) 3827(byte-defop-compiler cons 2)
3828(byte-defop-compiler aref 2) 3828(byte-defop-compiler aref 2)
3829(byte-defop-compiler set 2) 3829(byte-defop-compiler set 2)
3830(byte-defop-compiler (= byte-eqlsign) 2-and) 3830(byte-defop-compiler (= byte-eqlsign) 2-cmp)
3831(byte-defop-compiler (< byte-lss) 2-and) 3831(byte-defop-compiler (< byte-lss) 2-cmp)
3832(byte-defop-compiler (> byte-gtr) 2-and) 3832(byte-defop-compiler (> byte-gtr) 2-cmp)
3833(byte-defop-compiler (<= byte-leq) 2-and) 3833(byte-defop-compiler (<= byte-leq) 2-cmp)
3834(byte-defop-compiler (>= byte-geq) 2-and) 3834(byte-defop-compiler (>= byte-geq) 2-cmp)
3835(byte-defop-compiler get 2) 3835(byte-defop-compiler get 2)
3836(byte-defop-compiler nth 2) 3836(byte-defop-compiler nth 2)
3837(byte-defop-compiler substring 1-3) 3837(byte-defop-compiler substring 1-3)
@@ -3895,18 +3895,20 @@ If it is nil, then the handler is \"byte-compile-SYMBOL.\""
3895 (byte-compile-form (nth 2 form)) 3895 (byte-compile-form (nth 2 form))
3896 (byte-compile-out (get (car form) 'byte-opcode) 0))) 3896 (byte-compile-out (get (car form) 'byte-opcode) 0)))
3897 3897
3898(defun byte-compile-and-folded (form) 3898(defun byte-compile-cmp (form)
3899 "Compile calls to functions like `<='. 3899 "Compile calls to numeric comparisons such as `<', `=' etc."
3900These implicitly `and' together a bunch of two-arg bytecodes." 3900 ;; Lisp-level transforms should already have reduced valid calls to 2 args.
3901 (let ((l (length form))) 3901 (if (not (= (length form) 3))
3902 (cond 3902 (byte-compile-subr-wrong-args form "1 or more")
3903 ((< l 3) (byte-compile-form `(progn ,(nth 1 form) t))) 3903 (byte-compile-two-args
3904 ((= l 3) (byte-compile-two-args form)) 3904 (if (macroexp-const-p (nth 1 form))
3905 ;; Don't use `cl-every' here (see comment where we require cl-lib). 3905 ;; First argument is constant: flip it so that the constant
3906 ((not (memq nil (mapcar #'macroexp-copyable-p (nthcdr 2 form)))) 3906 ;; is last, which may allow more lapcode optimisations.
3907 (byte-compile-form `(and (,(car form) ,(nth 1 form) ,(nth 2 form)) 3907 (let* ((op (car form))
3908 (,(car form) ,@(nthcdr 2 form))))) 3908 (flipped-op (cdr (assq op '((< . >) (<= . >=)
3909 (t (byte-compile-normal-call form))))) 3909 (> . <) (>= . <=) (= . =))))))
3910 (list flipped-op (nth 2 form) (nth 1 form)))
3911 form))))
3910 3912
3911(defun byte-compile-three-args (form) 3913(defun byte-compile-three-args (form)
3912 (if (not (= (length form) 4)) 3914 (if (not (= (length form) 4))
@@ -4061,9 +4063,15 @@ This function is never called when `lexical-binding' is nil."
4061 (byte-compile-constant 1) 4063 (byte-compile-constant 1)
4062 (byte-compile-out (get '* 'byte-opcode) 0)) 4064 (byte-compile-out (get '* 'byte-opcode) 0))
4063 (3 4065 (3
4064 (byte-compile-form (nth 1 form)) 4066 (let ((arg1 (nth 1 form))
4065 (byte-compile-form (nth 2 form)) 4067 (arg2 (nth 2 form)))
4066 (byte-compile-out (get (car form) 'byte-opcode) 0)) 4068 (when (and (memq (car form) '(+ *))
4069 (macroexp-const-p arg1))
4070 ;; Put constant argument last for better LAP optimisation.
4071 (cl-rotatef arg1 arg2))
4072 (byte-compile-form arg1)
4073 (byte-compile-form arg2)
4074 (byte-compile-out (get (car form) 'byte-opcode) 0)))
4067 (_ 4075 (_
4068 ;; >2 args: compile as a single function call. 4076 ;; >2 args: compile as a single function call.
4069 (byte-compile-normal-call form)))) 4077 (byte-compile-normal-call form))))
diff --git a/lisp/emacs-lisp/comp.el b/lisp/emacs-lisp/comp.el
index 4c423be06c4..d2e7d933f4f 100644
--- a/lisp/emacs-lisp/comp.el
+++ b/lisp/emacs-lisp/comp.el
@@ -4112,13 +4112,16 @@ the deferred compilation mechanism."
4112 (native-elisp-load data))) 4112 (native-elisp-load data)))
4113 ;; We may have created a temporary file when we're being 4113 ;; We may have created a temporary file when we're being
4114 ;; called with something other than a file as the argument. 4114 ;; called with something other than a file as the argument.
4115 ;; Delete it. 4115 ;; Delete it if we can.
4116 (when (and (not (stringp function-or-file)) 4116 (when (and (not (stringp function-or-file))
4117 (not output) 4117 (not output)
4118 comp-ctxt 4118 comp-ctxt
4119 (comp-ctxt-output comp-ctxt) 4119 (comp-ctxt-output comp-ctxt)
4120 (file-exists-p (comp-ctxt-output comp-ctxt))) 4120 (file-exists-p (comp-ctxt-output comp-ctxt)))
4121 (delete-file (comp-ctxt-output comp-ctxt)))))))) 4121 (cond ((eq 'windows-nt system-type)
4122 ;; We may still be using the temporary .eln file.
4123 (ignore-errors (delete-file (comp-ctxt-output comp-ctxt))))
4124 (t (delete-file (comp-ctxt-output comp-ctxt))))))))))
4122 4125
4123(defun native-compile-async-skip-p (file load selector) 4126(defun native-compile-async-skip-p (file load selector)
4124 "Return non-nil if FILE's compilation should be skipped. 4127 "Return non-nil if FILE's compilation should be skipped.
diff --git a/lisp/net/tramp.el b/lisp/net/tramp.el
index 3871ee4dddd..21dbd40b1d2 100644
--- a/lisp/net/tramp.el
+++ b/lisp/net/tramp.el
@@ -642,7 +642,7 @@ This regexp must match both `tramp-initial-end-of-output' and
642 (rx 642 (rx
643 bol (* nonl) 643 bol (* nonl)
644 (group (regexp (regexp-opt password-word-equivalents))) 644 (group (regexp (regexp-opt password-word-equivalents)))
645 (* nonl) ":" (? "\^@") (* blank)) 645 (* nonl) (any "::៖") (? "\^@") (* blank))
646 "Regexp matching password-like prompts. 646 "Regexp matching password-like prompts.
647The regexp should match at end of buffer. 647The regexp should match at end of buffer.
648 648
@@ -652,7 +652,7 @@ usually more convenient to add new passphrases to that variable
652instead of altering this variable. 652instead of altering this variable.
653 653
654The `sudo' program appears to insert a `^@' character into the prompt." 654The `sudo' program appears to insert a `^@' character into the prompt."
655 :version "24.4" 655 :version "29.1"
656 :type 'regexp) 656 :type 'regexp)
657 657
658(defcustom tramp-wrong-passwd-regexp 658(defcustom tramp-wrong-passwd-regexp
diff --git a/lisp/org/org-agenda.el b/lisp/org/org-agenda.el
index 2d194ad3413..63107e8e6a4 100644
--- a/lisp/org/org-agenda.el
+++ b/lisp/org/org-agenda.el
@@ -3474,13 +3474,17 @@ This ensures the export commands can easily use it."
3474 (when (setq tmp (plist-get props 'date)) 3474 (when (setq tmp (plist-get props 'date))
3475 (when (integerp tmp) (setq tmp (calendar-gregorian-from-absolute tmp))) 3475 (when (integerp tmp) (setq tmp (calendar-gregorian-from-absolute tmp)))
3476 (let ((calendar-date-display-form 3476 (let ((calendar-date-display-form
3477 '(year "-" (string-pad month 2 ?0 'left) "-" (string-pad day 2 ?0 'left)))) 3477 '((format "%s-%.2d-%.2d" year
3478 (string-to-number month)
3479 (string-to-number day)))))
3478 (setq tmp (calendar-date-string tmp))) 3480 (setq tmp (calendar-date-string tmp)))
3479 (setq props (plist-put props 'date tmp))) 3481 (setq props (plist-put props 'date tmp)))
3480 (when (setq tmp (plist-get props 'day)) 3482 (when (setq tmp (plist-get props 'day))
3481 (when (integerp tmp) (setq tmp (calendar-gregorian-from-absolute tmp))) 3483 (when (integerp tmp) (setq tmp (calendar-gregorian-from-absolute tmp)))
3482 (let ((calendar-date-display-form 3484 (let ((calendar-date-display-form
3483 '(year "-" (string-pad month 2 ?0 'left) "-" (string-pad day 2 ?0 'left)))) 3485 '((format "%s-%.2d-%.2d" year
3486 (string-to-number month)
3487 (string-to-number day)))))
3484 (setq tmp (calendar-date-string tmp))) 3488 (setq tmp (calendar-date-string tmp)))
3485 (setq props (plist-put props 'day tmp)) 3489 (setq props (plist-put props 'day tmp))
3486 (setq props (plist-put props 'agenda-day tmp))) 3490 (setq props (plist-put props 'agenda-day tmp)))
diff --git a/lisp/org/org-macs.el b/lisp/org/org-macs.el
index 07c668a807d..8d7b0b034f8 100644
--- a/lisp/org/org-macs.el
+++ b/lisp/org/org-macs.el
@@ -46,7 +46,7 @@
46 ;; `org-git-version' check because the generated Org version strings 46 ;; `org-git-version' check because the generated Org version strings
47 ;; will not match. 47 ;; will not match.
48 `(unless (equal (org-release) ,(org-release)) 48 `(unless (equal (org-release) ,(org-release))
49 (warn "Org version mismatch. Make sure that correct `load-path' is set early in init.el 49 (warn "Org version mismatch. Org loading aborted.
50This warning usually appears when a built-in Org version is loaded 50This warning usually appears when a built-in Org version is loaded
51prior to the more recent Org version. 51prior to the more recent Org version.
52 52
diff --git a/lisp/org/org-version.el b/lisp/org/org-version.el
index 22f952d7a30..8372a0be4a5 100644
--- a/lisp/org/org-version.el
+++ b/lisp/org/org-version.el
@@ -11,7 +11,7 @@ Inserted by installing Org mode or when a release is made."
11(defun org-git-version () 11(defun org-git-version ()
12 "The Git version of Org mode. 12 "The Git version of Org mode.
13Inserted by installing Org or when a release is made." 13Inserted by installing Org or when a release is made."
14 (let ((org-git-version "release_9.6.1-16-ge37e9b")) 14 (let ((org-git-version "release_9.6.1-23-gc45a05"))
15 org-git-version)) 15 org-git-version))
16 16
17(provide 'org-version) 17(provide 'org-version)
diff --git a/lisp/org/org.el b/lisp/org/org.el
index 153e860f9a5..1b829d837c7 100644
--- a/lisp/org/org.el
+++ b/lisp/org/org.el
@@ -8608,6 +8608,7 @@ or to another Org file, automatically push the old position onto the ring."
8608(defvar org-agenda-buffer-name) 8608(defvar org-agenda-buffer-name)
8609(defun org-follow-timestamp-link () 8609(defun org-follow-timestamp-link ()
8610 "Open an agenda view for the time-stamp date/range at point." 8610 "Open an agenda view for the time-stamp date/range at point."
8611 (require 'org-agenda)
8611 ;; Avoid changing the global value. 8612 ;; Avoid changing the global value.
8612 (let ((org-agenda-buffer-name org-agenda-buffer-name)) 8613 (let ((org-agenda-buffer-name org-agenda-buffer-name))
8613 (cond 8614 (cond
diff --git a/lisp/org/ox.el b/lisp/org/ox.el
index 65f9ff18279..6f819def93a 100644
--- a/lisp/org/ox.el
+++ b/lisp/org/ox.el
@@ -6600,14 +6600,14 @@ see.
6600Optional argument POST-PROCESS is a function which should accept 6600Optional argument POST-PROCESS is a function which should accept
6601no argument. It is always called within the current process, 6601no argument. It is always called within the current process,
6602from BUFFER, with point at its beginning. Export back-ends can 6602from BUFFER, with point at its beginning. Export back-ends can
6603use it to set a major mode there, e.g, 6603use it to set a major mode there, e.g.,
6604 6604
6605 (defun org-latex-export-as-latex 6605 (defun org-latex-export-as-latex
6606 (&optional async subtreep visible-only body-only ext-plist) 6606 (&optional async subtreep visible-only body-only ext-plist)
6607 (interactive) 6607 (interactive)
6608 (org-export-to-buffer \\='latex \"*Org LATEX Export*\" 6608 (org-export-to-buffer \\='latex \"*Org LATEX Export*\"
6609 async subtreep visible-only body-only ext-plist 6609 async subtreep visible-only body-only ext-plist
6610 #'LaTeX-mode)) 6610 #\\='LaTeX-mode))
6611 6611
6612When expressed as an anonymous function, using `lambda', 6612When expressed as an anonymous function, using `lambda',
6613POST-PROCESS needs to be quoted. 6613POST-PROCESS needs to be quoted.
diff --git a/lisp/progmodes/c-ts-common.el b/lisp/progmodes/c-ts-common.el
index 6671d4be5b6..2d4a0d41c2a 100644
--- a/lisp/progmodes/c-ts-common.el
+++ b/lisp/progmodes/c-ts-common.el
@@ -2,7 +2,7 @@
2 2
3;; Copyright (C) 2023 Free Software Foundation, Inc. 3;; Copyright (C) 2023 Free Software Foundation, Inc.
4 4
5;; Author : 付禹安 (Yuan Fu) <casouri@gmail.com> 5;; Maintainer : 付禹安 (Yuan Fu) <casouri@gmail.com>
6;; Keywords : c c++ java javascript rust languages tree-sitter 6;; Keywords : c c++ java javascript rust languages tree-sitter
7 7
8;; This file is part of GNU Emacs. 8;; This file is part of GNU Emacs.
@@ -22,7 +22,10 @@
22 22
23;;; Commentary: 23;;; Commentary:
24;; 24;;
25;; For C-like language major modes: 25;; This file contains functions that can be shared by C-like language
26;; major modes, like indenting and filling "/* */" block comments.
27;;
28;; For indenting and filling comments:
26;; 29;;
27;; - Use `c-ts-common-comment-setup' to setup comment variables and 30;; - Use `c-ts-common-comment-setup' to setup comment variables and
28;; filling. 31;; filling.
@@ -30,6 +33,14 @@
30;; - Use simple-indent matcher `c-ts-common-looking-at-star' and 33;; - Use simple-indent matcher `c-ts-common-looking-at-star' and
31;; anchor `c-ts-common-comment-start-after-first-star' for indenting 34;; anchor `c-ts-common-comment-start-after-first-star' for indenting
32;; block comments. See `c-ts-mode--indent-styles' for example. 35;; block comments. See `c-ts-mode--indent-styles' for example.
36;;
37;; For indenting statements:
38;;
39;; - Set `c-ts-common-indent-offset',
40;; `c-ts-common-indent-block-type-regexp', and
41;; `c-ts-common-indent-bracketless-type-regexp', then use simple-indent
42;; offset `c-ts-common-statement-offset' in
43;; `treesit-simple-indent-rules'.
33 44
34;;; Code: 45;;; Code:
35 46
@@ -40,6 +51,8 @@
40(declare-function treesit-node-end "treesit.c") 51(declare-function treesit-node-end "treesit.c")
41(declare-function treesit-node-type "treesit.c") 52(declare-function treesit-node-type "treesit.c")
42 53
54;;; Comment indentation and filling
55
43(defun c-ts-common-looking-at-star (_n _p bol &rest _) 56(defun c-ts-common-looking-at-star (_n _p bol &rest _)
44 "A tree-sitter simple indent matcher. 57 "A tree-sitter simple indent matcher.
45Matches if there is a \"*\" after BOL." 58Matches if there is a \"*\" after BOL."
@@ -242,6 +255,107 @@ Set up:
242 (setq-local paragraph-separate paragraph-start) 255 (setq-local paragraph-separate paragraph-start)
243 (setq-local fill-paragraph-function #'c-ts-common--fill-paragraph)) 256 (setq-local fill-paragraph-function #'c-ts-common--fill-paragraph))
244 257
258;;; Statement indent
259
260(defvar c-ts-common-indent-offset nil
261 "Indent offset used by `c-ts-common' indent functions.
262
263This should be the symbol of the indent offset variable for the
264particular major mode. This cannot be nil for `c-ts-common'
265statement indent functions to work.")
266
267(defvar c-ts-common-indent-block-type-regexp nil
268 "Regexp matching types of block nodes (i.e., {} blocks).
269
270This cannot be nil for `c-ts-common' statement indent functions
271to work.")
272
273(defvar c-ts-common-indent-bracketless-type-regexp nil
274 "A regexp matching types of bracketless constructs.
275
276These constructs include if, while, do-while, for statements. In
277these statements, the body can omit the bracket, which requires
278special handling from our bracket-counting indent algorithm.
279
280This can be nil, meaning such special handling is not needed.")
281
282(defun c-ts-common-statement-offset (node parent &rest _)
283 "This anchor is used for children of a statement inside a block.
284
285This function basically counts the number of block nodes (i.e.,
286brackets) (defined by `c-ts-mode--indent-block-type-regexp')
287between NODE and the root node (not counting NODE itself), and
288multiply that by `c-ts-common-indent-offset'.
289
290To support GNU style, on each block level, this function also
291checks whether the opening bracket { is on its own line, if so,
292it adds an extra level, except for the top-level.
293
294PARENT is NODE's parent."
295 (let ((level 0))
296 ;; If point is on an empty line, NODE would be nil, but we pretend
297 ;; there is a statement node.
298 (when (null node)
299 (setq node t))
300 ;; If NODE is a opening bracket on its own line, take off one
301 ;; level because the code below assumes NODE is a statement
302 ;; _inside_ a {} block.
303 (when (string-match-p c-ts-common-indent-block-type-regexp
304 (treesit-node-type node))
305 (cl-decf level))
306 ;; Go up the tree and compute indent level.
307 (while (if (eq node t)
308 (setq node parent)
309 node)
310 (when (string-match-p c-ts-common-indent-block-type-regexp
311 (treesit-node-type node))
312 (cl-incf level)
313 (save-excursion
314 (goto-char (treesit-node-start node))
315 ;; Add an extra level if the opening bracket is on its own
316 ;; line, except (1) it's at top-level, or (2) it's immediate
317 ;; parent is another block.
318 (cond ((bolp) nil) ; Case (1).
319 ((let ((parent-type (treesit-node-type
320 (treesit-node-parent node))))
321 ;; Case (2).
322 (and parent-type
323 (or (string-match-p
324 c-ts-common-indent-block-type-regexp
325 parent-type))))
326 nil)
327 ;; Add a level.
328 ((looking-back (rx bol (* whitespace))
329 (line-beginning-position))
330 (cl-incf level)))))
331 (setq level (c-ts-mode--fix-bracketless-indent level node))
332 ;; Go up the tree.
333 (setq node (treesit-node-parent node)))
334 (* level (symbol-value c-ts-common-indent-offset))))
335
336(defun c-ts-mode--fix-bracketless-indent (level node)
337 "Takes LEVEL and NODE and return adjusted LEVEL.
338This fixes indentation for cases shown in bug#61026. Basically
339in C-like syntax, statements like if, for, while sometimes omit
340the bracket in the body."
341 (let ((block-re c-ts-common-indent-block-type-regexp)
342 (statement-re
343 c-ts-common-indent-bracketless-type-regexp)
344 (node-type (treesit-node-type node))
345 (parent-type (treesit-node-type (treesit-node-parent node))))
346 (if (and block-re statement-re node-type parent-type
347 (not (string-match-p block-re node-type))
348 (string-match-p statement-re parent-type))
349 (1+ level)
350 level)))
351
352(defun c-ts-mode--close-bracket-offset (node parent &rest _)
353 "Offset for the closing bracket, NODE.
354It's basically one level less that the statements in the block.
355PARENT is NODE's parent."
356 (- (c-ts-common-statement-offset node parent)
357 (symbol-value c-ts-common-indent-offset)))
358
245(provide 'c-ts-common) 359(provide 'c-ts-common)
246 360
247;;; c-ts-common.el ends here 361;;; c-ts-common.el ends here
diff --git a/lisp/progmodes/c-ts-mode.el b/lisp/progmodes/c-ts-mode.el
index 76ac92ed82d..8e9852ed4ee 100644
--- a/lisp/progmodes/c-ts-mode.el
+++ b/lisp/progmodes/c-ts-mode.el
@@ -63,11 +63,6 @@
63;; will set up Emacs to use the C/C++ modes defined here for other 63;; will set up Emacs to use the C/C++ modes defined here for other
64;; files, provided that you have the corresponding parser grammar 64;; files, provided that you have the corresponding parser grammar
65;; libraries installed. 65;; libraries installed.
66;;
67;; - Use variable `c-ts-mode-indent-block-type-regexp' with indent
68;; offset c-ts-mode--statement-offset for indenting statements.
69;; Again, see `c-ts-mode--indent-styles' for example.
70;;
71 66
72;;; Code: 67;;; Code:
73 68
@@ -92,6 +87,28 @@
92 :safe 'integerp 87 :safe 'integerp
93 :group 'c) 88 :group 'c)
94 89
90(defun c-ts-mode--indent-style-setter (sym val)
91 "Custom setter for `c-ts-mode-set-style'.
92Apart from setting the default value of SYM to VAL, also change
93the value of SYM in `c-ts-mode' and `c++-ts-mode' buffers to VAL."
94 (set-default sym val)
95 (named-let loop ((res nil)
96 (buffers (buffer-list)))
97 (if (null buffers)
98 (mapc (lambda (b)
99 (with-current-buffer b
100 (setq-local treesit-simple-indent-rules
101 (treesit--indent-rules-optimize
102 (c-ts-mode--get-indent-style
103 (if (eq major-mode 'c-ts-mode) 'c 'cpp))))))
104 res)
105 (let ((buffer (car buffers)))
106 (with-current-buffer buffer
107 ;; FIXME: Should we use `derived-mode-p' here?
108 (if (or (eq major-mode 'c-ts-mode) (eq major-mode 'c++-ts-mode))
109 (loop (append res (list buffer)) (cdr buffers))
110 (loop res (cdr buffers))))))))
111
95(defcustom c-ts-mode-indent-style 'gnu 112(defcustom c-ts-mode-indent-style 'gnu
96 "Style used for indentation. 113 "Style used for indentation.
97 114
@@ -100,13 +117,42 @@ one of the supplied styles doesn't suffice a function could be
100set instead. This function is expected return a list that 117set instead. This function is expected return a list that
101follows the form of `treesit-simple-indent-rules'." 118follows the form of `treesit-simple-indent-rules'."
102 :version "29.1" 119 :version "29.1"
103 :type '(choice (symbol :tag "Gnu" 'gnu) 120 :type '(choice (symbol :tag "Gnu" gnu)
104 (symbol :tag "K&R" 'k&r) 121 (symbol :tag "K&R" k&r)
105 (symbol :tag "Linux" 'linux) 122 (symbol :tag "Linux" linux)
106 (symbol :tag "BSD" 'bsd) 123 (symbol :tag "BSD" bsd)
107 (function :tag "A function for user customized style" ignore)) 124 (function :tag "A function for user customized style" ignore))
125 :set #'c-ts-mode--indent-style-setter
108 :group 'c) 126 :group 'c)
109 127
128(defun c-ts-mode--get-indent-style (mode)
129 "Helper function to set indentation style.
130MODE is either `c' or `cpp'."
131 (let ((style
132 (if (functionp c-ts-mode-indent-style)
133 (funcall c-ts-mode-indent-style)
134 (alist-get c-ts-mode-indent-style (c-ts-mode--indent-styles mode)))))
135 `((,mode ,@style))))
136
137(defun c-ts-mode-set-style ()
138 "Set the indent style of C/C++ modes globally.
139
140This changes the current indent style of every C/C++ buffer and
141the default C/C++ indent style in this Emacs session."
142 (interactive)
143 ;; FIXME: Should we use `derived-mode-p' here?
144 (or (eq major-mode 'c-ts-mode) (eq major-mode 'c++-ts-mode)
145 (error "Buffer %s is not a c-ts-mode (c-ts-mode-set-style)"
146 (buffer-name)))
147 (c-ts-mode--indent-style-setter
148 'c-ts-mode-indent-style
149 ;; NOTE: We can probably use the interactive form for this.
150 (intern
151 (completing-read
152 "Select style: "
153 (mapcar #'car (c-ts-mode--indent-styles (if (eq major-mode 'c-ts-mode) 'c 'cpp)))
154 nil t nil nil "gnu"))))
155
110;;; Syntax table 156;;; Syntax table
111 157
112(defvar c-ts-mode--syntax-table 158(defvar c-ts-mode--syntax-table
@@ -177,7 +223,7 @@ MODE is either `c' or `cpp'."
177 ;; Labels. 223 ;; Labels.
178 ((node-is "labeled_statement") parent-bol 0) 224 ((node-is "labeled_statement") parent-bol 0)
179 ((parent-is "labeled_statement") 225 ((parent-is "labeled_statement")
180 point-min c-ts-mode--statement-offset) 226 point-min c-ts-common-statement-offset)
181 227
182 ((match "preproc_ifdef" "compound_statement") point-min 0) 228 ((match "preproc_ifdef" "compound_statement") point-min 0)
183 ((match "#endif" "preproc_ifdef") point-min 0) 229 ((match "#endif" "preproc_ifdef") point-min 0)
@@ -186,15 +232,6 @@ MODE is either `c' or `cpp'."
186 ((match "preproc_function_def" "compound_statement") point-min 0) 232 ((match "preproc_function_def" "compound_statement") point-min 0)
187 ((match "preproc_call" "compound_statement") point-min 0) 233 ((match "preproc_call" "compound_statement") point-min 0)
188 234
189 ;; {} blocks.
190 ((node-is "}") point-min c-ts-mode--close-bracket-offset)
191 ((parent-is "compound_statement")
192 point-min c-ts-mode--statement-offset)
193 ((parent-is "enumerator_list")
194 point-min c-ts-mode--statement-offset)
195 ((parent-is "field_declaration_list")
196 point-min c-ts-mode--statement-offset)
197
198 ((parent-is "function_definition") parent-bol 0) 235 ((parent-is "function_definition") parent-bol 0)
199 ((parent-is "conditional_expression") first-sibling 0) 236 ((parent-is "conditional_expression") first-sibling 0)
200 ((parent-is "assignment_expression") parent-bol c-ts-mode-indent-offset) 237 ((parent-is "assignment_expression") parent-bol c-ts-mode-indent-offset)
@@ -215,13 +252,16 @@ MODE is either `c' or `cpp'."
215 ;; Indent the body of namespace definitions. 252 ;; Indent the body of namespace definitions.
216 ((parent-is "declaration_list") parent-bol c-ts-mode-indent-offset))) 253 ((parent-is "declaration_list") parent-bol c-ts-mode-indent-offset)))
217 254
255 ;; int[5] a = { 0, 0, 0, 0 };
218 ((parent-is "initializer_list") parent-bol c-ts-mode-indent-offset) 256 ((parent-is "initializer_list") parent-bol c-ts-mode-indent-offset)
219 ((parent-is "if_statement") parent-bol c-ts-mode-indent-offset) 257 ((parent-is "enumerator_list") point-min c-ts-common-statement-offset)
220 ((parent-is "for_statement") parent-bol c-ts-mode-indent-offset) 258 ((parent-is "field_declaration_list") point-min c-ts-common-statement-offset)
221 ((parent-is "while_statement") parent-bol c-ts-mode-indent-offset) 259
222 ((parent-is "switch_statement") parent-bol c-ts-mode-indent-offset) 260 ;; {} blocks.
223 ((parent-is "case_statement") parent-bol c-ts-mode-indent-offset) 261 ((node-is "}") point-min c-ts-mode--close-bracket-offset)
224 ((parent-is "do_statement") parent-bol c-ts-mode-indent-offset) 262 ((parent-is "compound_statement") point-min c-ts-common-statement-offset)
263 ((node-is "compound_statement") point-min c-ts-common-statement-offset)
264
225 ,@(when (eq mode 'cpp) 265 ,@(when (eq mode 'cpp)
226 `(((node-is "field_initializer_list") parent-bol ,(* c-ts-mode-indent-offset 2))))))) 266 `(((node-is "field_initializer_list") parent-bol ,(* c-ts-mode-indent-offset 2)))))))
227 `((gnu 267 `((gnu
@@ -249,19 +289,6 @@ MODE is either `c' or `cpp'."
249 ((parent-is "do_statement") parent-bol 0) 289 ((parent-is "do_statement") parent-bol 0)
250 ,@common)))) 290 ,@common))))
251 291
252(defun c-ts-mode--set-indent-style (mode)
253 "Helper function to set indentation style.
254MODE is either `c' or `cpp'."
255 (let ((style
256 (if (functionp c-ts-mode-indent-style)
257 (funcall c-ts-mode-indent-style)
258 (pcase c-ts-mode-indent-style
259 ('gnu (alist-get 'gnu (c-ts-mode--indent-styles mode)))
260 ('k&r (alist-get 'k&r (c-ts-mode--indent-styles mode)))
261 ('bsd (alist-get 'bsd (c-ts-mode--indent-styles mode)))
262 ('linux (alist-get 'linux (c-ts-mode--indent-styles mode)))))))
263 `((,mode ,@style))))
264
265(defun c-ts-mode--top-level-label-matcher (node &rest _) 292(defun c-ts-mode--top-level-label-matcher (node &rest _)
266 "A matcher that matches a top-level label. 293 "A matcher that matches a top-level label.
267NODE should be a labeled_statement." 294NODE should be a labeled_statement."
@@ -273,90 +300,6 @@ NODE should be a labeled_statement."
273 "labeled_statement") 300 "labeled_statement")
274 (not (treesit-node-top-level func "compound_statement"))))) 301 (not (treesit-node-top-level func "compound_statement")))))
275 302
276(defvar c-ts-mode-indent-block-type-regexp
277 (rx (or "compound_statement"
278 "field_declaration_list"
279 "enumerator_list"))
280 "Regexp matching types of block nodes (i.e., {} blocks).")
281
282(defvar c-ts-mode--statement-offset-post-processr nil
283 "A functions that makes adjustments to `c-ts-mode--statement-offset'.
284
285This is a function that takes two arguments, the current indent
286level and the current node, and returns a new level.
287
288When `c-ts-mode--statement-offset' runs and go up the parse tree,
289it increments the indent level when some condition are met in
290each level. At each level, after (possibly) incrementing the
291offset, it calls this function, passing it the current indent
292level and the current node, and use the return value as the new
293indent level.")
294
295(defun c-ts-mode--statement-offset (node parent &rest _)
296 "This anchor is used for children of a statement inside a block.
297
298This function basically counts the number of block nodes (defined
299by `c-ts-mode--indent-block-type-regexp') between NODE and the
300root node (not counting NODE itself), and multiply that by
301`c-ts-mode-indent-offset'.
302
303To support GNU style, on each block level, this function also
304checks whether the opening bracket { is on its own line, if so,
305it adds an extra level, except for the top-level.
306
307PARENT is NODE's parent."
308 (let ((level 0))
309 ;; If point is on an empty line, NODE would be nil, but we pretend
310 ;; there is a statement node.
311 (when (null node)
312 (setq node t))
313 (while (if (eq node t)
314 (setq node parent)
315 (setq node (treesit-node-parent node)))
316 (when (string-match-p c-ts-mode-indent-block-type-regexp
317 (treesit-node-type node))
318 (cl-incf level)
319 (save-excursion
320 (goto-char (treesit-node-start node))
321 ;; Add an extra level if the opening bracket is on its own
322 ;; line, except (1) it's at top-level, or (2) it's immediate
323 ;; parent is another block.
324 (cond ((bolp) nil) ; Case (1).
325 ((let ((parent-type (treesit-node-type
326 (treesit-node-parent node))))
327 ;; Case (2).
328 (and parent-type
329 (string-match-p c-ts-mode-indent-block-type-regexp
330 parent-type)))
331 nil)
332 ;; Add a level.
333 ((looking-back (rx bol (* whitespace))
334 (line-beginning-position))
335 (cl-incf level)))))
336 (when c-ts-mode--statement-offset-post-processr
337 (setq level (funcall c-ts-mode--statement-offset-post-processr
338 level node))))
339 (* level c-ts-mode-indent-offset)))
340
341(defun c-ts-mode--fix-bracketless-indent (level node)
342 "Takes LEVEL and NODE and returns adjusted LEVEL.
343This fixes indentation for cases shown in bug#61026. Basically
344in C/C++, constructs like if, for, while sometimes don't have
345bracket."
346 (if (and (not (equal (treesit-node-type node) "compound_statement"))
347 (member (treesit-node-type (treesit-node-parent node))
348 '("if_statement" "while_statement" "do_statement"
349 "for_statement")))
350 (1+ level)
351 level))
352
353(defun c-ts-mode--close-bracket-offset (node parent &rest _)
354 "Offset for the closing bracket, NODE.
355It's basically one level less that the statements in the block.
356PARENT is NODE's parent."
357 (- (c-ts-mode--statement-offset node parent)
358 c-ts-mode-indent-offset))
359
360;;; Font-lock 303;;; Font-lock
361 304
362(defvar c-ts-mode--preproc-keywords 305(defvar c-ts-mode--preproc-keywords
@@ -757,7 +700,8 @@ the semicolon. This function skips the semicolon."
757(defvar-keymap c-ts-mode-map 700(defvar-keymap c-ts-mode-map
758 :doc "Keymap for the C language with tree-sitter" 701 :doc "Keymap for the C language with tree-sitter"
759 :parent prog-mode-map 702 :parent prog-mode-map
760 "C-c C-q" #'c-ts-mode-indent-defun) 703 "C-c C-q" #'c-ts-mode-indent-defun
704 "C-c ." #'c-ts-mode-set-style)
761 705
762;;;###autoload 706;;;###autoload
763(define-derived-mode c-ts-base-mode prog-mode "C" 707(define-derived-mode c-ts-base-mode prog-mode "C"
@@ -817,8 +761,14 @@ the semicolon. This function skips the semicolon."
817 ;; Indent. 761 ;; Indent.
818 (when (eq c-ts-mode-indent-style 'linux) 762 (when (eq c-ts-mode-indent-style 'linux)
819 (setq-local indent-tabs-mode t)) 763 (setq-local indent-tabs-mode t))
820 (setq-local c-ts-mode--statement-offset-post-processr 764 (setq-local c-ts-common-indent-offset 'c-ts-mode-indent-offset)
821 #'c-ts-mode--fix-bracketless-indent) 765 (setq-local c-ts-common-indent-block-type-regexp
766 (rx (or "compound_statement"
767 "field_declaration_list"
768 "enumerator_list")))
769 (setq-local c-ts-common-indent-bracketless-type-regexp
770 (rx (or "if_statement" "do_statement"
771 "for_statement" "while_statement")))
822 772
823 ;; Comment 773 ;; Comment
824 (c-ts-common-comment-setup) 774 (c-ts-common-comment-setup)
@@ -871,7 +821,7 @@ in your configuration."
871 (setq-local comment-end " */") 821 (setq-local comment-end " */")
872 ;; Indent. 822 ;; Indent.
873 (setq-local treesit-simple-indent-rules 823 (setq-local treesit-simple-indent-rules
874 (c-ts-mode--set-indent-style 'c)) 824 (c-ts-mode--get-indent-style 'c))
875 ;; Font-lock. 825 ;; Font-lock.
876 (setq-local treesit-font-lock-settings (c-ts-mode--font-lock-settings 'c)) 826 (setq-local treesit-font-lock-settings (c-ts-mode--font-lock-settings 'c))
877 (treesit-major-mode-setup))) 827 (treesit-major-mode-setup)))
@@ -907,7 +857,7 @@ in your configuration."
907 857
908 ;; Indent. 858 ;; Indent.
909 (setq-local treesit-simple-indent-rules 859 (setq-local treesit-simple-indent-rules
910 (c-ts-mode--set-indent-style 'cpp)) 860 (c-ts-mode--get-indent-style 'cpp))
911 861
912 ;; Font-lock. 862 ;; Font-lock.
913 (setq-local treesit-font-lock-settings (c-ts-mode--font-lock-settings 'cpp)) 863 (setq-local treesit-font-lock-settings (c-ts-mode--font-lock-settings 'cpp))
diff --git a/lisp/progmodes/dockerfile-ts-mode.el b/lisp/progmodes/dockerfile-ts-mode.el
index 23ac48a6117..c9125bc6cbd 100644
--- a/lisp/progmodes/dockerfile-ts-mode.el
+++ b/lisp/progmodes/dockerfile-ts-mode.el
@@ -51,9 +51,27 @@
51 ((parent-is "expose_instruction") (nth-sibling 1) 0) 51 ((parent-is "expose_instruction") (nth-sibling 1) 0)
52 ((parent-is "label_instruction") (nth-sibling 1) 0) 52 ((parent-is "label_instruction") (nth-sibling 1) 0)
53 ((parent-is "shell_command") first-sibling 0) 53 ((parent-is "shell_command") first-sibling 0)
54 ((parent-is "string_array") first-sibling 1))) 54 ((parent-is "string_array") first-sibling 1)
55 ((dockerfile-ts-mode--line-continuation-p) dockerfile-ts-mode--line-continuation-anchor 0)))
55 "Tree-sitter indent rules.") 56 "Tree-sitter indent rules.")
56 57
58(defun dockerfile-ts-mode--line-continuation-p ()
59 "Return t if the current node is a line continuation node."
60 (lambda (node _ _ &rest _)
61 (string= (treesit-node-type node) "\n")))
62
63(defun dockerfile-ts-mode--line-continuation-anchor (_ _ &rest _)
64 "This anchor is used to align any nodes that are part of a line
65continuation to the previous entry."
66 (save-excursion
67 (forward-line -1)
68 (let ((prev-node (treesit-node-at (point))))
69 (if (string= (treesit-node-type prev-node) "\\\n")
70 (back-to-indentation)
71 (forward-word)
72 (forward-char))
73 (+ 1 (- (point) (pos-bol))))))
74
57(defvar dockerfile-ts-mode--keywords 75(defvar dockerfile-ts-mode--keywords
58 '("ADD" "ARG" "AS" "CMD" "COPY" "CROSS_BUILD" "ENTRYPOINT" "ENV" 76 '("ADD" "ARG" "AS" "CMD" "COPY" "CROSS_BUILD" "ENTRYPOINT" "ENV"
59 "EXPOSE" "FROM" "HEALTHCHECK" "LABEL" "MAINTAINER" "ONBUILD" "RUN" 77 "EXPOSE" "FROM" "HEALTHCHECK" "LABEL" "MAINTAINER" "ONBUILD" "RUN"
diff --git a/lisp/progmodes/java-ts-mode.el b/lisp/progmodes/java-ts-mode.el
index dbd63698770..e4153725efd 100644
--- a/lisp/progmodes/java-ts-mode.el
+++ b/lisp/progmodes/java-ts-mode.el
@@ -89,6 +89,7 @@
89 ((query "(method_declaration (block (_) @indent))") parent-bol java-ts-mode-indent-offset) 89 ((query "(method_declaration (block (_) @indent))") parent-bol java-ts-mode-indent-offset)
90 ((parent-is "local_variable_declaration") parent-bol java-ts-mode-indent-offset) 90 ((parent-is "local_variable_declaration") parent-bol java-ts-mode-indent-offset)
91 ((parent-is "expression_statement") parent-bol java-ts-mode-indent-offset) 91 ((parent-is "expression_statement") parent-bol java-ts-mode-indent-offset)
92 ((match "type_identifier" "field_declaration") parent-bol 0)
92 ((parent-is "field_declaration") parent-bol java-ts-mode-indent-offset) 93 ((parent-is "field_declaration") parent-bol java-ts-mode-indent-offset)
93 ((parent-is "return_statement") parent-bol java-ts-mode-indent-offset) 94 ((parent-is "return_statement") parent-bol java-ts-mode-indent-offset)
94 ((parent-is "variable_declarator") parent-bol java-ts-mode-indent-offset) 95 ((parent-is "variable_declarator") parent-bol java-ts-mode-indent-offset)
diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el
index a869cdc5fdb..df0d1c96965 100644
--- a/lisp/progmodes/python.el
+++ b/lisp/progmodes/python.el
@@ -6715,8 +6715,8 @@ implementations: `python-mode' and `python-ts-mode'."
6715 (when python-indent-guess-indent-offset 6715 (when python-indent-guess-indent-offset
6716 (python-indent-guess-indent-offset)) 6716 (python-indent-guess-indent-offset))
6717 6717
6718 (add-to-list 'auto-mode-alist 6718 (add-to-list 'auto-mode-alist '("\\.py[iw]?\\'" . python-ts-mode))
6719 '("\\.py[iw]?\\'\\|python[0-9.]*" . python-ts-mode)))) 6719 (add-to-list 'interpreter-mode-alist '("python[0-9.]*" . python-ts-mode))))
6720 6720
6721;;; Completion predicates for M-x 6721;;; Completion predicates for M-x
6722;; Commands that only make sense when editing Python code 6722;; Commands that only make sense when editing Python code
diff --git a/lisp/progmodes/rust-ts-mode.el b/lisp/progmodes/rust-ts-mode.el
index 3a6cb61b719..2812e39c101 100644
--- a/lisp/progmodes/rust-ts-mode.el
+++ b/lisp/progmodes/rust-ts-mode.el
@@ -275,6 +275,28 @@ Return nil if there is no name or if NODE is not a defun node."
275 (treesit-node-text 275 (treesit-node-text
276 (treesit-node-child-by-field-name node "name") t)))) 276 (treesit-node-child-by-field-name node "name") t))))
277 277
278(defun rust-ts-mode--syntax-propertize (beg end)
279 "Apply syntax text property to template delimiters between BEG and END.
280
281< and > are usually punctuation, e.g., as greater/less-than. But
282when used for types, they should be considered pairs.
283
284This function checks for < and > in the changed RANGES and apply
285appropriate text property to alter the syntax of template
286delimiters < and >'s."
287 (goto-char beg)
288 (while (re-search-forward (rx (or "<" ">")) end t)
289 (pcase (treesit-node-type
290 (treesit-node-parent
291 (treesit-node-at (match-beginning 0))))
292 ("type_arguments"
293 (put-text-property (match-beginning 0)
294 (match-end 0)
295 'syntax-table
296 (pcase (char-before)
297 (?< '(4 . ?>))
298 (?> '(5 . ?<))))))))
299
278;;;###autoload 300;;;###autoload
279(define-derived-mode rust-ts-mode prog-mode "Rust" 301(define-derived-mode rust-ts-mode prog-mode "Rust"
280 "Major mode for editing Rust, powered by tree-sitter." 302 "Major mode for editing Rust, powered by tree-sitter."
@@ -284,6 +306,10 @@ Return nil if there is no name or if NODE is not a defun node."
284 (when (treesit-ready-p 'rust) 306 (when (treesit-ready-p 'rust)
285 (treesit-parser-create 'rust) 307 (treesit-parser-create 'rust)
286 308
309 ;; Syntax.
310 (setq-local syntax-propertize-function
311 #'rust-ts-mode--syntax-propertize)
312
287 ;; Comments. 313 ;; Comments.
288 (c-ts-common-comment-setup) 314 (c-ts-common-comment-setup)
289 315
diff --git a/lisp/treesit.el b/lisp/treesit.el
index 29b29d2665a..c9f2e339dc2 100644
--- a/lisp/treesit.el
+++ b/lisp/treesit.el
@@ -554,7 +554,25 @@ omitted, default END to BEG."
554 "Generic tree-sitter font-lock error" 554 "Generic tree-sitter font-lock error"
555 'treesit-error) 555 'treesit-error)
556 556
557(defvar-local treesit-font-lock-level 3 557(defun treesit--font-lock-level-setter (sym val)
558 "Custom setter for `treesit-font-lock-level'."
559 (set-default sym val)
560 (named-let loop ((res nil)
561 (buffers (buffer-list)))
562 (if (null buffers)
563 (mapc (lambda (b)
564 (with-current-buffer b
565 (setq-local treesit-font-lock-level val)
566 (treesit-font-lock-recompute-features)
567 (treesit-font-lock-fontify-region (point-min) (point-max))))
568 res)
569 (let ((buffer (car buffers)))
570 (with-current-buffer buffer
571 (if (treesit-parser-list)
572 (loop (append res (list buffer)) (cdr buffers))
573 (loop res (cdr buffers))))))))
574
575(defcustom treesit-font-lock-level 3
558 "Decoration level to be used by tree-sitter fontifications. 576 "Decoration level to be used by tree-sitter fontifications.
559 577
560Major modes categorize their fontification features into levels, 578Major modes categorize their fontification features into levels,
@@ -562,16 +580,24 @@ from 1 which is the absolute minimum, to 4 that yields the maximum
562fontifications. 580fontifications.
563 581
564Level 1 usually contains only comments and definitions. 582Level 1 usually contains only comments and definitions.
565Level 2 usually adds keywords, strings, constants, types, etc. 583Level 2 usually adds keywords, strings, data types, etc.
566Level 3 usually represents a full-blown fontification, including 584Level 3 usually represents full-blown fontifications, including
567assignment, constants, numbers, properties, etc. 585assignments, constants, numbers and literals, properties, etc.
568Level 4 adds everything else that can be fontified: delimiters, 586Level 4 adds everything else that can be fontified: delimiters,
569operators, brackets, all functions and variables, etc. 587operators, brackets, punctuation, all functions and variables, etc.
570 588
571In addition to the decoration level, individual features can be 589In addition to the decoration level, individual features can be
572turned on/off by calling `treesit-font-lock-recompute-features'. 590turned on/off by calling `treesit-font-lock-recompute-features'.
573Changing the decoration level requires calling 591Changing the decoration level requires calling
574`treesit-font-lock-recompute-features' to have an effect.") 592`treesit-font-lock-recompute-features' to have an effect, unless
593done via `customize-variable'.
594
595To see which syntactical categories are fontified by each level
596in a particular major mode, examine the buffer-local value of the
597variable `treesit-font-lock-feature-list'."
598 :type 'integer
599 :set #'treesit--font-lock-level-setter
600 :version "29.1")
575 601
576(defvar-local treesit--font-lock-query-expand-range (cons 0 0) 602(defvar-local treesit--font-lock-query-expand-range (cons 0 0)
577 "The amount to expand the start and end of the region when fontifying. 603 "The amount to expand the start and end of the region when fontifying.
@@ -892,27 +918,20 @@ LIMIT is the recursion limit, which defaults to 100."
892 (push r result)) 918 (push r result))
893 (push child result)) 919 (push child result))
894 (setq child (treesit-node-next-sibling child))) 920 (setq child (treesit-node-next-sibling child)))
895 ;; If NODE has no child, keep NODE. 921 ;; If NODE has no child, keep NODE. If LIMIT is exceeded, return
896 (or result (list node)))) 922 ;; nil.
923 (or result (and (> limit 0) (list node)))))
897 924
898(defsubst treesit--node-length (node) 925(defsubst treesit--node-length (node)
899 "Return the length of the text of NODE." 926 "Return the length of the text of NODE."
900 (- (treesit-node-end node) (treesit-node-start node))) 927 (- (treesit-node-end node) (treesit-node-start node)))
901 928
902(defvar-local treesit--font-lock-fast-mode nil 929(defvar-local treesit--font-lock-fast-mode 'unspecified
903 "If this variable is t, change the way we query so it's faster. 930 "If this variable is t, change the way we query so it's faster.
904This is not a general optimization and should be RARELY needed! 931This is not a general optimization and should be RARELY needed!
905See comments in `treesit-font-lock-fontify-region' for more 932See comments in `treesit-font-lock-fontify-region' for more
906detail.") 933detail.")
907 934
908(defvar-local treesit--font-lock-fast-mode-grace-count 5
909 "Grace counts before we turn on the fast mode.
910
911When query takes abnormally long time to execute, we turn on the
912\"fast mode\", but just to be on the safe side, we only turn on
913the fast mode after this number of offenses. See bug#60691,
914bug#60223.")
915
916;; Some details worth explaining: 935;; Some details worth explaining:
917;; 936;;
918;; 1. When we apply face to a node, we clip the face into the 937;; 1. When we apply face to a node, we clip the face into the
@@ -964,36 +983,34 @@ If LOUDLY is non-nil, display some debugging information."
964 (enable (nth 1 setting)) 983 (enable (nth 1 setting))
965 (override (nth 3 setting)) 984 (override (nth 3 setting))
966 (language (treesit-query-language query))) 985 (language (treesit-query-language query)))
967 (when-let ((nodes (list (treesit-buffer-root-node language)))
968 ;; Only activate if ENABLE flag is t.
969 (activate (eq t enable)))
970 (ignore activate)
971 986
972 ;; If we run into problematic files, use the "fast mode" to 987 ;; Use deterministic way to decide whether to turn on "fast
973 ;; try to recover. See comment #2 above for more explanation. 988 ;; mode". (See bug#60691, bug#60223.)
974 (when treesit--font-lock-fast-mode 989 (when (eq treesit--font-lock-fast-mode 'unspecified)
975 (setq nodes (treesit--children-covering-range-recurse 990 (pcase-let ((`(,max-depth ,max-width)
976 (car nodes) start end (* 4 jit-lock-chunk-size)))) 991 (treesit-subtree-stat
992 (treesit-buffer-root-node language))))
993 (if (or (> max-depth 100) (> max-width 4000))
994 (setq treesit--font-lock-fast-mode t)
995 (setq treesit--font-lock-fast-mode nil))))
996
997 (when-let* ((root (treesit-buffer-root-node language))
998 (nodes (if (eq t treesit--font-lock-fast-mode)
999 (treesit--children-covering-range-recurse
1000 root start end (* 4 jit-lock-chunk-size))
1001 (list (treesit-buffer-root-node language))))
1002 ;; Only activate if ENABLE flag is t.
1003 (activate (eq t enable)))
1004 (ignore activate)
977 1005
978 ;; Query each node. 1006 ;; Query each node.
979 (dolist (sub-node nodes) 1007 (dolist (sub-node nodes)
980 (let* ((delta-start (car treesit--font-lock-query-expand-range)) 1008 (let* ((delta-start (car treesit--font-lock-query-expand-range))
981 (delta-end (cdr treesit--font-lock-query-expand-range)) 1009 (delta-end (cdr treesit--font-lock-query-expand-range))
982 (start-time (current-time))
983 (captures (treesit-query-capture 1010 (captures (treesit-query-capture
984 sub-node query 1011 sub-node query
985 (max (- start delta-start) (point-min)) 1012 (max (- start delta-start) (point-min))
986 (min (+ end delta-end) (point-max)))) 1013 (min (+ end delta-end) (point-max)))))
987 (end-time (current-time)))
988 ;; If for any query the query time is strangely long,
989 ;; switch to fast mode (see comments above).
990 (when (and (null treesit--font-lock-fast-mode)
991 (> (time-to-seconds
992 (time-subtract end-time start-time))
993 0.01))
994 (if (> treesit--font-lock-fast-mode-grace-count 0)
995 (cl-decf treesit--font-lock-fast-mode-grace-count)
996 (setq-local treesit--font-lock-fast-mode t)))
997 1014
998 ;; For each captured node, fontify that node. 1015 ;; For each captured node, fontify that node.
999 (with-silent-modifications 1016 (with-silent-modifications
@@ -1002,12 +1019,14 @@ If LOUDLY is non-nil, display some debugging information."
1002 (node (cdr capture)) 1019 (node (cdr capture))
1003 (node-start (treesit-node-start node)) 1020 (node-start (treesit-node-start node))
1004 (node-end (treesit-node-end node))) 1021 (node-end (treesit-node-end node)))
1022
1005 ;; If node is not in the region, take them out. See 1023 ;; If node is not in the region, take them out. See
1006 ;; comment #3 above for more detail. 1024 ;; comment #3 above for more detail.
1007 (if (and (facep face) 1025 (if (and (facep face)
1008 (or (>= start node-end) (>= node-start end))) 1026 (or (>= start node-end) (>= node-start end)))
1009 (when (or loudly treesit--font-lock-verbose) 1027 (when (or loudly treesit--font-lock-verbose)
1010 (message "Captured node %s(%s-%s) but it is outside of fontifing region" node node-start node-end)) 1028 (message "Captured node %s(%s-%s) but it is outside of fontifing region" node node-start node-end))
1029
1011 (cond 1030 (cond
1012 ((facep face) 1031 ((facep face)
1013 (treesit-fontify-with-override 1032 (treesit-fontify-with-override
@@ -1015,6 +1034,7 @@ If LOUDLY is non-nil, display some debugging information."
1015 face override)) 1034 face override))
1016 ((functionp face) 1035 ((functionp face)
1017 (funcall face node override start end))) 1036 (funcall face node override start end)))
1037
1018 ;; Don't raise an error if FACE is neither a face nor 1038 ;; Don't raise an error if FACE is neither a face nor
1019 ;; a function. This is to allow intermediate capture 1039 ;; a function. This is to allow intermediate capture
1020 ;; names used for #match and #eq. 1040 ;; names used for #match and #eq.
@@ -3033,10 +3053,10 @@ function signals an error."
3033 :no-value (treesit-parser-set-included-ranges parser '((1 . 4) (5 . 8)))) 3053 :no-value (treesit-parser-set-included-ranges parser '((1 . 4) (5 . 8))))
3034 (treesit-parser-included-ranges 3054 (treesit-parser-included-ranges
3035 :no-eval (treesit-parser-included-ranges parser) 3055 :no-eval (treesit-parser-included-ranges parser)
3036 :eg-result '((1 . 4) (5 . 8))) 3056 :eg-result ((1 . 4) (5 . 8)))
3037 (treesit-query-range 3057 (treesit-query-range
3038 :no-eval (treesit-query-range node '((script_element) @cap)) 3058 :no-eval (treesit-query-range node '((script_element) @cap))
3039 :eg-result-string '((1 . 4) (5 . 8))) 3059 :eg-result ((1 . 4) (5 . 8)))
3040 3060
3041 3061
3042 "Retrieving a node" 3062 "Retrieving a node"
@@ -3182,7 +3202,12 @@ function signals an error."
3182 :eg-result-string "#<treesit-node (translation_unit) in 1-11>") 3202 :eg-result-string "#<treesit-node (translation_unit) in 1-11>")
3183 (treesit-query-string 3203 (treesit-query-string
3184 :no-eval (treesit-query-string "int c = 0;" '((identifier) @id) 'c) 3204 :no-eval (treesit-query-string "int c = 0;" '((identifier) @id) 'c)
3185 :eg-result-string "((id . #<treesit-node (identifier) in 5-6>))")) 3205 :eg-result-string "((id . #<treesit-node (identifier) in 5-6>))")
3206
3207 "Misc"
3208 (treesit-subtree-stat
3209 :no-eval (treesit-subtree-stat node)
3210 :eg-result (6 33 487)))
3186 3211
3187(provide 'treesit) 3212(provide 'treesit)
3188 3213