From 12322796c249c193457545666b325539d5fa7dd6 Mon Sep 17 00:00:00 2001 From: Peter Slattery Date: Wed, 30 Jul 2025 21:15:16 -0700 Subject: [PATCH] Incrementally re-parse notes and scope delimiters within the range of a single edit. --- code/custom/4coder_code_index.cpp | 37 ++- code/custom/4coder_custom_hooks.cpp | 21 +- code/custom/4coder_tree_sitter.cpp | 396 ++++++++++++++++++++++------ code/custom/4coder_tree_sitter.h | 4 +- 4 files changed, 375 insertions(+), 83 deletions(-) diff --git a/code/custom/4coder_code_index.cpp b/code/custom/4coder_code_index.cpp index 58e9186b..224b61ea 100644 --- a/code/custom/4coder_code_index.cpp +++ b/code/custom/4coder_code_index.cpp @@ -230,11 +230,44 @@ code_index_shift_list(Code_Index_Nest_List *list, Range_i64 old_range, u64 new_s } function void -code_index_shift(Code_Index_File *file, Range_i64 old_range, u64 new_size) +code_index_shift_list(Code_Index_Scope_Delim_List *list, Range_i64 old_range, u64 new_size) { - code_index_shift_list(&file->nest_list, old_range, new_size); + i32 count = list->count; + Code_Index_Scope_Delim *delim = list->first; + for (i32 i = 0; i < count; i += 1, delim = delim->next) + { + if (old_range.min == old_range.max) + { + if (old_range.max <= delim->pos.min) + { + delim->pos.min = delim->pos.min + new_size - (old_range.max - old_range.min); + delim->pos.max = delim->pos.max + new_size - (old_range.max - old_range.min); + } + } + else + { + if (old_range.min <= delim->pos.min && delim->pos.min < old_range.max) { + delim->pos.min = old_range.first; + } else if (old_range.max <= delim->pos.min) { + delim->pos.min = delim->pos.min + new_size - (old_range.max - old_range.min); + } + + if (old_range.min < delim->pos.max && delim->pos.max <= old_range.max) { + delim->pos.max = old_range.first; + } else if (old_range.max <= delim->pos.max) { + delim->pos.max = delim->pos.max + new_size - (old_range.max - old_range.min); + } + } + } } +function void +code_index_shift(Code_Index_File *file, Range_i64 old_range, u64 new_size) +{ + // TODO(PS): @DontShiftNestList - This is unnecessary now that nest_list just gets fully rebuilt each edit + code_index_shift_list(&file->nest_list, old_range, new_size); + code_index_shift_list(&file->scope_delim_list, old_range, new_size); +} //////////////////////////////// // NOTE(allen): Parser Helpers diff --git a/code/custom/4coder_custom_hooks.cpp b/code/custom/4coder_custom_hooks.cpp index 45eefeb1..74fe871f 100644 --- a/code/custom/4coder_custom_hooks.cpp +++ b/code/custom/4coder_custom_hooks.cpp @@ -144,7 +144,9 @@ BUFFER_EDIT_RANGE_SIG(custom_buffer_edit_range){ { code_index_lock(); Code_Index_File *file = code_index_get_file(buffer_id); - if (file != 0) code_index_shift(file, old_range, range_size(new_range)); + if (file != 0) { + code_index_shift(file, old_range, range_size(new_range)); + } code_index_unlock(); } @@ -387,6 +389,23 @@ function void custom_render_buffer( // NOTE(allen): put the actual text on the actual screen draw_text_layout_default(app, text_layout_id); + // TEMP - highlight the node range of the last edit + if (use_tree_sitter_token_coloring) { + Managed_Scope scope = buffer_get_managed_scope(app, buffer); + Buffer_Tree_Sitter_Data* tree_data = scope_attachment(app, scope, buffer_tree_sitter_data_id, Buffer_Tree_Sitter_Data); + for (int i = 0; i < tree_data->last_update_node_range_count; i++) + { + Range_i64 last_update_range = tree_data->last_update_node_range[i]; + Rect_f32 range_min = text_layout_character_on_screen(app, text_layout_id, last_update_range.min); + Rect_f32 range_max = text_layout_character_on_screen(app, text_layout_id, last_update_range.max); + Rect_f32 range; + range.x0 = Min(range_min.x0, range_max.x0); + range.y0 = Min(range_min.y0, range_max.y0); + range.x1 = Max(range_min.x1, range_max.x1); + range.y1 = Max(range_min.y1, range_max.y1); + draw_rectangle(app, range, 0.f, 0x33FF00FF); + } + } draw_set_clip(app, prev_clip); } diff --git a/code/custom/4coder_tree_sitter.cpp b/code/custom/4coder_tree_sitter.cpp index 18a7f8c8..ec58d8ff 100644 --- a/code/custom/4coder_tree_sitter.cpp +++ b/code/custom/4coder_tree_sitter.cpp @@ -369,16 +369,42 @@ code_index_new_scope_delim(Code_Index_File* index, Arena* arena, Code_Index_Scop Code_Index_Scope_Delim *result = index->scope_delim_free; index->scope_delim_free = result->next; - result->next = 0; - - sll_queue_push(index->scope_delim_list.first, index->scope_delim_list.last, result); - index->scope_delim_list.count += 1; + block_zero_struct(result); result->kind = kind; result->pos = range; return result; } +function void +code_index_scope_delim_insert(Code_Index_Scope_Delim_List* list, Code_Index_Scope_Delim* prev, Code_Index_Scope_Delim* delim) +{ + if (!prev) + { + delim->next = list->first; + if (list->first) list->first->prev = delim; + else list->last = delim; + list->first = delim; + } + else + { + Code_Index_Scope_Delim* next = prev->next; + prev->next = delim; + delim->prev = prev; + delim->next = next; + if (next) next->prev = delim; + if (prev == list->last) list->last = delim; + } + list->count += 1; +} + +function void +code_index_free_scope_delim(Code_Index_File* index, Code_Index_Scope_Delim* delim) +{ + delim->next = index->scope_delim_free; + index->scope_delim_free = delim; +} + function Code_Index_Note* code_index_new_note(Code_Index_File* index, Arena* arena, Code_Index_Note_Kind kind, Range_i64 range, Code_Index_Nest_Stack* parent) { @@ -392,10 +418,8 @@ code_index_new_note(Code_Index_File* index, Arena* arena, Code_Index_Note_Kind k Code_Index_Note *result = index->note_free; index->note_free = result->next; - result->next = 0; + block_zero_struct(result); - sll_queue_push(index->note_list.first, index->note_list.last, result); - index->note_list.count += 1; result->file = index; result->note_kind = kind; result->pos = range; @@ -403,11 +427,38 @@ code_index_new_note(Code_Index_File* index, Arena* arena, Code_Index_Note_Kind k return result; } +function void +code_index_note_insert(Code_Index_Note_List* list, Code_Index_Note* prev, Code_Index_Note* note) +{ + if (!prev) + { + note->next = list->first; + if (!list->last) list->last = note; + list->first = note; + } + else + { + Code_Index_Note* next = prev->next; + prev->next = note; + note->next = next; + if (prev == list->last) list->last = note; + } + list->count += 1; +} + +function void +code_index_free_note(Code_Index_File* index, Code_Index_Note* note) +{ + block_zero_struct(note); + note->next = index->note_free; + index->note_free = note; +} + function Tree_Sitter_Code_Index_Update_State tree_sitter_code_index_update_state_create( Application_Links* app, Buffer_ID buffer_id, - Code_Index_File_Storage code_index_storage, + Code_Index_File_Storage* code_index_storage, Arena* scratch ){ Tree_Sitter_Code_Index_Update_State state = {}; @@ -415,11 +466,21 @@ tree_sitter_code_index_update_state_create( state.buffer_id = buffer_id; state.language = tree_sitter_language_for_buffer(app, state.buffer_id); if (!state.language) return state; - state.index_arena = code_index_storage.arena; - state.index = code_index_storage.file; + + if (code_index_storage != 0) + { + state.index_arena = code_index_storage->arena; + state.index = code_index_storage->file; + } else { + state.index_arena = make_arena_system(KB(16)); + state.index = push_array_zero(&state.index_arena, Code_Index_File, 1); + } + state.buffer_contents = push_whole_buffer(app, scratch, state.buffer_id); state.query = tree_sitter_query_init( - app, state.buffer_id, state.language->queries.ptr[Tree_Sitter_Language_Query_Tags] + app, + state.buffer_id, + state.language->queries.ptr[Tree_Sitter_Language_Query_Tags] ); state.nest_stack_first = 0; state.nest_stack_last = 0; @@ -427,20 +488,21 @@ tree_sitter_code_index_update_state_create( state.last_note_match_id = max_u32; state.ok = true; +/* Freeing all delims is undesired for incremental updates, and unnecessary if + we're working with a wholly new index. The same will be true for notes soon. if (state.index->scope_delim_list.last) { state.index->scope_delim_list.last->next = state.index->scope_delim_free; } state.index->scope_delim_free = state.index->scope_delim_list.first; - state.index->scope_delim_list.first = 0; - state.index->scope_delim_list.last = 0; + block_zero_struct(&state.index->scope_delim_list); + if (state.index->note_list.last) { state.index->note_list.last->next = state.index->note_free; } state.index->note_free = state.index->note_list.first; - state.index->note_list.first = 0; - state.index->note_list.last = 0; - + block_zero_struct(&state.index->note_list); +*/ return state; } @@ -475,28 +537,41 @@ tree_sitter_code_index_update_process_query_match( if (!skip) { - code_index_new_scope_delim(state->index, &state->index_arena, kind, type_range); + Code_Index_Scope_Delim* delim = code_index_new_scope_delim( + state->index, &state->index_arena, kind, type_range + ); + + zdll_push_back(state->index->scope_delim_list.first, state->index->scope_delim_list.last, delim); + state->index->scope_delim_list.count += 1; } } else if (string_match(capture_name, SCu8("definition.class"))) { state->last_note = code_index_new_note(state->index, &state->index_arena, CodeIndexNote_Type, type_range, state->nest_stack_last); state->last_note_match_id = query_match.id; + sll_queue_push(state->index->note_list.first, state->index->note_list.last, state->last_note); + state->index->note_list.count += 1; } else if (string_match(capture_name, SCu8("definition.function"))) { state->last_note = code_index_new_note(state->index, &state->index_arena, CodeIndexNote_Function, type_range, state->nest_stack_last); state->last_note_match_id = query_match.id; + sll_queue_push(state->index->note_list.first, state->index->note_list.last, state->last_note); + state->index->note_list.count += 1; } else if (string_match(capture_name, SCu8("definition.method"))) { state->last_note = code_index_new_note(state->index, &state->index_arena, CodeIndexNote_Function, type_range, state->nest_stack_last);; state->last_note_match_id = query_match.id; + sll_queue_push(state->index->note_list.first, state->index->note_list.last, state->last_note); + state->index->note_list.count += 1; } else if (string_match(capture_name, SCu8("definition.type"))) { state->last_note = code_index_new_note(state->index, &state->index_arena, CodeIndexNote_Type, type_range, state->nest_stack_last); state->last_note_match_id = query_match.id; + sll_queue_push(state->index->note_list.first, state->index->note_list.last, state->last_note); + state->index->note_list.count += 1; } else if (string_match(capture_name, SCu8("name"))) { @@ -511,14 +586,14 @@ function void tree_sitter_code_index_update_complete( Application_Links* app, Tree_Sitter_Code_Index_Update_State* state, - Arena* scratch + Arena* scratch, + bool update_was_incremental ){ tree_sitter_query_end(&state->query); if (state->ok) { Code_Index_Nest* free_nests = state->index->nest_list.first; - state->index->nest_list.first = 0; - state->index->nest_list.last = 0; + block_zero_struct(&state->index->nest_list); Code_Index_Scope_Delim* delim_at = state->index->scope_delim_list.first; while (delim_at != 0) @@ -543,13 +618,14 @@ tree_sitter_code_index_update_complete( Code_Index_Nest* nest = free_nests; if (nest->nest_list.first) { - nest->nest_list.last->next = free_nests; + nest->nest_list.last->next = nest->next; free_nests = nest->nest_list.first; } else { free_nests = nest->next; } + block_zero_struct(nest); nest->kind = CodeIndexNest_Scope; nest->delim = delim_at->kind; @@ -589,14 +665,19 @@ tree_sitter_code_index_update_complete( delim_at = delim_at->next; } + // TODO(PS): this should just be for the new notes - if a note wasn't + // updated incrementally this frame, then it already has it's text for (Code_Index_Note* note = state->index->note_list.first; note != 0 && note->next != note; note = note->next) { note->text = push_string_copy(&state->index_arena, string_substring(state->buffer_contents, note->pos)); } - code_index_lock(); - code_index_set_file(state->buffer_id, state->index_arena, state->index); - code_index_unlock(); + if (!update_was_incremental) + { + code_index_lock(); + code_index_set_file(state->buffer_id, state->index_arena, state->index); + code_index_unlock(); + } buffer_clear_layout_cache(app, state->buffer_id); } @@ -611,13 +692,10 @@ tree_sitter_code_index_update_full_file(Application_Links* app, Buffer_ID buffer { Scratch_Block scratch(app); - Code_Index_File_Storage storage; - storage.arena = make_arena_system(KB(16)); - storage.file = push_array_zero(&storage.arena, Code_Index_File, 1); Tree_Sitter_Code_Index_Update_State state = tree_sitter_code_index_update_state_create( app, buffer_id, - storage, + 0, scratch ); @@ -633,7 +711,7 @@ tree_sitter_code_index_update_full_file(Application_Links* app, Buffer_ID buffer ); } - tree_sitter_code_index_update_complete(app, &state, scratch); + tree_sitter_code_index_update_complete(app, &state, scratch, false); return state; } @@ -646,13 +724,10 @@ tree_sitter_code_index_update_async(Async_Context* actx, String_Const_u8 data) Scratch_Block scratch(app); acquire_global_frame_mutex(app); - Code_Index_File_Storage storage; - storage.arena = make_arena_system(KB(16)); - storage.file = push_array_zero(&storage.arena, Code_Index_File, 1); Tree_Sitter_Code_Index_Update_State state = tree_sitter_code_index_update_state_create( app, buffer_id, - storage, + 0, scratch ); release_global_frame_mutex(app); @@ -669,49 +744,9 @@ tree_sitter_code_index_update_async(Async_Context* actx, String_Const_u8 data) } } - tree_sitter_code_index_update_complete(app, &state, scratch); + tree_sitter_code_index_update_complete(app, &state, scratch, false); } -/* -function void -tree_sitter_code_index_update_incremental(Application_Links* app, Buffer_ID buffer_id) -{ - // TODO(PS): @Collapse with tree_sitter_code_index_update_state_create - Tree_Sitter_Code_Index_Update_State state = {}; - state.ok = false; - state.buffer_id = buffer_id; - state.language = tree_sitter_language_for_buffer(app, state.buffer_id); - if (!state.language) return state; - - Code_Index_File_Storage* storage = code_index_get_file_storage(state.buffer_id); - state.index_arena = storage->arena; - state.index = storage->file; - state.buffer_contents = push_whole_buffer(app, scratch, state.buffer_id); - state.query = tree_sitter_query_init( - app, state.buffer_id, state.language->queries.ptr[Tree_Sitter_Language_Query_Tags] - ); - state.nest_stack_first = 0; - state.nest_stack_last = 0; - state.last_note = 0; - state.last_note_match_id = max_u32; - state.ok = true; - - TSQueryMatch query_match; - u32 capture_index; - while (state.ok && tree_sitter_query_continue(&state.query, &query_match, &capture_index)) - { - tree_sitter_code_index_update_process_query_match( - &state, - query_match, - capture_index, - scratch - ); - } - - tree_sitter_code_index_update_complete(app, &state, scratch); -} -*/ - function void tree_sitter_code_index_update_tick(Application_Links* app) { @@ -725,12 +760,191 @@ tree_sitter_code_index_update_tick(Application_Links* app) Buffer_Tree_Sitter_Data* tree_data = scope_attachment(app, buffer_scope, buffer_tree_sitter_data_id, Buffer_Tree_Sitter_Data); if (!tree_data || !tree_data->tree) continue; -#if 1 - tree_sitter_code_index_update_full_file(app, buffer_id); -#else - tree_sitter_code_index_update_incremental(app, buffer_id); -#endif + Range_i64 old_range = tree_data->last_update_old_range; + Range_i64 new_range = tree_data->last_update_new_range; + if (old_range == new_range) continue; + + Scratch_Block scratch(app); + Code_Index_File_Storage* storage = code_index_get_file_storage(buffer_id); + Tree_Sitter_Code_Index_Update_State state = tree_sitter_code_index_update_state_create( + app, + buffer_id, + storage, + scratch + ); + + // Find the largest, non-root node for us to reparse in its entirety + TSNode first_node = ts_node_descendant_for_byte_range( + state.query.first_node, new_range.min, new_range.max + ); + + printf("Initial Scope\n"); + print_tree_sitter_tree(first_node, 0, ""); + + if (!ts_node_eq(first_node, state.query.first_node)) + { + TSNode parent = ts_node_parent(first_node); + while (!ts_node_has_error(parent) && !ts_node_eq(parent, state.query.first_node)) + { + first_node = parent; + parent = ts_node_parent(first_node); + printf("Ascending\n"); + print_tree_sitter_tree(first_node, 0, ""); + } + } + state.query.first_node = first_node; + + Range_i64 edit_range; + edit_range.min = (i64)ts_node_start_byte(state.query.first_node); + edit_range.max = (i64)ts_node_end_byte(state.query.first_node); + tree_data->last_update_node_range[0] = edit_range; // TODO(PS): TEMP - remove me once debugging is done + tree_data->last_update_node_range_count = 1; + + // Free Scope Delimiters & Notes that fall within old_range + Code_Index_Scope_Delim* delim = state.index->scope_delim_list.first; + Code_Index_Scope_Delim* before_range = 0; + Code_Index_Scope_Delim* after_range = 0; + while (delim) + { + Code_Index_Scope_Delim* next = delim->next; + if (delim->pos.min < edit_range.min && delim->pos.max <= edit_range.min) + { + before_range = delim; + } + if (range_overlap(delim->pos, edit_range)) + { + state.index->scope_delim_list.count -= 1; + zdll_remove( + state.index->scope_delim_list.first, + state.index->scope_delim_list.last, + delim + ); + code_index_free_scope_delim(state.index, delim); + } + if (delim->pos.min >= edit_range.max) + { + after_range = delim; + break; + } + delim = next; + } + + Code_Index_Note* note = state.index->note_list.first; + Code_Index_Note* prev = 0; + Code_Index_Note* note_before_range = 0; + Code_Index_Note* note_after_range = 0; + while (note) + { + Code_Index_Note* next = note->next; + if (range_overlap(note->pos, edit_range)) + { + Code_Index_Note* new_first = state.index->note_list.first; + Code_Index_Note* new_last = state.index->note_list.last; + Code_Index_Note* new_next = next; + + if (note == state.index->note_list.first) new_first = next; + if (note == state.index->note_list.last) + { + if (prev) new_last = prev; + else new_last = 0; + } + + if (new_first == 0) Assert(new_last == 0 && state.index->note_list.count == 1); + + state.index->note_list.count -= 1; + state.index->note_list.first = new_first; + state.index->note_list.last = new_last; + if (prev) prev->next = new_next; + code_index_free_note(state.index, note); + } + else + { + prev = note; + } + note = next; + } + if (state.index->note_list.count == 0) Assert(state.index->note_list.first == 0 && state.index->note_list.first == 0); + + Code_Index_Scope_Delim* delim_prev = before_range; + TSQueryMatch query_match; + u32 capture_index; + while (state.ok && tree_sitter_query_continue(&state.query, &query_match, &capture_index)) + { + // TODO(PS): @Collapse - the entire body of this while loop used to be in + // tree_sitter_code_index_update_process_query_match + // After we're done, see what we can do to collapse + + TSQueryCapture type_capture = query_match.captures[capture_index]; + + TSNode type_node = type_capture.node; + Range_i64 type_range = tree_sitter_node_to_range(type_node); + + u32 length; + const char* tmp = ts_query_capture_name_for_id(state.query.query, type_capture.index, &length); + String_Const_u8 capture_name = SCu8((char*)tmp, length); + + if (string_match(capture_name, SCu8("scope_begin")) || string_match(capture_name, SCu8("scope_end"))) + { + Code_Index_Scope_Delim_Kind kind; + String_Const_u8 delim_char = string_substring(state.buffer_contents, type_range); + bool skip = false; + if (delim_char.str[0] == '{') kind = CodeIndexScopeDelim_ScopeOpen; + else if (delim_char.str[0] == '}') kind = CodeIndexScopeDelim_ScopeClose; + else if (delim_char.str[0] == '(') kind = CodeIndexScopeDelim_ParenOpen; + else if (delim_char.str[0] == ')') kind = CodeIndexScopeDelim_ParenClose; + else if (delim_char.str[0] == '[') kind = CodeIndexScopeDelim_BracketOpen; + else if (delim_char.str[0] == ']') kind = CodeIndexScopeDelim_BracketClose; + else skip = true; + + if (!skip) + { + Code_Index_Scope_Delim* delim = code_index_new_scope_delim( + state.index, &state.index_arena, kind, type_range + ); + code_index_scope_delim_insert(&state.index->scope_delim_list, delim_prev, delim); + delim_prev = delim; + } + } + else if (string_match(capture_name, SCu8("definition.class"))) + { + Code_Index_Note* note = code_index_new_note(state.index, &state.index_arena, CodeIndexNote_Type, type_range, state.nest_stack_last); + state.last_note_match_id = query_match.id; + code_index_note_insert(&state.index->note_list, state.last_note, note); + state.last_note = note; + } + else if (string_match(capture_name, SCu8("definition.function"))) + { + Code_Index_Note* note = code_index_new_note(state.index, &state.index_arena, CodeIndexNote_Function, type_range, state.nest_stack_last); + state.last_note_match_id = query_match.id; + code_index_note_insert(&state.index->note_list, state.last_note, note); + state.last_note = note; + } + else if (string_match(capture_name, SCu8("definition.method"))) + { + Code_Index_Note* note = code_index_new_note(state.index, &state.index_arena, CodeIndexNote_Function, type_range, state.nest_stack_last);; + state.last_note_match_id = query_match.id; + code_index_note_insert(&state.index->note_list, state.last_note, note); + state.last_note = note; + } + else if (string_match(capture_name, SCu8("definition.type"))) + { + Code_Index_Note* note = code_index_new_note(state.index, &state.index_arena, CodeIndexNote_Type, type_range, state.nest_stack_last); + state.last_note_match_id = query_match.id; + code_index_note_insert(&state.index->note_list, state.last_note, note); + state.last_note = note; + } + else if (string_match(capture_name, SCu8("name"))) + { + if (state.last_note != 0 && state.last_note_match_id == query_match.id) + { + state.last_note->pos = Ii64_size(type_range.start, type_range.end - type_range.start); + } + } + } + + tree_sitter_code_index_update_complete(app, &state, scratch, true); } + buffer_modified_set_clear(); } @@ -1066,6 +1280,30 @@ CUSTOM_DOC("Creates a lister of locations that look like type definitions and de char* prefix_buffer = " "; +function void +print_tree_sitter_tree(TSNode cur_node, i32 level, const char* field) +{ + TSPoint start = ts_node_start_point(cur_node); + TSPoint end = ts_node_end_point(cur_node); + // + 1 on ts positions becuase the first line/column are zero in treesitter, + printf("%.*s%s: %s [%d, %d] - [%d, %d]\n", + level*2, prefix_buffer, field, ts_node_type(cur_node), + start.row + 1, start.column + 1, + end.row + 1, end.column + 1); + + u32 child_count = ts_node_child_count(cur_node); + for (u32 i = 0; i < child_count; ++i) + { + TSNode child = ts_node_child(cur_node, i); + if (ts_node_is_named(child)) + { + field = ts_node_field_name_for_child(cur_node, i); + if (!field) field = ""; + print_tree_sitter_tree(child, level + 1, field); + } + } +} + function void write_tree_sitter_tree_to_buffer__inner(Application_Links *app, Arena *arena, Buffer_ID buffer_id, TSNode cur_node, i32 level = 0, const char *field="") diff --git a/code/custom/4coder_tree_sitter.h b/code/custom/4coder_tree_sitter.h index 038744e7..25f3682e 100644 --- a/code/custom/4coder_tree_sitter.h +++ b/code/custom/4coder_tree_sitter.h @@ -49,6 +49,7 @@ struct Buffer_Tree_Sitter_Data // Code Index Update Requests Range_i64 last_update_old_range; Range_i64 last_update_new_range; + Range_i64 last_update_node_range; }; struct Tree_Sitter_Query_Cursor @@ -87,7 +88,6 @@ struct Tree_Sitter_Code_Index_Update_State String_Const_u8 buffer_contents; Tree_Sitter_Language_Definition* language; - Tree_Sitter_Query_Cursor query; Arena index_arena; Code_Index_File* index; @@ -124,4 +124,6 @@ function Tree_Sitter_Code_Index_Update_State tree_sitter_code_index_update_full_ function void tree_sitter_code_index_update_tick(Application_Links *app); function void tree_sitter_code_index_update_async(Async_Context* actx, String_Const_u8 data); +function void print_tree_sitter_tree(TSNode cur_node, i32 level, const char* field); + #endif //FCODER_TREE_SITTER_H