Optimized batch edits remeasurement

master
Allen Webster 2019-10-27 15:37:48 -07:00
parent f2097ac6bc
commit cd24295e8e
7 changed files with 218 additions and 58 deletions

View File

@ -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"

View File

@ -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){

View File

@ -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),

View File

@ -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*

View File

@ -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);
}
}
}

View File

@ -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;
}
}
}
}

View File

@ -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 },
};