diff options
| -rw-r--r-- | src/buffer.c | 21 | ||||
| -rw-r--r-- | src/buffer.h | 30 | ||||
| -rw-r--r-- | src/casefiddle.c | 13 | ||||
| -rw-r--r-- | src/editfns.c | 44 | ||||
| -rw-r--r-- | src/insdel.c | 55 | ||||
| -rw-r--r-- | src/treesit.c | 433 | ||||
| -rw-r--r-- | src/treesit.h | 23 | ||||
| -rw-r--r-- | test/src/treesit-tests.el | 45 |
8 files changed, 617 insertions, 47 deletions
diff --git a/src/buffer.c b/src/buffer.c index a408b799ff4..e1f138d6f42 100644 --- a/src/buffer.c +++ b/src/buffer.c | |||
| @@ -641,6 +641,13 @@ even if it is dead. The return value is never nil. */) | |||
| 641 | bset_width_table (b, Qnil); | 641 | bset_width_table (b, Qnil); |
| 642 | b->prevent_redisplay_optimizations_p = 1; | 642 | b->prevent_redisplay_optimizations_p = 1; |
| 643 | 643 | ||
| 644 | #ifdef HAVE_TREE_SITTER | ||
| 645 | b->ts_linecol_cache.pos = 1; | ||
| 646 | b->ts_linecol_cache.byte_pos = 1; | ||
| 647 | b->ts_linecol_cache.line = 1; | ||
| 648 | b->ts_linecol_cache.col = 0; | ||
| 649 | #endif | ||
| 650 | |||
| 644 | /* An ordinary buffer normally doesn't need markers | 651 | /* An ordinary buffer normally doesn't need markers |
| 645 | to handle BEGV and ZV. */ | 652 | to handle BEGV and ZV. */ |
| 646 | bset_pt_marker (b, Qnil); | 653 | bset_pt_marker (b, Qnil); |
| @@ -867,6 +874,13 @@ Interactively, CLONE and INHIBIT-BUFFER-HOOKS are nil. */) | |||
| 867 | b->bidi_paragraph_cache = 0; | 874 | b->bidi_paragraph_cache = 0; |
| 868 | bset_width_table (b, Qnil); | 875 | bset_width_table (b, Qnil); |
| 869 | 876 | ||
| 877 | #ifdef HAVE_TREE_SITTER | ||
| 878 | b->ts_linecol_cache.pos = 1; | ||
| 879 | b->ts_linecol_cache.byte_pos = 1; | ||
| 880 | b->ts_linecol_cache.line = 1; | ||
| 881 | b->ts_linecol_cache.col = 0; | ||
| 882 | #endif | ||
| 883 | |||
| 870 | name = Fcopy_sequence (name); | 884 | name = Fcopy_sequence (name); |
| 871 | set_string_intervals (name, NULL); | 885 | set_string_intervals (name, NULL); |
| 872 | bset_name (b, name); | 886 | bset_name (b, name); |
| @@ -2618,6 +2632,11 @@ results, see Info node `(elisp)Swapping Text'. */) | |||
| 2618 | bset_point_before_scroll (current_buffer, Qnil); | 2632 | bset_point_before_scroll (current_buffer, Qnil); |
| 2619 | bset_point_before_scroll (other_buffer, Qnil); | 2633 | bset_point_before_scroll (other_buffer, Qnil); |
| 2620 | 2634 | ||
| 2635 | #ifdef HAVE_TREE_SITTER | ||
| 2636 | swapfield_ (ts_parser_list, Lisp_Object); | ||
| 2637 | swapfield (ts_linecol_cache, struct ts_linecol); | ||
| 2638 | #endif | ||
| 2639 | |||
| 2621 | modiff_incr (¤t_buffer->text->modiff, 1); | 2640 | modiff_incr (¤t_buffer->text->modiff, 1); |
| 2622 | modiff_incr (&other_buffer->text->modiff, 1); | 2641 | modiff_incr (&other_buffer->text->modiff, 1); |
| 2623 | modiff_incr (¤t_buffer->text->chars_modiff, 1); | 2642 | modiff_incr (¤t_buffer->text->chars_modiff, 1); |
| @@ -5019,8 +5038,6 @@ DEFUN ("overlay-tree", Foverlay_tree, Soverlay_tree, 0, 1, 0, | |||
| 5019 | } | 5038 | } |
| 5020 | #endif | 5039 | #endif |
| 5021 | 5040 | ||
| 5022 | |||
| 5023 | |||
| 5024 | /* Initialize the buffer routines. */ | 5041 | /* Initialize the buffer routines. */ |
| 5025 | void | 5042 | void |
| 5026 | syms_of_buffer (void) | 5043 | syms_of_buffer (void) |
diff --git a/src/buffer.h b/src/buffer.h index 5c0a6ab3118..9479b141424 100644 --- a/src/buffer.h +++ b/src/buffer.h | |||
| @@ -220,6 +220,23 @@ extern ptrdiff_t advance_to_char_boundary (ptrdiff_t byte_pos); | |||
| 220 | 220 | ||
| 221 | /* Define the actual buffer data structures. */ | 221 | /* Define the actual buffer data structures. */ |
| 222 | 222 | ||
| 223 | /* This data structure stores the cache of a position and its line and | ||
| 224 | column number. The column number is counted in bytes. The line | ||
| 225 | number and column number don't consider narrowing. */ | ||
| 226 | struct ts_linecol | ||
| 227 | { | ||
| 228 | /* A position in the buffer. */ | ||
| 229 | ptrdiff_t pos; | ||
| 230 | /* The byte position of POS. */ | ||
| 231 | ptrdiff_t byte_pos; | ||
| 232 | /* The line number of this position. */ | ||
| 233 | ptrdiff_t line; | ||
| 234 | /* The column number (in bytes) of this position. Unlike Emacs' | ||
| 235 | convention, this is 0-based (because tree-sitter column is | ||
| 236 | 0-based). Simply the byte offset from BOL (or BOB). */ | ||
| 237 | ptrdiff_t col; | ||
| 238 | }; | ||
| 239 | |||
| 223 | /* This data structure describes the actual text contents of a buffer. | 240 | /* This data structure describes the actual text contents of a buffer. |
| 224 | It is shared between indirect buffers and their base buffer. */ | 241 | It is shared between indirect buffers and their base buffer. */ |
| 225 | 242 | ||
| @@ -700,6 +717,19 @@ struct buffer | |||
| 700 | /* The interval tree containing this buffer's overlays. */ | 717 | /* The interval tree containing this buffer's overlays. */ |
| 701 | struct itree_tree *overlays; | 718 | struct itree_tree *overlays; |
| 702 | 719 | ||
| 720 | /* Right now only tree-sitter makes use of this, so I don't want | ||
| 721 | non-tree-sitter build to pay for it. If something else can make | ||
| 722 | use of this, we can remove the gate. */ | ||
| 723 | #ifdef HAVE_TREE_SITTER | ||
| 724 | /* Cache of line and column number of a position. The position cached | ||
| 725 | is usually near point. Tree-sitter uses this cache to calculate | ||
| 726 | line and column of the beginning and end of buffer edits. This | ||
| 727 | cache is refreshed in buffer edit functions, so it's always | ||
| 728 | up-to-date. Usually, the newly calculated position and line/column | ||
| 729 | are saved to this field. Initialized to position 1. */ | ||
| 730 | struct ts_linecol ts_linecol_cache; | ||
| 731 | #endif | ||
| 732 | |||
| 703 | /* Changes in the buffer are recorded here for undo, and t means | 733 | /* Changes in the buffer are recorded here for undo, and t means |
| 704 | don't record anything. This information belongs to the base | 734 | don't record anything. This information belongs to the base |
| 705 | buffer of an indirect buffer. But we can't store it in the | 735 | buffer of an indirect buffer. But we can't store it in the |
diff --git a/src/casefiddle.c b/src/casefiddle.c index faeb16fb8f2..dba483460ce 100644 --- a/src/casefiddle.c +++ b/src/casefiddle.c | |||
| @@ -543,6 +543,12 @@ casify_region (enum case_action flag, Lisp_Object b, Lisp_Object e) | |||
| 543 | #ifdef HAVE_TREE_SITTER | 543 | #ifdef HAVE_TREE_SITTER |
| 544 | ptrdiff_t start_byte = CHAR_TO_BYTE (start); | 544 | ptrdiff_t start_byte = CHAR_TO_BYTE (start); |
| 545 | ptrdiff_t old_end_byte = CHAR_TO_BYTE (end); | 545 | ptrdiff_t old_end_byte = CHAR_TO_BYTE (end); |
| 546 | struct ts_linecol start_linecol | ||
| 547 | = treesit_linecol_maybe (start, start_byte, | ||
| 548 | current_buffer->ts_linecol_cache); | ||
| 549 | struct ts_linecol old_end_linecol | ||
| 550 | = treesit_linecol_maybe (end, old_end_byte, | ||
| 551 | current_buffer->ts_linecol_cache); | ||
| 546 | #endif | 552 | #endif |
| 547 | 553 | ||
| 548 | ptrdiff_t orig_end = end; | 554 | ptrdiff_t orig_end = end; |
| @@ -565,8 +571,11 @@ casify_region (enum case_action flag, Lisp_Object b, Lisp_Object e) | |||
| 565 | update_compositions (start, end, CHECK_ALL); | 571 | update_compositions (start, end, CHECK_ALL); |
| 566 | } | 572 | } |
| 567 | #ifdef HAVE_TREE_SITTER | 573 | #ifdef HAVE_TREE_SITTER |
| 568 | treesit_record_change (start_byte, old_end_byte, | 574 | ptrdiff_t new_end = orig_end + added; |
| 569 | CHAR_TO_BYTE (orig_end + added)); | 575 | ptrdiff_t new_end_byte = CHAR_TO_BYTE (new_end); |
| 576 | |||
| 577 | treesit_record_change (start_byte, old_end_byte, new_end_byte, | ||
| 578 | start_linecol, old_end_linecol, new_end); | ||
| 570 | #endif | 579 | #endif |
| 571 | 580 | ||
| 572 | return orig_end + added; | 581 | return orig_end + added; |
diff --git a/src/editfns.c b/src/editfns.c index f4e9a022fe2..5d5f4b30015 100644 --- a/src/editfns.c +++ b/src/editfns.c | |||
| @@ -2234,6 +2234,19 @@ Both characters must have the same length of multi-byte form. */) | |||
| 2234 | = !NILP (BVAR (current_buffer, enable_multibyte_characters)); | 2234 | = !NILP (BVAR (current_buffer, enable_multibyte_characters)); |
| 2235 | int fromc, toc; | 2235 | int fromc, toc; |
| 2236 | 2236 | ||
| 2237 | #ifdef HAVE_TREE_SITTER | ||
| 2238 | ptrdiff_t start_char = fix_position (start); | ||
| 2239 | ptrdiff_t old_end_char = fix_position (end); | ||
| 2240 | ptrdiff_t start_byte = CHAR_TO_BYTE (start_char); | ||
| 2241 | ptrdiff_t old_end_byte = CHAR_TO_BYTE (old_end_char); | ||
| 2242 | struct ts_linecol start_linecol | ||
| 2243 | = treesit_linecol_maybe (start_char, start_byte, | ||
| 2244 | current_buffer->ts_linecol_cache); | ||
| 2245 | struct ts_linecol old_end_linecol | ||
| 2246 | = treesit_linecol_maybe (old_end_char, old_end_byte, | ||
| 2247 | current_buffer->ts_linecol_cache); | ||
| 2248 | #endif | ||
| 2249 | |||
| 2237 | restart: | 2250 | restart: |
| 2238 | 2251 | ||
| 2239 | validate_region (&start, &end); | 2252 | validate_region (&start, &end); |
| @@ -2334,7 +2347,8 @@ Both characters must have the same length of multi-byte form. */) | |||
| 2334 | if (changed > 0) | 2347 | if (changed > 0) |
| 2335 | { | 2348 | { |
| 2336 | #ifdef HAVE_TREE_SITTER | 2349 | #ifdef HAVE_TREE_SITTER |
| 2337 | treesit_record_change (changed, last_changed, last_changed); | 2350 | treesit_record_change (start_byte, old_end_byte, old_end_byte, |
| 2351 | start_linecol, old_end_linecol, old_end_char); | ||
| 2338 | #endif | 2352 | #endif |
| 2339 | signal_after_change (changed, | 2353 | signal_after_change (changed, |
| 2340 | last_changed - changed, last_changed - changed); | 2354 | last_changed - changed, last_changed - changed); |
| @@ -2521,6 +2535,14 @@ It returns the number of characters changed. */) | |||
| 2521 | } | 2535 | } |
| 2522 | else | 2536 | else |
| 2523 | { | 2537 | { |
| 2538 | #ifdef HAVE_TREE_SITTER | ||
| 2539 | struct ts_linecol start_linecol | ||
| 2540 | = treesit_linecol_maybe (pos, pos_byte, | ||
| 2541 | current_buffer->ts_linecol_cache); | ||
| 2542 | struct ts_linecol old_end_linecol | ||
| 2543 | = treesit_linecol_maybe (pos + 1, pos_byte + len, | ||
| 2544 | start_linecol); | ||
| 2545 | #endif | ||
| 2524 | record_change (pos, 1); | 2546 | record_change (pos, 1); |
| 2525 | while (str_len-- > 0) | 2547 | while (str_len-- > 0) |
| 2526 | *p++ = *str++; | 2548 | *p++ = *str++; |
| @@ -2528,12 +2550,13 @@ It returns the number of characters changed. */) | |||
| 2528 | update_compositions (pos, pos + 1, CHECK_BORDER); | 2550 | update_compositions (pos, pos + 1, CHECK_BORDER); |
| 2529 | 2551 | ||
| 2530 | #ifdef HAVE_TREE_SITTER | 2552 | #ifdef HAVE_TREE_SITTER |
| 2531 | /* In the previous branch, replace_range() notifies | 2553 | /* In the previous branch, replace_range() notifies |
| 2532 | changes to tree-sitter, but in this branch, we | 2554 | changes to tree-sitter, but in this branch, we |
| 2533 | modified buffer content manually, so we need to | 2555 | modified buffer content manually, so we need to |
| 2534 | notify tree-sitter manually. */ | 2556 | notify tree-sitter manually. */ |
| 2535 | treesit_record_change (pos_byte, pos_byte + len, | 2557 | treesit_record_change (pos_byte, pos_byte + len, |
| 2536 | pos_byte + len); | 2558 | pos_byte + len, start_linecol, |
| 2559 | old_end_linecol, pos + 1); | ||
| 2537 | #endif | 2560 | #endif |
| 2538 | } | 2561 | } |
| 2539 | characters_changed++; | 2562 | characters_changed++; |
| @@ -4481,6 +4504,15 @@ ring. */) | |||
| 4481 | start1_byte = CHAR_TO_BYTE (start1); | 4504 | start1_byte = CHAR_TO_BYTE (start1); |
| 4482 | end2_byte = CHAR_TO_BYTE (end2); | 4505 | end2_byte = CHAR_TO_BYTE (end2); |
| 4483 | 4506 | ||
| 4507 | #ifdef HAVE_TREE_SITTER | ||
| 4508 | struct ts_linecol start_linecol | ||
| 4509 | = treesit_linecol_maybe (start1, start1_byte, | ||
| 4510 | current_buffer->ts_linecol_cache); | ||
| 4511 | struct ts_linecol old_end_linecol | ||
| 4512 | = treesit_linecol_maybe (end2, end2_byte, | ||
| 4513 | current_buffer->ts_linecol_cache); | ||
| 4514 | #endif | ||
| 4515 | |||
| 4484 | /* Make sure the gap won't interfere, by moving it out of the text | 4516 | /* Make sure the gap won't interfere, by moving it out of the text |
| 4485 | we will operate on. */ | 4517 | we will operate on. */ |
| 4486 | if (start1 < gap && gap < end2) | 4518 | if (start1 < gap && gap < end2) |
| @@ -4620,10 +4652,8 @@ ring. */) | |||
| 4620 | } | 4652 | } |
| 4621 | 4653 | ||
| 4622 | #ifdef HAVE_TREE_SITTER | 4654 | #ifdef HAVE_TREE_SITTER |
| 4623 | /* I don't think it's common to transpose two far-apart regions, so | 4655 | treesit_record_change (start1_byte, end2_byte, end2_byte, |
| 4624 | amalgamating the edit into one should be fine. This is what the | 4656 | start_linecol, old_end_linecol, end2); |
| 4625 | signal_after_change below does, too. */ | ||
| 4626 | treesit_record_change (start1_byte, end2_byte, end2_byte); | ||
| 4627 | #endif | 4657 | #endif |
| 4628 | 4658 | ||
| 4629 | signal_after_change (start1, end2 - start1, end2 - start1); | 4659 | signal_after_change (start1, end2 - start1, end2 - start1); |
diff --git a/src/insdel.c b/src/insdel.c index 9b770725971..dd5be1d9ca7 100644 --- a/src/insdel.c +++ b/src/insdel.c | |||
| @@ -891,6 +891,11 @@ insert_1_both (const char *string, | |||
| 891 | if (NILP (BVAR (current_buffer, enable_multibyte_characters))) | 891 | if (NILP (BVAR (current_buffer, enable_multibyte_characters))) |
| 892 | nchars = nbytes; | 892 | nchars = nbytes; |
| 893 | 893 | ||
| 894 | #ifdef HAVE_TREE_SITTER | ||
| 895 | struct ts_linecol start_linecol | ||
| 896 | = treesit_linecol_maybe (PT, PT_BYTE, current_buffer->ts_linecol_cache); | ||
| 897 | #endif | ||
| 898 | |||
| 894 | if (prepare) | 899 | if (prepare) |
| 895 | /* Do this before moving and increasing the gap, | 900 | /* Do this before moving and increasing the gap, |
| 896 | because the before-change hooks might move the gap | 901 | because the before-change hooks might move the gap |
| @@ -945,7 +950,9 @@ insert_1_both (const char *string, | |||
| 945 | #ifdef HAVE_TREE_SITTER | 950 | #ifdef HAVE_TREE_SITTER |
| 946 | eassert (nbytes >= 0); | 951 | eassert (nbytes >= 0); |
| 947 | eassert (PT_BYTE >= 0); | 952 | eassert (PT_BYTE >= 0); |
| 948 | treesit_record_change (PT_BYTE, PT_BYTE, PT_BYTE + nbytes); | 953 | |
| 954 | treesit_record_change (PT_BYTE, PT_BYTE, PT_BYTE + nbytes, | ||
| 955 | start_linecol, start_linecol, PT + nchars); | ||
| 949 | #endif | 956 | #endif |
| 950 | 957 | ||
| 951 | adjust_point (nchars, nbytes); | 958 | adjust_point (nchars, nbytes); |
| @@ -1017,6 +1024,11 @@ insert_from_string_1 (Lisp_Object string, ptrdiff_t pos, ptrdiff_t pos_byte, | |||
| 1017 | = count_size_as_multibyte (SDATA (string) + pos_byte, | 1024 | = count_size_as_multibyte (SDATA (string) + pos_byte, |
| 1018 | nbytes); | 1025 | nbytes); |
| 1019 | 1026 | ||
| 1027 | #ifdef HAVE_TREE_SITTER | ||
| 1028 | struct ts_linecol start_linecol | ||
| 1029 | = treesit_linecol_maybe (PT, PT_BYTE, current_buffer->ts_linecol_cache); | ||
| 1030 | #endif | ||
| 1031 | |||
| 1020 | /* Do this before moving and increasing the gap, | 1032 | /* Do this before moving and increasing the gap, |
| 1021 | because the before-change hooks might move the gap | 1033 | because the before-change hooks might move the gap |
| 1022 | or make it smaller. */ | 1034 | or make it smaller. */ |
| @@ -1081,7 +1093,9 @@ insert_from_string_1 (Lisp_Object string, ptrdiff_t pos, ptrdiff_t pos_byte, | |||
| 1081 | #ifdef HAVE_TREE_SITTER | 1093 | #ifdef HAVE_TREE_SITTER |
| 1082 | eassert (nbytes >= 0); | 1094 | eassert (nbytes >= 0); |
| 1083 | eassert (PT_BYTE >= 0); | 1095 | eassert (PT_BYTE >= 0); |
| 1084 | treesit_record_change (PT_BYTE, PT_BYTE, PT_BYTE + nbytes); | 1096 | |
| 1097 | treesit_record_change (PT_BYTE, PT_BYTE, PT_BYTE + nbytes, | ||
| 1098 | start_linecol, start_linecol, PT + nchars); | ||
| 1085 | #endif | 1099 | #endif |
| 1086 | 1100 | ||
| 1087 | adjust_point (nchars, outgoing_nbytes); | 1101 | adjust_point (nchars, outgoing_nbytes); |
| @@ -1094,7 +1108,8 @@ insert_from_string_1 (Lisp_Object string, ptrdiff_t pos, ptrdiff_t pos_byte, | |||
| 1094 | GPT_ADDR (if not text_at_gap_tail). | 1108 | GPT_ADDR (if not text_at_gap_tail). |
| 1095 | Contrary to insert_from_gap, this does not invalidate any cache, | 1109 | Contrary to insert_from_gap, this does not invalidate any cache, |
| 1096 | nor update any markers, nor record any buffer modification information | 1110 | nor update any markers, nor record any buffer modification information |
| 1097 | of any sort, with the single exception of notifying tree-sitter. */ | 1111 | of any sort, with the single exception of notifying tree-sitter and |
| 1112 | updating tree-sitter linecol cache. */ | ||
| 1098 | void | 1113 | void |
| 1099 | insert_from_gap_1 (ptrdiff_t nchars, ptrdiff_t nbytes, bool text_at_gap_tail) | 1114 | insert_from_gap_1 (ptrdiff_t nchars, ptrdiff_t nbytes, bool text_at_gap_tail) |
| 1100 | { | 1115 | { |
| @@ -1103,6 +1118,8 @@ insert_from_gap_1 (ptrdiff_t nchars, ptrdiff_t nbytes, bool text_at_gap_tail) | |||
| 1103 | 1118 | ||
| 1104 | #ifdef HAVE_TREE_SITTER | 1119 | #ifdef HAVE_TREE_SITTER |
| 1105 | ptrdiff_t ins_bytepos = GPT_BYTE; | 1120 | ptrdiff_t ins_bytepos = GPT_BYTE; |
| 1121 | struct ts_linecol start_linecol | ||
| 1122 | = treesit_linecol_maybe (GPT, GPT_BYTE, current_buffer->ts_linecol_cache); | ||
| 1106 | #endif | 1123 | #endif |
| 1107 | 1124 | ||
| 1108 | GAP_SIZE -= nbytes; | 1125 | GAP_SIZE -= nbytes; |
| @@ -1123,7 +1140,9 @@ insert_from_gap_1 (ptrdiff_t nchars, ptrdiff_t nbytes, bool text_at_gap_tail) | |||
| 1123 | #ifdef HAVE_TREE_SITTER | 1140 | #ifdef HAVE_TREE_SITTER |
| 1124 | eassert (nbytes >= 0); | 1141 | eassert (nbytes >= 0); |
| 1125 | eassert (ins_bytepos >= 0); | 1142 | eassert (ins_bytepos >= 0); |
| 1126 | treesit_record_change (ins_bytepos, ins_bytepos, ins_bytepos + nbytes); | 1143 | |
| 1144 | treesit_record_change (ins_bytepos, ins_bytepos, ins_bytepos + nbytes, | ||
| 1145 | start_linecol, start_linecol, ins_bytepos + nbytes); | ||
| 1127 | #endif | 1146 | #endif |
| 1128 | } | 1147 | } |
| 1129 | 1148 | ||
| @@ -1186,6 +1205,8 @@ insert_from_buffer (struct buffer *buf, | |||
| 1186 | 1205 | ||
| 1187 | #ifdef HAVE_TREE_SITTER | 1206 | #ifdef HAVE_TREE_SITTER |
| 1188 | ptrdiff_t obyte = PT_BYTE; | 1207 | ptrdiff_t obyte = PT_BYTE; |
| 1208 | struct ts_linecol start_linecol | ||
| 1209 | = treesit_linecol_maybe (opoint, obyte, current_buffer->ts_linecol_cache); | ||
| 1189 | #endif | 1210 | #endif |
| 1190 | 1211 | ||
| 1191 | insert_from_buffer_1 (buf, charpos, nchars, inherit); | 1212 | insert_from_buffer_1 (buf, charpos, nchars, inherit); |
| @@ -1196,7 +1217,9 @@ insert_from_buffer (struct buffer *buf, | |||
| 1196 | eassert (PT_BYTE >= BEG_BYTE); | 1217 | eassert (PT_BYTE >= BEG_BYTE); |
| 1197 | eassert (obyte >= BEG_BYTE); | 1218 | eassert (obyte >= BEG_BYTE); |
| 1198 | eassert (PT_BYTE >= obyte); | 1219 | eassert (PT_BYTE >= obyte); |
| 1199 | treesit_record_change (obyte, obyte, PT_BYTE); | 1220 | |
| 1221 | treesit_record_change (obyte, obyte, PT_BYTE, | ||
| 1222 | start_linecol, start_linecol, PT); | ||
| 1200 | #endif | 1223 | #endif |
| 1201 | } | 1224 | } |
| 1202 | 1225 | ||
| @@ -1481,6 +1504,14 @@ replace_range (ptrdiff_t from, ptrdiff_t to, Lisp_Object new, | |||
| 1481 | if (nbytes_del <= 0 && inschars == 0) | 1504 | if (nbytes_del <= 0 && inschars == 0) |
| 1482 | return; | 1505 | return; |
| 1483 | 1506 | ||
| 1507 | #ifdef HAVE_TREE_SITTER | ||
| 1508 | struct ts_linecol start_linecol | ||
| 1509 | = treesit_linecol_maybe (from, from_byte, current_buffer->ts_linecol_cache); | ||
| 1510 | struct ts_linecol old_end_linecol | ||
| 1511 | = treesit_linecol_maybe (to, to_byte, current_buffer->ts_linecol_cache); | ||
| 1512 | #endif | ||
| 1513 | |||
| 1514 | |||
| 1484 | ptrdiff_t insbeg_bytes, insend_bytes; | 1515 | ptrdiff_t insbeg_bytes, insend_bytes; |
| 1485 | ptrdiff_t insbytes; | 1516 | ptrdiff_t insbytes; |
| 1486 | unsigned char *insbeg_ptr; | 1517 | unsigned char *insbeg_ptr; |
| @@ -1623,7 +1654,9 @@ replace_range (ptrdiff_t from, ptrdiff_t to, Lisp_Object new, | |||
| 1623 | eassert (to_byte >= from_byte); | 1654 | eassert (to_byte >= from_byte); |
| 1624 | eassert (outgoing_insbytes >= 0); | 1655 | eassert (outgoing_insbytes >= 0); |
| 1625 | eassert (from_byte >= 0); | 1656 | eassert (from_byte >= 0); |
| 1626 | treesit_record_change (from_byte, to_byte, from_byte + outgoing_insbytes); | 1657 | |
| 1658 | treesit_record_change (from_byte, to_byte, from_byte + outgoing_insbytes, | ||
| 1659 | start_linecol, old_end_linecol, from + inschars); | ||
| 1627 | #endif | 1660 | #endif |
| 1628 | 1661 | ||
| 1629 | /* Relocate point as if it were a marker. */ | 1662 | /* Relocate point as if it were a marker. */ |
| @@ -1948,6 +1981,13 @@ del_range_2 (ptrdiff_t from, ptrdiff_t from_byte, | |||
| 1948 | nchars_del = to - from; | 1981 | nchars_del = to - from; |
| 1949 | nbytes_del = to_byte - from_byte; | 1982 | nbytes_del = to_byte - from_byte; |
| 1950 | 1983 | ||
| 1984 | #ifdef HAVE_TREE_SITTER | ||
| 1985 | struct ts_linecol start_linecol | ||
| 1986 | = treesit_linecol_maybe (from, from_byte, current_buffer->ts_linecol_cache); | ||
| 1987 | struct ts_linecol old_end_linecol | ||
| 1988 | = treesit_linecol_maybe (to, to_byte, current_buffer->ts_linecol_cache); | ||
| 1989 | #endif | ||
| 1990 | |||
| 1951 | /* Make sure the gap is somewhere in or next to what we are deleting. */ | 1991 | /* Make sure the gap is somewhere in or next to what we are deleting. */ |
| 1952 | if (from > GPT) | 1992 | if (from > GPT) |
| 1953 | gap_right (from, from_byte); | 1993 | gap_right (from, from_byte); |
| @@ -2007,7 +2047,8 @@ del_range_2 (ptrdiff_t from, ptrdiff_t from_byte, | |||
| 2007 | #ifdef HAVE_TREE_SITTER | 2047 | #ifdef HAVE_TREE_SITTER |
| 2008 | eassert (from_byte <= to_byte); | 2048 | eassert (from_byte <= to_byte); |
| 2009 | eassert (from_byte >= 0); | 2049 | eassert (from_byte >= 0); |
| 2010 | treesit_record_change (from_byte, to_byte, from_byte); | 2050 | treesit_record_change (from_byte, to_byte, from_byte, |
| 2051 | start_linecol, old_end_linecol, from); | ||
| 2011 | #endif | 2052 | #endif |
| 2012 | 2053 | ||
| 2013 | return deletion; | 2054 | return deletion; |
diff --git a/src/treesit.c b/src/treesit.c index b0979397d35..583a01b7f38 100644 --- a/src/treesit.c +++ b/src/treesit.c | |||
| @@ -864,6 +864,176 @@ loaded or the file name couldn't be determined, return nil. */) | |||
| 864 | } | 864 | } |
| 865 | 865 | ||
| 866 | 866 | ||
| 867 | /*** Linecol functions */ | ||
| 868 | |||
| 869 | #define TREE_SITTER_DEBUG_LINECOL true | ||
| 870 | |||
| 871 | static void | ||
| 872 | treesit_print_linecol (struct ts_linecol linecol) | ||
| 873 | { | ||
| 874 | printf ("{ line=%ld col=%ld pos=%ld byte_pos=%ld }\n", linecol.line, linecol.col, linecol.pos, linecol.byte_pos); | ||
| 875 | } | ||
| 876 | |||
| 877 | static void | ||
| 878 | treesit_validate_linecol (const char *name, struct ts_linecol linecol) | ||
| 879 | { | ||
| 880 | eassert (linecol.pos <= Z); | ||
| 881 | |||
| 882 | if (linecol.pos > Z) | ||
| 883 | { | ||
| 884 | printf ("OUT OF RANGE\n"); | ||
| 885 | treesit_print_linecol (linecol); | ||
| 886 | printf ("Z: %ld\n", Z); | ||
| 887 | } | ||
| 888 | |||
| 889 | ptrdiff_t true_line_count = count_lines (BEG, linecol.byte_pos) + 1; | ||
| 890 | eassert (true_line_count == linecol.line); | ||
| 891 | |||
| 892 | if (true_line_count != linecol.line) | ||
| 893 | { | ||
| 894 | printf ("MISMATCH\n"); | ||
| 895 | printf ("%s: ", name); | ||
| 896 | treesit_print_linecol (linecol); | ||
| 897 | printf ("true: %ld\n", true_line_count); | ||
| 898 | } | ||
| 899 | } | ||
| 900 | |||
| 901 | /* Calculate and return the line and column number of BYTE_POS by | ||
| 902 | scanning newlines from CACHE. CACHE must be valid. */ | ||
| 903 | static struct ts_linecol | ||
| 904 | treesit_linecol_of_pos (ptrdiff_t target_pos, ptrdiff_t target_byte_pos, | ||
| 905 | struct ts_linecol cache) | ||
| 906 | { | ||
| 907 | eassert (target_pos == target_byte_pos); | ||
| 908 | |||
| 909 | if (TREE_SITTER_DEBUG_LINECOL) | ||
| 910 | { | ||
| 911 | /* eassert (true_line_count == cache.line); */ | ||
| 912 | treesit_validate_linecol ("cache", cache); | ||
| 913 | } | ||
| 914 | |||
| 915 | /* When we finished searching for newlines between CACHE and | ||
| 916 | TARGET_POS, BYTE_POS_2 is at TARGET_POS, and BYTE_POS_1 is at the | ||
| 917 | previous newline. If TARGET_POS happends to be on a newline, | ||
| 918 | BYTE_POS_1 will be on that position. BYTE_POS_1 is used for | ||
| 919 | calculating the column. (If CACHE and TARGET_POS are in the same | ||
| 920 | line, BYTE_POS_1 is unset and we don't use it.) */ | ||
| 921 | ptrdiff_t byte_pos_1 = 0; | ||
| 922 | ptrdiff_t pos_2 = 0; | ||
| 923 | ptrdiff_t byte_pos_2 = 0; | ||
| 924 | /* Number of lines between CACHE and TARGET_POS. */ | ||
| 925 | ptrdiff_t line_delta = 0; | ||
| 926 | |||
| 927 | if (target_byte_pos == cache.byte_pos) | ||
| 928 | return cache; | ||
| 929 | |||
| 930 | /* Search forward. */ | ||
| 931 | if (cache.byte_pos < target_byte_pos) | ||
| 932 | { | ||
| 933 | pos_2 = cache.pos; | ||
| 934 | byte_pos_2 = cache.byte_pos; | ||
| 935 | while (byte_pos_2 < target_byte_pos) | ||
| 936 | { | ||
| 937 | ptrdiff_t counted = 0; | ||
| 938 | pos_2 = find_newline (pos_2, byte_pos_2, target_pos, target_byte_pos, | ||
| 939 | 1, &counted, &byte_pos_2, false); | ||
| 940 | |||
| 941 | if (counted > 0) byte_pos_1 = byte_pos_2; | ||
| 942 | line_delta += counted; | ||
| 943 | /* printf ("byte_pos_2=%ld counted=%ld line_delta=%ld\n", byte_pos_2, counted, line_delta); */ | ||
| 944 | } | ||
| 945 | eassert (byte_pos_2 == target_byte_pos); | ||
| 946 | /* At this point, byte_pos_2 is at target_pos, and byte_pos_1 is | ||
| 947 | at the previous newline if we went across any. */ | ||
| 948 | |||
| 949 | struct ts_linecol target_linecol; | ||
| 950 | target_linecol.pos = target_pos; | ||
| 951 | target_linecol.byte_pos = target_byte_pos; | ||
| 952 | target_linecol.line = cache.line + line_delta; | ||
| 953 | /* If we moved across any newline, use the previous newline to | ||
| 954 | calculate the column; if we stayed at the same line, use the | ||
| 955 | cached column to calculate the new column. */ | ||
| 956 | target_linecol.col = line_delta > 0 | ||
| 957 | ? target_byte_pos - byte_pos_1 | ||
| 958 | : target_byte_pos - cache.byte_pos + cache.col; | ||
| 959 | |||
| 960 | if (TREE_SITTER_DEBUG_LINECOL) | ||
| 961 | { | ||
| 962 | /* eassert (true_line_count == target_linecol.line); */ | ||
| 963 | treesit_validate_linecol ("target", target_linecol); | ||
| 964 | } | ||
| 965 | |||
| 966 | return target_linecol; | ||
| 967 | } | ||
| 968 | |||
| 969 | /* Search backward. */ | ||
| 970 | printf ("BACK\n"); | ||
| 971 | pos_2 = cache.pos + 1; | ||
| 972 | /* The "+1" Cancels out with the "-1" in the first iteration. */ | ||
| 973 | byte_pos_2 = cache.byte_pos + 1; | ||
| 974 | while (byte_pos_2 > target_byte_pos) | ||
| 975 | { | ||
| 976 | ptrdiff_t counted = 0; | ||
| 977 | /* pos_2 - 1 won't underflow because of the loop condition. */ | ||
| 978 | pos_2 = find_newline (pos_2 - 1, byte_pos_2 - 1, | ||
| 979 | target_pos, target_byte_pos, | ||
| 980 | -1, &counted, &byte_pos_2, false); | ||
| 981 | line_delta += counted; | ||
| 982 | } | ||
| 983 | eassert (byte_pos_2 == target_byte_pos); | ||
| 984 | /* At this point, pos_2 is at target_pos. */ | ||
| 985 | |||
| 986 | struct ts_linecol target_linecol; | ||
| 987 | target_linecol.pos = target_pos; | ||
| 988 | target_linecol.byte_pos = target_byte_pos; | ||
| 989 | target_linecol.line = cache.line + line_delta; | ||
| 990 | eassert (cache.line + line_delta > 0); | ||
| 991 | |||
| 992 | /* Calculate the column. */ | ||
| 993 | if (line_delta == 0) | ||
| 994 | { | ||
| 995 | target_linecol.col = cache.col - (cache.byte_pos - target_byte_pos); | ||
| 996 | } | ||
| 997 | else | ||
| 998 | { | ||
| 999 | /* We need to find the previous newline in order to calculate the | ||
| 1000 | column. Use POS_2 instead of POS_2 - 1, this way, if POS_2 is | ||
| 1001 | at BOL, we stay in the same place. */ | ||
| 1002 | pos_2 = find_newline (pos_2, byte_pos_2, BEG, BEG_BYTE, -1, | ||
| 1003 | NULL, &byte_pos_2, false); | ||
| 1004 | target_linecol.col = target_byte_pos - byte_pos_2; | ||
| 1005 | } | ||
| 1006 | |||
| 1007 | if (TREE_SITTER_DEBUG_LINECOL) | ||
| 1008 | { | ||
| 1009 | treesit_validate_linecol ("target", target_linecol); | ||
| 1010 | } | ||
| 1011 | |||
| 1012 | return target_linecol; | ||
| 1013 | } | ||
| 1014 | |||
| 1015 | /* Return a TSPoint given POS and VISIBLE_BEG. VISIBLE_BEG must be | ||
| 1016 | before POS. */ | ||
| 1017 | static TSPoint | ||
| 1018 | treesit_make_ts_point (struct ts_linecol visible_beg, | ||
| 1019 | struct ts_linecol pos) | ||
| 1020 | { | ||
| 1021 | TSPoint point; | ||
| 1022 | if (visible_beg.line == pos.line) | ||
| 1023 | { | ||
| 1024 | point.row = 0; | ||
| 1025 | point.column = pos.col - visible_beg.col; | ||
| 1026 | eassert (point.column >= 0); | ||
| 1027 | } | ||
| 1028 | else | ||
| 1029 | { | ||
| 1030 | point.row = pos.line - visible_beg.line; | ||
| 1031 | eassert (point.row > 0); | ||
| 1032 | point.column = pos.col; | ||
| 1033 | } | ||
| 1034 | return point; | ||
| 1035 | } | ||
| 1036 | |||
| 867 | /*** Parsing functions */ | 1037 | /*** Parsing functions */ |
| 868 | 1038 | ||
| 869 | static void | 1039 | static void |
| @@ -879,28 +1049,34 @@ treesit_check_parser (Lisp_Object obj) | |||
| 879 | larger than UINT32_MAX. */ | 1049 | larger than UINT32_MAX. */ |
| 880 | static inline void | 1050 | static inline void |
| 881 | treesit_tree_edit_1 (TSTree *tree, ptrdiff_t start_byte, | 1051 | treesit_tree_edit_1 (TSTree *tree, ptrdiff_t start_byte, |
| 882 | ptrdiff_t old_end_byte, ptrdiff_t new_end_byte) | 1052 | ptrdiff_t old_end_byte, ptrdiff_t new_end_byte, |
| 1053 | TSPoint start_point, TSPoint old_end_point, | ||
| 1054 | TSPoint new_end_point) | ||
| 883 | { | 1055 | { |
| 884 | eassert (start_byte >= 0); | 1056 | eassert (start_byte >= 0); |
| 885 | eassert (start_byte <= old_end_byte); | 1057 | eassert (start_byte <= old_end_byte); |
| 886 | eassert (start_byte <= new_end_byte); | 1058 | eassert (start_byte <= new_end_byte); |
| 887 | TSPoint dummy_point = {0, 0}; | ||
| 888 | eassert (start_byte <= UINT32_MAX); | 1059 | eassert (start_byte <= UINT32_MAX); |
| 889 | eassert (old_end_byte <= UINT32_MAX); | 1060 | eassert (old_end_byte <= UINT32_MAX); |
| 890 | eassert (new_end_byte <= UINT32_MAX); | 1061 | eassert (new_end_byte <= UINT32_MAX); |
| 891 | TSInputEdit edit = {(uint32_t) start_byte, | 1062 | TSInputEdit edit = {(uint32_t) start_byte, |
| 892 | (uint32_t) old_end_byte, | 1063 | (uint32_t) old_end_byte, |
| 893 | (uint32_t) new_end_byte, | 1064 | (uint32_t) new_end_byte, |
| 894 | dummy_point, dummy_point, dummy_point}; | 1065 | start_point, old_end_point, new_end_point}; |
| 895 | ts_tree_edit (tree, &edit); | 1066 | ts_tree_edit (tree, &edit); |
| 896 | } | 1067 | } |
| 897 | 1068 | ||
| 898 | /* Update each parser's tree after the user made an edit. This | 1069 | /* Update each parser's tree after the user made an edit. This function |
| 899 | function does not parse the buffer and only updates the tree, so it | 1070 | does not parse the buffer and only updates the tree, so it should be |
| 900 | should be very fast. */ | 1071 | very fast. If the caller knows there's no parser in the current |
| 901 | void | 1072 | buffer, they can pass empty linecol for |
| 902 | treesit_record_change (ptrdiff_t start_byte, ptrdiff_t old_end_byte, | 1073 | START/OLD_END/NEW_END_linecol. */ |
| 903 | ptrdiff_t new_end_byte) | 1074 | static void |
| 1075 | treesit_record_change_1 (ptrdiff_t start_byte, ptrdiff_t old_end_byte, | ||
| 1076 | ptrdiff_t new_end_byte, | ||
| 1077 | struct ts_linecol start_linecol, | ||
| 1078 | struct ts_linecol old_end_linecol, | ||
| 1079 | struct ts_linecol new_end_linecol) | ||
| 904 | { | 1080 | { |
| 905 | struct buffer *base_buffer = current_buffer; | 1081 | struct buffer *base_buffer = current_buffer; |
| 906 | if (current_buffer->base_buffer) | 1082 | if (current_buffer->base_buffer) |
| @@ -920,12 +1096,15 @@ treesit_record_change (ptrdiff_t start_byte, ptrdiff_t old_end_byte, | |||
| 920 | { | 1096 | { |
| 921 | eassert (start_byte <= old_end_byte); | 1097 | eassert (start_byte <= old_end_byte); |
| 922 | eassert (start_byte <= new_end_byte); | 1098 | eassert (start_byte <= new_end_byte); |
| 923 | /* Think the recorded change as a delete followed by an | 1099 | /* Before sending the edit to tree-sitter, we need to first |
| 924 | insert, and think of them as moving unchanged text back | 1100 | clip the beg/end to visible_beg and visible_end of the |
| 925 | and forth. After all, the whole point of updating the | 1101 | parser. A tip for understanding the code below: think the |
| 926 | tree is to update the position of unchanged text. */ | 1102 | recorded change as a delete followed by an insert, and |
| 927 | ptrdiff_t visible_beg = XTS_PARSER (lisp_parser)->visible_beg; | 1103 | think of them as moving unchanged text back and forth. |
| 928 | ptrdiff_t visible_end = XTS_PARSER (lisp_parser)->visible_end; | 1104 | After all, the whole point of updating the tree is to |
| 1105 | update the position of unchanged text. */ | ||
| 1106 | const ptrdiff_t visible_beg = XTS_PARSER (lisp_parser)->visible_beg; | ||
| 1107 | const ptrdiff_t visible_end = XTS_PARSER (lisp_parser)->visible_end; | ||
| 929 | eassert (visible_beg >= 0); | 1108 | eassert (visible_beg >= 0); |
| 930 | eassert (visible_beg <= visible_end); | 1109 | eassert (visible_beg <= visible_end); |
| 931 | 1110 | ||
| @@ -949,9 +1128,9 @@ treesit_record_change (ptrdiff_t start_byte, ptrdiff_t old_end_byte, | |||
| 949 | eassert (start_offset <= old_end_offset); | 1128 | eassert (start_offset <= old_end_offset); |
| 950 | eassert (start_offset <= new_end_offset); | 1129 | eassert (start_offset <= new_end_offset); |
| 951 | 1130 | ||
| 952 | treesit_tree_edit_1 (tree, start_offset, old_end_offset, | 1131 | /* We have the correct offset for start/end now, but don't |
| 953 | new_end_offset); | 1132 | update the tree yet, because we still need to calculate the |
| 954 | XTS_PARSER (lisp_parser)->need_reparse = true; | 1133 | TSPoint, which needs the updated visible_beg linecol. */ |
| 955 | 1134 | ||
| 956 | /* VISIBLE_BEG/END records tree-sitter's range of view in | 1135 | /* VISIBLE_BEG/END records tree-sitter's range of view in |
| 957 | the buffer. We need to adjust them when tree-sitter's | 1136 | the buffer. We need to adjust them when tree-sitter's |
| @@ -966,19 +1145,99 @@ treesit_record_change (ptrdiff_t start_byte, ptrdiff_t old_end_byte, | |||
| 966 | visi_beg_delta = (old_end_byte < visible_beg | 1145 | visi_beg_delta = (old_end_byte < visible_beg |
| 967 | ? new_end_byte - old_end_byte : 0); | 1146 | ? new_end_byte - old_end_byte : 0); |
| 968 | 1147 | ||
| 969 | XTS_PARSER (lisp_parser)->visible_beg = visible_beg + visi_beg_delta; | 1148 | ptrdiff_t old_visi_beg = visible_beg; |
| 970 | XTS_PARSER (lisp_parser)->visible_end = (visible_end | 1149 | struct ts_linecol old_visi_beg_linecol |
| 971 | + visi_beg_delta | 1150 | = XTS_PARSER (lisp_parser)->visi_beg_linecol; |
| 972 | + (new_end_offset | 1151 | /* struct ts_linecol old_visi_end_linecol */ |
| 973 | - old_end_offset)); | 1152 | /* = XTS_PARSER (lisp_parser)->visi_end_linecol; */ |
| 1153 | |||
| 1154 | const ptrdiff_t new_visible_beg = visible_beg + visi_beg_delta; | ||
| 1155 | const ptrdiff_t new_visible_end | ||
| 1156 | = (visible_end + visi_beg_delta | ||
| 1157 | + (new_end_offset - old_end_offset)); | ||
| 1158 | |||
| 1159 | XTS_PARSER (lisp_parser)->visible_beg = new_visible_beg; | ||
| 1160 | XTS_PARSER (lisp_parser)->visible_end = new_visible_end; | ||
| 1161 | XTS_PARSER (lisp_parser)->visi_beg_linecol | ||
| 1162 | = treesit_linecol_of_pos (BYTE_TO_CHAR (new_visible_beg), | ||
| 1163 | new_visible_beg, | ||
| 1164 | old_visi_beg <= start_byte | ||
| 1165 | ? old_visi_beg_linecol | ||
| 1166 | : start_linecol); | ||
| 1167 | /* FIXME: computing visi_end_linecol from new_end_linecol | ||
| 1168 | could be expensive, we need a more efficient way to compute | ||
| 1169 | it. */ | ||
| 1170 | XTS_PARSER (lisp_parser)->visi_end_linecol | ||
| 1171 | = treesit_linecol_of_pos (BYTE_TO_CHAR (new_visible_end), | ||
| 1172 | new_visible_end, | ||
| 1173 | new_end_linecol); | ||
| 974 | 1174 | ||
| 975 | eassert (XTS_PARSER (lisp_parser)->visible_beg >= 0); | 1175 | eassert (XTS_PARSER (lisp_parser)->visible_beg >= 0); |
| 976 | eassert (XTS_PARSER (lisp_parser)->visible_beg | 1176 | eassert (XTS_PARSER (lisp_parser)->visible_beg |
| 977 | <= XTS_PARSER (lisp_parser)->visible_end); | 1177 | <= XTS_PARSER (lisp_parser)->visible_end); |
| 1178 | |||
| 1179 | /* Now, calculate TSPoints and finally update the tree. */ | ||
| 1180 | struct ts_linecol new_begv_linecol | ||
| 1181 | = XTS_PARSER (lisp_parser)->visi_beg_linecol; | ||
| 1182 | TSPoint old_end_point = treesit_make_ts_point (old_visi_beg_linecol, | ||
| 1183 | old_end_linecol); | ||
| 1184 | TSPoint start_point = treesit_make_ts_point (new_begv_linecol, | ||
| 1185 | start_linecol); | ||
| 1186 | TSPoint new_end_point = treesit_make_ts_point (new_begv_linecol, | ||
| 1187 | new_end_linecol); | ||
| 1188 | |||
| 1189 | treesit_tree_edit_1 (tree, start_offset, old_end_offset, | ||
| 1190 | new_end_offset, start_point, old_end_point, | ||
| 1191 | new_end_point); | ||
| 1192 | XTS_PARSER (lisp_parser)->need_reparse = true; | ||
| 978 | } | 1193 | } |
| 979 | } | 1194 | } |
| 980 | } | 1195 | } |
| 981 | 1196 | ||
| 1197 | /* Return the linecol of POS, calculated from CACHE. But if there's no | ||
| 1198 | parser in the current buffer, skip calculation and return an empty | ||
| 1199 | linecol instead. */ | ||
| 1200 | struct ts_linecol | ||
| 1201 | treesit_linecol_maybe (ptrdiff_t pos, ptrdiff_t pos_byte, | ||
| 1202 | struct ts_linecol cache) | ||
| 1203 | { | ||
| 1204 | if (NILP (BVAR (current_buffer, ts_parser_list))) | ||
| 1205 | return TREESIT_EMPTY_LINECOL; | ||
| 1206 | |||
| 1207 | return treesit_linecol_of_pos (pos, pos_byte, cache); | ||
| 1208 | } | ||
| 1209 | |||
| 1210 | /* Update each parser's tree after the user made an edit. This function | ||
| 1211 | does not parse the buffer and only updates the tree, so it should be | ||
| 1212 | very fast. | ||
| 1213 | |||
| 1214 | This is a wrapper over treesit_record_change that does a bit more | ||
| 1215 | boilerplate work: it (optionally) calculates linecol for new_end, | ||
| 1216 | pass all the positions into treesit_record_change_1 which does the | ||
| 1217 | real work, and finally (optionally) sets buffer's linecol cache to | ||
| 1218 | new_end's linecol. | ||
| 1219 | |||
| 1220 | If NEW_END is next to NEW_END_BYTE in the arglist, caller might | ||
| 1221 | accidentally swap them, so I placed NEW_END at the end of the | ||
| 1222 | arglist. */ | ||
| 1223 | void | ||
| 1224 | treesit_record_change (ptrdiff_t start_byte, ptrdiff_t old_end_byte, | ||
| 1225 | ptrdiff_t new_end_byte, | ||
| 1226 | struct ts_linecol start_linecol, | ||
| 1227 | struct ts_linecol old_end_linecol, | ||
| 1228 | ptrdiff_t new_end) | ||
| 1229 | { | ||
| 1230 | struct ts_linecol new_end_linecol | ||
| 1231 | = treesit_linecol_maybe (new_end, new_end_byte, start_linecol); | ||
| 1232 | |||
| 1233 | treesit_record_change_1 (start_byte, old_end_byte, new_end_byte, | ||
| 1234 | start_linecol, old_end_linecol, new_end_linecol); | ||
| 1235 | |||
| 1236 | treesit_print_linecol (new_end_linecol); | ||
| 1237 | if (new_end_linecol.pos != 0) | ||
| 1238 | current_buffer->ts_linecol_cache = new_end_linecol; | ||
| 1239 | } | ||
| 1240 | |||
| 982 | static TSRange *treesit_make_ts_ranges (Lisp_Object, Lisp_Object, | 1241 | static TSRange *treesit_make_ts_ranges (Lisp_Object, Lisp_Object, |
| 983 | uint32_t *); | 1242 | uint32_t *); |
| 984 | 1243 | ||
| @@ -1046,6 +1305,7 @@ treesit_sync_visible_region (Lisp_Object parser) | |||
| 1046 | 1305 | ||
| 1047 | ptrdiff_t visible_beg = XTS_PARSER (parser)->visible_beg; | 1306 | ptrdiff_t visible_beg = XTS_PARSER (parser)->visible_beg; |
| 1048 | ptrdiff_t visible_end = XTS_PARSER (parser)->visible_end; | 1307 | ptrdiff_t visible_end = XTS_PARSER (parser)->visible_end; |
| 1308 | |||
| 1049 | eassert (0 <= visible_beg); | 1309 | eassert (0 <= visible_beg); |
| 1050 | eassert (visible_beg <= visible_end); | 1310 | eassert (visible_beg <= visible_end); |
| 1051 | 1311 | ||
| @@ -1066,39 +1326,72 @@ treesit_sync_visible_region (Lisp_Object parser) | |||
| 1066 | from ________|xxxx|__ | 1326 | from ________|xxxx|__ |
| 1067 | to |xxxx|__________ */ | 1327 | to |xxxx|__________ */ |
| 1068 | 1328 | ||
| 1329 | struct ts_linecol buffer_linecol_cache = buffer->ts_linecol_cache; | ||
| 1330 | struct ts_linecol visi_beg_linecol = XTS_PARSER (parser)->visi_beg_linecol; | ||
| 1331 | struct ts_linecol visi_end_linecol = XTS_PARSER (parser)->visi_end_linecol; | ||
| 1332 | struct ts_linecol buffer_begv_linecol | ||
| 1333 | = treesit_linecol_of_pos (BUF_BEGV (buffer), BUF_BEGV_BYTE (buffer), | ||
| 1334 | visi_beg_linecol); | ||
| 1335 | struct ts_linecol buffer_zv_linecol | ||
| 1336 | = treesit_linecol_of_pos (BUF_ZV (buffer), BUF_ZV_BYTE (buffer), | ||
| 1337 | buffer_linecol_cache); | ||
| 1338 | eassert (visi_beg_linecol.byte_pos == visible_beg); | ||
| 1339 | |||
| 1069 | /* 1. Make sure visible_beg <= BUF_BEGV_BYTE. */ | 1340 | /* 1. Make sure visible_beg <= BUF_BEGV_BYTE. */ |
| 1070 | if (visible_beg > BUF_BEGV_BYTE (buffer)) | 1341 | if (visible_beg > BUF_BEGV_BYTE (buffer)) |
| 1071 | { | 1342 | { |
| 1343 | TSPoint new_end = treesit_make_ts_point (buffer_begv_linecol, | ||
| 1344 | visi_beg_linecol); | ||
| 1072 | /* Tree-sitter sees: insert at the beginning. */ | 1345 | /* Tree-sitter sees: insert at the beginning. */ |
| 1073 | treesit_tree_edit_1 (tree, 0, 0, visible_beg - BUF_BEGV_BYTE (buffer)); | 1346 | treesit_tree_edit_1 (tree, 0, 0, visible_beg - BUF_BEGV_BYTE (buffer), |
| 1347 | TREESIT_TS_POINT_1_0, TREESIT_TS_POINT_1_0, | ||
| 1348 | new_end); | ||
| 1074 | visible_beg = BUF_BEGV_BYTE (buffer); | 1349 | visible_beg = BUF_BEGV_BYTE (buffer); |
| 1350 | visi_beg_linecol = buffer_begv_linecol; | ||
| 1075 | eassert (visible_beg <= visible_end); | 1351 | eassert (visible_beg <= visible_end); |
| 1076 | } | 1352 | } |
| 1077 | /* 2. Make sure visible_end = BUF_ZV_BYTE. */ | 1353 | /* 2. Make sure visible_end = BUF_ZV_BYTE. */ |
| 1078 | if (visible_end < BUF_ZV_BYTE (buffer)) | 1354 | if (visible_end < BUF_ZV_BYTE (buffer)) |
| 1079 | { | 1355 | { |
| 1356 | TSPoint start = treesit_make_ts_point (visi_beg_linecol, | ||
| 1357 | visi_end_linecol); | ||
| 1358 | TSPoint new_end = treesit_make_ts_point (visi_beg_linecol, | ||
| 1359 | buffer_zv_linecol); | ||
| 1080 | /* Tree-sitter sees: insert at the end. */ | 1360 | /* Tree-sitter sees: insert at the end. */ |
| 1081 | treesit_tree_edit_1 (tree, visible_end - visible_beg, | 1361 | treesit_tree_edit_1 (tree, visible_end - visible_beg, |
| 1082 | visible_end - visible_beg, | 1362 | visible_end - visible_beg, |
| 1083 | BUF_ZV_BYTE (buffer) - visible_beg); | 1363 | BUF_ZV_BYTE (buffer) - visible_beg, |
| 1364 | start, start, new_end); | ||
| 1084 | visible_end = BUF_ZV_BYTE (buffer); | 1365 | visible_end = BUF_ZV_BYTE (buffer); |
| 1366 | visi_end_linecol = buffer_zv_linecol; | ||
| 1085 | eassert (visible_beg <= visible_end); | 1367 | eassert (visible_beg <= visible_end); |
| 1086 | } | 1368 | } |
| 1087 | else if (visible_end > BUF_ZV_BYTE (buffer)) | 1369 | else if (visible_end > BUF_ZV_BYTE (buffer)) |
| 1088 | { | 1370 | { |
| 1371 | TSPoint start = treesit_make_ts_point (visi_beg_linecol, | ||
| 1372 | buffer_zv_linecol); | ||
| 1373 | TSPoint old_end = treesit_make_ts_point (visi_beg_linecol, | ||
| 1374 | visi_end_linecol); | ||
| 1089 | /* Tree-sitter sees: delete at the end. */ | 1375 | /* Tree-sitter sees: delete at the end. */ |
| 1090 | treesit_tree_edit_1 (tree, BUF_ZV_BYTE (buffer) - visible_beg, | 1376 | treesit_tree_edit_1 (tree, BUF_ZV_BYTE (buffer) - visible_beg, |
| 1091 | visible_end - visible_beg, | 1377 | visible_end - visible_beg, |
| 1092 | BUF_ZV_BYTE (buffer) - visible_beg); | 1378 | BUF_ZV_BYTE (buffer) - visible_beg, |
| 1379 | start, old_end, start); | ||
| 1093 | visible_end = BUF_ZV_BYTE (buffer); | 1380 | visible_end = BUF_ZV_BYTE (buffer); |
| 1381 | visi_end_linecol = buffer_zv_linecol; | ||
| 1094 | eassert (visible_beg <= visible_end); | 1382 | eassert (visible_beg <= visible_end); |
| 1095 | } | 1383 | } |
| 1096 | /* 3. Make sure visible_beg = BUF_BEGV_BYTE. */ | 1384 | /* 3. Make sure visible_beg = BUF_BEGV_BYTE. */ |
| 1097 | if (visible_beg < BUF_BEGV_BYTE (buffer)) | 1385 | if (visible_beg < BUF_BEGV_BYTE (buffer)) |
| 1098 | { | 1386 | { |
| 1387 | TSPoint old_end = treesit_make_ts_point (visi_beg_linecol, | ||
| 1388 | buffer_begv_linecol); | ||
| 1099 | /* Tree-sitter sees: delete at the beginning. */ | 1389 | /* Tree-sitter sees: delete at the beginning. */ |
| 1100 | treesit_tree_edit_1 (tree, 0, BUF_BEGV_BYTE (buffer) - visible_beg, 0); | 1390 | treesit_tree_edit_1 (tree, 0, BUF_BEGV_BYTE (buffer) - visible_beg, 0, |
| 1391 | TREESIT_TS_POINT_1_0, old_end, | ||
| 1392 | TREESIT_TS_POINT_1_0); | ||
| 1101 | visible_beg = BUF_BEGV_BYTE (buffer); | 1393 | visible_beg = BUF_BEGV_BYTE (buffer); |
| 1394 | visi_beg_linecol = buffer_begv_linecol; | ||
| 1102 | eassert (visible_beg <= visible_end); | 1395 | eassert (visible_beg <= visible_end); |
| 1103 | } | 1396 | } |
| 1104 | eassert (0 <= visible_beg); | 1397 | eassert (0 <= visible_beg); |
| @@ -1108,6 +1401,8 @@ treesit_sync_visible_region (Lisp_Object parser) | |||
| 1108 | 1401 | ||
| 1109 | XTS_PARSER (parser)->visible_beg = visible_beg; | 1402 | XTS_PARSER (parser)->visible_beg = visible_beg; |
| 1110 | XTS_PARSER (parser)->visible_end = visible_end; | 1403 | XTS_PARSER (parser)->visible_end = visible_end; |
| 1404 | XTS_PARSER (parser)->visi_beg_linecol = visi_beg_linecol; | ||
| 1405 | XTS_PARSER (parser)->visi_end_linecol = visi_end_linecol; | ||
| 1111 | 1406 | ||
| 1112 | /* Fix ranges so that the ranges stays with in visible_end. Here we | 1407 | /* Fix ranges so that the ranges stays with in visible_end. Here we |
| 1113 | try to do minimal work so that the ranges is minimally correct and | 1408 | try to do minimal work so that the ranges is minimally correct and |
| @@ -1381,6 +1676,19 @@ make_treesit_parser (Lisp_Object buffer, TSParser *parser, | |||
| 1381 | lisp_parser->need_to_gc_buffer = false; | 1676 | lisp_parser->need_to_gc_buffer = false; |
| 1382 | lisp_parser->within_reparse = false; | 1677 | lisp_parser->within_reparse = false; |
| 1383 | eassert (lisp_parser->visible_beg <= lisp_parser->visible_end); | 1678 | eassert (lisp_parser->visible_beg <= lisp_parser->visible_end); |
| 1679 | |||
| 1680 | struct buffer *old_buf = current_buffer; | ||
| 1681 | set_buffer_internal (XBUFFER (buffer)); | ||
| 1682 | |||
| 1683 | /* treesit_linecol_of_pos doesn't signal, so no need to | ||
| 1684 | unwind-protect. */ | ||
| 1685 | lisp_parser->visi_beg_linecol = treesit_linecol_of_pos (BEGV, BEGV_BYTE, | ||
| 1686 | TREESIT_BOB_LINECOL); | ||
| 1687 | lisp_parser->visi_end_linecol | ||
| 1688 | = treesit_linecol_of_pos (ZV, ZV_BYTE, lisp_parser->visi_beg_linecol); | ||
| 1689 | |||
| 1690 | set_buffer_internal (old_buf); | ||
| 1691 | |||
| 1384 | return make_lisp_ptr (lisp_parser, Lisp_Vectorlike); | 1692 | return make_lisp_ptr (lisp_parser, Lisp_Vectorlike); |
| 1385 | } | 1693 | } |
| 1386 | 1694 | ||
| @@ -4376,6 +4684,66 @@ nodes in the subtree, including NODE. */) | |||
| 4376 | } | 4684 | } |
| 4377 | } | 4685 | } |
| 4378 | 4686 | ||
| 4687 | DEFUN ("treesit--linecol-at", Ftreesit__linecol_at, | ||
| 4688 | Streesit__linecol_at, 1, 1, 0, | ||
| 4689 | doc: /* Test buffer-local linecol cache. | ||
| 4690 | |||
| 4691 | Calculate the line and column at POS using the buffer-local cache, | ||
| 4692 | return the line and column in the form of | ||
| 4693 | |||
| 4694 | (LINE . COL) | ||
| 4695 | |||
| 4696 | This is used for testing and debugging only. */) | ||
| 4697 | (Lisp_Object pos) | ||
| 4698 | { | ||
| 4699 | CHECK_NUMBER (pos); | ||
| 4700 | struct ts_linecol pos_linecol | ||
| 4701 | = treesit_linecol_of_pos (XFIXNUM (pos), CHAR_TO_BYTE (XFIXNUM (pos)), | ||
| 4702 | current_buffer->ts_linecol_cache); | ||
| 4703 | return Fcons (make_fixnum (pos_linecol.line), make_fixnum (pos_linecol.col)); | ||
| 4704 | } | ||
| 4705 | |||
| 4706 | DEFUN ("treesit--linecol-cache-set", Ftreesit__linecol_cache_set, | ||
| 4707 | Streesit__linecol_cache_set, 4, 4, 0, | ||
| 4708 | doc: /* Set the linecol cache for the current buffer. | ||
| 4709 | |||
| 4710 | This is used for testing and debugging only. */) | ||
| 4711 | (Lisp_Object line, Lisp_Object col, Lisp_Object pos, Lisp_Object bytepos) | ||
| 4712 | { | ||
| 4713 | CHECK_FIXNUM (line); | ||
| 4714 | CHECK_FIXNUM (col); | ||
| 4715 | CHECK_FIXNUM (pos); | ||
| 4716 | CHECK_FIXNUM (bytepos); | ||
| 4717 | |||
| 4718 | current_buffer->ts_linecol_cache.line = XFIXNUM (line); | ||
| 4719 | current_buffer->ts_linecol_cache.col = XFIXNUM (col); | ||
| 4720 | current_buffer->ts_linecol_cache.pos = XFIXNUM (pos); | ||
| 4721 | current_buffer->ts_linecol_cache.byte_pos = XFIXNUM (bytepos); | ||
| 4722 | |||
| 4723 | return Qnil; | ||
| 4724 | } | ||
| 4725 | |||
| 4726 | DEFUN ("treesit--linecol-cache", Ftreesit__linecol_cache, | ||
| 4727 | Streesit__linecol_cache, 0, 0, 0, | ||
| 4728 | doc: /* Return the buffer-local linecol cache for debugging. | ||
| 4729 | |||
| 4730 | Return a plist (:line LINE :col COL :pos POS :bytepos BYTEPOS). This is | ||
| 4731 | used for testing and debugging only. */) | ||
| 4732 | (void) | ||
| 4733 | { | ||
| 4734 | struct ts_linecol cache = current_buffer->ts_linecol_cache; | ||
| 4735 | |||
| 4736 | Lisp_Object plist = (list4 (QCpos, make_fixnum (cache.pos), | ||
| 4737 | QCbytepos, make_fixnum (cache.byte_pos))); | ||
| 4738 | plist = Fcons (make_fixnum (cache.col), plist); | ||
| 4739 | plist = Fcons (QCcol, plist); | ||
| 4740 | plist = Fcons (make_fixnum (cache.line), plist); | ||
| 4741 | plist = Fcons (QCline, plist); | ||
| 4742 | |||
| 4743 | return plist; | ||
| 4744 | } | ||
| 4745 | |||
| 4746 | |||
| 4379 | #endif /* HAVE_TREE_SITTER */ | 4747 | #endif /* HAVE_TREE_SITTER */ |
| 4380 | 4748 | ||
| 4381 | DEFUN ("treesit-available-p", Ftreesit_available_p, | 4749 | DEFUN ("treesit-available-p", Ftreesit_available_p, |
| @@ -4418,6 +4786,11 @@ syms_of_treesit (void) | |||
| 4418 | DEFSYM (QCequal, ":equal"); | 4786 | DEFSYM (QCequal, ":equal"); |
| 4419 | DEFSYM (QCmatch, ":match"); | 4787 | DEFSYM (QCmatch, ":match"); |
| 4420 | DEFSYM (QCpred, ":pred"); | 4788 | DEFSYM (QCpred, ":pred"); |
| 4789 | DEFSYM (QCline, ":line"); | ||
| 4790 | DEFSYM (QCcol, ":col"); | ||
| 4791 | DEFSYM (QCpos, ":pos"); | ||
| 4792 | DEFSYM (QCbytepos, ":bytepos"); | ||
| 4793 | |||
| 4421 | 4794 | ||
| 4422 | DEFSYM (Qnot_found, "not-found"); | 4795 | DEFSYM (Qnot_found, "not-found"); |
| 4423 | DEFSYM (Qsymbol_error, "symbol-error"); | 4796 | DEFSYM (Qsymbol_error, "symbol-error"); |
| @@ -4649,6 +5022,10 @@ applies to LANGUAGE-A will be redirected to LANGUAGE-B instead. */); | |||
| 4649 | defsubr (&Streesit_induce_sparse_tree); | 5022 | defsubr (&Streesit_induce_sparse_tree); |
| 4650 | defsubr (&Streesit_node_match_p); | 5023 | defsubr (&Streesit_node_match_p); |
| 4651 | defsubr (&Streesit_subtree_stat); | 5024 | defsubr (&Streesit_subtree_stat); |
| 5025 | |||
| 5026 | defsubr (&Streesit__linecol_at); | ||
| 5027 | defsubr (&Streesit__linecol_cache); | ||
| 5028 | defsubr (&Streesit__linecol_cache_set); | ||
| 4652 | #endif /* HAVE_TREE_SITTER */ | 5029 | #endif /* HAVE_TREE_SITTER */ |
| 4653 | defsubr (&Streesit_available_p); | 5030 | defsubr (&Streesit_available_p); |
| 4654 | #ifdef WINDOWSNT | 5031 | #ifdef WINDOWSNT |
diff --git a/src/treesit.h b/src/treesit.h index 0d4635f4253..b6a3f3a652c 100644 --- a/src/treesit.h +++ b/src/treesit.h | |||
| @@ -26,6 +26,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */ | |||
| 26 | 26 | ||
| 27 | #include <tree_sitter/api.h> | 27 | #include <tree_sitter/api.h> |
| 28 | #include "lisp.h" | 28 | #include "lisp.h" |
| 29 | #include "buffer.h" | ||
| 29 | 30 | ||
| 30 | INLINE_HEADER_BEGIN | 31 | INLINE_HEADER_BEGIN |
| 31 | 32 | ||
| @@ -97,6 +98,14 @@ struct Lisp_TS_Parser | |||
| 97 | (ref:visible-beg-null) in treesit.c for more explanation. */ | 98 | (ref:visible-beg-null) in treesit.c for more explanation. */ |
| 98 | ptrdiff_t visible_beg; | 99 | ptrdiff_t visible_beg; |
| 99 | ptrdiff_t visible_end; | 100 | ptrdiff_t visible_end; |
| 101 | /* Caches the line and column number of VISIBLE_BEG. It's always | ||
| 102 | valid and matches VISIBLE_BEG (because it's updated at each buffer | ||
| 103 | edit). (It has to be, because in treesit_record_change, we need to | ||
| 104 | calculate the line/col offset of old_end_linecol, the exact reason | ||
| 105 | why is left as an exercise to the reader.) */ | ||
| 106 | struct ts_linecol visi_beg_linecol; | ||
| 107 | /* Similar to VISI_BEG_LINECOL but caches VISIBLE_END. */ | ||
| 108 | struct ts_linecol visi_end_linecol; | ||
| 100 | /* This counter is incremented every time a change is made to the | 109 | /* This counter is incremented every time a change is made to the |
| 101 | buffer in treesit_record_change. The node retrieved from this parser | 110 | buffer in treesit_record_change. The node retrieved from this parser |
| 102 | inherits this timestamp. This way we can make sure the node is | 111 | inherits this timestamp. This way we can make sure the node is |
| @@ -222,7 +231,19 @@ CHECK_TS_COMPILED_QUERY (Lisp_Object query) | |||
| 222 | 231 | ||
| 223 | INLINE_HEADER_END | 232 | INLINE_HEADER_END |
| 224 | 233 | ||
| 225 | extern void treesit_record_change (ptrdiff_t, ptrdiff_t, ptrdiff_t); | 234 | /* A linecol_cache that points to BOB, this is always valid. */ |
| 235 | static const struct ts_linecol TREESIT_BOB_LINECOL = { 1, 1, 1, 0 }; | ||
| 236 | /* An uninitialized linecol. */ | ||
| 237 | static const struct ts_linecol TREESIT_EMPTY_LINECOL = { 0, 0, 0, 0 }; | ||
| 238 | static const TSPoint TREESIT_TS_POINT_1_0 = { 1, 0 }; | ||
| 239 | |||
| 240 | extern struct ts_linecol linecol_offset (struct ts_linecol, | ||
| 241 | struct ts_linecol); | ||
| 242 | extern struct ts_linecol treesit_linecol_maybe (ptrdiff_t, ptrdiff_t, | ||
| 243 | struct ts_linecol); | ||
| 244 | extern void treesit_record_change (ptrdiff_t, ptrdiff_t, ptrdiff_t, | ||
| 245 | struct ts_linecol, struct ts_linecol, | ||
| 246 | ptrdiff_t); | ||
| 226 | extern Lisp_Object make_treesit_parser (Lisp_Object, TSParser *, TSTree *, | 247 | extern Lisp_Object make_treesit_parser (Lisp_Object, TSParser *, TSTree *, |
| 227 | Lisp_Object, Lisp_Object); | 248 | Lisp_Object, Lisp_Object); |
| 228 | extern Lisp_Object make_treesit_node (Lisp_Object, TSNode); | 249 | extern Lisp_Object make_treesit_node (Lisp_Object, TSNode); |
diff --git a/test/src/treesit-tests.el b/test/src/treesit-tests.el index caacb74315d..11d5e1b2c8c 100644 --- a/test/src/treesit-tests.el +++ b/test/src/treesit-tests.el | |||
| @@ -224,6 +224,51 @@ | |||
| 224 | (kill-buffer base) | 224 | (kill-buffer base) |
| 225 | (kill-buffer indirect)))) | 225 | (kill-buffer indirect)))) |
| 226 | 226 | ||
| 227 | ;;; Linecol | ||
| 228 | |||
| 229 | (ert-deftest treesit-linecol-basic () | ||
| 230 | "Tests for basic lincol synchronization." | ||
| 231 | (with-temp-buffer | ||
| 232 | (should (equal (treesit--linecol-cache) | ||
| 233 | '(:line 1 :col 0 :pos 1 :bytepos 1))) | ||
| 234 | (should (equal (treesit--linecol-at (point)) | ||
| 235 | '(1 . 0))) | ||
| 236 | (insert "\n") | ||
| 237 | ;; Buffer content: a single newline. | ||
| 238 | (should (equal (treesit--linecol-at (point)) | ||
| 239 | '(2 . 0))) | ||
| 240 | |||
| 241 | (treesit--linecol-cache-set 2 0 2 2) | ||
| 242 | (should (equal (treesit--linecol-cache) | ||
| 243 | '(:line 2 :col 0 :pos 2 :bytepos 2))) | ||
| 244 | |||
| 245 | (goto-char (point-min)) | ||
| 246 | (should (equal (treesit--linecol-at (point)) | ||
| 247 | '(1 . 0))) | ||
| 248 | |||
| 249 | (insert "0123456789") | ||
| 250 | ;; Buffer content: ten chars followed by a newline. | ||
| 251 | (treesit--linecol-cache-set 1 0 1 1) | ||
| 252 | (should (equal (treesit--linecol-at (point)) | ||
| 253 | '(1 . 10))) | ||
| 254 | |||
| 255 | (goto-char (point-max)) | ||
| 256 | (should (equal (treesit--linecol-at (point)) | ||
| 257 | '(2 . 0))) | ||
| 258 | |||
| 259 | (treesit--linecol-cache-set 1 5 6 6) | ||
| 260 | (should (equal (treesit--linecol-at (point)) | ||
| 261 | '(2 . 0))) | ||
| 262 | |||
| 263 | (treesit--linecol-cache-set 2 0 12 12) | ||
| 264 | ;; Position 6 is in the middle of the first line. | ||
| 265 | (should (equal (treesit--linecol-at 6) | ||
| 266 | '(1 . 5))) | ||
| 267 | ;; Position 11 is at the end of the line. | ||
| 268 | (should (equal (treesit--linecol-at 11) | ||
| 269 | '(1 . 10))) | ||
| 270 | )) | ||
| 271 | |||
| 227 | ;;; Tree traversal | 272 | ;;; Tree traversal |
| 228 | 273 | ||
| 229 | (ert-deftest treesit-search-subtree () | 274 | (ert-deftest treesit-search-subtree () |