]> git.eshelyaron.com Git - emacs.git/commitdiff
Allow tree-sitter to notify parse-tree changes
authorYuan Fu <casouri@gmail.com>
Tue, 15 Nov 2022 10:16:11 +0000 (02:16 -0800)
committerYuan Fu <casouri@gmail.com>
Tue, 15 Nov 2022 10:16:11 +0000 (02:16 -0800)
* src/treesit.c (treesit_call_after_change_functions): New function.
(treesit_ensure_parsed): Call treesit_call_after_change_functions
right after re-parse.
(make_treesit_parser): Initialize after_change_functions.
(Ftreesit_parser_notifiers)
(Ftreesit_parser_add_notifier)
(Ftreesit_parser_remove_notifier): New functions.
* src/treesit.h (Lisp_TS_Parser): New field after_change_functions.

src/treesit.c
src/treesit.h

index a70a199cfedd08973ab2937f96fe295313617f89..858148b8496d53f78205701998172707f9b09ad1 100644 (file)
@@ -843,6 +843,30 @@ treesit_check_buffer_size (struct buffer *buffer)
              make_fixnum (buffer_size));
 }
 
+static Lisp_Object
+treesit_make_ranges (const TSRange *, uint32_t, struct buffer *);
+
+static void
+treesit_call_after_change_functions (TSTree *old_tree, TSTree *new_tree,
+                                    Lisp_Object parser)
+{
+  uint32_t len;
+  TSRange *ranges = ts_tree_get_changed_ranges (old_tree, new_tree, &len);
+  struct buffer *buf = XBUFFER (XTS_PARSER (parser)->buffer);
+  Lisp_Object lisp_ranges = treesit_make_ranges(ranges, len, buf);
+  xfree (ranges);
+
+  specpdl_ref count = SPECPDL_INDEX ();
+
+  /* let's trust the after change functions and not clone a new ranges
+     for each of them.  */
+  Lisp_Object functions = XTS_PARSER (parser)->after_change_functions;
+  FOR_EACH_TAIL (functions)
+    safe_call2(XCAR (functions), lisp_ranges, parser);
+
+  unbind_to (count, Qnil);
+}
+
 /* Parse the buffer.  We don't parse until we have to.  When we have
    to, we call this function to parse and update the tree.  */
 static void
@@ -875,7 +899,11 @@ treesit_ensure_parsed (Lisp_Object parser)
     }
 
   if (tree != NULL)
-    ts_tree_delete (tree);
+    {
+      treesit_call_after_change_functions(tree, new_tree, parser);
+      ts_tree_delete (tree);
+    }
+
   XTS_PARSER (parser)->tree = new_tree;
   XTS_PARSER (parser)->need_reparse = false;
 }
@@ -945,6 +973,7 @@ make_treesit_parser (Lisp_Object buffer, TSParser *parser,
                                       buffer, PVEC_TS_PARSER);
 
   lisp_parser->language_symbol = language_symbol;
+  lisp_parser->after_change_functions = Qnil;
   lisp_parser->buffer = buffer;
   lisp_parser->parser = parser;
   lisp_parser->tree = tree;
@@ -1473,6 +1502,57 @@ return nil.  */)
   return treesit_make_ranges (ranges, len, buffer);
 }
 
+DEFUN ("treesit-parser-notifiers",
+       Ftreesit_parser_notifiers,
+       Streesit_parser_notifiers,
+       1, 1, 0,
+       doc: /* Return the after-change functions for PARSER.  */)
+  (Lisp_Object parser)
+{
+  treesit_check_parser (parser);
+
+  Lisp_Object return_list = Qnil;
+  Lisp_Object tail = XTS_PARSER (parser)->after_change_functions;
+  FOR_EACH_TAIL (tail)
+    return_list = Fcons (XCAR (tail), return_list);
+
+  return return_list;
+}
+
+DEFUN ("treesit-parser-add-notifier",
+       Ftreesit_parser_add_notifier,
+       Streesit_parser_add_notifier,
+       2, 2, 0,
+       doc: /* Add FUNCTION to PARSER's after-change notifiers.  */)
+  (Lisp_Object parser, Lisp_Object function)
+{
+  treesit_check_parser (parser);
+  /* For simplicity we don't accept lambda functions.  */
+  CHECK_SYMBOL (function);
+
+  Lisp_Object functions = XTS_PARSER (parser)->after_change_functions;
+  if (!Fmemq (function, functions))
+    XTS_PARSER (parser)->after_change_functions = Fcons (function, functions);
+  return Qnil;
+}
+
+DEFUN ("treesit-parser-remove-notifier",
+       Ftreesit_parser_remove_notifier,
+       Streesit_parser_remove_notifier,
+       2, 2, 0,
+       doc: /* Remove FUNCTION from PARSER's after-change notifiers.  */)
+  (Lisp_Object parser, Lisp_Object function)
+{
+  treesit_check_parser (parser);
+  /* For simplicity we don't accept lambda functions.  */
+  CHECK_SYMBOL (function);
+
+  Lisp_Object functions = XTS_PARSER (parser)->after_change_functions;
+  if (Fmemq (function, functions))
+    XTS_PARSER (parser)->after_change_functions = Fdelq (function, functions);
+  return Qnil;
+}
+
 /*** Node API  */
 
 /* Check that OBJ is a positive integer and signal an error if
@@ -2934,6 +3014,10 @@ then in the system default locations for dynamic libraries, in that order.  */);
   defsubr (&Streesit_parser_set_included_ranges);
   defsubr (&Streesit_parser_included_ranges);
 
+  defsubr (&Streesit_parser_notifiers);
+  defsubr (&Streesit_parser_add_notifier);
+  defsubr (&Streesit_parser_remove_notifier);
+
   defsubr (&Streesit_node_type);
   defsubr (&Streesit_node_start);
   defsubr (&Streesit_node_end);
index 169d8819d7769c4ed524026006fb388776a7a57b..2d2a91cd3665afa28b56b65066cae524b69a2bbd 100644 (file)
@@ -33,6 +33,10 @@ struct Lisp_TS_Parser
   /* A symbol representing the language this parser uses.  See the
      manual for more explanation.  */
   Lisp_Object language_symbol;
+  /* A list of functions to call after re-parse.  Every function is
+     called with the changed ranges and the parser.  The changed
+     ranges is a list of (BEG . END).  */
+  Lisp_Object after_change_functions;
   /* The buffer associated with this parser.  */
   Lisp_Object buffer;
   /* The pointer to the tree-sitter parser.  Never NULL.  */