Merge from origin/emacs-29

909091d757 ; Minor cleanup for tree-sitter font-lock rules in js-ts-...
e78e69b331 Clean up font-lock rules in js-ts-mode
0a61e4e2b7 ; * doc/lispref/parsing.texi (Using Parser): Minor improv...
398ed75c27 ; * lisp/progmodes/c-ts-mode.el (c-ts-mode--fill-paragrap...
19b8733aa2 Fix syntax for < and > in c++-ts-mode (bug#60351)
f509246ba1 Call tree-sitter parser notifier on the first parse
ec6feeaa19 Fix tree-sitter parser notifier recursion
This commit is contained in:
Stefan Kangas 2022-12-29 06:30:09 +01:00
commit d9d90666f5
4 changed files with 130 additions and 79 deletions

View file

@ -505,7 +505,9 @@ notification.
Every time a parser reparses a buffer, it compares the old and new
parse-tree, computes the ranges in which nodes have changed, and
passes the ranges to notifier functions.
passes the ranges to notifier functions. Note that the initial parse
is also considered a ``change'', so notifier functions are called on
the initial parse, with range being the whole buffer.
@defun treesit-parser-add-notifier parser function
This function adds @var{function} to @var{parser}'s list of

View file

@ -63,6 +63,8 @@ follows the form of `treesit-simple-indent-rules'."
(function :tag "A function for user customized style" ignore))
:group 'c)
;;; Syntax table
(defvar c-ts-mode--syntax-table
(let ((table (make-syntax-table)))
;; Taken from the cc-langs version
@ -85,13 +87,27 @@ follows the form of `treesit-simple-indent-rules'."
table)
"Syntax table for `c-ts-mode'.")
(defvar c++-ts-mode--syntax-table
(let ((table (make-syntax-table c-ts-mode--syntax-table)))
;; Template delimiters.
(modify-syntax-entry ?< "(" table)
(modify-syntax-entry ?> ")" table)
table)
"Syntax table for `c++-ts-mode'.")
(defun c-ts-mode--syntax-propertize (beg end)
"Apply syntax text property to template delimiters between BEG and END.
< and > are usually punctuation, e.g., in ->. But when used for
templates, they should be considered pairs.
This function checks for < and > in the changed RANGES and apply
appropriate text property to alter the syntax of template
delimiters < and >'s."
(goto-char beg)
(while (re-search-forward (rx (or "<" ">")) end t)
(pcase (treesit-node-type
(treesit-node-parent
(treesit-node-at (match-beginning 0))))
("template_argument_list"
(put-text-property (match-beginning 0)
(match-end 0)
'syntax-table
(pcase (char-before)
(?< '(4 . ?>))
(?> '(5 . ?<))))))))
;;; Indent
@ -574,6 +590,10 @@ ARG is passed to `fill-paragraph'."
(goto-char (match-beginning 1))
(setq start-marker (point-marker))
(replace-match " " nil nil nil 1))
;; Include whitespaces before /*.
(goto-char start)
(beginning-of-line)
(setq start (point))
;; Mask spaces before "*/" if it is attached at the end
;; of a sentence rather than on its own line.
(goto-char end)
@ -645,11 +665,18 @@ Set up:
(concat (rx (* (syntax whitespace))
(group (or (seq "/" (+ "/")) (* "*"))))
adaptive-fill-regexp))
;; Same as `adaptive-fill-regexp'.
;; Note the missing * comparing to `adaptive-fill-regexp'. The
;; reason for its absence is a bit convoluted to explain. Suffice
;; to say that without it, filling a single line paragraph that
;; starts with /* doesn't insert * at the beginning of each
;; following line, and filling a multi-line paragraph whose first
;; two lines start with * does insert * at the beginning of each
;; following line. If you know how does adaptive filling works, you
;; know what I mean.
(setq-local adaptive-fill-first-line-regexp
(rx bos
(seq (* (syntax whitespace))
(group (or (seq "/" (+ "/")) (* "*")))
(group (seq "/" (+ "/")))
(* (syntax whitespace)))
eos))
;; Same as `adaptive-fill-regexp'.
@ -751,7 +778,6 @@ Set up:
(define-derived-mode c++-ts-mode c-ts-base-mode "C++"
"Major mode for editing C++, powered by tree-sitter."
:group 'c++
:syntax-table c++-ts-mode--syntax-table
(unless (treesit-ready-p 'cpp)
(error "Tree-sitter for C++ isn't available"))
@ -761,6 +787,8 @@ Set up:
"raw_string_literal")))
(treesit-parser-create 'cpp)
(setq-local syntax-propertize-function
#'c-ts-mode--syntax-propertize)
(setq-local treesit-simple-indent-rules
(c-ts-mode--set-indent-style 'cpp))

View file

@ -3479,36 +3479,35 @@ This function is intended for use in `after-change-functions'."
(treesit-font-lock-rules
:language 'javascript
:override t
:feature 'comment
`((comment) @font-lock-comment-face)
'((comment) @font-lock-comment-face)
:language 'javascript
:override t
:feature 'constant
`(((identifier) @font-lock-constant-face
'(((identifier) @font-lock-constant-face
(:match "^[A-Z_][A-Z_\\d]*$" @font-lock-constant-face))
[(true) (false) (null)] @font-lock-constant-face)
:language 'javascript
:override t
:feature 'keyword
`([,@js--treesit-keywords] @font-lock-keyword-face
[(this) (super)] @font-lock-keyword-face)
:language 'javascript
:override t
:feature 'string
`((regex pattern: (regex_pattern)) @font-lock-string-face
(string) @font-lock-string-face
(template_string) @js--fontify-template-string
(template_substitution ["${" "}"] @font-lock-builtin-face))
'((regex pattern: (regex_pattern)) @font-lock-string-face
(string) @font-lock-string-face)
:language 'javascript
:feature 'string-interpolation
:override t
:feature 'declaration
`((function
'((template_string) @js--fontify-template-string
(template_substitution ["${" "}"] @font-lock-delimiter-face))
:language 'javascript
:feature 'definition
'((function
name: (identifier) @font-lock-function-name-face)
(class_declaration
@ -3535,24 +3534,10 @@ This function is intended for use in `after-change-functions'."
value: (array (number) (function))))
:language 'javascript
:override t
:feature 'identifier
`((new_expression
constructor: (identifier) @font-lock-type-face)
(for_in_statement
left: (identifier) @font-lock-variable-name-face)
(arrow_function
parameter: (identifier) @font-lock-variable-name-face))
:language 'javascript
:override t
:feature 'property
;; This needs to be before function-name feature, because methods
;; can be both property and function-name, and we want them in
;; function-name face.
`((property_identifier) @font-lock-property-face
'(((property_identifier) @font-lock-property-face
(:pred js--treesit-property-not-function-p
@font-lock-property-face))
(pair value: (identifier) @font-lock-variable-name-face)
@ -3561,36 +3546,27 @@ This function is intended for use in `after-change-functions'."
((shorthand_property_identifier_pattern) @font-lock-property-face))
:language 'javascript
:override t
:feature 'expression
`((assignment_expression
left: [(identifier) @font-lock-function-name-face
(member_expression property: (property_identifier)
@font-lock-function-name-face)]
right: [(function) (arrow_function)])
:feature 'assignment
'((assignment_expression
left: (_) @js--treesit-fontify-assignment-lhs))
(call_expression
:language 'javascript
:feature 'function
'((call_expression
function: [(identifier) @font-lock-function-name-face
(member_expression
property:
(property_identifier) @font-lock-function-name-face)])
(assignment_expression
left: [(identifier) @font-lock-variable-name-face
(member_expression
property: (property_identifier) @font-lock-variable-name-face)]))
(method_definition
name: (property_identifier) @font-lock-function-name-face)
(function_declaration
name: (identifier) @font-lock-function-name-face)
(function
name: (identifier) @font-lock-function-name-face))
:language 'javascript
:override t
:feature 'pattern
`((pair_pattern key: (property_identifier) @font-lock-variable-name-face)
(array_pattern (identifier) @font-lock-variable-name-face))
:language 'javascript
:override t
:feature 'jsx
`(
(jsx_opening_element
'((jsx_opening_element
[(nested_identifier (identifier)) (identifier)]
@font-lock-function-name-face)
@ -3608,7 +3584,7 @@ This function is intended for use in `after-change-functions'."
:language 'javascript
:feature 'number
`((number) @font-lock-number-face
'((number) @font-lock-number-face
((identifier) @font-lock-number-face
(:match "^\\(:?NaN\\|Infinity\\)$" @font-lock-number-face)))
@ -3657,6 +3633,31 @@ OVERRIDE is the override flag described in
(setq font-beg (treesit-node-end child)
child (treesit-node-next-sibling child)))))
(defun js--treesit-property-not-function-p (node)
"Check that NODE, a property_identifier, is not used as a function."
(not (equal (treesit-node-type
(treesit-node-parent ; Maybe call_expression.
(treesit-node-parent ; Maybe member_expression.
node)))
"call_expression")))
(defvar js--treesit-lhs-identifier-query
(treesit-query-compile 'javascript '((identifier) @id
(property_identifier) @id))
"Query that captures identifier and query_identifier.")
(defun js--treesit-fontify-assignment-lhs (node override start end &rest _)
"Fontify the lhs NODE of an assignment_expression.
For OVERRIDE, START, END, see `treesit-font-lock-rules'."
(dolist (node (treesit-query-capture
node js--treesit-lhs-identifier-query nil nil t))
(treesit-fontify-with-override
(treesit-node-start node) (treesit-node-end node)
(pcase (treesit-node-type node)
("identifier" 'font-lock-variable-name-face)
("property_identifier" 'font-lock-property-face))
override start end)))
(defun js--treesit-defun-name (node)
"Return the defun name of NODE.
Return nil if there is no name or if NODE is not a defun node."
@ -3815,11 +3816,12 @@ Currently there are `js-mode' and `js-ts-mode'."
;; Fontification.
(setq-local treesit-font-lock-settings js--treesit-font-lock-settings)
(setq-local treesit-font-lock-feature-list
'(( comment declaration)
'(( comment definition)
( keyword string)
( constant escape-sequence expression
identifier jsx number pattern property)
( bracket delimiter operator)))
( assignment constant escape-sequence jsx number
pattern)
( bracket delimiter function operator property
string-interpolation)))
;; Imenu
(setq-local treesit-simple-imenu-settings
`(("Function" "\\`function_declaration\\'" nil nil)

View file

@ -933,11 +933,24 @@ static void
treesit_call_after_change_functions (TSTree *old_tree, TSTree *new_tree,
Lisp_Object parser)
{
uint32_t len;
TSRange *ranges = ts_tree_get_changed_ranges (old_tree, new_tree, &len);
/* If the old_tree is NULL, meaning this is the first parse, the
changed range is the whole buffer. */
Lisp_Object lisp_ranges;
struct buffer *buf = XBUFFER (XTS_PARSER (parser)->buffer);
Lisp_Object lisp_ranges = treesit_make_ranges (ranges, len, buf);
xfree (ranges);
if (old_tree)
{
uint32_t len;
TSRange *ranges = ts_tree_get_changed_ranges (old_tree, new_tree, &len);
lisp_ranges = treesit_make_ranges (ranges, len, buf);
xfree (ranges);
}
else
{
struct buffer *oldbuf = current_buffer;
set_buffer_internal (buf);
lisp_ranges = Fcons (Fcons (Fpoint_min (), Fpoint_max ()), Qnil);
set_buffer_internal (oldbuf);
}
specpdl_ref count = SPECPDL_INDEX ();
@ -955,6 +968,11 @@ treesit_call_after_change_functions (TSTree *old_tree, TSTree *new_tree,
static void
treesit_ensure_parsed (Lisp_Object parser)
{
/* Make sure this comes before everything else, see comment
(ref:notifier-inside-ensure-parsed) for more detail. */
if (!XTS_PARSER (parser)->need_reparse)
return;
struct buffer *buffer = XBUFFER (XTS_PARSER (parser)->buffer);
/* Before we parse, catch up with the narrowing situation. */
@ -963,8 +981,6 @@ treesit_ensure_parsed (Lisp_Object parser)
because it might set the flag to true. */
treesit_sync_visible_region (parser);
if (!XTS_PARSER (parser)->need_reparse)
return;
TSParser *treesit_parser = XTS_PARSER (parser)->parser;
TSTree *tree = XTS_PARSER (parser)->tree;
TSInput input = XTS_PARSER (parser)->input;
@ -984,14 +1000,17 @@ treesit_ensure_parsed (Lisp_Object parser)
xsignal1 (Qtreesit_parse_error, buf);
}
if (tree != NULL)
{
treesit_call_after_change_functions (tree, new_tree, parser);
ts_tree_delete (tree);
}
XTS_PARSER (parser)->tree = new_tree;
XTS_PARSER (parser)->need_reparse = false;
/* After-change functions should run at the very end, most crucially
after need_reparse is set to false, this way if the function
calls some tree-sitter function which invokes
treesit_ensure_parsed again, it returns early and do not
recursively call the after change functions again.
(ref:notifier-inside-ensure-parsed) */
treesit_call_after_change_functions (tree, new_tree, parser);
ts_tree_delete (tree);
}
/* This is the read function provided to tree-sitter to read from a