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-mode0a61e4e2b7
; * 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 parseec6feeaa19
Fix tree-sitter parser notifier recursion
This commit is contained in:
commit
d9d90666f5
4 changed files with 130 additions and 79 deletions
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue