From c88a1631e3f76926bf0fed49fa1b704d6eaf2155 Mon Sep 17 00:00:00 2001 From: Yuan Fu Date: Sun, 9 Oct 2022 15:17:11 -0700 Subject: [PATCH] Make tree-sitter font-lock support decoration levels * admin/notes/tree-sitter/html-manual/Parser_002dbased-Font-Lock.html: Update. * admin/notes/tree-sitter/html-manual/Parsing-Program-Source.html: Update. * doc/lispref/modes.texi: Mention the new :level option. * lisp/treesit.el (treesit-font-lock-settings): Update docstring. (treesit-font-lock-rules): Support :level. Relayout the let form. (treesit-font-lock-fontify-region): Support :level. --- .../Parser_002dbased-Font-Lock.html | 9 +++ .../html-manual/Parsing-Program-Source.html | 4 ++ doc/lispref/modes.texi | 10 ++++ lisp/treesit.el | 57 ++++++++++++++----- 4 files changed, 67 insertions(+), 13 deletions(-) diff --git a/admin/notes/tree-sitter/html-manual/Parser_002dbased-Font-Lock.html b/admin/notes/tree-sitter/html-manual/Parser_002dbased-Font-Lock.html index 246ebf05193..c91152edc0e 100644 --- a/admin/notes/tree-sitter/html-manual/Parser_002dbased-Font-Lock.html +++ b/admin/notes/tree-sitter/html-manual/Parser_002dbased-Font-Lock.html @@ -117,8 +117,17 @@ every query must specify the language. Other keywords are optional: keepFill-in regions without an existing face :togglesymbolIf non-nil, its value should be a variable name. The variable’s value (nil/non-nil) disables/enables the query during fontification. +nilAlways enable this query. +:levelintegerIf non-nil, its value should be the decoration level for this query. +Decoration level is controlled by font-lock-maximum-decoration. +nilAlways enable this query. +

Note that a query is applied only when both :toggle and +:level permit it. :level is used for global, +coarse-grained control, whereas :toggle is for local, +fine-grained control. +

Capture names in query should be face names like font-lock-keyword-face. The captured node will be fontified with that face. Capture names can also be function names, in which diff --git a/admin/notes/tree-sitter/html-manual/Parsing-Program-Source.html b/admin/notes/tree-sitter/html-manual/Parsing-Program-Source.html index 7b6e51468a6..81b42f7f526 100644 --- a/admin/notes/tree-sitter/html-manual/Parsing-Program-Source.html +++ b/admin/notes/tree-sitter/html-manual/Parsing-Program-Source.html @@ -94,6 +94,10 @@ for this Emacs instance. see Parser-based Font Lock, Parser-based Indentation, and Moving over Balanced Expressions.

+

About naming convention: use “tree-sitter” when referring to it as a +noun, like python-use-tree-sitter, but use “treesit” for +prefixes, like python-treesit-indent-function. +

To access the syntax tree of the text in a buffer, we need to first load a language definition and create a parser with it. Next, we can query the parser for specific nodes in the syntax tree. Then, we can diff --git a/doc/lispref/modes.texi b/doc/lispref/modes.texi index dcc0b2958d5..b1287c6ad05 100644 --- a/doc/lispref/modes.texi +++ b/doc/lispref/modes.texi @@ -3935,8 +3935,18 @@ every query must specify the language. Other keywords are optional: @item @code{:toggle} @tab @var{symbol} @tab If non-nil, its value should be a variable name. The variable's value (nil/non-nil) disables/enables the query during fontification. +@item @tab nil @tab Always enable this query. +@item @code{:level} @tab @var{integer} @tab +If non-nil, its value should be the decoration level for this query. +Decoration level is controlled by @code{font-lock-maximum-decoration}. +@item @tab nil @tab Always enable this query. @end multitable +Note that a query is applied only when both @code{:toggle} and +@code{:level} permit it. @code{:level} is used for global, +coarse-grained control, whereas @code{:toggle} is for local, +fine-grained control. + Capture names in @var{query} should be face names like @code{font-lock-keyword-face}. The captured node will be fontified with that face. Capture names can also be function names, in which diff --git a/lisp/treesit.el b/lisp/treesit.el index db96a899750..7132f1d5ee6 100644 --- a/lisp/treesit.el +++ b/lisp/treesit.el @@ -289,7 +289,7 @@ should always use `treesit-font-lock-rules' to set this variable. Each SETTING is of form - (LANGUAGE QUERY OVERRIDE TOGGLE) + (LANGUAGE QUERY OVERRIDE TOGGLE LEVEL) Each SETTING controls one parser (often of different language). LANGUAGE is the language symbol. See Info node `(elisp)Language @@ -308,7 +308,11 @@ t, nil, append, prepend, keep. See more in TOGGLE should be a variable (symbol) or nil. The variable's value (nil/non-nil) controls whether to activate the query during -fontification. If TOGGLE is nil, the query is always activated.") +fontification. If TOGGLE is nil, the query is always activated. + +LEVEL is the decoration level of this query or nil. Decoration +level is controlled by `font-lock-maximum-decoration'. If LEVEL +is nil, the query is always activated.") (defun treesit-font-lock-rules (&rest args) "Return a value suitable for `treesit-font-lock-settings'. @@ -344,6 +348,15 @@ include: The value of that variable (non-nil/nil) activates/deactivates the query during fontification. + nil Always activate this query. + :level If non-nil, the value is the decoration + level of this query. + (See `font-lock-maximum-decoration'.) + nil Always activate this query. + +Note that a query is applied only when both :toggle and :level +permit it. :level is used for global, coarse-grained control, +whereas :toggle is for local, fine-grained control. Capture names in QUERY should be face names like `font-lock-keyword-face'. The captured node will be fontified @@ -356,18 +369,16 @@ a capture name is not a face name nor a function name, it is ignored. \(fn :KEYWORD VALUE QUERY...)" - (let (;; Tracks the current language that following queries will - ;; apply to. - (current-language nil) - ;; Tracks :override flag. - (current-override nil) - ;; Track :toggle flag. - (current-toggle t) + (let (;; Tracks the current :language/:override/:toggle/:level value + ;; that following queries will apply to. + current-language current-override + current-toggle current-level ;; The list this function returns. (result nil)) (while args (let ((token (pop args))) (pcase token + ;; (1) Process keywords. (:language (let ((lang (pop args))) (when (or (not (symbolp lang)) (null lang)) @@ -393,6 +404,14 @@ ignored. `("Value of :toggle should be a variable name" ,var))) (setq current-toggle var))) + (:level + (let ((level (pop args))) + (when (not (and (integerp level) (> level 0))) + (signal 'treesit-font-lock-error + `("Value of :level should be a positive integer" + ,level))) + (setq current-level level))) + ;; (2) Process query. ((pred treesit-query-p) (when (null current-language) (signal 'treesit-font-lock-error @@ -402,12 +421,14 @@ ignored. (push `(,current-language ,(treesit-query-compile current-language token) ,current-override - ,current-toggle) + ,current-toggle + ,current-level) result)) ;; Clears any configurations set for this query. (setq current-language nil current-override nil - current-toggle nil)) + current-toggle nil + current-level nil)) (_ (signal 'treesit-font-lock-error `("Unexpected value" token)))))) (nreverse result))) @@ -423,10 +444,20 @@ If LOUDLY is non-nil, message some debugging information." (match-pattern (nth 1 setting)) (override (nth 2 setting)) (toggle (nth 3 setting)) + (level (nth 4 setting)) (parser (treesit-parser-create language))) (when-let ((node (treesit-node-on start end parser)) - (activate (or (null toggle) - (symbol-value toggle)))) + ;; Only activate this query if both :toggle and + ;; :level permit it. + (activate + (and (or (null toggle) + (symbol-value toggle)) + (or (null level) + (pcase (font-lock-value-in-major-mode + font-lock-maximum-decoration) + ('t t) + ('nil (eq level 1)) + (lvl (<= level lvl))))))) (ignore activate) (let ((captures (treesit-query-capture node match-pattern -- 2.39.2