]> git.eshelyaron.com Git - emacs.git/commitdiff
Add tree-sitter-parser-embed-level and parent-node
authorYuan Fu <casouri@gmail.com>
Thu, 27 Feb 2025 11:05:26 +0000 (03:05 -0800)
committerEshel Yaron <me@eshelyaron.com>
Fri, 28 Feb 2025 11:20:00 +0000 (12:20 +0100)
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)

src/treesit.c
src/treesit.h

index 62606d99749646648dbba71517cce4dca6d2ca37..16308193bf597c91681b57d5f0ab7577513db6bc 100644 (file)
@@ -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);
index 19dc28af2462bdab1817b1074caeca267b7b2fb1..d19a0e76216a4afc0875e41833838a77cc28650c 100644 (file)
@@ -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.  */