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"
|
||||
'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
|
||||
"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
|
||||
calls to `treesit--pre-redisplay'.")
|
||||
|
||||
(defun treesit--pre-redisplay (&rest _)
|
||||
"Force a reparse on the primary parser and do some work.
|
||||
(defun treesit--font-lock-mark-ranges-to-fontify (ranges _parser)
|
||||
"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
|
||||
1) update non-primary parsers' ranges in the changed ranges
|
||||
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,
|
||||
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
|
||||
fontify the slash, but also the whole block comment, which previously
|
||||
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))
|
||||
(let ((primary-parser
|
||||
;; TODO: We need something less ugly than this for getting
|
||||
;; the primary parser/language.
|
||||
(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)))))
|
||||
;; 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))))))
|
||||
(when treesit-primary-parser
|
||||
;; Force a reparse on the primary parser, if everything is setup
|
||||
;; correctly, the parser should call
|
||||
;; `treesit--font-lock-mark-ranges-to-fontify' (which should be a
|
||||
;; notifier function of the primary parser).
|
||||
(treesit-parser-root-node treesit-primary-parser))
|
||||
|
||||
(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."
|
||||
(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))
|
||||
(if (and new-start (< new-start start))
|
||||
(progn
|
||||
|
@ -2991,6 +3024,8 @@ enable tree-sitter navigation commands for them.
|
|||
|
||||
Make sure necessary parsers are created for the current buffer
|
||||
before calling this function."
|
||||
(unless treesit-primary-parser
|
||||
(setq treesit-primary-parser (treesit--guess-primary-parser)))
|
||||
;; Font-lock.
|
||||
(when treesit-font-lock-settings
|
||||
;; `font-lock-mode' wouldn't set up properly if
|
||||
|
@ -3000,7 +3035,10 @@ before calling this function."
|
|||
(font-lock-fontify-syntactically-function
|
||||
. treesit-font-lock-fontify-region)))
|
||||
(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
|
||||
(add-hook 'syntax-propertize-extend-region-functions
|
||||
#'treesit--pre-syntax-ppss 0 t)
|
||||
|
|
Loading…
Add table
Reference in a new issue