From: Yuan Fu Date: Thu, 27 Feb 2025 11:05:26 +0000 (-0800) Subject: Add tree-sitter-parser-embed-level and parent-node X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=d9857bf14c109dc4c9054edfb59ce2e474b0c200;p=emacs.git Add tree-sitter-parser-embed-level and parent-node Add parser properties embed-level and parent-node. They'll be help us implement arbitrarily nested embeded parser, and navigation across embedded and host parsers, respectively. * src/treesit.c: (Ftreesit_parser_embed_level): (Ftreesit_parser_set_embed_level): (Ftreesit_parser_parent_node): (Ftreesit_parser_set_parent_node): New functions. (cherry picked from commit 30e1508ef2d40e221736cea2c50c64941d7d2f0d) --- diff --git a/src/treesit.c b/src/treesit.c index 62606d99749..16308193bf5 100644 --- a/src/treesit.c +++ b/src/treesit.c @@ -1367,6 +1367,8 @@ make_treesit_parser (Lisp_Object buffer, TSParser *parser, lisp_parser->after_change_functions = Qnil; lisp_parser->tag = tag; lisp_parser->last_set_ranges = Qnil; + lisp_parser->embed_level = Qnil; + lisp_parser->parent_node = Qnil; lisp_parser->buffer = buffer; lisp_parser->parser = parser; lisp_parser->tree = tree; @@ -1818,6 +1820,69 @@ DEFUN ("treesit-parser-tag", return XTS_PARSER (parser)->tag; } +DEFUN ("treesit-parser-embed-level", + Ftreesit_parser_embed_level, Streesit_parser_embed_level, + 1, 1, 0, + doc: /* Return PARSER's embed level. + +The embed level can be either nil or a non-negative integer. A value of +nil means the parser isn't part of the embedded parser tree. The +primary parser has embed level 0, from it, each layer of embedded parser +has +1 embed level. */) + (Lisp_Object parser) +{ + treesit_check_parser (parser); + return XTS_PARSER (parser)->embed_level; +} + +/* TODO: Mention in manual, once the API stabilizes. */ +DEFUN ("treesit-parser-set-embed-level", + Ftreesit_parser_set_embed_level, Streesit_parser_set_embed_level, + 2, 2, 0, + doc: /* Set the embed level for PARSER to LEVEL. */) + (Lisp_Object parser, Lisp_Object level) +{ + treesit_check_parser (parser); + if (!NILP (level)) + { + CHECK_NUMBER (level); + if (XFIXNUM (level) < 0) + xsignal (Qargs_out_of_range, list1 (level)); + } + + XTS_PARSER (parser)->embed_level = level; + return level; +} + +DEFUN ("treesit-parser-parent-node", + Ftreesit_parser_parent_node, Streesit_parser_parent_node, + 1, 1, 0, + doc: /* Return PARSER's parent node, if one exists. + +Only embeded local parser can have parent node. When Emacs uses a node +in the host parser to create this local parser, that node is considered +the parent node of the local parser. */) + (Lisp_Object parser) +{ + treesit_check_parser (parser); + return XTS_PARSER (parser)->parent_node; +} + +DEFUN ("treesit-parser-set-parent-node", + Ftreesit_parser_set_parent_node, Streesit_parser_set_parent_node, + 2, 2, 0, + doc: /* Return PARSER's parent node to NODE. */) + (Lisp_Object parser, Lisp_Object node) +{ + treesit_check_parser (parser); + if (!NILP (node)) + CHECK_TS_NODE (node); + + XTS_PARSER (parser)->parent_node = node; + return node; +} + + /* Return true if PARSER is not deleted and its buffer is live. */ static bool treesit_parser_live_p (Lisp_Object parser) @@ -4538,6 +4603,10 @@ applies to LANGUAGE-A will be redirected to LANGUAGE-B instead. */); defsubr (&Streesit_parser_buffer); defsubr (&Streesit_parser_language); defsubr (&Streesit_parser_tag); + defsubr (&Streesit_parser_embed_level); + defsubr (&Streesit_parser_set_embed_level); + defsubr (&Streesit_parser_parent_node); + defsubr (&Streesit_parser_set_parent_node); defsubr (&Streesit_parser_root_node); defsubr (&Streesit_parse_string); diff --git a/src/treesit.h b/src/treesit.h index 19dc28af246..d19a0e76216 100644 --- a/src/treesit.h +++ b/src/treesit.h @@ -63,6 +63,28 @@ struct Lisp_TS_Parser but rather return DEFAULT_RANGE. (A single range where start_byte = 0, end_byte = UINT32_MAX). */ Lisp_Object last_set_ranges; + /* Parsers for embedded code blocks will have a non-zero embed level. + The primary parser has level 0, and each layer of embedded parser + gets +1 level. The embed level can be either a non-negative + integer or nil. Every parser created by treesit-parser-create + starts with a nil level. If the value is nil, that means the range + functions (treesit-update-ranges and friends) haven't touched this + parser yet, and this parser isn't part of the embed parser tree. */ + Lisp_Object embed_level; + /* Some comments: Technically you could calculate embed_level by + following parent_node, but parent_node might be outdated so it's a + good idea to record embed_level separately. Embed_level and + parent_node could have been implemented as "parser properties" with + an obarray, but ultimately I think two explicit fields helps + documentation better and it's not clear to me that a property list + for a parser will be useful beyond this. And we can always convert + these to properties later, but not vice versa. */ + /* When an embedded parser is created, it's usually based on a node in + the host parser. This field saves that node so it possible to + climb up and out of the embedded parser into the host parser. Note + that the range of the embedded parser doesn't have to match that of + the parent node. */ + Lisp_Object parent_node; /* The buffer associated with this parser. */ Lisp_Object buffer; /* The pointer to the tree-sitter parser. Never NULL. */