mirror of
git://git.sv.gnu.org/emacs.git
synced 2025-12-07 06:50:23 -08:00
Use parser notifier to set parser ranges
This is a continuation from an earlier commit where I added treesit-parser-changed-ranges and friends. Dmitry raised an concern about the edge case where the parser re-parses multiple times before treesit--pre-redisplay has a chance to run and process changed ranges. Instead of making treesit-parser-changed-ranges DTRT and become more complicated, it's agreed that using parser notifier is a better solution (and treesit-parser-changed-ranges can probably be removed). So, I took out the code that does the work from treesit--pre-redisplay and put them into treesit--font-lock-mark-ranges-to-fontify. This function will be called on each parser re-parse. And in treesit--pre-redisplay, to ensure that treesit--f-l-m-r-to-f gets called, we force the primary parser to re-parse. I also added a new variable that major modes need to set, treesit-primary-parser. I also added code that makes Emacs guess the primary parser if that variable isn't set. Documentation fot treesit-primary-parser will come later. For futher reference, the message id for the message that prompted this change is <dc94733b-df75-446c-980e-1c8ea65826cf@gutov.dev> * lisp/treesit.el (treesit-primary-parser): New variable. (treesit--font-lock-mark-ranges-to-fontify): New function. (treesit--guess-primary-parser): New function. (treesit--pre-redisplay): Extract out. (treesit--pre-syntax-ppss): Add comments. (treesit-major-mode-setup): Guess and set treesit-primary-parser; add treesit--font-lock-mark-ranges-to-fontify as a notifier to the primary parser.
This commit is contained in:
parent
b44d511102
commit
760b54de08
1 changed files with 68 additions and 30 deletions
|
|
@ -793,6 +793,18 @@ omitted, default END to BEG."
|
||||||
"Generic tree-sitter font-lock error"
|
"Generic tree-sitter font-lock error"
|
||||||
'treesit-error)
|
'treesit-error)
|
||||||
|
|
||||||
|
;; The primary parser will be access frequently (after each re-parse,
|
||||||
|
;; before redisplay, etc, see
|
||||||
|
;; `treesit--font-lock-mark-ranges-to-fontify'), so we don't want to
|
||||||
|
;; allow it to be a callback function which returns the primary parser
|
||||||
|
;; (it might be slow). It's not something that needs to be dynamic
|
||||||
|
;; anyway.
|
||||||
|
(defvar-local treesit-primary-parser nil
|
||||||
|
"The primary parser for this buffer.
|
||||||
|
|
||||||
|
The primary parser should be a parser that parses the entire buffer, as
|
||||||
|
opposed to embedded parsers which parses only part of the buffer.")
|
||||||
|
|
||||||
(defvar-local treesit-font-lock-settings nil
|
(defvar-local treesit-font-lock-settings nil
|
||||||
"A list of SETTINGs for treesit-based fontification.
|
"A list of SETTINGs for treesit-based fontification.
|
||||||
|
|
||||||
|
|
@ -1391,13 +1403,15 @@ Because `pre-redisplay-functions' could be called multiple times
|
||||||
during a single command loop, we use this variable to debounce
|
during a single command loop, we use this variable to debounce
|
||||||
calls to `treesit--pre-redisplay'.")
|
calls to `treesit--pre-redisplay'.")
|
||||||
|
|
||||||
(defun treesit--pre-redisplay (&rest _)
|
(defun treesit--font-lock-mark-ranges-to-fontify (ranges _parser)
|
||||||
"Force a reparse on the primary parser and do some work.
|
"A notifier that marks ranges that needs refontification.
|
||||||
|
|
||||||
|
For RANGES and PARSER see `treesit-parser-add-notifier'.
|
||||||
|
|
||||||
After the parser reparses, we get the changed ranges, and
|
After the parser reparses, we get the changed ranges, and
|
||||||
1) update non-primary parsers' ranges in the changed ranges
|
1) update non-primary parsers' ranges in the changed ranges
|
||||||
2) mark these ranges as to-be-fontified,
|
2) mark these ranges as to-be-fontified,
|
||||||
3) tell syntax-ppss to start reparsing from the min point of the ranges
|
3) tell syntax-ppss to start reparsing from the min point of the ranges.
|
||||||
|
|
||||||
We need to mark to-be-fontified ranges before redisplay starts working,
|
We need to mark to-be-fontified ranges before redisplay starts working,
|
||||||
because sometimes the range edited by the user is not the only range
|
because sometimes the range edited by the user is not the only range
|
||||||
|
|
@ -1405,33 +1419,48 @@ that needs to be refontified. For example, when the user types the
|
||||||
final slash of a C block comment /* xxx */, not only do we need to
|
final slash of a C block comment /* xxx */, not only do we need to
|
||||||
fontify the slash, but also the whole block comment, which previously
|
fontify the slash, but also the whole block comment, which previously
|
||||||
wasn't fontified as comment due to incomplete parse tree."
|
wasn't fontified as comment due to incomplete parse tree."
|
||||||
|
(dolist (range ranges)
|
||||||
|
;; 1. Update ranges.
|
||||||
|
(treesit-update-ranges (car range) (cdr range))
|
||||||
|
;; 2. Mark the changed ranges to be fontified.
|
||||||
|
(when treesit--font-lock-verbose
|
||||||
|
(message "Notifier received range: %s-%s"
|
||||||
|
(car range) (cdr range)))
|
||||||
|
(with-silent-modifications
|
||||||
|
(put-text-property (car range) (cdr range) 'fontified nil))
|
||||||
|
;; 3. Set `treesit--syntax-propertize-start'.
|
||||||
|
(if (null treesit--syntax-propertize-start)
|
||||||
|
(setq treesit--syntax-propertize-start (car range))
|
||||||
|
(setq treesit--syntax-propertize-start
|
||||||
|
(min treesit--syntax-propertize-start (car range))))))
|
||||||
|
|
||||||
|
(defun treesit--guess-primary-parser ()
|
||||||
|
"Guess the primary parser of the current buffer and return it.
|
||||||
|
|
||||||
|
Normally in a tree-sitter major mode, there is a primary parser that
|
||||||
|
parses the entire buffer (as opposed to embedded parsers which only
|
||||||
|
parses part of the buffer). This function tries to find and return that
|
||||||
|
parser."
|
||||||
|
(if treesit-range-settings
|
||||||
|
(let ((query (car (car treesit-range-settings))))
|
||||||
|
(if (treesit-query-p query)
|
||||||
|
(treesit-parser-create
|
||||||
|
(treesit-query-language query))
|
||||||
|
(car (treesit-parser-list))))
|
||||||
|
(car (treesit-parser-list))))
|
||||||
|
|
||||||
|
(defun treesit--pre-redisplay (&rest _)
|
||||||
|
"Force a reparse on the primary parser and mark regions to be fontified.
|
||||||
|
|
||||||
|
The actual work is carried out by
|
||||||
|
`treesit--font-lock-mark-ranges-to-fontify', which see."
|
||||||
(unless (eq treesit--pre-redisplay-tick (buffer-chars-modified-tick))
|
(unless (eq treesit--pre-redisplay-tick (buffer-chars-modified-tick))
|
||||||
(let ((primary-parser
|
(when treesit-primary-parser
|
||||||
;; TODO: We need something less ugly than this for getting
|
;; Force a reparse on the primary parser, if everything is setup
|
||||||
;; the primary parser/language.
|
;; correctly, the parser should call
|
||||||
(if treesit-range-settings
|
;; `treesit--font-lock-mark-ranges-to-fontify' (which should be a
|
||||||
(let ((query (car (car treesit-range-settings))))
|
;; notifier function of the primary parser).
|
||||||
(if (treesit-query-p query)
|
(treesit-parser-root-node treesit-primary-parser))
|
||||||
(treesit-parser-create
|
|
||||||
(treesit-query-language query))
|
|
||||||
(car (treesit-parser-list))))
|
|
||||||
(car (treesit-parser-list)))))
|
|
||||||
;; Force a reparse on the primary parser.
|
|
||||||
(treesit-parser-root-node primary-parser)
|
|
||||||
(dolist (range (treesit-parser-changed-ranges primary-parser))
|
|
||||||
;; 1. Update ranges.
|
|
||||||
(treesit-update-ranges (car range) (cdr range))
|
|
||||||
;; 2. Mark the changed ranges to be fontified.
|
|
||||||
(when treesit--font-lock-verbose
|
|
||||||
(message "Notifier received range: %s-%s"
|
|
||||||
(car range) (cdr range)))
|
|
||||||
(with-silent-modifications
|
|
||||||
(put-text-property (car range) (cdr range) 'fontified nil))
|
|
||||||
;; 3. Set `treesit--syntax-propertize-start'.
|
|
||||||
(if (null treesit--syntax-propertize-start)
|
|
||||||
(setq treesit--syntax-propertize-start (car range))
|
|
||||||
(setq treesit--syntax-propertize-start
|
|
||||||
(min treesit--syntax-propertize-start (car range))))))
|
|
||||||
|
|
||||||
(setq treesit--pre-redisplay-tick (buffer-chars-modified-tick))))
|
(setq treesit--pre-redisplay-tick (buffer-chars-modified-tick))))
|
||||||
|
|
||||||
|
|
@ -1445,6 +1474,10 @@ whole region affected by the last reparse.
|
||||||
|
|
||||||
START and END mark the current to-be-propertized region."
|
START and END mark the current to-be-propertized region."
|
||||||
(treesit--pre-redisplay)
|
(treesit--pre-redisplay)
|
||||||
|
;; `treesit--syntax-propertize-start' is set by
|
||||||
|
;; `treesit--font-lock-mark-ranges-to-fontify', which is called after
|
||||||
|
;; each re-parser on the primary parser and in
|
||||||
|
;; `treesit--pre-redisplay'.
|
||||||
(let ((new-start treesit--syntax-propertize-start))
|
(let ((new-start treesit--syntax-propertize-start))
|
||||||
(if (and new-start (< new-start start))
|
(if (and new-start (< new-start start))
|
||||||
(progn
|
(progn
|
||||||
|
|
@ -2991,6 +3024,8 @@ enable tree-sitter navigation commands for them.
|
||||||
|
|
||||||
Make sure necessary parsers are created for the current buffer
|
Make sure necessary parsers are created for the current buffer
|
||||||
before calling this function."
|
before calling this function."
|
||||||
|
(unless treesit-primary-parser
|
||||||
|
(setq treesit-primary-parser (treesit--guess-primary-parser)))
|
||||||
;; Font-lock.
|
;; Font-lock.
|
||||||
(when treesit-font-lock-settings
|
(when treesit-font-lock-settings
|
||||||
;; `font-lock-mode' wouldn't set up properly if
|
;; `font-lock-mode' wouldn't set up properly if
|
||||||
|
|
@ -3000,7 +3035,10 @@ before calling this function."
|
||||||
(font-lock-fontify-syntactically-function
|
(font-lock-fontify-syntactically-function
|
||||||
. treesit-font-lock-fontify-region)))
|
. treesit-font-lock-fontify-region)))
|
||||||
(treesit-font-lock-recompute-features)
|
(treesit-font-lock-recompute-features)
|
||||||
(add-hook 'pre-redisplay-functions #'treesit--pre-redisplay 0 t))
|
(add-hook 'pre-redisplay-functions #'treesit--pre-redisplay 0 t)
|
||||||
|
(when treesit-primary-parser
|
||||||
|
(treesit-parser-add-notifier
|
||||||
|
treesit-primary-parser #'treesit--font-lock-mark-ranges-to-fontify)))
|
||||||
;; Syntax
|
;; Syntax
|
||||||
(add-hook 'syntax-propertize-extend-region-functions
|
(add-hook 'syntax-propertize-extend-region-functions
|
||||||
#'treesit--pre-syntax-ppss 0 t)
|
#'treesit--pre-syntax-ppss 0 t)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue