From cd24295e8e82034a91cb04c3962fa0483fd732df Mon Sep 17 00:00:00 2001 From: Allen Webster Date: Sun, 27 Oct 2019 15:37:48 -0700 Subject: [PATCH] Optimized batch edits remeasurement --- 4ed_app_target.cpp | 1 + 4ed_buffer.cpp | 192 +++++++++++++++++++++++----- 4ed_buffer.h | 22 ++++ 4ed_edit.cpp | 44 +++---- custom/4coder_lister_base.cpp | 12 +- custom/4coder_profile_inspect.cpp | 3 + custom/generated/command_metadata.h | 2 +- 7 files changed, 218 insertions(+), 58 deletions(-) diff --git a/4ed_app_target.cpp b/4ed_app_target.cpp index 37cd31ae..b7b43b93 100644 --- a/4ed_app_target.cpp +++ b/4ed_app_target.cpp @@ -68,6 +68,7 @@ #include "4coder_app_links_allocator.cpp" #include "4coder_system_allocator.cpp" #include "4coder_profile.cpp" +#include "4coder_profile_static_enable.cpp" #include "4coder_hash_functions.cpp" #include "4coder_table.cpp" #include "4coder_log.cpp" diff --git a/4ed_buffer.cpp b/4ed_buffer.cpp index a1a000f3..d22fecb9 100644 --- a/4ed_buffer.cpp +++ b/4ed_buffer.cpp @@ -424,6 +424,7 @@ buffer_eol_convert_out(Arena *arena, Gap_Buffer *buffer, Interval_i64 range){ return(SCu8(memory, ptr)); } +#if 0 internal i64 buffer_count_newlines(Arena *scratch, Gap_Buffer *buffer, i64 start, i64 end){ Temp_Memory temp = begin_temp(scratch); @@ -447,6 +448,7 @@ buffer_count_newlines(Arena *scratch, Gap_Buffer *buffer, i64 start, i64 end){ return(count); } +#endif internal void buffer_starts__ensure_max_size(Gap_Buffer *buffer, i64 max_size){ @@ -491,15 +493,172 @@ buffer_measure_starts(Arena *scratch, Gap_Buffer *buffer){ end_temp(temp); } +internal i64 +buffer_get_line_index(Gap_Buffer *buffer, i64 pos){ + i64 i = 0; + if (buffer->line_start_count > 2){ + i64 start = 0; + i64 one_past_last = buffer->line_start_count - 1; + i64 *array = buffer->line_starts; + pos = clamp_bot(0, pos); + for (;;){ + i = (start + one_past_last) >> 1; + if (array[i] < pos){ + start = i; + } + else if (array[i] > pos){ + one_past_last = i; + } + else{ + break; + } + if (start + 1 >= one_past_last){ + i = start; + break; + } + } + } + return(i); +} + +Line_Move* +push_line_move(Arena *arena, Line_Move *moves, i64 new_line_first, + i64 old_line_first, i64 old_line_opl, i64 text_shift){ + Line_Move *move = push_array(arena, Line_Move, 1); + move->next = moves; + move->kind = LineMove_ShiftOldValues; + move->new_line_first = new_line_first; + move->old_line_first = old_line_first; + move->old_line_opl = old_line_opl; + move->text_shift = text_shift; + return(move); +} + +Line_Move* +push_line_move(Arena *arena, Line_Move *moves, i64 new_line_first, + String_Const_u8 string, i64 text_base){ + Line_Move *move = push_array(arena, Line_Move, 1); + move->next = moves; + move->kind = LineMove_MeasureString; + move->new_line_first = new_line_first; + move->string = string; + move->text_base = text_base; + return(move); +} + +function i64 +count_lines(String_Const_u8 string){ + i64 result = 0; + for (umem i = 0; i < string.size; i += 1){ + if (string.str[i] == '\n'){ + result += 1; + } + } + return(result); +} + +function void +fill_line_starts(i64 *lines_starts, String_Const_u8 string, i64 text_base){ + i64 *ptr = lines_starts; + for (umem i = 0; i < string.size; i += 1){ + if (string.str[i] == '\n'){ + *ptr = text_base + i + 1; + ptr += 1; + } + } +} + +function void +buffer_remeasure_starts(Thread_Context *tctx, Gap_Buffer *buffer, Batch_Edit *batch){ + Scratch_Block scratch(tctx); + + i64 line_start_count = buffer_line_count(buffer) + 1; + + Line_Move *moves = 0; + i64 current_line = 0; + i64 text_shift = 0; + i64 line_shift = 0; + for (Batch_Edit *node = batch; + node != 0; + node = node->next){ + i64 first_line = buffer_get_line_index(buffer, node->edit.range.first); + i64 opl_line = buffer_get_line_index(buffer, node->edit.range.one_past_last); + i64 new_line_count = count_lines(node->edit.text); + i64 deleted_line_count = opl_line - first_line; + + Assert(first_line <= opl_line); + Assert(opl_line <= line_start_count); + + if (current_line <= first_line && + (text_shift != 0 || line_shift != 0)){ + moves = push_line_move(scratch, moves, current_line + line_shift, + current_line, first_line + 1, text_shift); + } + + if (new_line_count != 0){ + moves = push_line_move(scratch, moves, first_line + 1 + line_shift, + node->edit.text, node->edit.range.first + text_shift); + } + + text_shift += node->edit.text.size - range_size(node->edit.range); + line_shift += new_line_count - deleted_line_count; + current_line = opl_line + 1; + } + + moves = push_line_move(scratch, moves, current_line + line_shift, + current_line, line_start_count, text_shift); + line_start_count = line_start_count + line_shift; + + buffer_starts__ensure_max_size(buffer, line_start_count + 1); + buffer->line_start_count = line_start_count; + + i64 *array = buffer->line_starts; + + for (Line_Move *node = moves; + node != 0; + node = node->next){ + if (node->kind == LineMove_ShiftOldValues){ + i64 line_index_shift = node->new_line_first - node->old_line_first; + i64 move_text_shift = node->text_shift; + if (line_index_shift > 0){ + for (i64 i = node->old_line_opl - 1; + i >= node->old_line_first; + i -= 1){ + array[i + line_index_shift] = array[i] + move_text_shift; + } + } + else{ + for (i64 i = node->old_line_first; + i < node->old_line_opl; + i += 1){ + array[i + line_index_shift] = array[i] + move_text_shift; + } + } + } + } + + for (Line_Move *node = moves; + node != 0; + node = node->next){ + if (node->kind == LineMove_MeasureString){ + fill_line_starts(array + node->new_line_first, node->string, node->text_base); + } + } +} + +#if 0 internal void -buffer_remeasure_starts(Arena *scratch, Gap_Buffer *buffer, Interval_i64 old_line_indexes, i64 line_shift, i64 text_shift){ +buffer_remeasure_starts(Arena *scratch, Gap_Buffer *buffer, + Range_i64 old_line_indexes, i64 line_shift, i64 text_shift){ Temp_Memory temp = begin_temp(scratch); buffer_starts__ensure_max_size(buffer, buffer->line_start_count + line_shift); i64 new_line_indexes_opl = old_line_indexes.one_past_last + line_shift; i64 old_line_start_count = buffer->line_start_count; i64 *line_start_ptr = buffer->line_starts + old_line_indexes.one_past_last; - for (i64 i = old_line_indexes.one_past_last; i < old_line_start_count; i += 1, line_start_ptr += 1){ + for (i64 i = old_line_indexes.one_past_last; + i < old_line_start_count; + i += 1, line_start_ptr += 1){ *line_start_ptr += text_shift; } block_copy_dynamic_array(buffer->line_starts + new_line_indexes_opl, @@ -532,34 +691,7 @@ buffer_remeasure_starts(Arena *scratch, Gap_Buffer *buffer, Interval_i64 old_lin buffer->line_start_count += line_shift; end_temp(temp); } - -internal i64 -buffer_get_line_index(Gap_Buffer *buffer, i64 pos){ - i64 i = 0; - if (buffer->line_start_count > 2){ - i64 start = 0; - i64 one_past_last = buffer->line_start_count - 1; - i64 *array = buffer->line_starts; - pos = clamp_bot(0, pos); - for (;;){ - i = (start + one_past_last) >> 1; - if (array[i] < pos){ - start = i; - } - else if (array[i] > pos){ - one_past_last = i; - } - else{ - break; - } - if (start + 1 >= one_past_last){ - i = start; - break; - } - } - } - return(i); -} +#endif internal Interval_i64 buffer_get_pos_range_from_line_number(Gap_Buffer *buffer, i64 line_number){ diff --git a/4ed_buffer.h b/4ed_buffer.h index 51e40215..c0c213c6 100644 --- a/4ed_buffer.h +++ b/4ed_buffer.h @@ -41,6 +41,28 @@ struct Buffer_Chunk_Position{ i64 chunk_index; }; +typedef i32 Line_Move_Kind; +enum{ + LineMove_ShiftOldValues, + LineMove_MeasureString, +}; +struct Line_Move{ + Line_Move *next; + Line_Move_Kind kind; + i64 new_line_first; + union{ + struct{ + i64 old_line_first; + i64 old_line_opl; + i64 text_shift; + }; + struct{ + String_Const_u8 string; + i64 text_base; + }; + }; +}; + typedef u32 Buffer_Layout_Flag; enum{ BRFlag_Special_Character = (1 << 0), diff --git a/4ed_edit.cpp b/4ed_edit.cpp index a97f0c0a..882e4bc2 100644 --- a/4ed_edit.cpp +++ b/4ed_edit.cpp @@ -225,33 +225,20 @@ edit__apply(Thread_Context *tctx, Models *models, Editing_File *file, Assert(edit.range.first <= edit.range.one_past_last); Assert(edit.range.one_past_last <= buffer_size(buffer)); - Scratch_Block scratch(tctx, Scratch_Share); - // NOTE(allen): history update if (!behaviors.do_not_post_to_history){ + ProfileTLBlock(tctx, &models->profile_list, "edit apply history"); history_record_edit(&models->global_history, &file->state.history, buffer, edit); file->state.current_record_index = history_get_record_count(&file->state.history); } - // NOTE(allen): compute shift - i64 shift_amount = replace_range_shift(edit.range, (i64)edit.text.size); - - // NOTE(allen): actual text replacement - buffer_replace_range(buffer, edit.range, edit.text, shift_amount); - - // NOTE(allen): line meta data - i64 line_start = buffer_get_line_index(buffer, edit.range.first); - i64 line_end = buffer_get_line_index(buffer, edit.range.one_past_last); - i64 replaced_line_count = line_end - line_start; - i64 new_line_count = buffer_count_newlines(scratch, buffer, edit.range.first, - edit.range.first + edit.text.size); - i64 line_shift = new_line_count - replaced_line_count; - - file_clear_layout_cache(file); - buffer_remeasure_starts(scratch, buffer, Ii64(line_start, line_end + 1), - line_shift, shift_amount); + { + ProfileTLBlock(tctx, &models->profile_list, "edit apply replace range"); + i64 shift_amount = replace_range_shift(edit.range, (i64)edit.text.size); + buffer_replace_range(buffer, edit.range, edit.text, shift_amount); + } } internal void @@ -262,11 +249,14 @@ edit_single(Thread_Context *tctx, Models *models, Editing_File *file, edit__apply(tctx, models, file, range, string, behaviors); + file_clear_layout_cache(file); + Batch_Edit batch = {}; batch.edit.text = string; batch.edit.range = range; - edit_fix_markers(tctx, models, file, &batch); + buffer_remeasure_starts(tctx, &file->state.buffer, &batch); + edit_fix_markers(tctx, models, file, &batch); post_edit_call_hook(tctx, models, file, Ii64_size(range.first, string.size), range_size(range)); } @@ -413,8 +403,6 @@ edit_merge_history_range(Thread_Context *tctx, Models *models, Editing_File *fil return(result); } -#include "4coder_profile_static_enable.cpp" - function b32 edit_batch_check(Thread_Context *tctx, Profile_Global_List *list, Batch_Edit *batch){ ProfileTLScope(tctx, list, "batch check"); @@ -453,7 +441,10 @@ edit_batch(Thread_Context *tctx, Models *models, Editing_File *file, Range_i64 old_range = Ii64_neg_inf; Range_i64 new_range = Ii64_neg_inf; - ProfileTLBlockNamed(tctx, &models->profile_list, "batch text edits", profile_edits); + Gap_Buffer *buffer = &file->state.buffer; + + ProfileTLBlockNamed(tctx, &models->profile_list, "batch text edits", + profile_edits); i32 batch_count = 0; i64 shift = 0; for (Batch_Edit *edit = batch; @@ -472,7 +463,7 @@ edit_batch(Thread_Context *tctx, Models *models, Editing_File *file, i64 new_max = (i64)(edit_range.min + insert_string.size); new_range.max = max(new_range.max, new_max); - i64 size = buffer_size(&file->state.buffer); + i64 size = buffer_size(buffer); if (0 <= edit_range.first && edit_range.first <= edit_range.one_past_last && edit_range.one_past_last <= size){ @@ -497,6 +488,9 @@ edit_batch(Thread_Context *tctx, Models *models, Editing_File *file, } } + file_clear_layout_cache(file); + + buffer_remeasure_starts(tctx, buffer, batch); edit_fix_markers(tctx, models, file, batch); post_edit_call_hook(tctx, models, file, new_range, range_size(old_range)); @@ -506,8 +500,6 @@ edit_batch(Thread_Context *tctx, Models *models, Editing_File *file, return(result); } -#include "4coder_profile_static_disable.cpp" - //////////////////////////////// internal Editing_File* diff --git a/custom/4coder_lister_base.cpp b/custom/4coder_lister_base.cpp index 9d2c24b1..26b72389 100644 --- a/custom/4coder_lister_base.cpp +++ b/custom/4coder_lister_base.cpp @@ -637,9 +637,19 @@ run_lister(Application_Links *app, Lister *lister){ if (!handled){ Mapping *mapping = lister->mapping; Command_Map *map = lister->map; - if (ui_fallback_command_dispatch(app, view, mapping, map, &in)){ + + Fallback_Dispatch_Result disp_result = + fallback_command_dispatch(app, mapping, map, &in); + if (disp_result.code == FallbackDispatch_DelayedUICall){ + call_after_ctx_shutdown(app, view, disp_result.func); break; } + if (disp_result.code == FallbackDispatch_Unhandled){ + leave_current_input_unhandled(app); + } + else{ + lister_call_refresh_handler(app, lister); + } } } diff --git a/custom/4coder_profile_inspect.cpp b/custom/4coder_profile_inspect.cpp index 12d47f22..d6dc72fe 100644 --- a/custom/4coder_profile_inspect.cpp +++ b/custom/4coder_profile_inspect.cpp @@ -469,6 +469,9 @@ profile_draw_node(Application_Links *app, View_ID view, Face_ID face_id, draw_rectangle_outline(app, box, 6.f, 3.f, margin); y_pos = y.max; + if (y_pos >= info_box.y1){ + break; + } } } } diff --git a/custom/generated/command_metadata.h b/custom/generated/command_metadata.h index bd59803a..78b00ce6 100644 --- a/custom/generated/command_metadata.h +++ b/custom/generated/command_metadata.h @@ -445,7 +445,7 @@ static Command_Metadata fcoder_metacmd_table[213] = { { PROC_LINKS(miblo_decrement_time_stamp, 0), false, "miblo_decrement_time_stamp", 26, "Decrement a time stamp under the cursor by one second. (format [m]m:ss or h:mm:ss", 81, "w:\\4ed\\code\\custom\\4coder_miblo_numbers.cpp", 43, 237 }, { PROC_LINKS(miblo_increment_time_stamp_minute, 0), false, "miblo_increment_time_stamp_minute", 33, "Increment a time stamp under the cursor by one minute. (format [m]m:ss or h:mm:ss", 81, "w:\\4ed\\code\\custom\\4coder_miblo_numbers.cpp", 43, 243 }, { PROC_LINKS(miblo_decrement_time_stamp_minute, 0), false, "miblo_decrement_time_stamp_minute", 33, "Decrement a time stamp under the cursor by one minute. (format [m]m:ss or h:mm:ss", 81, "w:\\4ed\\code\\custom\\4coder_miblo_numbers.cpp", 43, 249 }, -{ PROC_LINKS(profile_inspect, 0), true, "profile_inspect", 15, "Inspect all currently collected profiling information in 4coder's self profiler.", 80, "w:\\4ed\\code\\custom\\4coder_profile_inspect.cpp", 45, 776 }, +{ PROC_LINKS(profile_inspect, 0), true, "profile_inspect", 15, "Inspect all currently collected profiling information in 4coder's self profiler.", 80, "w:\\4ed\\code\\custom\\4coder_profile_inspect.cpp", 45, 779 }, { PROC_LINKS(default_startup, 0), false, "default_startup", 15, "Default command for responding to a startup event", 49, "w:\\4ed\\code\\custom\\4coder_default_hooks.cpp", 43, 7 }, { PROC_LINKS(default_try_exit, 0), false, "default_try_exit", 16, "Default command for responding to a try-exit event", 50, "w:\\4ed\\code\\custom\\4coder_default_hooks.cpp", 43, 22 }, };