567 lines
20 KiB
C++
567 lines
20 KiB
C++
/*
|
|
* Mr. 4th Dimention - Allen Webster
|
|
*
|
|
* 03.01.2017
|
|
*
|
|
* Working_Set data structure
|
|
*
|
|
*/
|
|
|
|
// TOP
|
|
|
|
internal void
|
|
working_set_extend_memory(Working_Set *working_set, Editing_File *new_space, i16 number_of_files){
|
|
Assert(working_set->array_count < working_set->array_max);
|
|
|
|
i16 high_part = working_set->array_count++;
|
|
working_set->file_arrays[high_part].files = new_space;
|
|
working_set->file_arrays[high_part].size = number_of_files;
|
|
|
|
working_set->file_max += number_of_files;
|
|
|
|
Buffer_Slot_ID id = {};
|
|
id.part[1] = high_part;
|
|
|
|
Editing_File *file_ptr = new_space;
|
|
Node *free_sentinel = &working_set->free_sentinel;
|
|
for (i16 i = 0; i < number_of_files; ++i, ++file_ptr){
|
|
id.part[0] = i;
|
|
file_ptr->id = id;
|
|
dll_insert(free_sentinel, &file_ptr->main_chain_node);
|
|
}
|
|
}
|
|
|
|
internal void
|
|
working_set_file_default_settings(Working_Set *working_set, Editing_File *file){
|
|
memset(&file->settings, 0, sizeof(file->settings));
|
|
file->settings.display_width = working_set->default_display_width;
|
|
file->settings.minimum_base_display_width = working_set->default_minimum_base_display_width;
|
|
file->settings.wrap_indicator = WrapIndicator_Show_At_Wrap_Edge;
|
|
}
|
|
|
|
// TODO(allen): do(restructure so that editing file is returned cleared to zero)
|
|
internal Editing_File*
|
|
working_set_alloc_always(Working_Set *working_set, Heap *heap, Lifetime_Allocator *lifetime_allocator){
|
|
Editing_File *result = 0;
|
|
if (working_set->file_count == working_set->file_max && working_set->array_count < working_set->array_max){
|
|
i16 new_count = (i16)clamp_top(working_set->file_max, max_i16);
|
|
Editing_File *new_chunk = heap_array(heap, Editing_File, new_count);
|
|
working_set_extend_memory(working_set, new_chunk, new_count);
|
|
}
|
|
|
|
if (working_set->file_count < working_set->file_max){
|
|
Node *node = working_set->free_sentinel.next;
|
|
Assert(node != &working_set->free_sentinel);
|
|
result = CastFromMember(Editing_File, main_chain_node, node);
|
|
|
|
++working_set->file_count;
|
|
|
|
dll_remove(node);
|
|
dll_insert(&working_set->used_sentinel, node);
|
|
|
|
Node node_val = result->main_chain_node;
|
|
Buffer_Slot_ID id_val = result->id;
|
|
memset(result, 0, sizeof(*result));
|
|
result->main_chain_node = node_val;
|
|
result->id = id_val;
|
|
|
|
working_set_file_default_settings(working_set, result);
|
|
}
|
|
|
|
return(result);
|
|
}
|
|
|
|
internal void
|
|
working_set_free_file(Heap *heap, Working_Set *working_set, Editing_File *file){
|
|
if (working_set->sync_check_iter == &file->main_chain_node){
|
|
working_set->sync_check_iter = working_set->sync_check_iter->next;
|
|
}
|
|
file->is_dummy = true;
|
|
dll_remove(&file->main_chain_node);
|
|
dll_insert(&working_set->free_sentinel, &file->main_chain_node);
|
|
--working_set->file_count;
|
|
}
|
|
|
|
internal Editing_File*
|
|
working_set_index(Working_Set *working_set, Buffer_Slot_ID id){
|
|
Editing_File *result = 0;
|
|
File_Array *array = 0;
|
|
if (id.part[1] >= 0 && id.part[1] < working_set->array_count){
|
|
array = working_set->file_arrays + id.part[1];
|
|
if (id.part[0] >= 0 && id.part[0] < array->size){
|
|
result = array->files + id.part[0];
|
|
}
|
|
}
|
|
return(result);
|
|
}
|
|
|
|
internal Editing_File*
|
|
working_set_index(Working_Set *working_set, i32 id){
|
|
return(working_set_index(working_set, to_file_id(id)));
|
|
}
|
|
|
|
internal Editing_File*
|
|
working_set_get_active_file(Working_Set *working_set, Buffer_Slot_ID id){
|
|
Editing_File *result = working_set_index(working_set, id);
|
|
if (result != 0 && result->is_dummy){
|
|
result = 0;
|
|
}
|
|
return(result);
|
|
}
|
|
|
|
internal Editing_File*
|
|
working_set_get_active_file(Working_Set *working_set, Buffer_ID id){
|
|
return(working_set_get_active_file(working_set, to_file_id(id)));
|
|
}
|
|
|
|
// TODO(allen): REWRITE all of working set
|
|
internal void
|
|
working_set_init(System_Functions *system, Working_Set *working_set, Arena *arena){
|
|
i16 init_count = 16;
|
|
i16 array_init_count = 256;
|
|
|
|
dll_init_sentinel(&working_set->free_sentinel);
|
|
dll_init_sentinel(&working_set->used_sentinel);
|
|
|
|
working_set->edit_finished_list_first = 0;
|
|
working_set->edit_finished_list_last = 0;
|
|
working_set->edit_finished_count = 0;
|
|
|
|
working_set->time_of_next_edit_finished_signal = 0;
|
|
working_set->edit_finished_timer = system->wake_up_timer_create();
|
|
working_set->do_not_mark_edits = false;
|
|
|
|
working_set->array_max = array_init_count;
|
|
working_set->file_arrays = push_array(arena, File_Array, array_init_count);
|
|
|
|
Editing_File *files = push_array(arena, Editing_File, init_count);
|
|
working_set_extend_memory(working_set, files, init_count);
|
|
|
|
// TODO(NAME): do(clean up the rest of the null_file)
|
|
// Unclear that this is still needed. But double check that the buffer id 0 does not start getting used by the next real buffer when this
|
|
// is removed before actually removing it. Buffer id cannot be allowed to be zero on real buffers.
|
|
#if 1
|
|
// NOTE(allen): init null file
|
|
{
|
|
Editing_File *null_file = working_set_index(working_set, 0);
|
|
dll_remove(&null_file->main_chain_node);
|
|
null_file->is_dummy = true;
|
|
++working_set->file_count;
|
|
}
|
|
#endif
|
|
|
|
working_set->canon_table = make_table_Data_u64(arena->base_allocator, 128);
|
|
working_set->name_table = make_table_Data_u64(arena->base_allocator, 128);
|
|
}
|
|
|
|
internal Editing_File*
|
|
working_set_contains__generic(Working_Set *working_set, Table_Data_u64 *table, String_Const_u8 name){
|
|
Editing_File *result = 0;
|
|
u64 val = 0;
|
|
if (table_read(table, make_data(name.str, name.size), &val)){
|
|
result = working_set_index(working_set, to_file_id((Buffer_ID)val));
|
|
}
|
|
return(result);
|
|
}
|
|
|
|
internal b32
|
|
working_set_add__generic(Table_Data_u64 *table, Buffer_ID id, String_Const_u8 name){
|
|
return(table_insert(table, make_data(name.str, name.size), id));
|
|
}
|
|
|
|
internal void
|
|
working_set_remove__generic(Table_Data_u64 *table, String_Const_u8 name){
|
|
table_erase(table, make_data(name.str, name.size));
|
|
}
|
|
|
|
internal Editing_File*
|
|
working_set_contains_canon(Working_Set *working_set, String_Const_u8 name){
|
|
return(working_set_contains__generic(working_set, &working_set->canon_table, name));
|
|
}
|
|
|
|
internal b32
|
|
working_set_canon_add(Working_Set *working_set, Editing_File *file, String_Const_u8 name){
|
|
return(working_set_add__generic(&working_set->canon_table, file->id.id, name));
|
|
}
|
|
|
|
internal void
|
|
working_set_canon_remove(Working_Set *working_set, String_Const_u8 name){
|
|
working_set_remove__generic(&working_set->canon_table, name);
|
|
}
|
|
|
|
internal Editing_File*
|
|
working_set_contains_name(Working_Set *working_set, String_Const_u8 name){
|
|
return(working_set_contains__generic(working_set, &working_set->name_table, name));
|
|
}
|
|
|
|
internal b32
|
|
working_set_add_name(Heap *heap, Working_Set *working_set, Editing_File *file, String_Const_u8 name){
|
|
return(working_set_add__generic(&working_set->name_table, file->id.id, name));
|
|
}
|
|
|
|
internal void
|
|
working_set_remove_name(Working_Set *working_set, String_Const_u8 name){
|
|
working_set_remove__generic(&working_set->name_table, name);
|
|
}
|
|
|
|
internal Editing_File*
|
|
get_file_from_identifier(System_Functions *system, Working_Set *working_set, Buffer_Identifier buffer){
|
|
Editing_File *file = 0;
|
|
if (buffer.id != 0){
|
|
file = working_set_get_active_file(working_set, buffer.id);
|
|
}
|
|
else if (buffer.name != 0){
|
|
String_Const_u8 name = SCu8(buffer.name, buffer.name_len);
|
|
file = working_set_contains_name(working_set, name);
|
|
}
|
|
return(file);
|
|
}
|
|
|
|
////////////////////////////////
|
|
|
|
// TODO(allen): Bring the clipboard fully to the custom side.
|
|
internal String_Const_u8*
|
|
working_set_next_clipboard_string(Heap *heap, Working_Set *working, umem str_size){
|
|
i32 clipboard_current = working->clipboard_current;
|
|
if (working->clipboard_size == 0){
|
|
clipboard_current = 0;
|
|
working->clipboard_size = 1;
|
|
}
|
|
else{
|
|
++clipboard_current;
|
|
if (clipboard_current >= working->clipboard_max_size){
|
|
clipboard_current = 0;
|
|
}
|
|
else if (working->clipboard_size <= clipboard_current){
|
|
working->clipboard_size = clipboard_current + 1;
|
|
}
|
|
}
|
|
String_Const_u8 *result = &working->clipboards[clipboard_current];
|
|
working->clipboard_current = clipboard_current;
|
|
working->clipboard_rolling = clipboard_current;
|
|
if (result->str != 0){
|
|
heap_free(heap, result->str);
|
|
}
|
|
u8 *new_str = (u8*)heap_allocate(heap, (i32)(str_size + 1));
|
|
*result = SCu8(new_str, str_size);
|
|
return(result);
|
|
}
|
|
|
|
internal String_Const_u8*
|
|
working_set_clipboard_index(Working_Set *working, i32 index){
|
|
String_Const_u8 *result = 0;
|
|
i32 size = working->clipboard_size;
|
|
i32 current = working->clipboard_current;
|
|
if (index >= 0 && size > 0){
|
|
index = index % size;
|
|
index = current + size - index;
|
|
index = index % size;
|
|
result = &working->clipboards[index];
|
|
}
|
|
return(result);
|
|
}
|
|
|
|
internal String_Const_u8*
|
|
working_set_clipboard_head(Working_Set *working){
|
|
String_Const_u8 *result = 0;
|
|
if (working->clipboard_size > 0){
|
|
working->clipboard_rolling = 0;
|
|
result = working_set_clipboard_index(working, working->clipboard_rolling);
|
|
}
|
|
return(result);
|
|
}
|
|
|
|
internal String_Const_u8*
|
|
working_set_clipboard_roll_down(Working_Set *working){
|
|
String_Const_u8 *result = 0;
|
|
if (working->clipboard_size > 0){
|
|
i32 clipboard_index = working->clipboard_rolling;
|
|
++clipboard_index;
|
|
working->clipboard_rolling = clipboard_index;
|
|
result = working_set_clipboard_index(working, working->clipboard_rolling);
|
|
}
|
|
return(result);
|
|
}
|
|
|
|
////////////////////////////////
|
|
|
|
// TODO(allen): get rid of this???
|
|
internal b32
|
|
get_canon_name(System_Functions *system, Arena *scratch, String_Const_u8 file_name, Editing_File_Name *canon_name){
|
|
Temp_Memory temp = begin_temp(scratch);
|
|
String_Const_u8 canonical = system->get_canonical(scratch, file_name);
|
|
umem size = Min(sizeof(canon_name->name_space), canonical.size);
|
|
block_copy(canon_name->name_space, canonical.str, size);
|
|
canon_name->name_size = size;
|
|
end_temp(temp);
|
|
file_name_terminate(canon_name);
|
|
return(canon_name->name_size > 0);
|
|
}
|
|
|
|
internal void
|
|
file_bind_file_name(System_Functions *system, Heap *heap, Working_Set *working_set, Editing_File *file, String_Const_u8 canon_file_name){
|
|
Assert(file->unique_name.name_size == 0);
|
|
Assert(file->canon.name_size == 0);
|
|
umem size = canon_file_name.size;
|
|
size = clamp_top(size, sizeof(file->canon.name_space) - 1);
|
|
file->canon.name_size = size;
|
|
block_copy(file->canon.name_space, canon_file_name.str, size);
|
|
file_name_terminate(&file->canon);
|
|
system->add_listener((char*)file->canon.name_space);
|
|
b32 result = working_set_canon_add(working_set, file, string_from_file_name(&file->canon));
|
|
Assert(result);
|
|
}
|
|
|
|
internal void
|
|
buffer_unbind_file(System_Functions *system, Working_Set *working_set, Editing_File *file){
|
|
Assert(file->unique_name.name_size == 0);
|
|
Assert(file->canon.name_size != 0);
|
|
system->remove_listener((char*)file->canon.name_space);
|
|
working_set_canon_remove(working_set, string_from_file_name(&file->canon));
|
|
file->canon.name_size = 0;
|
|
}
|
|
|
|
internal b32
|
|
buffer_name_has_conflict(Working_Set *working_set, String_Const_u8 base_name){
|
|
b32 hit_conflict = false;
|
|
Node *used_nodes = &working_set->used_sentinel;
|
|
for (Node *node = used_nodes->next; node != used_nodes; node = node->next){
|
|
Editing_File *file_ptr = CastFromMember(Editing_File, main_chain_node, node);
|
|
if (file_is_ready(file_ptr) && string_match(base_name, string_from_file_name(&file_ptr->unique_name))){
|
|
hit_conflict = true;
|
|
break;
|
|
}
|
|
}
|
|
return(hit_conflict);
|
|
}
|
|
|
|
internal void
|
|
buffer_resolve_name_low_level(Arena *scratch, Working_Set *working_set, Editing_File_Name *name, String_Const_u8 base_name){
|
|
umem size = base_name.size;
|
|
size = clamp_top(size, sizeof(name->name_space));
|
|
block_copy(name->name_space, base_name.str, size);
|
|
String_u8 string = Su8(name->name_space, size, sizeof(name->name_space));
|
|
umem original_size = string.size;
|
|
u64 file_x = 0;
|
|
for (b32 hit_conflict = true; hit_conflict;){
|
|
hit_conflict = buffer_name_has_conflict(working_set, string.string);
|
|
if (hit_conflict){
|
|
file_x += 1;
|
|
string.size = original_size;
|
|
Temp_Memory temp = begin_temp(scratch);
|
|
String_Const_u8 int_str = string_from_integer(scratch, file_x, 10);
|
|
string_append(&string, string_u8_litexpr(" ("));
|
|
string_append(&string, int_str);
|
|
string_append(&string, string_u8_litexpr(")"));
|
|
end_temp(temp);
|
|
}
|
|
}
|
|
name->name_size = string.size;
|
|
}
|
|
|
|
internal void
|
|
buffer_bind_name_low_level(Arena *scratch, Heap *heap, Working_Set *working_set, Editing_File *file, String_Const_u8 base_name, String_Const_u8 name){
|
|
Assert(file->base_name.name_size == 0);
|
|
Assert(file->unique_name.name_size == 0);
|
|
|
|
Editing_File_Name new_name = {};
|
|
buffer_resolve_name_low_level(scratch, working_set, &new_name, name);
|
|
|
|
{
|
|
umem size = base_name.size;
|
|
size = clamp_top(size, sizeof(file->base_name.name_space));
|
|
block_copy(file->base_name.name_space, base_name.str, size);
|
|
file->base_name.name_size = size;
|
|
}
|
|
|
|
{
|
|
umem size = new_name.name_size;
|
|
block_copy(file->unique_name.name_space, new_name.name_space, size);
|
|
file->unique_name.name_size = size;
|
|
}
|
|
|
|
b32 result = working_set_add_name(heap, working_set, file, string_from_file_name(&file->unique_name));
|
|
Assert(result);
|
|
}
|
|
|
|
internal void
|
|
buffer_unbind_name_low_level(Working_Set *working_set, Editing_File *file){
|
|
Assert(file->base_name.name_size != 0);
|
|
Assert(file->unique_name.name_size != 0);
|
|
working_set_remove_name(working_set, string_from_file_name(&file->unique_name));
|
|
file->base_name.name_size = 0;
|
|
file->unique_name.name_size = 0;
|
|
}
|
|
|
|
internal void
|
|
buffer_bind_name(Models *models, Heap *heap, Arena *scratch, Working_Set *working_set, Editing_File *file, String_Const_u8 base_name){
|
|
Temp_Memory temp = begin_temp(scratch);
|
|
|
|
// List of conflict files.
|
|
struct Node_Ptr{
|
|
Node_Ptr *next;
|
|
Editing_File *file_ptr;
|
|
};
|
|
Node_Ptr *conflict_first = 0;
|
|
Node_Ptr *conflict_last = 0;
|
|
i32 conflict_count = 0;
|
|
|
|
{
|
|
Node_Ptr *node = push_array(scratch, Node_Ptr, 1);
|
|
sll_queue_push(conflict_first, conflict_last, node);
|
|
node->file_ptr = file;
|
|
conflict_count += 1;
|
|
}
|
|
|
|
Node *used_nodes = &working_set->used_sentinel;
|
|
for (Node *node = used_nodes->next;
|
|
node != used_nodes;
|
|
node = node->next){
|
|
Editing_File *file_ptr = CastFromMember(Editing_File, main_chain_node, node);
|
|
if (file_is_ready(file_ptr) && string_match(base_name, string_from_file_name(&file_ptr->base_name))){
|
|
Node_Ptr *new_node = push_array(scratch, Node_Ptr, 1);
|
|
sll_queue_push(conflict_first, conflict_last, new_node);
|
|
new_node->file_ptr = file_ptr;
|
|
conflict_count += 1;
|
|
}
|
|
}
|
|
|
|
// Fill conflict array.
|
|
Buffer_Name_Conflict_Entry *conflicts = push_array(scratch, Buffer_Name_Conflict_Entry, conflict_count);
|
|
|
|
{
|
|
i32 i = 0;
|
|
for (Node_Ptr *node = conflict_first;
|
|
node != 0;
|
|
node = node->next, i += 1){
|
|
Editing_File *file_ptr = node->file_ptr;
|
|
Buffer_Name_Conflict_Entry *entry = &conflicts[i];
|
|
entry->buffer_id = file_ptr->id.id;
|
|
|
|
entry->file_name = push_string_copy(scratch, string_from_file_name(&file_ptr->canon));
|
|
entry->base_name = push_string_copy(scratch, base_name);
|
|
|
|
String_Const_u8 b = base_name;
|
|
if (i > 0){
|
|
b = string_from_file_name(&file_ptr->unique_name);
|
|
}
|
|
umem unique_name_capacity = 256;
|
|
u8 *unique_name_buffer = push_array(scratch, u8, unique_name_capacity);
|
|
Assert(b.size <= unique_name_capacity);
|
|
block_copy(unique_name_buffer, b.str, b.size);
|
|
entry->unique_name_in_out = unique_name_buffer;
|
|
entry->unique_name_len_in_out = b.size;
|
|
entry->unique_name_capacity = unique_name_capacity;
|
|
}
|
|
}
|
|
|
|
// Get user's resolution data.
|
|
if (models->buffer_name_resolver != 0){
|
|
models->buffer_name_resolver(&models->app_links, conflicts, conflict_count);
|
|
}
|
|
|
|
// Re-bind all of the files
|
|
{
|
|
i32 i = 0;
|
|
for (Node_Ptr *node = conflict_first;
|
|
node != 0;
|
|
node = node->next, i += 1){
|
|
Editing_File *file_ptr = node->file_ptr;
|
|
if (file_ptr->unique_name.name_size > 0){
|
|
buffer_unbind_name_low_level(working_set, file_ptr);
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
i32 i = 0;
|
|
for (Node_Ptr *node = conflict_first;
|
|
node != 0;
|
|
node = node->next, i += 1){
|
|
Editing_File *file_ptr = node->file_ptr;
|
|
Buffer_Name_Conflict_Entry *entry = &conflicts[i];
|
|
String_Const_u8 unique_name = SCu8(entry->unique_name_in_out, entry->unique_name_len_in_out);
|
|
buffer_bind_name_low_level(scratch, heap, working_set, file_ptr, base_name, unique_name);
|
|
}
|
|
}
|
|
|
|
end_temp(temp);
|
|
}
|
|
|
|
////////////////////////////////
|
|
|
|
internal void
|
|
file_touch(Working_Set *working_set, Editing_File *file){
|
|
Assert(file != 0);
|
|
Assert(!file->is_dummy);
|
|
dll_remove(&file->main_chain_node);
|
|
dll_insert(&working_set->used_sentinel, &file->main_chain_node);
|
|
}
|
|
|
|
internal Editing_File*
|
|
file_get_next(Working_Set *working_set, Editing_File *file){
|
|
if (file != 0){
|
|
file = CastFromMember(Editing_File, main_chain_node, file->main_chain_node.next);
|
|
if (file == CastFromMember(Editing_File, main_chain_node, &working_set->used_sentinel)){
|
|
file = 0;
|
|
}
|
|
}
|
|
else{
|
|
if (working_set->file_count > 0){
|
|
Node *node = working_set->used_sentinel.next;
|
|
file = CastFromMember(Editing_File, main_chain_node, node);
|
|
}
|
|
}
|
|
return(file);
|
|
}
|
|
|
|
internal void
|
|
file_mark_edit_finished(Working_Set *working_set, Editing_File *file){
|
|
// TODO(allen): do(propogate do_not_mark_edits down the edit pipeline to here)
|
|
// This current method only works for synchronous calls, asynchronous calls will get the
|
|
// wrong do_not_mark_edits value.
|
|
if (!working_set->do_not_mark_edits){
|
|
if (!file->edit_finished_marked == 0){
|
|
zdll_push_back(working_set->edit_finished_list_first,
|
|
working_set->edit_finished_list_last,
|
|
&file->edit_finished_mark_node);
|
|
file->edit_finished_marked = true;
|
|
working_set->edit_finished_count += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
internal b32
|
|
file_unmark_edit_finished(Working_Set *working_set, Editing_File *file){
|
|
b32 result = false;
|
|
if (!working_set->do_not_mark_edits){
|
|
if (file->edit_finished_marked){
|
|
zdll_remove(working_set->edit_finished_list_first,
|
|
working_set->edit_finished_list_last,
|
|
&file->edit_finished_mark_node);
|
|
file->edit_finished_mark_node.next = 0;
|
|
file->edit_finished_mark_node.prev = 0;
|
|
file->edit_finished_marked = false;
|
|
working_set->edit_finished_count -= 1;
|
|
|
|
result = true;
|
|
}
|
|
}
|
|
return(result);
|
|
}
|
|
|
|
////////////////////////////////
|
|
|
|
internal Editing_File*
|
|
imp_get_file(Models *models, Buffer_ID buffer_id){
|
|
Working_Set *working_set = &models->working_set;
|
|
Editing_File *file = working_set_get_active_file(working_set, buffer_id);
|
|
if (file != 0 && !file_is_ready(file)){
|
|
file = 0;
|
|
}
|
|
return(file);
|
|
}
|
|
|
|
// BOTTOM
|
|
|