tree sitter logic handles edits to a parsed buffer

- async_task_cancel_nowait implementation
This commit is contained in:
Peter Slattery 2025-07-10 09:14:35 -07:00
parent 43fb4a757a
commit 79695eca2c
3 changed files with 208 additions and 23 deletions

View File

@ -84,6 +84,175 @@ BUFFER_HOOK_SIG(custom_end_buffer){
return(0);
}
///////////////////////////////////////////////////////////////////////////
// Buffer Edit Range
///////////////////////////////////////////////////////////////////////////
BUFFER_EDIT_RANGE_SIG(custom_buffer_edit_range){
// buffer_id, new_range, original_size
ProfileScope(app, "default edit range");
Range_i64 old_range = Ii64(old_cursor_range.min.pos, old_cursor_range.max.pos);
buffer_shift_fade_ranges(buffer_id, old_range.max, (new_range.max - old_range.max));
{
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));
code_index_unlock();
}
i64 insert_size = range_size(new_range);
i64 text_shift = replace_range_shift(old_range, insert_size);
Scratch_Block scratch(app);
Managed_Scope scope = buffer_get_managed_scope(app, buffer_id);
{ // Tree Sitter
Buffer_Tree_Sitter_Data* tree_data = scope_attachment(app, scope, buffer_tree_sitter_data_id, Buffer_Tree_Sitter_Data);
if (tree_data->tree)
{
Async_Task* tree_sitter_parse_task = scope_attachment(app, scope, buffer_tree_sitter_parse_task_id, Async_Task);
if (async_task_is_running_or_pending(&global_async_system, *tree_sitter_parse_task))
{
// NOTE(jack): We dont want to wait for ts_tree delete, as it is slow for large files
// (noticable spikes when it fires in 4coder_base_types.cpp ~200Kb)
// And if we restart the next task before it attempts the delete we may be able to
// avoid the delete entirely as the reference count may have increased from the next
// parse attempt
async_task_cancel_nowait(app, &global_async_system, *tree_sitter_parse_task);
*tree_sitter_parse_task = 0;
}
i64 new_end_line = get_line_number_from_pos(app, buffer_id, new_range.end);
i64 new_end_pos = new_range.end - get_line_start_pos(app, buffer_id, new_end_line);
TSInputEdit edit;
edit.start_byte = (u32)old_range.start;
edit.old_end_byte = (u32)old_range.end;
edit.new_end_byte = (u32)new_range.end;
edit.start_point = {
(u32)old_cursor_range.start.line,
(u32)old_cursor_range.start.col
};
edit.old_end_point = {
(u32)old_cursor_range.end.line,
(u32)old_cursor_range.end.col
};
// TODO(PS): jack says this works but looks wrong???
edit.new_end_point = {
(u32)new_end_line - 1,
(u32)new_end_pos + 1,
};
ts_tree_edit(tree_data->tree, &edit);
*tree_sitter_parse_task = async_task_no_dep(&global_async_system, tree_sitter_parse_async, make_data_struct(&buffer_id));
}
}
Async_Task *lex_task_ptr = scope_attachment(app, scope, buffer_lex_task, Async_Task);
Base_Allocator *allocator = managed_scope_allocator(app, scope);
b32 do_full_relex = false;
if (async_task_is_running_or_pending(&global_async_system, *lex_task_ptr))
{
async_task_cancel(app, &global_async_system, *lex_task_ptr);
buffer_unmark_as_modified(buffer_id);
do_full_relex = true;
*lex_task_ptr = 0;
}
Token_Array *ptr = scope_attachment(app, scope, attachment_tokens, Token_Array);
if (ptr != 0 && ptr->tokens != 0)
{
ProfileBlockNamed(app, "attempt resync", profile_attempt_resync);
i64 token_index_first = token_relex_first(ptr, old_range.first, 1);
i64 token_index_resync_guess =
token_relex_resync(ptr, old_range.one_past_last, 16);
if (token_index_resync_guess - token_index_first >= 4000)
{
do_full_relex = true;
}
else
{
Token *token_first = ptr->tokens + token_index_first;
Token *token_resync = ptr->tokens + token_index_resync_guess;
Range_i64 relex_range = Ii64(token_first->pos, token_resync->pos + token_resync->size + text_shift);
String_Const_u8 partial_text = push_buffer_range(app, scratch, buffer_id, relex_range);
Token_List relex_list = lex_full_input_cpp(scratch, partial_text);
if (relex_range.one_past_last < buffer_get_size(app, buffer_id))
{
token_drop_eof(&relex_list);
}
Token_Relex relex = token_relex(relex_list, relex_range.first - text_shift, ptr->tokens, token_index_first, token_index_resync_guess);
ProfileCloseNow(profile_attempt_resync);
if (!relex.successful_resync)
{
do_full_relex = true;
}
else
{
ProfileBlock(app, "apply resync");
i64 token_index_resync = relex.first_resync_index;
Range_i64 head = Ii64(0, token_index_first);
Range_i64 replaced = Ii64(token_index_first, token_index_resync);
Range_i64 tail = Ii64(token_index_resync, ptr->count);
i64 resynced_count = (token_index_resync_guess + 1) - token_index_resync;
i64 relexed_count = relex_list.total_count - resynced_count;
i64 tail_shift = relexed_count - (token_index_resync - token_index_first);
i64 new_tokens_count = ptr->count + tail_shift;
Token *new_tokens = base_array(allocator, Token, new_tokens_count);
Token *old_tokens = ptr->tokens;
block_copy_array_shift(new_tokens, old_tokens, head, 0);
token_fill_memory_from_list(new_tokens + replaced.first, &relex_list, relexed_count);
for (i64 i = 0, index = replaced.first; i < relexed_count; i += 1, index += 1)
{
new_tokens[index].pos += relex_range.first;
}
for (i64 i = tail.first; i < tail.one_past_last; i += 1)
{
old_tokens[i].pos += text_shift;
}
block_copy_array_shift(new_tokens, ptr->tokens, tail, tail_shift);
base_free(allocator, ptr->tokens);
ptr->tokens = new_tokens;
ptr->count = new_tokens_count;
ptr->max = new_tokens_count;
buffer_mark_as_modified(buffer_id);
}
}
}
if (do_full_relex)
{
*lex_task_ptr = async_task_no_dep(&global_async_system, do_full_lex_async,
make_data_struct(&buffer_id));
}
loco_on_buffer_edit(app, buffer_id, old_range, new_range);
// no meaning for return
return(0);
}
///////////////////////////////////////////////////////////////////////////
// Render Buffer
///////////////////////////////////////////////////////////////////////////

View File

@ -520,32 +520,20 @@ custom_keyboard_bindings()
}
void
custom_layer_init(Application_Links *app){
Thread_Context *tctx = get_thread_context(app);
default_framework_init(app);
custom_layer_init(Application_Links *app)
{
Thread_Context *tctx = get_thread_context(app);
default_framework_init(app);
set_all_default_hooks(app);
modal_init(3, tctx);
set_all_default_hooks(app);
modal_init(3, tctx);
set_custom_hook(app, HookID_BeginBuffer, custom_begin_buffer);
set_custom_hook(app, HookID_EndBuffer, custom_end_buffer);
set_custom_hook(app, HookID_RenderCaller, custom_render_caller);
custom_keyboard_bindings();
#if 0
mapping_init(tctx, &framework_mapping);
String_ID global_map_id = vars_save_string_lit("keys_global");
String_ID file_map_id = vars_save_string_lit("keys_file");
String_ID code_map_id = vars_save_string_lit("keys_code");
#if OS_MAC
setup_mac_mapping(&framework_mapping, global_map_id, file_map_id, code_map_id);
#else
setup_default_mapping(&framework_mapping, global_map_id, file_map_id, code_map_id);
#endif
setup_essential_mapping(&framework_mapping, global_map_id, file_map_id, code_map_id);
#endif
set_custom_hook(app, HookID_BeginBuffer, custom_begin_buffer);
set_custom_hook(app, HookID_BufferEditRange, custom_buffer_edit_range);
set_custom_hook(app, HookID_EndBuffer, custom_end_buffer);
set_custom_hook(app, HookID_RenderCaller, custom_render_caller);
custom_keyboard_bindings();
tree_sitter_init(app);
}

View File

@ -318,6 +318,34 @@ tree_sitter_code_index_update_tick(Application_Links* app)
#endif
}
////////////////////////////////////////////////////////////////////
// Async Management
////////////////////////////////////////////////////////////////////
function void
async_task_cancel_nowait(Application_Links* app, Async_System* async_system, Async_Task task)
{
system_mutex_acquire(async_system->mutex);
Async_Node *node = async_get_pending_node(async_system, task);
if (node != 0)
{
dll_remove(&node->node);
async_system->task_count -= 1;
async_free_node(async_system, node);
}
else
{
node = async_get_running_node(async_system, task);
if (node != 0)
{
b32 *cancel_signal = &node->thread->cancel_signal;
atomic_write_b32(cancel_signal, true);
// async_task_wait__inner(app, async_system, task);
}
}
system_mutex_release(async_system->mutex);
}
////////////////////////////////////////////////////////////////////
// Token Highlighting
////////////////////////////////////////////////////////////////////