]> git.eshelyaron.com Git - emacs.git/commitdiff
Validate font-lock rules when setting up tree-sitter major mode
authorYuan Fu <casouri@gmail.com>
Tue, 3 Dec 2024 04:47:38 +0000 (20:47 -0800)
committerEshel Yaron <me@eshelyaron.com>
Fri, 6 Dec 2024 15:42:48 +0000 (16:42 +0100)
When tree-sitter grammar make breaking changes, major modes
breaks completely: no highlighting is shown.  This new function
will run in treesit-major-mode-setup, validate each
font-lock features, and disable the ones that are not compatible
with the new grammar, so that the rest of the features still
work.  It also displays a warning explaining the situation,
which looks like this:

    Warning (treesit-font-lock-rules-mismatch): Emacs cannot
    compile every font-lock rules because a mismatch between the
    grammar and the rules.  This is most likely due to a
    mismatch between the font-lock rules defined by the major
    mode and the tree-sitter grammar.

    This error can be fixed by either downgrading the
    grammar (tree-sitter-c) on your system, or upgrading the
    major mode package.  The following are the temporarily
    disabled features:

    - `preprocessor' for c.

* lisp/treesit.el (treesit-validate-font-lock-rules): New function.
(treesit-major-mode-setup): Validate font-lock rules.

(cherry picked from commit ed9eaaa9964f46ef5f9a9c084a1cb8f1ae1926a3)

lisp/treesit.el

index be6c88206382b348e4458ea564e37ef8c6c82162..221c539f86a596659267ca225ff46a5f7d4cd181 100644 (file)
@@ -1210,6 +1210,48 @@ docstring of `treesit-font-lock-rules' for what is a feature."
              (append rules
                      (nthcdr feature-idx treesit-font-lock-settings)))))))
 
+(defun treesit-validate-font-lock-rules (settings)
+  "Validate font-lock rules in SETTINGS before major mode starts.
+
+If the tree-sitter grammar currently installed on the system is
+incompatible with the major mode's font-lock rules, this procedure will
+detect the problematic rule, disable it temporarily, and notify the
+user."
+  (let ((faulty-features ()))
+    (dolist (setting settings)
+      (let* ((query (treesit-font-lock-setting-query setting))
+             (lang (treesit-query-language query))
+             (enabled (treesit-font-lock-setting-enable setting)))
+        (when (and enabled
+                   (condition-case nil
+                       (progn
+                         (treesit-query-compile lang query 'eager)
+                         nil)
+                     (treesit-query-error t)))
+          (push (cons (treesit-font-lock-setting-feature setting)
+                      lang)
+                faulty-features))))
+    (when faulty-features
+      (treesit-font-lock-recompute-features
+       nil (mapcar #'car faulty-features))
+      (let* ((languages
+              (string-join
+               (delete-dups (mapcar (lambda (feat)
+                                      (format "tree-sitter-%s" (cdr feat)))
+                                    faulty-features))
+               ", "))
+             (features (string-join
+                        (mapcar
+                         (lambda (feat)
+                           (format "- `%s' for %s"
+                                   (car feat) (cdr feat)))
+                         faulty-features)
+                        ",\n")))
+        (display-warning
+         'treesit-font-lock-rules-mismatch
+         (format "Emacs cannot compile every font-lock rules because a mismatch between the grammar and the rules.  This is most likely due to a mismatch between the font-lock rules defined by the major mode and the tree-sitter grammar.\n\nThis error can be fixed by either downgrading the grammar (%s) on your system, or upgrading the major mode package.  The following are the temporarily disabled features:\n\n%s."
+                 languages features))))))
+
 (defun treesit-fontify-with-override
     (start end face override &optional bound-start bound-end)
   "Apply FACE to the region between START and END.
@@ -3134,7 +3176,8 @@ before calling this function."
     (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)))
+       treesit-primary-parser #'treesit--font-lock-mark-ranges-to-fontify))
+    (treesit-validate-font-lock-rules treesit-font-lock-settings))
   ;; Syntax
   (add-hook 'syntax-propertize-extend-region-functions
             #'treesit--pre-syntax-ppss 0 t)