Some batch edit optimizations; next need to support batch cursor fixing

This commit is contained in:
Allen Webster 2019-10-25 17:27:47 -07:00
parent 14b71b8172
commit 529ef3928e
6 changed files with 167 additions and 98 deletions

View File

@ -10,10 +10,30 @@
// TOP
internal void
edit_pre_state_change(Models *models, Editing_File *file){
pre_edit_state_change(Models *models, Editing_File *file){
file_add_dirty_flag(file, DirtyState_UnsavedChanges);
}
internal void
pre_edit_history_prep(Editing_File *file, Edit_Behaviors behaviors){
if (!behaviors.do_not_post_to_history){
history_dump_records_after_index(&file->state.history,
file->state.current_record_index);
}
}
internal void
post_edit_call_hook(Thread_Context *tctx, Models *models, Editing_File *file,
Range_i64 new_range, umem original_size){
// NOTE(allen): edit range hook
if (models->buffer_edit_range != 0){
Application_Links app = {};
app.tctx = tctx;
app.cmd_context = models;
models->buffer_edit_range(&app, file->id, new_range, original_size);
}
}
internal void
edit_fix_markers__write_workspace_markers(Dynamic_Workspace *workspace, Buffer_ID buffer_id,
Cursor_With_Index *cursors, Cursor_With_Index *r_cursors,
@ -138,8 +158,14 @@ edit_fix_markers(Thread_Context *tctx, Models *models, Editing_File *file, Edit
if (cursor_count > 0 || r_cursor_count > 0){
buffer_sort_cursors( cursors, cursor_count);
buffer_sort_cursors(r_cursors, r_cursor_count);
buffer_update_cursors( cursors, cursor_count, edit.range.first, edit.range.one_past_last, edit.text.size, false);
buffer_update_cursors(r_cursors, r_cursor_count, edit.range.first, edit.range.one_past_last, edit.text.size, true);
buffer_update_cursors( cursors, cursor_count,
edit.range.first, edit.range.one_past_last,
edit.text.size, false);
buffer_update_cursors(r_cursors, r_cursor_count,
edit.range.first, edit.range.one_past_last,
edit.text.size, true);
buffer_unsort_cursors( cursors, cursor_count);
buffer_unsort_cursors(r_cursors, r_cursor_count);
@ -178,7 +204,20 @@ edit_fix_markers(Thread_Context *tctx, Models *models, Editing_File *file, Edit
}
internal void
edit_single(Thread_Context *tctx, Models *models, Editing_File *file,
file_end_file(Thread_Context *tctx, Models *models, Editing_File *file){
if (models->end_buffer != 0){
Application_Links app = {};
app.tctx = tctx;
app.cmd_context = models;
models->end_buffer(&app, file->id);
}
Lifetime_Allocator *lifetime_allocator = &models->lifetime_allocator;
lifetime_free_object(lifetime_allocator, file->lifetime_object);
file->lifetime_object = lifetime_alloc_object(lifetime_allocator, DynamicWorkspace_Buffer, file);
}
internal void
edit__apply(Thread_Context *tctx, Models *models, Editing_File *file,
Interval_i64 range, String_Const_u8 string, Edit_Behaviors behaviors){
Edit edit = {};
edit.text = string;
@ -193,21 +232,13 @@ edit_single(Thread_Context *tctx, Models *models, Editing_File *file,
// NOTE(allen): history update
if (!behaviors.do_not_post_to_history){
history_dump_records_after_index(&file->state.history, file->state.current_record_index);
history_record_edit(&models->global_history, &file->state.history, buffer, edit);
file->state.current_record_index = history_get_record_count(&file->state.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): fixing stuff beforewards????
edit_pre_state_change(models, file);
// NOTE(allen): save the original text for the edit range hook.
String_Const_u8 original_text = {};
if (models->buffer_edit_range != 0){
original_text = buffer_stringify(scratch, &file->state.buffer, edit.range);
}
// NOTE(allen): expand spec, compute shift
// NOTE(allen): compute shift
i64 shift_amount = replace_range_shift(edit.range, (i64)edit.text.size);
// NOTE(allen): actual text replacement
@ -225,28 +256,18 @@ edit_single(Thread_Context *tctx, Models *models, Editing_File *file,
// NOTE(allen): cursor fixing
edit_fix_markers(tctx, models, file, edit);
// NOTE(allen): edit range hook
if (models->buffer_edit_range != 0){
Interval_i64 new_range = Ii64(edit.range.first, edit.range.first + edit.text.size);
Application_Links app = {};
app.tctx = tctx;;
app.cmd_context = models;
models->buffer_edit_range(&app, file->id, new_range, original_text);
}
}
internal void
file_end_file(Thread_Context *tctx, Models *models, Editing_File *file){
if (models->end_buffer != 0){
Application_Links app = {};
app.tctx = tctx;
app.cmd_context = models;
models->end_buffer(&app, file->id);
}
Lifetime_Allocator *lifetime_allocator = &models->lifetime_allocator;
lifetime_free_object(lifetime_allocator, file->lifetime_object);
file->lifetime_object = lifetime_alloc_object(lifetime_allocator, DynamicWorkspace_Buffer, file);
edit_single(Thread_Context *tctx, Models *models, Editing_File *file,
Range_i64 range, String_Const_u8 string, Edit_Behaviors behaviors){
pre_edit_state_change(models, file);
pre_edit_history_prep(file, behaviors);
edit__apply(tctx, models, file, range, string, behaviors);
post_edit_call_hook(tctx, models, file,
Ii64_size(range.first, string.size), range_size(range));
}
internal void
@ -395,25 +416,41 @@ internal b32
edit_batch(Thread_Context *tctx, Models *models, Editing_File *file,
Batch_Edit *batch, Edit_Behaviors behaviors){
b32 result = true;
if (batch != 0){
pre_edit_state_change(models, file);
pre_edit_history_prep(file, behaviors);
History_Record_Index start_index = 0;
if (history_is_activated(&file->state.history)){
start_index = file->state.current_record_index;
}
Range_i64 old_range = Ii64_neg_inf;
Range_i64 new_range = Ii64_neg_inf;
i64 shift = 0;
for (Batch_Edit *edit = batch;
edit != 0;
edit = edit->next){
String_Const_u8 insert_string = edit->edit.text;
Interval_i64 edit_range = edit->edit.range;
Range_i64 edit_range = edit->edit.range;
old_range.min = min(old_range.min, edit_range.min);
old_range.max = max(old_range.max, edit_range.max);
edit_range.first += shift;
edit_range.one_past_last += shift;
new_range.min = min(new_range.min, edit_range.min);
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);
if (0 <= edit_range.first &&
edit_range.first <= edit_range.one_past_last &&
edit_range.one_past_last <= size){
edit_single(tctx, models, file, edit_range, insert_string, behaviors);
edit__apply(tctx, models, file, edit_range, insert_string, behaviors);
shift += replace_range_shift(edit_range, insert_string.size);
}
else{
@ -428,7 +465,10 @@ edit_batch(Thread_Context *tctx, Models *models, Editing_File *file,
edit_merge_history_range(tctx, models, file, start_index + 1, last_index, RecordMergeFlag_StateInRange_ErrorOut);
}
}
post_edit_call_hook(tctx, models, file, new_range, range_size(old_range));
}
return(result);
}

View File

@ -1797,6 +1797,11 @@ If32(){
return(interval);
}
global Interval_i32 Ii32_neg_inf = {max_i32, min_i32};
global Interval_i64 Ii64_neg_inf = {max_i64, min_i64};
global Interval_u64 Iu64_neg_inf = {max_u64, 0};
global Interval_f32 If32_neg_inf = {max_f32, -max_f32};
internal b32
operator==(Interval_i32 a, Interval_i32 b){
return(a.min == b.min && a.max == b.max);

View File

@ -794,10 +794,10 @@ BUFFER_HOOK_SIG(default_file_save){
}
BUFFER_EDIT_RANGE_SIG(default_buffer_edit_range){
// buffer_id, new_range, text
// buffer_id, new_range, original_size
ProfileScope(app, "default edit range");
Interval_i64 old_range = Ii64(new_range.first, new_range.first + text.size);
Interval_i64 old_range = Ii64(new_range.first, new_range.first + original_size);
i64 insert_size = range_size(new_range);
i64 text_shift = replace_range_shift(old_range, insert_size);
@ -812,63 +812,81 @@ BUFFER_EDIT_RANGE_SIG(default_buffer_edit_range){
else{
Token_Array *ptr = scope_attachment(app, scope, attachment_tokens, Token_Array);
if (ptr != 0 && ptr->tokens != 0){
ProfileBlockNamed(app, "attempt resync", profile_attempt_resync);
Base_Allocator *allocator = managed_scope_allocator(app, scope);
b32 do_full_relex = false;
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);
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);
}
Base_Allocator *allocator = managed_scope_allocator(app, scope);
Token_Relex relex = token_relex(relex_list, relex_range.first - text_shift,
ptr->tokens, token_index_first, token_index_resync_guess);
if (relex.successful_resync){
i64 token_index_resync = relex.first_resync_index;
Interval_i64 head = Ii64(0, token_index_first);
Interval_i64 replaced = Ii64(token_index_first, token_index_resync);
Interval_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;
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);
profile_attempt_resync.close_now();
if (relex.successful_resync){
ProfileBlock(app, "apply resync");
i64 token_index_resync = relex.first_resync_index;
Interval_i64 head = Ii64(0, token_index_first);
Interval_i64 replaced = Ii64(token_index_first, token_index_resync);
Interval_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;
}
else{
do_full_relex = true;
}
}
if (do_full_relex){
base_free(allocator, ptr->tokens);
block_zero_struct(ptr);
*lex_task_ptr = async_task_no_dep(&global_async_system, do_full_lex_async,
*lex_task_ptr = async_task_no_dep(&global_async_system,
do_full_lex_async,
make_data_struct(&buffer_id));
}
}

View File

@ -14,6 +14,7 @@ rewrite_lines_to_crlf(Application_Links *app, Buffer_ID buffer){
Batch_Edit *first = 0;
Batch_Edit *last = 0;
ProfileBlockNamed(app, "build batch edit", profile_batch);
i64 pos = -1;
Character_Predicate pred_cr = character_predicate_from_character('\r');
Character_Predicate pred_lf = character_predicate_from_character('\n');
@ -46,6 +47,7 @@ rewrite_lines_to_crlf(Application_Links *app, Buffer_ID buffer){
edit->edit.range = Ii64(pos);
}
}
profile_batch.close_now();
buffer_batch_edit(app, buffer, first);
}
@ -58,6 +60,7 @@ rewrite_lines_to_lf(Application_Links *app, Buffer_ID buffer){
Batch_Edit *first = 0;
Batch_Edit *last = 0;
ProfileBlockNamed(app, "build batch edit", profile_batch);
i64 pos = -1;
Character_Predicate pred = character_predicate_from_character('\r');
for (;;){
@ -73,6 +76,7 @@ rewrite_lines_to_lf(Application_Links *app, Buffer_ID buffer){
edit->edit.text = string_u8_litexpr("");
edit->edit.range = match.range;
}
profile_batch.close_now();
buffer_batch_edit(app, buffer, first);
}

View File

@ -558,16 +558,17 @@ struct Buffer_Name_Conflict_Entry{
typedef void Buffer_Name_Resolver_Function(Application_Links *app, Buffer_Name_Conflict_Entry *conflicts, i32 conflict_count);
#define BUFFER_NAME_RESOLVER_SIG(n) \
void n(Application_Links *app, Buffer_Name_Conflict_Entry *conflicts, i32 conflict_count)
void n(Application_Links *app, Buffer_Name_Conflict_Entry *conflicts, \
i32 conflict_count)
typedef i32 Buffer_Hook_Function(Application_Links *app, Buffer_ID buffer_id);
#define BUFFER_HOOK_SIG(name) i32 name(Application_Links *app, Buffer_ID buffer_id)
typedef i32 Buffer_Edit_Range_Function(Application_Links *app, Buffer_ID buffer_id,
Range_i64 new_range, String_Const_u8 text);
Range_i64 new_range, umem original_size);
#define BUFFER_EDIT_RANGE_SIG(name) \
i32 name(Application_Links *app, Buffer_ID buffer_id, \
Interval_i64 new_range, String_Const_u8 text)
Interval_i64 new_range, umem original_size)
typedef Vec2_f32 Delta_Rule_Function(Vec2_f32 pending, b32 is_new_target, f32 dt, void *data);
#define DELTA_RULE_SIG(name) \
@ -580,7 +581,8 @@ typedef void New_Clipboard_Contents_Function(Application_Links *app, String_Cons
void name(Application_Links *app, String_Const_u8 contents)
typedef void Render_Caller_Function(Application_Links *app, Frame_Info frame_info, View_ID view);
#define RENDER_CALLER_SIG(name) void name(Application_Links *app, Frame_Info frame_info, View_ID view)
#define RENDER_CALLER_SIG(name) \
void name(Application_Links *app, Frame_Info frame_info, View_ID view)
typedef i64 Command_Map_ID;

View File

@ -354,10 +354,10 @@ static Command_Metadata fcoder_metacmd_table[213] = {
{ PROC_LINKS(redo_all_buffers, 0), false, "redo_all_buffers", 16, "Advances forward through the undo history in the buffer containing the most recent regular edit.", 96, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 1922 },
{ PROC_LINKS(open_in_other, 0), false, "open_in_other", 13, "Interactively opens a file in the other panel.", 46, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 2023 },
{ PROC_LINKS(default_file_externally_modified, 0), false, "default_file_externally_modified", 32, "Notes the external modification of attached files by printing a message.", 72, "w:\\4ed\\code\\custom\\4coder_base_commands.cpp", 43, 2030 },
{ PROC_LINKS(set_eol_mode_to_crlf, 0), false, "set_eol_mode_to_crlf", 20, "Puts the buffer in crlf line ending mode.", 41, "w:\\4ed\\code\\custom\\4coder_eol.cpp", 33, 82 },
{ PROC_LINKS(set_eol_mode_to_lf, 0), false, "set_eol_mode_to_lf", 18, "Puts the buffer in lf line ending mode.", 39, "w:\\4ed\\code\\custom\\4coder_eol.cpp", 33, 93 },
{ PROC_LINKS(set_eol_mode_to_binary, 0), false, "set_eol_mode_to_binary", 22, "Puts the buffer in bin line ending mode.", 40, "w:\\4ed\\code\\custom\\4coder_eol.cpp", 33, 104 },
{ PROC_LINKS(set_eol_mode_from_contents, 0), false, "set_eol_mode_from_contents", 26, "Sets the buffer's line ending mode to match the contents of the buffer.", 71, "w:\\4ed\\code\\custom\\4coder_eol.cpp", 33, 115 },
{ PROC_LINKS(set_eol_mode_to_crlf, 0), false, "set_eol_mode_to_crlf", 20, "Puts the buffer in crlf line ending mode.", 41, "w:\\4ed\\code\\custom\\4coder_eol.cpp", 33, 86 },
{ PROC_LINKS(set_eol_mode_to_lf, 0), false, "set_eol_mode_to_lf", 18, "Puts the buffer in lf line ending mode.", 39, "w:\\4ed\\code\\custom\\4coder_eol.cpp", 33, 97 },
{ PROC_LINKS(set_eol_mode_to_binary, 0), false, "set_eol_mode_to_binary", 22, "Puts the buffer in bin line ending mode.", 40, "w:\\4ed\\code\\custom\\4coder_eol.cpp", 33, 108 },
{ PROC_LINKS(set_eol_mode_from_contents, 0), false, "set_eol_mode_from_contents", 26, "Sets the buffer's line ending mode to match the contents of the buffer.", 71, "w:\\4ed\\code\\custom\\4coder_eol.cpp", 33, 119 },
{ PROC_LINKS(interactive_switch_buffer, 0), true, "interactive_switch_buffer", 25, "Interactively switch to an open buffer.", 39, "w:\\4ed\\code\\custom\\4coder_lists.cpp", 35, 416 },
{ PROC_LINKS(interactive_kill_buffer, 0), true, "interactive_kill_buffer", 23, "Interactively kill an open buffer.", 34, "w:\\4ed\\code\\custom\\4coder_lists.cpp", 35, 426 },
{ PROC_LINKS(interactive_open_or_new, 0), true, "interactive_open_or_new", 23, "Interactively open a file out of the file system.", 49, "w:\\4ed\\code\\custom\\4coder_lists.cpp", 35, 474 },