"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.
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
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))))
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
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
(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)