642 lines
26 KiB
C++
642 lines
26 KiB
C++
/*
|
|
* Mr. 4th Dimention - Allen Webster
|
|
*
|
|
* 25.03.2018
|
|
*
|
|
* High level edit procedures
|
|
*
|
|
*/
|
|
|
|
// TOP
|
|
|
|
internal void
|
|
edit_pre_state_change(System_Functions *system, Heap *heap, Models *models, Editing_File *file){
|
|
if (file->state.still_lexing){
|
|
system->cancel_job(BACKGROUND_THREADS, file->state.lex_job);
|
|
if (file->state.swap_array.tokens){
|
|
heap_free(heap, file->state.swap_array.tokens);
|
|
file->state.swap_array.tokens = 0;
|
|
}
|
|
file->state.still_lexing = 0;
|
|
}
|
|
if (file->state.dirty == DirtyState_UpToDate){
|
|
file_set_dirty_flag(file, DirtyState_UnsavedChanges);
|
|
}
|
|
file_unmark_edit_finished(file);
|
|
}
|
|
|
|
internal void
|
|
edit_fix_markers__write_workspace_markers(Dynamic_Workspace *workspace, Buffer_ID buffer_id,
|
|
Cursor_With_Index *cursors, Cursor_With_Index *r_cursors, i32 *cursor_count, i32 *r_cursor_count){
|
|
for (Managed_Buffer_Markers_Header *node = workspace->buffer_markers_list.first;
|
|
node != 0;
|
|
node = node->next){
|
|
if (node->buffer_id != buffer_id) continue;
|
|
|
|
Marker *markers = (Marker*)(node + 1);
|
|
Assert(sizeof(*markers) == node->std_header.item_size);
|
|
i32 count = node->std_header.count;
|
|
for (i32 i = 0; i < count; i += 1){
|
|
if (markers[i].lean_right){
|
|
write_cursor_with_index(r_cursors, r_cursor_count, markers[i].pos);
|
|
}
|
|
else{
|
|
write_cursor_with_index(cursors , cursor_count , markers[i].pos);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
internal void
|
|
edit_fix_markers__read_workspace_markers(Dynamic_Workspace *workspace, Buffer_ID buffer_id,
|
|
Cursor_With_Index *cursors, Cursor_With_Index *r_cursors, i32 *cursor_count, i32 *r_cursor_count){
|
|
for (Managed_Buffer_Markers_Header *node = workspace->buffer_markers_list.first;
|
|
node != 0;
|
|
node = node->next){
|
|
if (node->buffer_id != buffer_id) continue;
|
|
|
|
Marker *markers = (Marker*)(node + 1);
|
|
Assert(sizeof(*markers) == node->std_header.item_size);
|
|
i32 count = node->std_header.count;
|
|
for (i32 i = 0; i < count; i += 1){
|
|
if (markers[i].lean_right){
|
|
markers[i].pos = r_cursors[(*r_cursor_count)++].pos;
|
|
}
|
|
else{
|
|
markers[i].pos = cursors[(*cursor_count)++].pos;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
internal void
|
|
edit_fix_markers(System_Functions *system, Models *models, Editing_File *file, Edit_Array edits){
|
|
Partition *part = &models->mem.part;
|
|
Layout *layout = &models->layout;
|
|
|
|
Assert(edits.count > 0);
|
|
|
|
Temp_Memory cursor_temp = begin_temp_memory(part);
|
|
|
|
Lifetime_Object *file_lifetime_object = file->lifetime_object;
|
|
Buffer_ID file_id = file->id.id;
|
|
Assert(file_lifetime_object != 0);
|
|
|
|
i32 cursor_max = layout_get_open_panel_count(layout)*3;
|
|
i32 total_marker_count = 0;
|
|
{
|
|
total_marker_count += file_lifetime_object->workspace.total_marker_count;
|
|
|
|
i32 key_count = file_lifetime_object->key_count;
|
|
i32 key_index = 0;
|
|
for (Lifetime_Key_Ref_Node *key_node = file_lifetime_object->key_node_first;
|
|
key_node != 0;
|
|
key_node = key_node->next){
|
|
i32 count = clamp_top(lifetime_key_reference_per_node, key_count - key_index);
|
|
for (i32 i = 0; i < count; i += 1){
|
|
Lifetime_Key *key = key_node->keys[i];
|
|
total_marker_count += key->dynamic_workspace.total_marker_count;
|
|
}
|
|
key_index += count;
|
|
}
|
|
}
|
|
|
|
cursor_max += total_marker_count;
|
|
Cursor_With_Index *cursors = push_array(part, Cursor_With_Index, cursor_max);
|
|
Cursor_With_Index *r_cursors = push_array(part, Cursor_With_Index, cursor_max);
|
|
i32 cursor_count = 0;
|
|
i32 r_cursor_count = 0;
|
|
Assert(cursors != 0);
|
|
Assert(r_cursors != 0);
|
|
|
|
for (Panel *panel = layout_get_first_open_panel(layout);
|
|
panel != 0;
|
|
panel = layout_get_next_open_panel(layout, panel)){
|
|
View *view = panel->view;
|
|
if (view->transient.file_data.file == file){
|
|
write_cursor_with_index(cursors, &cursor_count, view->transient.edit_pos.cursor.pos);
|
|
write_cursor_with_index(cursors, &cursor_count, view->transient.edit_pos.mark);
|
|
write_cursor_with_index(cursors, &cursor_count, view->transient.edit_pos.scroll_i);
|
|
}
|
|
}
|
|
|
|
edit_fix_markers__write_workspace_markers(&file_lifetime_object->workspace, file_id, cursors, r_cursors, &cursor_count, &r_cursor_count);
|
|
|
|
{
|
|
i32 key_count = file_lifetime_object->key_count;
|
|
i32 key_index = 0;
|
|
for (Lifetime_Key_Ref_Node *key_node = file_lifetime_object->key_node_first;
|
|
key_node != 0;
|
|
key_node = key_node->next){
|
|
i32 count = clamp_top(lifetime_key_reference_per_node, key_count - key_index);
|
|
for (i32 i = 0; i < count; i += 1){
|
|
Lifetime_Key *key = key_node->keys[i];
|
|
edit_fix_markers__write_workspace_markers(&key->dynamic_workspace, file_id, cursors, r_cursors, &cursor_count, &r_cursor_count);
|
|
}
|
|
key_index += count;
|
|
}
|
|
}
|
|
|
|
if (cursor_count > 0 || r_cursor_count > 0){
|
|
buffer_sort_cursors(cursors, cursor_count);
|
|
#if 0
|
|
if (desc.is_batch){
|
|
buffer_batch_edit_update_cursors(cursors, cursor_count, desc.batch, desc.batch_size, false);
|
|
buffer_batch_edit_update_cursors(r_cursors, r_cursor_count, desc.batch, desc.batch_size, true);
|
|
}
|
|
else{
|
|
buffer_update_cursors(cursors, cursor_count, desc.start, desc.end, desc.shift_amount + (desc.end - desc.start), false);
|
|
buffer_update_cursors(r_cursors, r_cursor_count, desc.start, desc.end, desc.shift_amount + (desc.end - desc.start), true);
|
|
}
|
|
#endif
|
|
|
|
if (edits.count > 1){
|
|
buffer_batch_edit_update_cursors( cursors, cursor_count, edits, false);
|
|
buffer_batch_edit_update_cursors(r_cursors, r_cursor_count, edits, true);
|
|
}
|
|
else{
|
|
Edit edit = edits.vals[0];
|
|
buffer_update_cursors( cursors, cursor_count, edit.range.first, edit.range.one_past_last, edit.length, false);
|
|
buffer_update_cursors(r_cursors, r_cursor_count, edit.range.first, edit.range.one_past_last, edit.length, true);
|
|
}
|
|
|
|
buffer_unsort_cursors(cursors, cursor_count);
|
|
|
|
cursor_count = 0;
|
|
r_cursor_count = 0;
|
|
for (Panel *panel = layout_get_first_open_panel(layout);
|
|
panel != 0;
|
|
panel = layout_get_next_open_panel(layout, panel)){
|
|
View *view = panel->view;
|
|
if (view->transient.file_data.file == file){
|
|
i32 cursor_pos = cursors[cursor_count++].pos;
|
|
Full_Cursor new_cursor = file_compute_cursor(system, file, seek_pos(cursor_pos), 0);
|
|
|
|
GUI_Scroll_Vars scroll = view->transient.edit_pos.scroll;
|
|
|
|
view->transient.edit_pos.mark = cursors[cursor_count++].pos;
|
|
i32 new_scroll_i = cursors[cursor_count++].pos;
|
|
if (view->transient.edit_pos.scroll_i != new_scroll_i){
|
|
view->transient.edit_pos.scroll_i = new_scroll_i;
|
|
|
|
Full_Cursor temp_cursor = file_compute_cursor(system, file, seek_pos(view->transient.edit_pos.scroll_i), 0);
|
|
|
|
f32 y_offset = MOD(view->transient.edit_pos.scroll.scroll_y, view->transient.line_height);
|
|
f32 y_position = temp_cursor.wrapped_y;
|
|
if (file->settings.unwrapped_lines){
|
|
y_position = temp_cursor.unwrapped_y;
|
|
}
|
|
y_position += y_offset;
|
|
|
|
scroll.target_y += round32(y_position - scroll.scroll_y);
|
|
scroll.scroll_y = y_position;
|
|
}
|
|
|
|
view_set_cursor_and_scroll(view, new_cursor, 1, view->transient.file_data.file->settings.unwrapped_lines, scroll);
|
|
}
|
|
}
|
|
|
|
edit_fix_markers__read_workspace_markers(&file_lifetime_object->workspace, file_id, cursors, r_cursors, &cursor_count, &r_cursor_count);
|
|
|
|
i32 key_count = file_lifetime_object->key_count;
|
|
i32 key_index = 0;
|
|
for (Lifetime_Key_Ref_Node *key_node = file_lifetime_object->key_node_first;
|
|
key_node != 0;
|
|
key_node = key_node->next){
|
|
i32 count = clamp_top(lifetime_key_reference_per_node, key_count - key_index);
|
|
for (i32 i = 0; i < count; i += 1){
|
|
Lifetime_Key *key = key_node->keys[i];
|
|
edit_fix_markers__read_workspace_markers(&key->dynamic_workspace, file_id, cursors, r_cursors, &cursor_count, &r_cursor_count);
|
|
}
|
|
key_index += count;
|
|
}
|
|
}
|
|
|
|
end_temp_memory(cursor_temp);
|
|
}
|
|
|
|
internal void
|
|
edit_fix_markers(System_Functions *system, Models *models, Editing_File *file, Edit edit){
|
|
Edit_Array edits = {};
|
|
edits.vals = &edit;
|
|
edits.count = 1;
|
|
edit_fix_markers(system, models, file, edits);
|
|
}
|
|
|
|
internal void
|
|
edit_single(System_Functions *system, Models *models, Editing_File *file, Edit edit, Edit_Behaviors behaviors){
|
|
Mem_Options *mem = &models->mem;
|
|
Heap *heap = &mem->heap;
|
|
Partition *part = &mem->part;
|
|
|
|
Gap_Buffer *buffer = &file->state.buffer;
|
|
Assert(0 <= edit.range.first);
|
|
Assert(edit.range.first <= edit.range.one_past_last);
|
|
Assert(edit.range.one_past_last <= buffer_size(buffer));
|
|
|
|
// 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(heap, &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(system, &mem->heap, models, file);
|
|
|
|
// NOTE(allen): expand spec, compute shift
|
|
i32 shift_amount = buffer_replace_range_compute_shift(edit.range.first, edit.range.one_past_last, edit.length);
|
|
|
|
// NOTE(allen): actual text replacement
|
|
i32 scratch_size = part_remaining(part);
|
|
Assert(scratch_size > 0);
|
|
i32 request_amount = 0;
|
|
for (;buffer_replace_range(buffer, edit.range.first, edit.range.one_past_last, edit.str, edit.length,
|
|
shift_amount, part->base + part->pos, scratch_size, &request_amount);){
|
|
void *new_data = 0;
|
|
if (request_amount > 0){
|
|
new_data = heap_allocate(heap, request_amount);
|
|
}
|
|
void *old_data = buffer_edit_provide_memory(buffer, new_data, request_amount);
|
|
if (old_data){
|
|
heap_free(heap, old_data);
|
|
}
|
|
}
|
|
|
|
// NOTE(allen): line meta data
|
|
i32 line_start = buffer_get_line_number(buffer, edit.range.first);
|
|
i32 line_end = buffer_get_line_number(buffer, edit.range.one_past_last);
|
|
i32 replaced_line_count = line_end - line_start;
|
|
i32 new_line_count = buffer_count_newlines(buffer, edit.range.first, edit.range.first + edit.length);
|
|
i32 line_shift = new_line_count - replaced_line_count;
|
|
|
|
Font_Pointers font = system->font.get_pointers_by_id(file->settings.font_id);
|
|
Assert(font.valid);
|
|
|
|
file_grow_starts_as_needed(heap, buffer, line_shift);
|
|
buffer_remeasure_starts(buffer, line_start, line_end, line_shift, shift_amount);
|
|
|
|
file_allocate_character_starts_as_needed(heap, file);
|
|
buffer_remeasure_character_starts(system, font, buffer, line_start, line_end, line_shift, file->state.character_starts, 0, file->settings.virtual_white);
|
|
|
|
// NOTE(allen): token fixing
|
|
if (file->settings.tokens_exist){
|
|
file_relex(system, models, file, edit.range.first, edit.range.one_past_last, shift_amount);
|
|
}
|
|
|
|
// NOTE(allen): wrap meta data
|
|
file_measure_wraps(system, &models->mem, file, font);
|
|
|
|
// NOTE(allen): cursor fixing
|
|
edit_fix_markers(system, models, file, edit);
|
|
|
|
// NOTE(allen): mark edit finished
|
|
if (file->settings.tokens_exist){
|
|
if (file->settings.virtual_white){
|
|
file_mark_edit_finished(&models->working_set, file);
|
|
}
|
|
}
|
|
else{
|
|
file_mark_edit_finished(&models->working_set, file);
|
|
}
|
|
}
|
|
|
|
#if 0
|
|
internal Edit_Spec
|
|
edit_compute_batch_spec(Heap *heap, Editing_File *file, Buffer_Edit *edits, char *str_base, i32 str_size,
|
|
Buffer_Edit *inverse_array, char *inv_str, i32 inv_max, i32 edit_count, i32 batch_type){
|
|
|
|
i32 inv_str_pos = 0;
|
|
Buffer_Invert_Batch state = {};
|
|
if (buffer_invert_batch(&state, &file->state.buffer, edits, edit_count, inverse_array, inv_str, &inv_str_pos, inv_max)){
|
|
InvalidCodePath;
|
|
}
|
|
|
|
i32 first_child = undo_children_push(heap, &file->state.undo.children, edits, edit_count, (u8*)(str_base), str_size);
|
|
i32 inverse_first_child = undo_children_push(heap, &file->state.undo.children, inverse_array, edit_count, (u8*)(inv_str), inv_str_pos);
|
|
|
|
Edit_Spec spec = {};
|
|
spec.step.type = ED_NORMAL;
|
|
spec.step.first_child = first_child;
|
|
spec.step.inverse_first_child = inverse_first_child;
|
|
spec.step.special_type = batch_type;
|
|
spec.step.child_count = edit_count;
|
|
spec.step.inverse_child_count = edit_count;
|
|
return(spec);
|
|
}
|
|
#endif
|
|
|
|
internal void
|
|
edit_batch(System_Functions *system, Models *models, Editing_File *file, Edit_Array edits, Edit_Behaviors behaviors){
|
|
Mem_Options *mem = &models->mem;
|
|
Heap *heap = &mem->heap;
|
|
Partition *part = &mem->part;
|
|
|
|
Gap_Buffer *buffer = &file->state.buffer;
|
|
Assert(edits.count > 0);
|
|
|
|
// 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(heap, &models->global_history, &file->state.history, buffer, edits, behaviors.batch_type);
|
|
file->state.current_record_index = history_get_record_count(&file->state.history);
|
|
}
|
|
|
|
// NOTE(allen): fixing stuff "beforewards"???
|
|
edit_pre_state_change(system, &mem->heap, models, file);
|
|
|
|
// NOTE(allen): actual text replacement
|
|
void *scratch = push_array(part, u8, 0);
|
|
i32 scratch_size = part_remaining(part);
|
|
Buffer_Batch_State state = {};
|
|
i32 request_amount = 0;
|
|
for (;buffer_batch_edit_step(&state, buffer, edits, scratch, scratch_size, &request_amount);){
|
|
void *new_data = 0;
|
|
if (request_amount > 0){
|
|
new_data = heap_allocate(heap, request_amount);
|
|
}
|
|
void *old_data = buffer_edit_provide_memory(&file->state.buffer, new_data, request_amount);
|
|
if (old_data){
|
|
heap_free(heap, old_data);
|
|
}
|
|
}
|
|
i32 shift_total = state.shift_total;
|
|
|
|
// NOTE(allen): line meta data
|
|
// TODO(allen): Let's try to switch to remeasuring here moron!
|
|
file_measure_starts(heap, &file->state.buffer);
|
|
|
|
Font_Pointers font = system->font.get_pointers_by_id(file->settings.font_id);
|
|
Assert(font.valid);
|
|
|
|
file_allocate_character_starts_as_needed(heap, file);
|
|
buffer_measure_character_starts(system, font, &file->state.buffer, file->state.character_starts, 0, file->settings.virtual_white);
|
|
|
|
// NOTE(allen): token fixing
|
|
switch (behaviors.batch_type){
|
|
case BatchEdit_Normal:
|
|
{
|
|
if (file->settings.tokens_exist){
|
|
// TODO(allen): Write a smart fast one here someday.
|
|
i32 start = edits.vals[0].range.first;
|
|
i32 end = edits.vals[edits.count - 1].range.one_past_last;
|
|
file_relex(system, models, file, start, end, shift_total);
|
|
}
|
|
else{
|
|
file_mark_edit_finished(&models->working_set, file);
|
|
}
|
|
}break;
|
|
|
|
case BatchEdit_PreserveTokens:
|
|
{
|
|
if (file->state.tokens_complete){
|
|
Cpp_Token_Array tokens = file->state.token_array;
|
|
Cpp_Token *token = tokens.tokens;
|
|
Cpp_Token *end_token = tokens.tokens + tokens.count;
|
|
Cpp_Token original = {};
|
|
|
|
Edit *edit = edits.vals;
|
|
Edit *one_past_last_edit = edits.vals + edits.count;
|
|
|
|
i32 shift_amount = 0;
|
|
i32 local_shift = 0;
|
|
|
|
for (;token < end_token; ++token){
|
|
original = *token;
|
|
for (;edit < one_past_last_edit && edit->range.first <= original.start;
|
|
++edit){
|
|
local_shift = (edit->length - (edit->range.one_past_last - edit->range.first));
|
|
shift_amount += local_shift;
|
|
}
|
|
token->start += shift_amount;
|
|
local_shift = 0;
|
|
i32 original_end = original.start + original.size;
|
|
for (;edit < one_past_last_edit && edit->range.first < original_end;
|
|
++edit){
|
|
local_shift += (edit->length - (edit->range.one_past_last - edit->range.first));
|
|
}
|
|
token->size += local_shift;
|
|
shift_amount += local_shift;
|
|
}
|
|
file_mark_edit_finished(&models->working_set, file);
|
|
}
|
|
}break;
|
|
}
|
|
|
|
// NOTE(allen): wrap meta data
|
|
file_measure_wraps(system, &models->mem, file, font);
|
|
|
|
// NOTE(allen): cursor fixing
|
|
edit_fix_markers(system, models, file, edits);
|
|
|
|
// NOTE(allen): mark edit finished
|
|
if (file->settings.tokens_exist){
|
|
if (file->settings.virtual_white){
|
|
file_mark_edit_finished(&models->working_set, file);
|
|
}
|
|
}
|
|
else{
|
|
file_mark_edit_finished(&models->working_set, file);
|
|
}
|
|
}
|
|
|
|
internal void
|
|
file_end_file(Models *models, Editing_File *file){
|
|
if (models->hook_end_file != 0){
|
|
models->hook_end_file(&models->app_links, file->id.id);
|
|
}
|
|
|
|
Heap *heap = &models->mem.heap;
|
|
Lifetime_Allocator *lifetime_allocator = &models->lifetime_allocator;
|
|
lifetime_free_object(heap, lifetime_allocator, file->lifetime_object);
|
|
file->lifetime_object = lifetime_alloc_object(heap, lifetime_allocator, DynamicWorkspace_Buffer, file);
|
|
}
|
|
|
|
internal void
|
|
edit_clear(System_Functions *system, Models *models, Editing_File *file){
|
|
file_end_file(models, file);
|
|
|
|
b32 no_views_see_file = true;
|
|
|
|
Layout *layout = &models->layout;
|
|
for (Panel *panel = layout_get_first_open_panel(layout);
|
|
panel != 0;
|
|
panel = layout_get_next_open_panel(layout, panel)){
|
|
View *view = panel->view;
|
|
if (view->transient.file_data.file == file){
|
|
Full_Cursor cursor = {};
|
|
cursor.line = 1;
|
|
cursor.character = 1;
|
|
cursor.wrap_line = 1;
|
|
view_set_cursor(view, cursor, true, file->settings.unwrapped_lines);
|
|
no_views_see_file = false;
|
|
}
|
|
}
|
|
|
|
if (no_views_see_file){
|
|
block_zero(file->state.edit_pos_stack, sizeof(file->state.edit_pos_stack));
|
|
file->state.edit_pos_stack_top = -1;
|
|
}
|
|
|
|
Edit edit = {};
|
|
edit.range.one_past_last = buffer_size(&file->state.buffer);
|
|
|
|
Edit_Behaviors behaviors = {};
|
|
edit_single(system, models, file, edit, behaviors);
|
|
}
|
|
|
|
internal void
|
|
edit__apply_record_forward(System_Functions *system, Models *models, Editing_File *file, Record *record, Edit_Behaviors behaviors_prototype){
|
|
// NOTE(allen): // NOTE(allen): // NOTE(allen): // NOTE(allen): // NOTE(allen):
|
|
// Whenever you change this also change the backward version!
|
|
|
|
switch (record->kind){
|
|
case RecordKind_Single:
|
|
{
|
|
Edit edit = {};
|
|
edit.str = record->single.str_forward;
|
|
edit.length = record->single.length_forward;
|
|
edit.range.first = record->single.first;
|
|
edit.range.one_past_last = edit.range.first + record->single.length_backward;
|
|
edit_single(system, models, file, edit, behaviors_prototype);
|
|
}break;
|
|
|
|
case RecordKind_Batch:
|
|
{
|
|
Partition *scratch = &models->mem.part;
|
|
Temp_Memory temp = begin_temp_memory(scratch);
|
|
|
|
i32 count = record->batch.count;
|
|
Edit_Array edits = {};
|
|
edits.vals = push_array(scratch, Edit, count);
|
|
edits.count = count;
|
|
|
|
Edit *edit = edits.vals;
|
|
Record_Batch_Slot *batch_slot = record->batch.batch_records;
|
|
char *str_base_forward = record->batch.str_base_forward;
|
|
for (i32 i = 0; i < count; i += 1, edit += 1, batch_slot += 1){
|
|
edit->str = str_base_forward;
|
|
edit->length = batch_slot->length_forward;
|
|
edit->range.first = batch_slot->first;
|
|
edit->range.one_past_last = edit->range.first + batch_slot->length_backward;
|
|
str_base_forward += batch_slot->length_forward;
|
|
}
|
|
|
|
Edit_Behaviors behaviors = behaviors_prototype;
|
|
behaviors.batch_type = record->batch.type;
|
|
edit_batch(system, models, file, edits, behaviors);
|
|
|
|
end_temp_memory(temp);
|
|
}break;
|
|
|
|
case RecordKind_Group:
|
|
{
|
|
Node *sentinel = &record->group.children;
|
|
for (Node *node = sentinel->next;
|
|
node != sentinel;
|
|
node = node->next){
|
|
Record *record = CastFromMember(Record, node, node);
|
|
edit__apply_record_forward(system, models, file, record, behaviors_prototype);
|
|
}
|
|
}break;
|
|
}
|
|
}
|
|
|
|
internal void
|
|
edit__apply_record_backward(System_Functions *system, Models *models, Editing_File *file, Record *record, Edit_Behaviors behaviors_prototype){
|
|
// NOTE(allen): // NOTE(allen): // NOTE(allen): // NOTE(allen): // NOTE(allen):
|
|
// Whenever you change this also change the forward version!
|
|
|
|
switch (record->kind){
|
|
case RecordKind_Single:
|
|
{
|
|
Edit edit = {};
|
|
edit.str = record->single.str_backward;
|
|
edit.length = record->single.length_backward;
|
|
edit.range.first = record->single.first;
|
|
edit.range.one_past_last = edit.range.first + record->single.length_forward;
|
|
edit_single(system, models, file, edit, behaviors_prototype);
|
|
}break;
|
|
|
|
case RecordKind_Batch:
|
|
{
|
|
Partition *scratch = &models->mem.part;
|
|
Temp_Memory temp = begin_temp_memory(scratch);
|
|
|
|
i32 count = record->batch.count;
|
|
Edit_Array edits = {};
|
|
edits.vals = push_array(scratch, Edit, count);
|
|
edits.count = count;
|
|
|
|
i32 shift_amount = 0;
|
|
|
|
Edit *edit = edits.vals;
|
|
Record_Batch_Slot *batch_slot = record->batch.batch_records;
|
|
char *str_base_backward = record->batch.str_base_backward;
|
|
for (i32 i = 0; i < count; i += 1, edit += 1, batch_slot += 1){
|
|
edit->str = str_base_backward;
|
|
edit->length = batch_slot->length_backward;
|
|
edit->range.first = batch_slot->first + shift_amount;
|
|
edit->range.one_past_last = edit->range.first + batch_slot->length_forward;
|
|
str_base_backward += batch_slot->length_backward;
|
|
shift_amount += batch_slot->length_forward - batch_slot->length_backward;
|
|
}
|
|
|
|
Edit_Behaviors behaviors = behaviors_prototype;
|
|
behaviors.batch_type = record->batch.type;
|
|
edit_batch(system, models, file, edits, behaviors);
|
|
|
|
end_temp_memory(temp);
|
|
}break;
|
|
|
|
case RecordKind_Group:
|
|
{
|
|
Node *sentinel = &record->group.children;
|
|
for (Node *node = sentinel->prev;
|
|
node != sentinel;
|
|
node = node->prev){
|
|
Record *record = CastFromMember(Record, node, node);
|
|
edit__apply_record_backward(system, models, file, record, behaviors_prototype);
|
|
}
|
|
}break;
|
|
}
|
|
}
|
|
|
|
internal void
|
|
edit_change_current_history_state(System_Functions *system, Models *models, Editing_File *file, i32 target_index){
|
|
History *history = &file->state.history;
|
|
if (history->activated && file->state.current_record_index != target_index){
|
|
Assert(0 <= target_index && target_index <= history->record_count);
|
|
|
|
i32 current = file->state.current_record_index;
|
|
Record *record = history_get_record(history, current);
|
|
Assert(record != 0);
|
|
Record *dummy_record = history_get_dummy_record(history);
|
|
|
|
Edit_Behaviors behaviors_prototype = {};
|
|
behaviors_prototype.do_not_post_to_history = true;
|
|
|
|
if (current < target_index){
|
|
do{
|
|
current += 1;
|
|
record = CastFromMember(Record, node, record->node.next);
|
|
Assert(record != dummy_record);
|
|
edit__apply_record_forward(system, models, file, record, behaviors_prototype);
|
|
} while (current != target_index);
|
|
}
|
|
else{
|
|
do{
|
|
Assert(record != dummy_record);
|
|
edit__apply_record_backward(system, models, file, record, behaviors_prototype);
|
|
current -= 1;
|
|
record = CastFromMember(Record, node, record->node.prev);
|
|
} while (current != target_index);
|
|
}
|
|
|
|
file->state.current_record_index = current;
|
|
}
|
|
}
|
|
|
|
// BOTTOM
|
|
|