diff options
| author | Yuan Fu | 2022-12-07 14:50:16 -0800 |
|---|---|---|
| committer | Yuan Fu | 2022-12-09 17:24:08 -0800 |
| commit | ebef8905b0df9572e80e20fdc8da7829b9270e3f (patch) | |
| tree | 05f3b264babb0edf4d1a4d6985eca3f7aac1ed3d | |
| parent | 8f53fa10d9453f36aa601e5943cb903adeacc7fe (diff) | |
| download | emacs-ebef8905b0df9572e80e20fdc8da7829b9270e3f.tar.gz emacs-ebef8905b0df9572e80e20fdc8da7829b9270e3f.zip | |
Make indirect buffers use tree-sitter parsers of their base buffer
Fix the problem described in bug#59693.
* src/treesit.c (treesit_record_change): Always use the base buffer.
(Ftreesit_parser_create): Always use the base buffer. Also change the
for loop into FOR_EACH_TAIL (stylistic change).
(Ftreesit_parser_list): Always use the base buffer.
* doc/lispref/parsing.texi (Using Parser): Update manual.
* test/src/treesit-tests.el (treesit-indirect-buffer): New test.
| -rw-r--r-- | doc/lispref/parsing.texi | 10 | ||||
| -rw-r--r-- | src/treesit.c | 61 | ||||
| -rw-r--r-- | test/src/treesit-tests.el | 34 |
3 files changed, 87 insertions, 18 deletions
diff --git a/doc/lispref/parsing.texi b/doc/lispref/parsing.texi index 3223875320a..af7be2ebf36 100644 --- a/doc/lispref/parsing.texi +++ b/doc/lispref/parsing.texi | |||
| @@ -409,6 +409,13 @@ Create a parser for the specified @var{buffer} and @var{language} | |||
| 409 | By default, this function reuses a parser if one already exists for | 409 | By default, this function reuses a parser if one already exists for |
| 410 | @var{language} in @var{buffer}, but if @var{no-reuse} is | 410 | @var{language} in @var{buffer}, but if @var{no-reuse} is |
| 411 | non-@code{nil}, this function always creates a new parser. | 411 | non-@code{nil}, this function always creates a new parser. |
| 412 | |||
| 413 | If that buffer is an indirect buffer, its base buffer is used instead. | ||
| 414 | That is, indirect buffers use their base buffer's parsers. If the | ||
| 415 | base buffer is narrowed, an indirect buffer might not be able to | ||
| 416 | retrieve information of the portion of the buffer text that are | ||
| 417 | invisible in the base buffer. Lisp programs should widen as necessary | ||
| 418 | should they want to use a parser in an indirect buffer. | ||
| 412 | @end defun | 419 | @end defun |
| 413 | 420 | ||
| 414 | Given a parser, we can query information about it. | 421 | Given a parser, we can query information about it. |
| @@ -447,7 +454,8 @@ tree incrementally. | |||
| 447 | @defun treesit-parser-list &optional buffer | 454 | @defun treesit-parser-list &optional buffer |
| 448 | This function returns the parser list of @var{buffer}. If | 455 | This function returns the parser list of @var{buffer}. If |
| 449 | @var{buffer} is @code{nil} or omitted, it defaults to the current | 456 | @var{buffer} is @code{nil} or omitted, it defaults to the current |
| 450 | buffer. | 457 | buffer. If that buffer is an indirect buffer, its base buffer is used |
| 458 | instead. That is, indirect buffers use their base buffer's parsers. | ||
| 451 | @end defun | 459 | @end defun |
| 452 | 460 | ||
| 453 | @defun treesit-parser-delete parser | 461 | @defun treesit-parser-delete parser |
diff --git a/src/treesit.c b/src/treesit.c index 8b485ca4ece..d361a3da932 100644 --- a/src/treesit.c +++ b/src/treesit.c | |||
| @@ -384,7 +384,18 @@ init_treesit_functions (void) | |||
| 384 | mysteriously drops. 3) what if a user uses so many stuff that the | 384 | mysteriously drops. 3) what if a user uses so many stuff that the |
| 385 | default cache size (20) is not enough and we end up thrashing? | 385 | default cache size (20) is not enough and we end up thrashing? |
| 386 | These are all imaginary scenarios but they are not impossible | 386 | These are all imaginary scenarios but they are not impossible |
| 387 | :-) */ | 387 | :-) |
| 388 | |||
| 389 | Parsers in indirect buffers: We make indirect buffers to share the | ||
| 390 | parser of its base buffer. Indirect buffers and their base buffer | ||
| 391 | share the same buffer content but not other buffer attributes. If | ||
| 392 | they have separate parser lists, changes made in an indirect buffer | ||
| 393 | will only update parsers of that indirect buffer, and not parsers | ||
| 394 | in the base buffer or other indirect buffers, and vice versa. We | ||
| 395 | could keep track of all the base and indirect buffers, and update | ||
| 396 | all of their parsers, but ultimately decide to take a simpler | ||
| 397 | approach, which is to make indirect buffers share their base | ||
| 398 | buffer's parser list. The discussion can be found in bug#59693. */ | ||
| 388 | 399 | ||
| 389 | 400 | ||
| 390 | /*** Initialization */ | 401 | /*** Initialization */ |
| @@ -697,9 +708,10 @@ void | |||
| 697 | treesit_record_change (ptrdiff_t start_byte, ptrdiff_t old_end_byte, | 708 | treesit_record_change (ptrdiff_t start_byte, ptrdiff_t old_end_byte, |
| 698 | ptrdiff_t new_end_byte) | 709 | ptrdiff_t new_end_byte) |
| 699 | { | 710 | { |
| 700 | Lisp_Object parser_list; | 711 | struct buffer *base_buffer = current_buffer; |
| 701 | 712 | if (current_buffer->base_buffer) | |
| 702 | parser_list = BVAR (current_buffer, ts_parser_list); | 713 | base_buffer = current_buffer->base_buffer; |
| 714 | Lisp_Object parser_list = BVAR (base_buffer, ts_parser_list); | ||
| 703 | 715 | ||
| 704 | FOR_EACH_TAIL_SAFE (parser_list) | 716 | FOR_EACH_TAIL_SAFE (parser_list) |
| 705 | { | 717 | { |
| @@ -1252,12 +1264,16 @@ DEFUN ("treesit-parser-create", | |||
| 1252 | 1, 3, 0, | 1264 | 1, 3, 0, |
| 1253 | doc: /* Create and return a parser in BUFFER for LANGUAGE. | 1265 | doc: /* Create and return a parser in BUFFER for LANGUAGE. |
| 1254 | 1266 | ||
| 1255 | The parser is automatically added to BUFFER's parser list, as | 1267 | The parser is automatically added to BUFFER's parser list, as returned |
| 1256 | returned by `treesit-parser-list'. | 1268 | by `treesit-parser-list'. LANGUAGE is a language symbol. If BUFFER |
| 1257 | LANGUAGE is a language symbol. If BUFFER is nil or omitted, it | 1269 | is nil or omitted, it defaults to the current buffer. If BUFFER |
| 1258 | defaults to the current buffer. If BUFFER already has a parser for | 1270 | already has a parser for LANGUAGE, return that parser, but if NO-REUSE |
| 1259 | LANGUAGE, return that parser, but if NO-REUSE is non-nil, always | 1271 | is non-nil, always create a new parser. |
| 1260 | create a new parser. */) | 1272 | |
| 1273 | If that buffer is an indirect buffer, its base buffer is used instead. | ||
| 1274 | That is, indirect buffers use their base buffer's parsers. Lisp | ||
| 1275 | programs should widen as necessary should they want to use a parser in | ||
| 1276 | an indirect buffer. */) | ||
| 1261 | (Lisp_Object language, Lisp_Object buffer, Lisp_Object no_reuse) | 1277 | (Lisp_Object language, Lisp_Object buffer, Lisp_Object no_reuse) |
| 1262 | { | 1278 | { |
| 1263 | treesit_initialize (); | 1279 | treesit_initialize (); |
| @@ -1271,16 +1287,21 @@ create a new parser. */) | |||
| 1271 | CHECK_BUFFER (buffer); | 1287 | CHECK_BUFFER (buffer); |
| 1272 | buf = XBUFFER (buffer); | 1288 | buf = XBUFFER (buffer); |
| 1273 | } | 1289 | } |
| 1290 | if (buf->base_buffer) | ||
| 1291 | buf = buf->base_buffer; | ||
| 1292 | |||
| 1274 | treesit_check_buffer_size (buf); | 1293 | treesit_check_buffer_size (buf); |
| 1275 | 1294 | ||
| 1276 | /* See if we can reuse a parser. */ | 1295 | /* See if we can reuse a parser. */ |
| 1277 | for (Lisp_Object tail = BVAR (buf, ts_parser_list); | 1296 | if (NILP (no_reuse)) |
| 1278 | NILP (no_reuse) && !NILP (tail); | ||
| 1279 | tail = XCDR (tail)) | ||
| 1280 | { | 1297 | { |
| 1281 | struct Lisp_TS_Parser *parser = XTS_PARSER (XCAR (tail)); | 1298 | Lisp_Object tail = BVAR (buf, ts_parser_list); |
| 1282 | if (EQ (parser->language_symbol, language)) | 1299 | FOR_EACH_TAIL (tail) |
| 1283 | return XCAR (tail); | 1300 | { |
| 1301 | struct Lisp_TS_Parser *parser = XTS_PARSER (XCAR (tail)); | ||
| 1302 | if (EQ (parser->language_symbol, language)) | ||
| 1303 | return XCAR (tail); | ||
| 1304 | } | ||
| 1284 | } | 1305 | } |
| 1285 | 1306 | ||
| 1286 | /* Load language. */ | 1307 | /* Load language. */ |
| @@ -1329,7 +1350,10 @@ DEFUN ("treesit-parser-list", | |||
| 1329 | Ftreesit_parser_list, Streesit_parser_list, | 1350 | Ftreesit_parser_list, Streesit_parser_list, |
| 1330 | 0, 1, 0, | 1351 | 0, 1, 0, |
| 1331 | doc: /* Return BUFFER's parser list. | 1352 | doc: /* Return BUFFER's parser list. |
| 1332 | BUFFER defaults to the current buffer. */) | 1353 | |
| 1354 | BUFFER defaults to the current buffer. If that buffer is an indirect | ||
| 1355 | buffer, its base buffer is used instead. That is, indirect buffers | ||
| 1356 | use their base buffer's parsers. */) | ||
| 1333 | (Lisp_Object buffer) | 1357 | (Lisp_Object buffer) |
| 1334 | { | 1358 | { |
| 1335 | struct buffer *buf; | 1359 | struct buffer *buf; |
| @@ -1340,6 +1364,9 @@ BUFFER defaults to the current buffer. */) | |||
| 1340 | CHECK_BUFFER (buffer); | 1364 | CHECK_BUFFER (buffer); |
| 1341 | buf = XBUFFER (buffer); | 1365 | buf = XBUFFER (buffer); |
| 1342 | } | 1366 | } |
| 1367 | if (buf->base_buffer) | ||
| 1368 | buf = buf->base_buffer; | ||
| 1369 | |||
| 1343 | /* Return a fresh list so messing with that list doesn't affect our | 1370 | /* Return a fresh list so messing with that list doesn't affect our |
| 1344 | internal data. */ | 1371 | internal data. */ |
| 1345 | Lisp_Object return_list = Qnil; | 1372 | Lisp_Object return_list = Qnil; |
diff --git a/test/src/treesit-tests.el b/test/src/treesit-tests.el index aba12759c34..1cc2217bd3b 100644 --- a/test/src/treesit-tests.el +++ b/test/src/treesit-tests.el | |||
| @@ -161,6 +161,40 @@ | |||
| 161 | (should (treesit-node-eq root-node root-node)) | 161 | (should (treesit-node-eq root-node root-node)) |
| 162 | (should (not (treesit-node-eq root-node doc-node)))))) | 162 | (should (not (treesit-node-eq root-node doc-node)))))) |
| 163 | 163 | ||
| 164 | (ert-deftest treesit-indirect-buffer () | ||
| 165 | "Tests for indirect buffers." | ||
| 166 | (skip-unless (treesit-language-available-p 'json)) | ||
| 167 | (let ((base (get-buffer-create "*treesit test*")) | ||
| 168 | parser indirect) | ||
| 169 | (unwind-protect | ||
| 170 | (progn | ||
| 171 | (with-current-buffer base | ||
| 172 | (setq indirect (clone-indirect-buffer "*treesit test 1*" nil))) | ||
| 173 | (with-current-buffer indirect | ||
| 174 | (setq parser (treesit-parser-create 'json))) | ||
| 175 | ;; 1. Parser created in the indirect buffer should be | ||
| 176 | ;; actually be created in the base buffer. | ||
| 177 | (with-current-buffer base | ||
| 178 | (should (equal (list parser) | ||
| 179 | (treesit-parser-list))) | ||
| 180 | (insert "[1,2,3]")) | ||
| 181 | ;; Change in the base buffer should be reflected in the | ||
| 182 | ;; indirect buffer. | ||
| 183 | (with-current-buffer indirect | ||
| 184 | (should (eq (treesit-node-end | ||
| 185 | (treesit-buffer-root-node)) | ||
| 186 | 8)) | ||
| 187 | (erase-buffer)) | ||
| 188 | ;; Change in the indirect buffer should be reflected in the | ||
| 189 | ;; base buffer. | ||
| 190 | (with-current-buffer base | ||
| 191 | (should (eq (treesit-node-end | ||
| 192 | (treesit-buffer-root-node)) | ||
| 193 | 1)) | ||
| 194 | (erase-buffer))) | ||
| 195 | (kill-buffer base) | ||
| 196 | (kill-buffer indirect)))) | ||
| 197 | |||
| 164 | (ert-deftest treesit-query-api () | 198 | (ert-deftest treesit-query-api () |
| 165 | "Tests for query API." | 199 | "Tests for query API." |
| 166 | (skip-unless (treesit-language-available-p 'json)) | 200 | (skip-unless (treesit-language-available-p 'json)) |