From: Yuan Fu Date: Tue, 15 Nov 2022 10:16:11 +0000 (-0800) Subject: Allow tree-sitter to notify parse-tree changes X-Git-Tag: emacs-29.0.90~1668 X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=34e50dc4a23505dce0499f120477e2e1a1327432;p=emacs.git Allow tree-sitter to notify parse-tree changes * 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. --- diff --git a/src/treesit.c b/src/treesit.c index a70a199cfed..858148b8496 100644 --- a/src/treesit.c +++ b/src/treesit.c @@ -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); diff --git a/src/treesit.h b/src/treesit.h index 169d8819d77..2d2a91cd366 100644 --- a/src/treesit.h +++ b/src/treesit.h @@ -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. */