aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPo Lu2023-01-30 21:19:55 +0800
committerPo Lu2023-01-30 21:19:55 +0800
commit46e8ab23eaeb5e453042f430fc016cf9ffc2ac37 (patch)
treee01b8db7b92c7e823b0957f4c3cd5db7a68fef23
parentf69583941c873506b017bd5f5a81a3dfe15d7e22 (diff)
parent3f069bd796b0024033640051b5f74ba9834985f8 (diff)
downloademacs-46e8ab23eaeb5e453042f430fc016cf9ffc2ac37.tar.gz
emacs-46e8ab23eaeb5e453042f430fc016cf9ffc2ac37.zip
Merge remote-tracking branch 'origin/master' into feature/android
-rw-r--r--doc/emacs/display.texi131
-rw-r--r--doc/emacs/emacs.texi4
-rw-r--r--doc/emacs/files.texi11
-rw-r--r--doc/emacs/frames.texi1
-rw-r--r--doc/emacs/misc.texi6
-rw-r--r--doc/emacs/programs.texi42
-rw-r--r--doc/emacs/text.texi12
-rw-r--r--doc/lispref/variables.texi1
-rw-r--r--etc/NEWS.296
-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
-rw-r--r--src/comp.c3
-rw-r--r--src/treesit.c63
-rw-r--r--test/lisp/progmodes/c-ts-mode-resources/indent.erts8
28 files changed, 640 insertions, 250 deletions
diff --git a/doc/emacs/display.texi b/doc/emacs/display.texi
index f77ab569483..97732b65e32 100644
--- a/doc/emacs/display.texi
+++ b/doc/emacs/display.texi
@@ -1024,17 +1024,65 @@ customize-group @key{RET} font-lock-faces @key{RET}}. You can then
1024use that customization buffer to customize the appearance of these 1024use that customization buffer to customize the appearance of these
1025faces. @xref{Face Customization}. 1025faces. @xref{Face Customization}.
1026 1026
1027@cindex just-in-time (JIT) font-lock
1028@cindex background syntax highlighting
1029 Fontifying very large buffers can take a long time. To avoid large
1030delays when a file is visited, Emacs initially fontifies only the
1031visible portion of a buffer. As you scroll through the buffer, each
1032portion that becomes visible is fontified as soon as it is displayed;
1033this type of Font Lock is called @dfn{Just-In-Time} (or @dfn{JIT})
1034Lock. You can control how JIT Lock behaves, including telling it to
1035perform fontification while idle, by customizing variables in the
1036customization group @samp{jit-lock}. @xref{Specific Customization}.
1037
1038 The information that major modes use for determining which parts of
1039buffer text to fontify and what faces to use can be based on several
1040different ways of analyzing the text:
1041
1042@itemize @bullet
1043@item
1044Search for keywords and other textual patterns based on regular
1045expressions (@pxref{Regexp Search,, Regular Expression Search}).
1046
1047@item
1048Find syntactically distinct parts of text based on built-in syntax
1049tables (@pxref{Syntax Tables,,, elisp, The Emacs Lisp Reference
1050Manual}).
1051
1052@item
1053Use syntax tree produced by a full-blown parser, via a special-purpose
1054library, such as the tree-sitter library (@pxref{Parsing Program
1055Source,,, elisp, The Emacs Lisp Reference Manual}), or an external
1056program.
1057@end itemize
1058
1059@menu
1060* Traditional Font Lock:: Font Lock based on regexps and syntax tables.
1061* Parser-based Font Lock:: Font Lock based on external parser.
1062@end menu
1063
1064@node Traditional Font Lock
1065@subsection Traditional Font Lock
1066@cindex traditional font-lock
1067
1068 ``Traditional'' methods of providing font-lock information are based
1069on regular-expression search and on syntactic analysis using syntax
1070tables built into Emacs. This subsection describes the use and
1071customization of font-lock for major modes which use these traditional
1072methods.
1073
1027@vindex font-lock-maximum-decoration 1074@vindex font-lock-maximum-decoration
1028 You can customize the variable @code{font-lock-maximum-decoration} 1075 You can control the amount of fontification applied by Font Lock
1029to alter the amount of fontification applied by Font Lock mode, for 1076mode by customizing the variable @code{font-lock-maximum-decoration},
1030major modes that support this feature. The value should be a number 1077for major modes that support this feature. The value of this variable
1031(with 1 representing a minimal amount of fontification; some modes 1078should be a number (with 1 representing a minimal amount of
1032support levels as high as 3); or @code{t}, meaning ``as high as 1079fontification; some modes support levels as high as 3); or @code{t},
1033possible'' (the default). To be effective for a given file buffer, 1080meaning ``as high as possible'' (the default). To be effective for a
1034the customization of @code{font-lock-maximum-decoration} should be 1081given file buffer, the customization of
1035done @emph{before} the file is visited; if you already have the file 1082@code{font-lock-maximum-decoration} should be done @emph{before} the
1036visited in a buffer when you customize this variable, kill the buffer 1083file is visited; if you already have the file visited in a buffer when
1037and visit the file again after the customization. 1084you customize this variable, kill the buffer and visit the file again
1085after the customization.
1038 1086
1039You can also specify different numbers for particular major modes; for 1087You can also specify different numbers for particular major modes; for
1040example, to use level 1 for C/C++ modes, and the default level 1088example, to use level 1 for C/C++ modes, and the default level
@@ -1082,16 +1130,59 @@ keywords by customizing the @code{font-lock-ignore} option,
1082@pxref{Customizing Keywords,,, elisp, The Emacs Lisp Reference 1130@pxref{Customizing Keywords,,, elisp, The Emacs Lisp Reference
1083Manual}. 1131Manual}.
1084 1132
1085@cindex just-in-time (JIT) font-lock 1133@node Parser-based Font Lock
1086@cindex background syntax highlighting 1134@subsection Parser-based Font Lock
1087 Fontifying large buffers can take a long time. To avoid large 1135@cindex font-lock via tree-sitter
1088delays when a file is visited, Emacs initially fontifies only the 1136@cindex parser-based font-lock
1089visible portion of a buffer. As you scroll through the buffer, each 1137 If your Emacs was built with the tree-sitter library, it can use the
1090portion that becomes visible is fontified as soon as it is displayed; 1138results of parsing the buffer text by that library for the purposes of
1091this type of Font Lock is called @dfn{Just-In-Time} (or @dfn{JIT}) 1139fontification. This is usually faster and more accurate than the
1092Lock. You can control how JIT Lock behaves, including telling it to 1140``traditional'' methods described in the previous subsection, since
1093perform fontification while idle, by customizing variables in the 1141the tree-sitter library provides full-blown parsers for programming
1094customization group @samp{jit-lock}. @xref{Specific Customization}. 1142languages and other kinds of formatted text which it supports. Major
1143modes which utilize the tree-sitter library are named
1144@code{@var{foo}-ts-mode}, with the @samp{-ts-} part indicating the use
1145of the library. This subsection documents the Font Lock support based
1146on the tree-sitter library.
1147
1148@vindex treesit-font-lock-level
1149 You can control the amount of fontification applied by Font Lock
1150mode of major modes based on tree-sitter by customizing the variable
1151@code{treesit-font-lock-level}. Its value is a number between 1 and
11524:
1153
1154@table @asis
1155@item Level 1
1156This level usually fontifies only comments and function names in
1157function definitions.
1158@item Level 2
1159This level adds fontification of keywords, strings, and data types.
1160@item Level 3
1161This is the default level; it adds fontification of assignments,
1162numbers, properties, etc.
1163@item Level 4
1164This level adds everything else that can be fontified: operators,
1165delimiters, brackets, other punctuation, function names in function
1166calls, variables, etc.
1167@end table
1168
1169@vindex treesit-font-lock-feature-list
1170@noindent
1171What exactly constitutes each of the syntactical categories mentioned
1172above depends on the major mode and the parser grammar used by
1173tree-sitter for the major-mode's language. However, in general the
1174categories follow the conventions of the programming language or the
1175file format supported by the major mode. The buffer-local value of
1176the variable @code{treesit-font-lock-feature-list} holds the
1177fontification features supported by a tree-sitter based major mode,
1178where each sub-list shows the features provided by the corresponding
1179fontification level.
1180
1181 Once you change the value of @code{treesit-font-lock-level} via
1182@w{@kbd{M-x customize-variable}} (@pxref{Specific Customization}), it
1183takes effect immediately in all the existing buffers and for files you
1184visit in the future in the same session.
1185
1095 1186
1096@node Highlight Interactively 1187@node Highlight Interactively
1097@section Interactive Highlighting 1188@section Interactive Highlighting
diff --git a/doc/emacs/emacs.texi b/doc/emacs/emacs.texi
index 30758efb43a..59e7f5bb64b 100644
--- a/doc/emacs/emacs.texi
+++ b/doc/emacs/emacs.texi
@@ -385,6 +385,10 @@ Controlling the Display
385* Visual Line Mode:: Word wrap and screen line-based editing. 385* Visual Line Mode:: Word wrap and screen line-based editing.
386* Display Custom:: Information on variables for customizing display. 386* Display Custom:: Information on variables for customizing display.
387 387
388Font Lock
389* Traditional Font Lock:: Font Lock based on regexps and syntax tables.
390* Parser-based Font Lock:: Font Lock based on external parser.
391
388Searching and Replacement 392Searching and Replacement
389 393
390* Incremental Search:: Search happens as you type the string. 394* Incremental Search:: Search happens as you type the string.
diff --git a/doc/emacs/files.texi b/doc/emacs/files.texi
index 42e252c417b..664b9d5d9a3 100644
--- a/doc/emacs/files.texi
+++ b/doc/emacs/files.texi
@@ -215,6 +215,17 @@ by the integers that Emacs can represent (@pxref{Buffers}). If you
215try, Emacs displays an error message saying that the maximum buffer 215try, Emacs displays an error message saying that the maximum buffer
216size has been exceeded. 216size has been exceeded.
217 217
218@vindex treesit-max-buffer-size
219 If you try to visit a file whose major mode (@pxref{Major Modes})
220uses the tree-sitter parsing library, Emacs will display a warning if
221the file's size in bytes is larger than the value of the variable
222@code{treesit-max-buffer-size}. The default value is 40 megabytes for
22364-bit Emacs and 15 megabytes for 32-bit Emacs. This avoids the
224danger of having Emacs run out of memory by preventing the activation
225of major modes based on tree-sitter in such large buffers, because a
226typical tree-sitter parser needs about 10 times as much memory as the
227text it parses.
228
218@cindex wildcard characters in file names 229@cindex wildcard characters in file names
219@vindex find-file-wildcards 230@vindex find-file-wildcards
220 If the file name you specify contains shell-style wildcard 231 If the file name you specify contains shell-style wildcard
diff --git a/doc/emacs/frames.texi b/doc/emacs/frames.texi
index 3ee6eb59dbb..ce631561be7 100644
--- a/doc/emacs/frames.texi
+++ b/doc/emacs/frames.texi
@@ -334,6 +334,7 @@ In this way, you can use the mouse to move point over a button without
334activating it. Dragging the mouse over or onto a button has its usual 334activating it. Dragging the mouse over or onto a button has its usual
335behavior of setting the region, and does not activate the button. 335behavior of setting the region, and does not activate the button.
336 336
337@vindex mouse-1-click-follows-link
337 You can change how @kbd{mouse-1} applies to buttons by customizing 338 You can change how @kbd{mouse-1} applies to buttons by customizing
338the variable @code{mouse-1-click-follows-link}. If the value is a 339the variable @code{mouse-1-click-follows-link}. If the value is a
339positive integer, that determines how long you need to hold the mouse 340positive integer, that determines how long you need to hold the mouse
diff --git a/doc/emacs/misc.texi b/doc/emacs/misc.texi
index 3ee8ee5ee39..a5e1689b6c7 100644
--- a/doc/emacs/misc.texi
+++ b/doc/emacs/misc.texi
@@ -470,11 +470,7 @@ documents. It provides features such as slicing, zooming, and
470searching inside documents. It works by converting the document to a 470searching inside documents. It works by converting the document to a
471set of images using the @command{gs} (GhostScript) or 471set of images using the @command{gs} (GhostScript) or
472@command{pdfdraw}/@command{mutool draw} (MuPDF) commands and other 472@command{pdfdraw}/@command{mutool draw} (MuPDF) commands and other
473external tools @footnote{PostScript files require GhostScript, DVI 473external tools, and then displays those converted images.
474files require @code{dvipdf} or @code{dvipdfm}, OpenDocument and
475Microsoft Office documents require the @code{unoconv} tool, and EPUB,
476CBZ, FB2, XPS and OXPS files require @code{mutool} to be available.},
477and displaying those images.
478 474
479@findex doc-view-toggle-display 475@findex doc-view-toggle-display
480@findex doc-view-minor-mode 476@findex doc-view-minor-mode
diff --git a/doc/emacs/programs.texi b/doc/emacs/programs.texi
index 065ed1c51f7..d983c2b59c6 100644
--- a/doc/emacs/programs.texi
+++ b/doc/emacs/programs.texi
@@ -255,6 +255,17 @@ they do their standard jobs in a way better fitting a particular
255language. Other major modes may replace any or all of these key 255language. Other major modes may replace any or all of these key
256bindings for that purpose. 256bindings for that purpose.
257 257
258@cindex nested defuns
259@vindex treesit-defun-tactic
260 Some programming languages supported @dfn{nested defuns}, whereby a
261defun (such as a function or a method or a class) can be defined
262inside (i.e., as part of the body) of another defun. The commands
263described above by default find the beginning and the end of the
264@emph{innermost} defun around point. Major modes based on the
265tree-sitter library provide control of this behavior: if the variable
266@code{treesit-defun-tactic} is set to the value @code{top-level}, the
267defun commands will find the @emph{outermost} defuns instead.
268
258@node Moving by Sentences 269@node Moving by Sentences
259@subsection Moving by Sentences 270@subsection Moving by Sentences
260@cindex sentences, in programming languages 271@cindex sentences, in programming languages
@@ -599,15 +610,19 @@ then indent it like this:
599@item C-c C-q 610@item C-c C-q
600@kindex C-c C-q @r{(C mode)} 611@kindex C-c C-q @r{(C mode)}
601@findex c-indent-defun 612@findex c-indent-defun
613@findex c-ts-mode-indent-defun
602Reindent the current top-level function definition or aggregate type 614Reindent the current top-level function definition or aggregate type
603declaration (@code{c-indent-defun}). 615declaration (@code{c-indent-defun} in CC mode,
616@code{c-ts-mode-indent-defun} in @code{c-ts-mode} based on tree-sitter).
604 617
605@item C-M-q 618@item C-M-q
606@kindex C-M-q @r{(C mode)} 619@kindex C-M-q @r{(C mode)}
607@findex c-indent-exp 620@findex c-indent-exp
608Reindent each line in the balanced expression that follows point 621@findex prog-indent-sexp
609(@code{c-indent-exp}). A prefix argument inhibits warning messages 622Reindent each line in the balanced expression that follows point. In
610about invalid syntax. 623CC mode, this invokes @code{c-indent-exp}; in tree-sitter based
624@code{c-ts-mode} this invokes a more general @code{prog-indent-sexp}.
625A prefix argument inhibits warning messages about invalid syntax.
611 626
612@item @key{TAB} 627@item @key{TAB}
613@findex c-indent-line-or-region 628@findex c-indent-line-or-region
@@ -647,7 +662,8 @@ onto the indentation of the @dfn{anchor statement}.
647 662
648@table @kbd 663@table @kbd
649@item C-c . @var{style} @key{RET} 664@item C-c . @var{style} @key{RET}
650Select a predefined style @var{style} (@code{c-set-style}). 665Select a predefined style @var{style} (@code{c-set-style} in CC mode,
666@code{c-ts-mode-set-style} in @code{c-ts-mode} based on tree-sitter).
651@end table 667@end table
652 668
653 A @dfn{style} is a named collection of customizations that can be 669 A @dfn{style} is a named collection of customizations that can be
@@ -663,6 +679,7 @@ typing @kbd{C-M-q} at the start of a function definition.
663 679
664@kindex C-c . @r{(C mode)} 680@kindex C-c . @r{(C mode)}
665@findex c-set-style 681@findex c-set-style
682@findex c-ts-mode-set-style
666 To choose a style for the current buffer, use the command @w{@kbd{C-c 683 To choose a style for the current buffer, use the command @w{@kbd{C-c
667.}}. Specify a style name as an argument (case is not significant). 684.}}. Specify a style name as an argument (case is not significant).
668This command affects the current buffer only, and it affects only 685This command affects the current buffer only, and it affects only
@@ -671,11 +688,11 @@ the code already in the buffer. To reindent the whole buffer in the
671new style, you can type @kbd{C-x h C-M-\}. 688new style, you can type @kbd{C-x h C-M-\}.
672 689
673@vindex c-default-style 690@vindex c-default-style
674 You can also set the variable @code{c-default-style} to specify the 691 When using CC mode, you can also set the variable
675default style for various major modes. Its value should be either the 692@code{c-default-style} to specify the default style for various major
676style's name (a string) or an alist, in which each element specifies 693modes. Its value should be either the style's name (a string) or an
677one major mode and which indentation style to use for it. For 694alist, in which each element specifies one major mode and which
678example, 695indentation style to use for it. For example,
679 696
680@example 697@example
681(setq c-default-style 698(setq c-default-style
@@ -692,6 +709,11 @@ one of the C-like major modes; thus, if you specify a new default
692style for Java mode, you can make it take effect in an existing Java 709style for Java mode, you can make it take effect in an existing Java
693mode buffer by typing @kbd{M-x java-mode} there. 710mode buffer by typing @kbd{M-x java-mode} there.
694 711
712@vindex c-ts-mode-indent-style
713 When using the tree-sitter based @code{c-ts-mode}, you can set the
714default indentation style by customizing the variable
715@code{c-ts-mode-indent-style}.
716
695 The @code{gnu} style specifies the formatting recommended by the GNU 717 The @code{gnu} style specifies the formatting recommended by the GNU
696Project for C; it is the default, so as to encourage use of our 718Project for C; it is the default, so as to encourage use of our
697recommended style. 719recommended style.
diff --git a/doc/emacs/text.texi b/doc/emacs/text.texi
index 78e89d8031a..18f2274cfa6 100644
--- a/doc/emacs/text.texi
+++ b/doc/emacs/text.texi
@@ -1021,14 +1021,16 @@ this variable is @code{insert}, the buttons are inserted directly into
1021the buffer text, so @key{RET} on the button will also toggle display 1021the buffer text, so @key{RET} on the button will also toggle display
1022of the section, like a mouse click does. If the value is 1022of the section, like a mouse click does. If the value is
1023@code{in-margins}, Outline minor mode will use the window margins to 1023@code{in-margins}, Outline minor mode will use the window margins to
1024indicate that a section is hidden. 1024indicate that a section is hidden. The buttons are customizable as icons
1025(@pxref{Icons}).
1025 1026
1026@vindex outline-minor-mode-cycle 1027@vindex outline-minor-mode-cycle
1027 If the @code{outline-minor-mode-cycle} user option is 1028 If the @code{outline-minor-mode-cycle} user option is
1028non-@code{nil}, the @kbd{TAB} and @kbd{S-@key{TAB}} keys are enabled on the 1029non-@code{nil}, the @kbd{TAB} and @kbd{S-@key{TAB}} keys that cycle
1029outline heading lines. @kbd{TAB} cycles hiding, showing the 1030the visibility are enabled on the outline heading lines
1030sub-heading, and showing all for the current section. @kbd{S-@key{TAB}} 1031(@pxref{Outline Visibility, outline-cycle}). @kbd{TAB} cycles hiding,
1031does the same for the entire buffer. 1032showing the sub-heading, and showing all for the current section.
1033@kbd{S-@key{TAB}} does the same for the entire buffer.
1032 1034
1033@node Outline Format 1035@node Outline Format
1034@subsection Format of Outlines 1036@subsection Format of Outlines
diff --git a/doc/lispref/variables.texi b/doc/lispref/variables.texi
index 39d0906f6c4..5584cbce9a6 100644
--- a/doc/lispref/variables.texi
+++ b/doc/lispref/variables.texi
@@ -2023,6 +2023,7 @@ file-local variables stored in @code{file-local-variables-alist}.
2023@end defvar 2023@end defvar
2024 2024
2025@cindex safe local variable 2025@cindex safe local variable
2026@cindex @code{safe-local-variable}, property of variable
2026 You can specify safe values for a variable with a 2027 You can specify safe values for a variable with a
2027@code{safe-local-variable} property. The property has to be a 2028@code{safe-local-variable} property. The property has to be a
2028function of one argument; any value is safe if the function returns 2029function of one argument; any value is safe if the function returns
diff --git a/etc/NEWS.29 b/etc/NEWS.29
index 4d199676848..fb211f9b7d0 100644
--- a/etc/NEWS.29
+++ b/etc/NEWS.29
@@ -1018,6 +1018,8 @@ quotes removed.
1018 1018
1019--- 1019---
1020*** 'M-x apropos-variable' output now includes values of variables. 1020*** 'M-x apropos-variable' output now includes values of variables.
1021Such apropos buffer is more easily viewed with outlining after
1022enabling 'outline-minor-mode' in 'apropos-mode'.
1021 1023
1022+++ 1024+++
1023*** New docstring syntax to indicate that symbols shouldn't be links. 1025*** New docstring syntax to indicate that symbols shouldn't be links.
@@ -2480,6 +2482,10 @@ matches.
2480--- 2482---
2481*** New function 'xref-show-xrefs'. 2483*** New function 'xref-show-xrefs'.
2482 2484
2485*** 'outline-minor-mode' is supported in Xref buffers.
2486You can enable outlining by adding 'outline-minor-mode' to
2487'xref-after-update-hook'.
2488
2483** File Notifications 2489** File Notifications
2484 2490
2485+++ 2491+++
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
diff --git a/src/comp.c b/src/comp.c
index 6ff1915ef5b..ba549155925 100644
--- a/src/comp.c
+++ b/src/comp.c
@@ -5912,6 +5912,3 @@ file -> CU. */);
5912 5912
5913 defsubr (&Snative_comp_available_p); 5913 defsubr (&Snative_comp_available_p);
5914} 5914}
5915/* Local Variables: */
5916/* c-file-offsets: ((arglist-intro . +)) */
5917/* End: */
diff --git a/src/treesit.c b/src/treesit.c
index 917db582676..b210ec0923a 100644
--- a/src/treesit.c
+++ b/src/treesit.c
@@ -3312,6 +3312,68 @@ a regexp. */)
3312 return parent; 3312 return parent;
3313} 3313}
3314 3314
3315DEFUN ("treesit-subtree-stat",
3316 Ftreesit_subtree_stat,
3317 Streesit_subtree_stat, 1, 1, 0,
3318 doc: /* Return information about the subtree of NODE.
3319
3320Return a list (MAX-DEPTH MAX-WIDTH COUNT), where MAX-DEPTH is the
3321maximum depth of the subtree, MAX-WIDTH is the maximum number of
3322direct children of nodes in the subtree, and COUNT is the number of
3323nodes in the subtree, including NODE. */)
3324 (Lisp_Object node)
3325{
3326 /* Having a limit on the depth to traverse doesn't have much impact
3327 on the time it takes, so I left that out. */
3328 CHECK_TS_NODE (node);
3329
3330 treesit_initialize ();
3331
3332 TSTreeCursor cursor = ts_tree_cursor_new (XTS_NODE (node)->node);
3333 ptrdiff_t max_depth = 1;
3334 ptrdiff_t max_width = 0;
3335 ptrdiff_t count = 0;
3336 ptrdiff_t current_depth = 0;
3337
3338 /* Traverse the subtree depth-first. */
3339 while (true)
3340 {
3341 count++;
3342
3343 /* Go down depth-first. */
3344 while (ts_tree_cursor_goto_first_child (&cursor))
3345 {
3346 current_depth++;
3347 count++;
3348 /* While we're at here, measure the number of siblings. */
3349 ptrdiff_t width_count = 1;
3350 while (ts_tree_cursor_goto_next_sibling (&cursor))
3351 width_count++;
3352 max_width = max (max_width, width_count);
3353 /* Go back to the first sibling. */
3354 treesit_assume_true (ts_tree_cursor_goto_parent (&cursor));
3355 treesit_assume_true (ts_tree_cursor_goto_first_child (&cursor));
3356 }
3357 max_depth = max (max_depth, current_depth);
3358
3359 /* Go to next sibling. If there is no next sibling, go to
3360 parent's next sibling, and so on. If there is no more
3361 parent, we've traversed the whole subtree, stop. */
3362 while (!ts_tree_cursor_goto_next_sibling (&cursor))
3363 {
3364 if (ts_tree_cursor_goto_parent (&cursor))
3365 current_depth--;
3366 else
3367 {
3368 ts_tree_cursor_delete (&cursor);
3369 return list3 (make_fixnum (max_depth),
3370 make_fixnum (max_width),
3371 make_fixnum (count));
3372 }
3373 }
3374 }
3375}
3376
3315#endif /* HAVE_TREE_SITTER */ 3377#endif /* HAVE_TREE_SITTER */
3316 3378
3317DEFUN ("treesit-available-p", Ftreesit_available_p, 3379DEFUN ("treesit-available-p", Ftreesit_available_p,
@@ -3511,6 +3573,7 @@ then in the system default locations for dynamic libraries, in that order. */);
3511 defsubr (&Streesit_search_subtree); 3573 defsubr (&Streesit_search_subtree);
3512 defsubr (&Streesit_search_forward); 3574 defsubr (&Streesit_search_forward);
3513 defsubr (&Streesit_induce_sparse_tree); 3575 defsubr (&Streesit_induce_sparse_tree);
3576 defsubr (&Streesit_subtree_stat);
3514#endif /* HAVE_TREE_SITTER */ 3577#endif /* HAVE_TREE_SITTER */
3515 defsubr (&Streesit_available_p); 3578 defsubr (&Streesit_available_p);
3516} 3579}
diff --git a/test/lisp/progmodes/c-ts-mode-resources/indent.erts b/test/lisp/progmodes/c-ts-mode-resources/indent.erts
index 67654404a77..0ecbf922b15 100644
--- a/test/lisp/progmodes/c-ts-mode-resources/indent.erts
+++ b/test/lisp/progmodes/c-ts-mode-resources/indent.erts
@@ -114,7 +114,9 @@ int main() {
114 { 114 {
115 puts ("Hello"); 115 puts ("Hello");
116 } 116 }
117 for (int i=0; i<5; i++) 117 for (int i=0;
118 i<5;
119 i++)
118 if (true) 120 if (true)
119 { 121 {
120 puts ("Hello"); 122 puts ("Hello");
@@ -141,7 +143,9 @@ int main() {
141 if (true) { 143 if (true) {
142 puts ("Hello"); 144 puts ("Hello");
143 } 145 }
144 for (int i=0; i<5; i++) 146 for (int i=0;
147 i<5;
148 i++)
145 if (true) { 149 if (true) {
146 puts ("Hello"); 150 puts ("Hello");
147 } 151 }