mirror of
https://github.com/Ed94/raddebugger.git
synced 2026-06-26 05:25:00 -07:00
eliminate txti layer
This commit is contained in:
@@ -45,9 +45,6 @@ main_thread_base_entry_point(void (*entry_point)(CmdLine *cmdline), char **argum
|
||||
#if defined(FUZZY_SEARCH_H) && !defined(FZY_INIT_MANUAL)
|
||||
fzy_init();
|
||||
#endif
|
||||
#if defined(TXTI_H) && !defined(TXTI_INIT_MANUAL)
|
||||
txti_init();
|
||||
#endif
|
||||
#if defined(DEMON_CORE_H) && !defined(DMN_INIT_MANUAL)
|
||||
dmn_init();
|
||||
#endif
|
||||
|
||||
+2
-36
@@ -1398,19 +1398,6 @@ df_entity_fuzzy_item_array_from_entity_array_needle(Arena *arena, DF_EntityArray
|
||||
return result;
|
||||
}
|
||||
|
||||
//- rjf: entity -> text info
|
||||
|
||||
internal TXTI_Handle
|
||||
df_txti_handle_from_entity(DF_Entity *entity)
|
||||
{
|
||||
TXTI_Handle handle = {0};
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
String8 path = df_full_path_from_entity(scratch.arena, entity);
|
||||
handle = txti_handle_from_path(path);
|
||||
scratch_end(scratch);
|
||||
return handle;
|
||||
}
|
||||
|
||||
//- rjf: full path building, from file/folder entities
|
||||
|
||||
internal String8
|
||||
@@ -7123,10 +7110,8 @@ df_core_begin_frame(Arena *arena, DF_CmdList *cmds, F32 dt)
|
||||
DF_EntityList existing_processes = df_query_cached_entity_list_with_kind(DF_EntityKind_Process);
|
||||
if(existing_processes.count == 0)
|
||||
{
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
DF_Entity *session_log = df_log_from_entity(df_entity_root());
|
||||
TXTI_Handle session_log_handle = df_txti_handle_from_entity(session_log);
|
||||
txti_reload(session_log_handle, df_full_path_from_entity(scratch.arena, session_log));
|
||||
MTX_Op op = {r1u64(0, 0xffffffffffffffffull), str8_lit("[new session]\n")};
|
||||
mtx_push_op(df_state->output_log_key, op);
|
||||
DF_EntityList bps = df_query_cached_entity_list_with_kind(DF_EntityKind_Breakpoint);
|
||||
for(DF_EntityNode *n = bps.first; n != 0; n = n->next)
|
||||
{
|
||||
@@ -7315,25 +7300,6 @@ df_core_begin_frame(Arena *arena, DF_CmdList *cmds, F32 dt)
|
||||
{
|
||||
MTX_Op op = {r1u64(max_U64, max_U64), event->string};
|
||||
mtx_push_op(df_state->output_log_key, op);
|
||||
#if 0
|
||||
String8 string = event->string;
|
||||
DF_Entity *root = df_entity_root();
|
||||
DF_Entity *thread = df_entity_from_ctrl_handle(event->machine_id, event->entity);
|
||||
DF_Entity *process = df_entity_ancestor_from_kind(thread, DF_EntityKind_Process);
|
||||
DF_Entity *machine = df_entity_ancestor_from_kind(process, DF_EntityKind_Machine);
|
||||
DF_Entity *root_log = df_log_from_entity(root);
|
||||
DF_Entity *thread_log = df_log_from_entity(thread);
|
||||
DF_Entity *process_log = df_log_from_entity(process);
|
||||
DF_Entity *machine_log = df_log_from_entity(machine);
|
||||
TXTI_Handle root_log_handle = df_txti_handle_from_entity(root_log);
|
||||
TXTI_Handle thread_log_handle = df_txti_handle_from_entity(thread_log);
|
||||
TXTI_Handle process_log_handle = df_txti_handle_from_entity(process_log);
|
||||
TXTI_Handle machine_log_handle = df_txti_handle_from_entity(machine_log);
|
||||
txti_append(root_log_handle, string);
|
||||
txti_append(thread_log_handle, string);
|
||||
txti_append(process_log_handle, string);
|
||||
txti_append(machine_log_handle, string);
|
||||
#endif
|
||||
}break;
|
||||
|
||||
case CTRL_EventKind_ThreadName:
|
||||
|
||||
@@ -1536,9 +1536,6 @@ internal DF_EntityArray df_entity_array_from_list(Arena *arena, DF_EntityList *l
|
||||
internal DF_EntityFuzzyItemArray df_entity_fuzzy_item_array_from_entity_list_needle(Arena *arena, DF_EntityList *list, String8 needle);
|
||||
internal DF_EntityFuzzyItemArray df_entity_fuzzy_item_array_from_entity_array_needle(Arena *arena, DF_EntityArray *array, String8 needle);
|
||||
|
||||
//- rjf: entity -> text info
|
||||
internal TXTI_Handle df_txti_handle_from_entity(DF_Entity *entity);
|
||||
|
||||
//- rjf: full path building, from file/folder entities
|
||||
internal String8 df_full_path_from_entity(Arena *arena, DF_Entity *entity);
|
||||
|
||||
|
||||
@@ -12479,188 +12479,6 @@ df_do_txt_controls(TXT_TextInfo *info, String8 data, U64 line_count_per_page, Tx
|
||||
return change;
|
||||
}
|
||||
|
||||
internal B32
|
||||
df_do_txti_controls(TXTI_Handle handle, U64 line_count_per_page, TxtPt *cursor, TxtPt *mark, S64 *preferred_column)
|
||||
{
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
B32 change = 0;
|
||||
UI_EventList *events = ui_events();
|
||||
TXTI_BufferInfo buffer_info = txti_buffer_info_from_handle(scratch.arena, handle);
|
||||
for(UI_EventNode *n = events->first, *next = 0; n != 0; n = next)
|
||||
{
|
||||
next = n->next;
|
||||
B32 taken = 0;
|
||||
if(n->v.kind != UI_EventKind_Navigate && n->v.kind != UI_EventKind_Edit)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
String8 line = txti_string_from_handle_line_num(scratch.arena, handle, cursor->line);
|
||||
UI_TxtOp single_line_op = ui_single_line_txt_op_from_event(scratch.arena, &n->v, line, *cursor, *mark);
|
||||
|
||||
//- rjf: invalid single-line op or endpoint units => try multiline
|
||||
if(n->v.delta_unit == UI_EventDeltaUnit_Whole || single_line_op.flags & UI_TxtOpFlag_Invalid)
|
||||
{
|
||||
U64 line_count = buffer_info.total_line_count;
|
||||
String8 prev_line = txti_string_from_handle_line_num(scratch.arena, handle, cursor->line-1);
|
||||
String8 next_line = txti_string_from_handle_line_num(scratch.arena, handle, cursor->line+1);
|
||||
Vec2S32 delta = n->v.delta_2s32;
|
||||
|
||||
//- rjf: wrap lines right
|
||||
if(n->v.delta_unit != UI_EventDeltaUnit_Whole && delta.x > 0 && cursor->column == line.size+1 && cursor->line+1 <= line_count)
|
||||
{
|
||||
cursor->line += 1;
|
||||
cursor->column = 1;
|
||||
*preferred_column = 1;
|
||||
change = 1;
|
||||
taken = 1;
|
||||
}
|
||||
|
||||
//- rjf: wrap lines left
|
||||
if(n->v.delta_unit != UI_EventDeltaUnit_Whole && delta.x < 0 && cursor->column == 1 && cursor->line-1 >= 1)
|
||||
{
|
||||
cursor->line -= 1;
|
||||
cursor->column = prev_line.size+1;
|
||||
*preferred_column = prev_line.size+1;
|
||||
change = 1;
|
||||
taken = 1;
|
||||
}
|
||||
|
||||
//- rjf: movement down (plain)
|
||||
if(n->v.delta_unit == UI_EventDeltaUnit_Char && delta.y > 0 && cursor->line+1 <= line_count)
|
||||
{
|
||||
cursor->line += 1;
|
||||
cursor->column = Min(*preferred_column, next_line.size+1);
|
||||
change = 1;
|
||||
taken = 1;
|
||||
}
|
||||
|
||||
//- rjf: movement up (plain)
|
||||
if(n->v.delta_unit == UI_EventDeltaUnit_Char && delta.y < 0 && cursor->line-1 >= 1)
|
||||
{
|
||||
cursor->line -= 1;
|
||||
cursor->column = Min(*preferred_column, prev_line.size+1);
|
||||
change = 1;
|
||||
taken = 1;
|
||||
}
|
||||
|
||||
//- rjf: movement down (chunk)
|
||||
if(n->v.delta_unit == UI_EventDeltaUnit_Word && delta.y > 0 && cursor->line+1 <= line_count)
|
||||
{
|
||||
for(S64 line_num = cursor->line+1; line_num <= line_count; line_num += 1)
|
||||
{
|
||||
String8 line = txti_string_from_handle_line_num(scratch.arena, handle, line_num);
|
||||
U64 line_size = line.size;
|
||||
if(line_size == 0)
|
||||
{
|
||||
cursor->line = line_num;
|
||||
cursor->column = 1;
|
||||
break;
|
||||
}
|
||||
else if(line_num == line_count)
|
||||
{
|
||||
cursor->line = line_num;
|
||||
cursor->column = line_size+1;
|
||||
}
|
||||
}
|
||||
change = 1;
|
||||
taken = 1;
|
||||
}
|
||||
|
||||
//- rjf: movement up (chunk)
|
||||
if(n->v.delta_unit == UI_EventDeltaUnit_Word && delta.y < 0 && cursor->line-1 >= 1)
|
||||
{
|
||||
for(S64 line_num = cursor->line-1; line_num > 0; line_num -= 1)
|
||||
{
|
||||
String8 line = txti_string_from_handle_line_num(scratch.arena, handle, line_num);
|
||||
U64 line_size = line.size;
|
||||
if(line_size == 0)
|
||||
{
|
||||
cursor->line = line_num;
|
||||
cursor->column = 1;
|
||||
break;
|
||||
}
|
||||
else if(line_num == 1)
|
||||
{
|
||||
cursor->line = line_num;
|
||||
cursor->column = 1;
|
||||
}
|
||||
}
|
||||
change = 1;
|
||||
taken = 1;
|
||||
}
|
||||
|
||||
//- rjf: movement down (page)
|
||||
if(n->v.delta_unit == UI_EventDeltaUnit_Page && delta.y > 0)
|
||||
{
|
||||
cursor->line += line_count_per_page;
|
||||
cursor->column = 1;
|
||||
cursor->line = Clamp(1, cursor->line, line_count);
|
||||
change = 1;
|
||||
taken = 1;
|
||||
}
|
||||
|
||||
//- rjf: movement up (page)
|
||||
if(n->v.delta_unit == UI_EventDeltaUnit_Page && delta.y < 0)
|
||||
{
|
||||
cursor->line -= line_count_per_page;
|
||||
cursor->column = 1;
|
||||
cursor->line = Clamp(1, cursor->line, line_count);
|
||||
change = 1;
|
||||
taken = 1;
|
||||
}
|
||||
|
||||
//- rjf: movement to endpoint (+)
|
||||
if(n->v.delta_unit == UI_EventDeltaUnit_Whole && (delta.y > 0 || delta.x > 0))
|
||||
{
|
||||
*cursor = txt_pt(line_count, buffer_info.last_line_size);
|
||||
change = 1;
|
||||
taken = 1;
|
||||
}
|
||||
|
||||
//- rjf: movement to endpoint (-)
|
||||
if(n->v.delta_unit == UI_EventDeltaUnit_Whole && (delta.y < 0 || delta.x < 0))
|
||||
{
|
||||
*cursor = txt_pt(1, 1);
|
||||
change = 1;
|
||||
taken = 1;
|
||||
}
|
||||
|
||||
//- rjf: stick mark to cursor, when we don't want to keep it in the same spot
|
||||
if(!(n->v.flags & UI_EventFlag_KeepMark))
|
||||
{
|
||||
*mark = *cursor;
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: valid single-line op => do single-line op
|
||||
else
|
||||
{
|
||||
*cursor = single_line_op.cursor;
|
||||
*mark = single_line_op.mark;
|
||||
*preferred_column = cursor->column;
|
||||
change = 1;
|
||||
taken = 1;
|
||||
}
|
||||
|
||||
//- rjf: copy
|
||||
if(n->v.flags & UI_EventFlag_Copy)
|
||||
{
|
||||
String8 text = txti_string_from_handle_txt_rng(scratch.arena, handle, txt_rng(*cursor, *mark));
|
||||
os_set_clipboard_text(text);
|
||||
taken = 1;
|
||||
}
|
||||
|
||||
//- rjf: consume
|
||||
if(taken)
|
||||
{
|
||||
ui_eat_event(events, n);
|
||||
}
|
||||
}
|
||||
|
||||
scratch_end(scratch);
|
||||
return change;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: UI Widgets: Fancy Labels
|
||||
|
||||
|
||||
@@ -1084,7 +1084,6 @@ internal DF_CodeSliceSignal df_code_slice(DF_Window *ws, DF_CtrlCtx *ctrl_ctx, E
|
||||
internal DF_CodeSliceSignal df_code_slicef(DF_Window *ws, DF_CtrlCtx *ctrl_ctx, EVAL_ParseCtx *parse_ctx, DF_CodeSliceParams *params, TxtPt *cursor, TxtPt *mark, S64 *preferred_column, char *fmt, ...);
|
||||
|
||||
internal B32 df_do_txt_controls(TXT_TextInfo *info, String8 data, U64 line_count_per_page, TxtPt *cursor, TxtPt *mark, S64 *preferred_column);
|
||||
internal B32 df_do_txti_controls(TXTI_Handle handle, U64 line_count_per_page, TxtPt *cursor, TxtPt *mark, S64 *preferred_column);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: UI Widgets: Fancy Labels
|
||||
|
||||
@@ -115,8 +115,8 @@ mtx_mut_thread__entry_point(void *p)
|
||||
//- rjf: construct new buffer
|
||||
if(op.range.max != op.range.min || op.replace.size != 0)
|
||||
{
|
||||
Arena *arena = arena_alloc();
|
||||
U64 new_data_size = data.size + op.replace.size - dim_1u64(op.range);
|
||||
Arena *arena = arena_alloc__sized(new_data_size + ARENA_HEADER_SIZE, new_data_size + ARENA_HEADER_SIZE);
|
||||
U8 *new_data_base = push_array_no_zero(arena, U8, new_data_size);
|
||||
String8 pre_replace_data = str8_substr(data, r1u64(0, op.range.min));
|
||||
String8 post_replace_data = str8_substr(data, r1u64(op.range.max, data.size));
|
||||
|
||||
@@ -77,14 +77,6 @@ update_and_render(OS_Handle repaint_window_handle, void *user_data)
|
||||
//
|
||||
F32 dt = 1.f/target_hz;
|
||||
|
||||
//////////////////////////////
|
||||
//- rjf: last frame before sleep -> disable txti change detection
|
||||
//
|
||||
if(df_gfx_state->num_frames_requested == 0)
|
||||
{
|
||||
txti_set_external_change_detection_enabled(0);
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
//- rjf: get events from the OS
|
||||
//
|
||||
@@ -94,11 +86,6 @@ update_and_render(OS_Handle repaint_window_handle, void *user_data)
|
||||
events = os_get_events(scratch.arena, df_gfx_state->num_frames_requested == 0);
|
||||
}
|
||||
|
||||
//////////////////////////////
|
||||
//- rjf: enable txti change detection
|
||||
//
|
||||
txti_set_external_change_detection_enabled(1);
|
||||
|
||||
//////////////////////////////
|
||||
//- rjf: begin measuring actual per-frame work
|
||||
//
|
||||
|
||||
@@ -38,7 +38,6 @@
|
||||
#include "text_cache/text_cache.h"
|
||||
#include "mutable_text/mutable_text.h"
|
||||
#include "path/path.h"
|
||||
#include "txti/txti.h"
|
||||
#include "coff/coff.h"
|
||||
#include "pe/pe.h"
|
||||
#include "codeview/codeview.h"
|
||||
@@ -79,7 +78,6 @@
|
||||
#include "text_cache/text_cache.c"
|
||||
#include "mutable_text/mutable_text.c"
|
||||
#include "path/path.c"
|
||||
#include "txti/txti.c"
|
||||
#include "coff/coff.c"
|
||||
#include "pe/pe.c"
|
||||
#include "codeview/codeview.c"
|
||||
|
||||
-895
@@ -1,895 +0,0 @@
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Main Layer Initialization
|
||||
|
||||
internal void
|
||||
txti_init(void)
|
||||
{
|
||||
Arena *arena = arena_alloc();
|
||||
txti_state = push_array(arena, TXTI_State, 1);
|
||||
txti_state->arena = arena;
|
||||
txti_state->entity_map.slots_count = 1024;
|
||||
txti_state->entity_map.slots = push_array(txti_state->arena, TXTI_EntitySlot, txti_state->entity_map.slots_count);
|
||||
txti_state->entity_map_stripes.count = 64;
|
||||
txti_state->entity_map_stripes.v = push_array(txti_state->arena, TXTI_Stripe, txti_state->entity_map_stripes.count);
|
||||
for(U64 idx = 0; idx < txti_state->entity_map_stripes.count; idx += 1)
|
||||
{
|
||||
txti_state->entity_map_stripes.v[idx].arena = arena_alloc();
|
||||
txti_state->entity_map_stripes.v[idx].cv = os_condition_variable_alloc();
|
||||
txti_state->entity_map_stripes.v[idx].rw_mutex = os_rw_mutex_alloc();
|
||||
}
|
||||
txti_state->mut_thread_count = Clamp(1, os_logical_core_count(), 4);
|
||||
txti_state->mut_threads = push_array(txti_state->arena, TXTI_MutThread, txti_state->mut_thread_count);
|
||||
for(U64 idx = 0; idx < txti_state->mut_thread_count; idx += 1)
|
||||
{
|
||||
TXTI_MutThread *thread = &txti_state->mut_threads[idx];
|
||||
thread->msg_arena = arena_alloc();
|
||||
thread->msg_mutex = os_mutex_alloc();
|
||||
thread->msg_cv = os_condition_variable_alloc();
|
||||
thread->thread = os_launch_thread(txti_mut_thread_entry_point, (void *)idx, 0);
|
||||
}
|
||||
txti_state->detector_thread = os_launch_thread(txti_detector_thread_entry_point, 0, 0);
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Basic Helpers
|
||||
|
||||
internal U64
|
||||
txti_hash_from_string(String8 string)
|
||||
{
|
||||
U64 result = 5381;
|
||||
for(U64 i = 0; i < string.size; i += 1)
|
||||
{
|
||||
result = ((result << 5) + result) + string.str[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Message Type Functions
|
||||
|
||||
internal void
|
||||
txti_msg_list_push(Arena *arena, TXTI_MsgList *msgs, TXTI_Msg *msg)
|
||||
{
|
||||
TXTI_MsgNode *node = push_array(arena, TXTI_MsgNode, 1);
|
||||
MemoryCopyStruct(&node->v, msg);
|
||||
SLLQueuePush(msgs->first, msgs->last, node);
|
||||
msgs->count += 1;
|
||||
}
|
||||
|
||||
internal void
|
||||
txti_msg_list_concat_in_place(TXTI_MsgList *dst, TXTI_MsgList *src)
|
||||
{
|
||||
if(dst->last == 0)
|
||||
{
|
||||
MemoryCopyStruct(dst, src);
|
||||
}
|
||||
else if(src->first)
|
||||
{
|
||||
dst->last->next = src->first;
|
||||
dst->last = src->last;
|
||||
dst->count += src->count;
|
||||
}
|
||||
MemoryZeroStruct(src);
|
||||
}
|
||||
|
||||
internal TXTI_MsgList
|
||||
txti_msg_list_deep_copy(Arena *arena, TXTI_MsgList *src)
|
||||
{
|
||||
TXTI_MsgList dst = {0};
|
||||
for(TXTI_MsgNode *src_n = src->first; src_n != 0; src_n = src_n->next)
|
||||
{
|
||||
TXTI_MsgNode *dst_n = push_array(arena, TXTI_MsgNode, 1);
|
||||
SLLQueuePush(dst.first, dst.last, dst_n);
|
||||
dst.count += 1;
|
||||
MemoryCopyStruct(&dst_n->v, &src_n->v);
|
||||
dst_n->v.string = push_str8_copy(arena, src_n->v.string);
|
||||
}
|
||||
return dst;
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Entities API
|
||||
|
||||
//- rjf: opening entities & correllation w/ path
|
||||
|
||||
internal TXTI_Handle
|
||||
txti_handle_from_path(String8 path)
|
||||
{
|
||||
TXTI_Handle handle = {0};
|
||||
{
|
||||
// rjf: path -> hash * slot *stripe
|
||||
U64 hash = txti_hash_from_string(path);
|
||||
U64 slot_idx = hash%txti_state->entity_map.slots_count;
|
||||
U64 stripe_idx = slot_idx%txti_state->entity_map_stripes.count;
|
||||
TXTI_EntitySlot *slot = &txti_state->entity_map.slots[slot_idx];
|
||||
TXTI_Stripe *stripe = &txti_state->entity_map_stripes.v[stripe_idx];
|
||||
|
||||
// rjf: determine if entity exists (shared lock)
|
||||
TXTI_Entity *found_entity = 0;
|
||||
OS_MutexScopeR(stripe->rw_mutex)
|
||||
{
|
||||
for(TXTI_Entity *entity = slot->first; entity != 0; entity = entity->next)
|
||||
{
|
||||
if(str8_match(entity->path, path, 0))
|
||||
{
|
||||
found_entity = entity;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(found_entity != 0)
|
||||
{
|
||||
handle.u64[0] = hash;
|
||||
handle.u64[1] = found_entity->id;
|
||||
}
|
||||
}
|
||||
|
||||
// rjf: if entity does not exist -> exclusive lock & check again -- if still
|
||||
// does not exist, then build it
|
||||
if(found_entity == 0) OS_MutexScopeW(stripe->rw_mutex)
|
||||
{
|
||||
for(TXTI_Entity *entity = slot->first; entity != 0; entity = entity->next)
|
||||
{
|
||||
if(str8_match(entity->path, path, 0))
|
||||
{
|
||||
found_entity = entity;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(found_entity == 0)
|
||||
{
|
||||
TXTI_Entity *entity = push_array(stripe->arena, TXTI_Entity, 1);
|
||||
entity->path = push_str8_copy(stripe->arena, path);
|
||||
entity->id = ins_atomic_u64_inc_eval(&txti_state->entity_id_gen);
|
||||
for(U64 idx = 0; idx < TXTI_ENTITY_BUFFER_COUNT; idx += 1)
|
||||
{
|
||||
TXTI_Buffer *buffer = &entity->buffers[idx];
|
||||
buffer->data_arena = arena_alloc__sized(GB(32), KB(64));
|
||||
buffer->analysis_arena = arena_alloc__sized(GB(32), KB(64));
|
||||
buffer->data_arena->align = 1;
|
||||
}
|
||||
SLLQueuePush(slot->first, slot->last, entity);
|
||||
found_entity = entity;
|
||||
}
|
||||
handle.u64[0] = hash;
|
||||
handle.u64[1] = found_entity->id;
|
||||
}
|
||||
}
|
||||
return handle;
|
||||
}
|
||||
|
||||
//- rjf: buffer introspection
|
||||
|
||||
internal TXTI_BufferInfo
|
||||
txti_buffer_info_from_handle(Arena *arena, TXTI_Handle handle)
|
||||
{
|
||||
TXTI_BufferInfo result = {0};
|
||||
U64 hash = handle.u64[0];
|
||||
U64 id = handle.u64[1];
|
||||
U64 slot_idx = hash%txti_state->entity_map.slots_count;
|
||||
U64 stripe_idx = slot_idx%txti_state->entity_map_stripes.count;
|
||||
TXTI_EntitySlot *slot = &txti_state->entity_map.slots[slot_idx];
|
||||
TXTI_Stripe *stripe = &txti_state->entity_map_stripes.v[stripe_idx];
|
||||
OS_MutexScopeR(stripe->rw_mutex)
|
||||
{
|
||||
TXTI_Entity *entity = 0;
|
||||
for(TXTI_Entity *e = slot->first; e != 0; e = e->next)
|
||||
{
|
||||
if(e->id == id)
|
||||
{
|
||||
entity = e;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(entity != 0)
|
||||
{
|
||||
TXTI_Buffer *buffer = &entity->buffers[entity->buffer_apply_gen%TXTI_ENTITY_BUFFER_COUNT];
|
||||
result.path = push_str8_copy(arena, entity->path);
|
||||
result.timestamp = entity->timestamp;
|
||||
result.line_end_kind = entity->line_end_kind;
|
||||
result.total_line_count = buffer->lines_count;
|
||||
result.max_line_size = buffer->lines_max_size;
|
||||
result.mut_gen = entity->mut_gen;
|
||||
result.buffer_apply_gen = entity->buffer_apply_gen;
|
||||
result.bytes_processed = ins_atomic_u64_eval(&entity->bytes_processed);
|
||||
result.bytes_to_process = ins_atomic_u64_eval(&entity->bytes_to_process);
|
||||
}
|
||||
}
|
||||
result.total_line_count = Max(1, result.total_line_count);
|
||||
return result;
|
||||
}
|
||||
|
||||
internal TXTI_Slice
|
||||
txti_slice_from_handle_line_range(Arena *arena, TXTI_Handle handle, Rng1S64 line_range)
|
||||
{
|
||||
ProfBeginFunction();
|
||||
TXTI_Slice result = {0};
|
||||
Temp scratch = scratch_begin(&arena, 1);
|
||||
U64 hash = handle.u64[0];
|
||||
U64 id = handle.u64[1];
|
||||
U64 slot_idx = hash%txti_state->entity_map.slots_count;
|
||||
U64 stripe_idx = slot_idx%txti_state->entity_map_stripes.count;
|
||||
TXTI_EntitySlot *slot = &txti_state->entity_map.slots[slot_idx];
|
||||
TXTI_Stripe *stripe = &txti_state->entity_map_stripes.v[stripe_idx];
|
||||
OS_MutexScopeR(stripe->rw_mutex)
|
||||
{
|
||||
TXTI_Entity *entity = 0;
|
||||
for(TXTI_Entity *e = slot->first; e != 0; e = e->next)
|
||||
{
|
||||
if(e->id == id)
|
||||
{
|
||||
entity = e;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(entity != 0)
|
||||
{
|
||||
TXTI_Buffer *buffer = &entity->buffers[entity->buffer_apply_gen%TXTI_ENTITY_BUFFER_COUNT];
|
||||
Rng1S64 line_range_clamped = r1s64(Clamp(1, line_range.min, (S64)buffer->lines_count), Clamp(1, line_range.max, (S64)buffer->lines_count));
|
||||
|
||||
// rjf: allocate output arrays
|
||||
result.line_count = (U64)dim_1s64(line_range_clamped)+1;
|
||||
result.line_text = push_array(arena, String8, result.line_count);
|
||||
result.line_ranges = push_array(arena, Rng1U64, result.line_count);
|
||||
result.line_tokens = push_array(arena, TXT_TokenArray, result.line_count);
|
||||
|
||||
// rjf: fill line ranges & text
|
||||
U64 line_slice_idx = 0;
|
||||
U64 line_buffer_idx = line_range_clamped.min-1;
|
||||
ProfScope("fill line ranges & text")
|
||||
for(S64 line_num = line_range_clamped.min;
|
||||
line_num <= line_range_clamped.max && line_buffer_idx < buffer->lines_count;
|
||||
line_num += 1,
|
||||
line_slice_idx += 1,
|
||||
line_buffer_idx += 1)
|
||||
{
|
||||
Rng1U64 range = buffer->lines_ranges[line_buffer_idx];
|
||||
String8 line_text_internal = str8_substr(buffer->data, range);
|
||||
result.line_ranges[line_slice_idx] = range;
|
||||
result.line_text[line_slice_idx] = push_str8_copy(arena, line_text_internal);
|
||||
}
|
||||
|
||||
// rjf: binary search to find first token
|
||||
TXT_Token *tokens_first = 0;
|
||||
ProfScope("binary search to find first token")
|
||||
{
|
||||
Rng1U64 slice_range = r1u64(result.line_ranges[0].min, result.line_ranges[result.line_count-1].max);
|
||||
U64 min_idx = 0;
|
||||
U64 opl_idx = buffer->tokens.count;
|
||||
for(;;)
|
||||
{
|
||||
U64 mid_idx = (opl_idx+min_idx)/2;
|
||||
if(mid_idx >= opl_idx)
|
||||
{
|
||||
break;
|
||||
}
|
||||
TXT_Token *mid_token = &buffer->tokens.v[mid_idx];
|
||||
if(mid_token->range.min > slice_range.max)
|
||||
{
|
||||
opl_idx = mid_idx;
|
||||
}
|
||||
else if(mid_token->range.max < slice_range.min)
|
||||
{
|
||||
min_idx = mid_idx;
|
||||
}
|
||||
else if(tokens_first == 0 || mid_token->range.min < tokens_first->range.min)
|
||||
{
|
||||
tokens_first = mid_token;
|
||||
opl_idx = mid_idx;
|
||||
}
|
||||
if(mid_idx == min_idx && mid_idx+1 == opl_idx)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rjf: grab per-line tokens
|
||||
TXT_TokenList *line_tokens_lists = push_array(scratch.arena, TXT_TokenList, result.line_count);
|
||||
if(tokens_first != 0) ProfScope("grab per-line tokens")
|
||||
{
|
||||
TXT_Token *tokens_opl = buffer->tokens.v+buffer->tokens.count;
|
||||
U64 line_slice_idx = 0;
|
||||
for(TXT_Token *token = tokens_first; token < tokens_opl && line_slice_idx < result.line_count;)
|
||||
{
|
||||
if(token->range.min < result.line_ranges[line_slice_idx].max)
|
||||
{
|
||||
if(token->range.max > result.line_ranges[line_slice_idx].min)
|
||||
{
|
||||
txt_token_list_push(scratch.arena, &line_tokens_lists[line_slice_idx], token);
|
||||
}
|
||||
B32 need_token_advance = 0;
|
||||
B32 need_line_advance = 0;
|
||||
if(token->range.max >= result.line_ranges[line_slice_idx].max)
|
||||
{
|
||||
need_line_advance = 1;
|
||||
}
|
||||
if(token->range.max <= result.line_ranges[line_slice_idx].max)
|
||||
{
|
||||
need_token_advance += 1;
|
||||
}
|
||||
if(need_line_advance) { line_slice_idx += 1; }
|
||||
if(need_token_advance) { token += 1; }
|
||||
}
|
||||
else
|
||||
{
|
||||
line_slice_idx += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rjf: bake per-line tokens to arrays
|
||||
for(U64 line_slice_idx = 0; line_slice_idx < result.line_count; line_slice_idx += 1)
|
||||
{
|
||||
result.line_tokens[line_slice_idx] = txt_token_array_from_list(arena, &line_tokens_lists[line_slice_idx]);
|
||||
}
|
||||
}
|
||||
}
|
||||
scratch_end(scratch);
|
||||
ProfEnd();
|
||||
return result;
|
||||
}
|
||||
|
||||
internal String8
|
||||
txti_string_from_handle_txt_rng(Arena *arena, TXTI_Handle handle, TxtRng range)
|
||||
{
|
||||
String8 result = {0};
|
||||
Temp scratch = scratch_begin(&arena, 1);
|
||||
{
|
||||
Rng1S64 line_range = r1s64(ClampBot(1, range.min.line), ClampBot(1, range.max.line));
|
||||
TXTI_BufferInfo info = txti_buffer_info_from_handle(scratch.arena, handle);
|
||||
TXTI_Slice slice = txti_slice_from_handle_line_range(scratch.arena, handle, line_range);
|
||||
String8List line_strings = {0};
|
||||
for(U64 line_idx = 0; line_idx < slice.line_count; line_idx += 1)
|
||||
{
|
||||
String8 line_text = slice.line_text[line_idx];
|
||||
if(line_idx == slice.line_count-1)
|
||||
{
|
||||
line_text = str8_prefix(line_text, range.max.column-1);
|
||||
}
|
||||
if(line_idx == 0)
|
||||
{
|
||||
line_text = str8_skip(line_text, range.min.column-1);
|
||||
}
|
||||
str8_list_push(scratch.arena, &line_strings, line_text);
|
||||
}
|
||||
StringJoin join = {0};
|
||||
switch(info.line_end_kind)
|
||||
{
|
||||
default:
|
||||
case TXT_LineEndKind_LF:{join.sep = str8_lit("\n");}break;
|
||||
case TXT_LineEndKind_CRLF:{join.sep = str8_lit("\r\n");}break;
|
||||
}
|
||||
result = str8_list_join(arena, &line_strings, &join);
|
||||
}
|
||||
scratch_end(scratch);
|
||||
return result;
|
||||
}
|
||||
|
||||
internal String8
|
||||
txti_string_from_handle_line_num(Arena *arena, TXTI_Handle handle, S64 line_num)
|
||||
{
|
||||
String8 result = {0};
|
||||
TXTI_Slice slice = txti_slice_from_handle_line_range(arena, handle, r1s64(line_num, line_num));
|
||||
if(slice.line_count != 0)
|
||||
{
|
||||
result = slice.line_text[0];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
internal Rng1U64
|
||||
txti_expr_range_from_line_off_range_string_tokens(U64 off, Rng1U64 line_range, String8 line_text, TXT_TokenArray *line_tokens)
|
||||
{
|
||||
Rng1U64 result = {0};
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
{
|
||||
// rjf: unpack line info
|
||||
TXT_Token *line_tokens_first = line_tokens->v;
|
||||
TXT_Token *line_tokens_opl = line_tokens->v+line_tokens->count;
|
||||
|
||||
// rjf: find token containing `off`
|
||||
TXT_Token *pt_token = 0;
|
||||
for(TXT_Token *token = line_tokens_first;
|
||||
token < line_tokens_opl;
|
||||
token += 1)
|
||||
{
|
||||
if(contains_1u64(token->range, off))
|
||||
{
|
||||
Rng1U64 token_range_clamped = intersect_1u64(line_range, token->range);
|
||||
String8 token_string = str8_substr(line_text, r1u64(token_range_clamped.max - line_range.min, token_range_clamped.max - line_range.min));
|
||||
B32 token_ender = 0;
|
||||
switch(token->kind)
|
||||
{
|
||||
default:{}break;
|
||||
case TXT_TokenKind_Symbol:
|
||||
{
|
||||
token_ender = (str8_match(token_string, str8_lit("]"), 0));
|
||||
}break;
|
||||
case TXT_TokenKind_Identifier:
|
||||
case TXT_TokenKind_Keyword:
|
||||
case TXT_TokenKind_String:
|
||||
case TXT_TokenKind_Meta:
|
||||
{
|
||||
token_ender = 1;
|
||||
}break;
|
||||
}
|
||||
if(token_ender)
|
||||
{
|
||||
pt_token = token;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// rjf: found token containing `off`? -> mark that as our initial range
|
||||
if(pt_token != 0)
|
||||
{
|
||||
result = pt_token->range;
|
||||
}
|
||||
|
||||
// rjf: walk back from pt_token - try to find plausible start of expression
|
||||
if(pt_token != 0)
|
||||
{
|
||||
B32 walkback_done = 0;
|
||||
S32 nest = 0;
|
||||
for(TXT_Token *wb_token = pt_token;
|
||||
wb_token >= line_tokens_first && walkback_done == 0;
|
||||
wb_token -= 1)
|
||||
{
|
||||
Rng1U64 wb_token_range_clamped = intersect_1u64(line_range, wb_token->range);
|
||||
String8 wb_token_string = str8_substr(line_text, r1u64(wb_token_range_clamped.min - line_range.min, wb_token_range_clamped.max - line_range.min));
|
||||
B32 include_wb_token = 0;
|
||||
switch(wb_token->kind)
|
||||
{
|
||||
default:{}break;
|
||||
case TXT_TokenKind_Symbol:
|
||||
{
|
||||
B32 is_scope_resolution = str8_match(wb_token_string, str8_lit("::"), 0);
|
||||
B32 is_dot = str8_match(wb_token_string, str8_lit("."), 0);
|
||||
B32 is_arrow = str8_match(wb_token_string, str8_lit("->"), 0);
|
||||
B32 is_open_bracket = str8_match(wb_token_string, str8_lit("["), 0);
|
||||
B32 is_close_bracket = str8_match(wb_token_string, str8_lit("]"), 0);
|
||||
nest -= !!(is_open_bracket);
|
||||
nest += !!(is_close_bracket);
|
||||
if(is_scope_resolution ||
|
||||
is_dot ||
|
||||
is_arrow ||
|
||||
is_open_bracket||
|
||||
is_close_bracket)
|
||||
{
|
||||
include_wb_token = 1;
|
||||
}
|
||||
}break;
|
||||
case TXT_TokenKind_Identifier:
|
||||
{
|
||||
include_wb_token = 1;
|
||||
}break;
|
||||
}
|
||||
if(include_wb_token)
|
||||
{
|
||||
result = union_1u64(result, wb_token->range);
|
||||
}
|
||||
else if(nest == 0)
|
||||
{
|
||||
walkback_done = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
scratch_end(scratch);
|
||||
return result;
|
||||
}
|
||||
|
||||
internal TxtRng
|
||||
txti_expr_range_from_handle_pt(TXTI_Handle handle, TxtPt pt)
|
||||
{
|
||||
TxtRng result = {0};
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
TXTI_Slice slice = txti_slice_from_handle_line_range(scratch.arena, handle, r1s64(pt.line, pt.line));
|
||||
if(slice.line_count != 0)
|
||||
{
|
||||
// rjf: unpack line info
|
||||
String8 line_text = slice.line_text[0];
|
||||
Rng1U64 line_range = slice.line_ranges[0];
|
||||
TXT_TokenArray line_tokens = slice.line_tokens[0];
|
||||
TXT_Token *line_tokens_first = line_tokens.v;
|
||||
TXT_Token *line_tokens_opl = line_tokens.v+line_tokens.count;
|
||||
U64 pt_off = line_range.min + (pt.column-1);
|
||||
|
||||
// rjf: grab offset range of expression
|
||||
Rng1U64 expr_off_rng = txti_expr_range_from_line_off_range_string_tokens(pt_off, line_range, line_text, &line_tokens);
|
||||
|
||||
// rjf: convert offset range into text range
|
||||
result = txt_rng(txt_pt(pt.line, 1+(expr_off_rng.min-line_range.min)), txt_pt(pt.line, 1+(expr_off_rng.max-line_range.min)));
|
||||
}
|
||||
scratch_end(scratch);
|
||||
return result;
|
||||
}
|
||||
|
||||
//- rjf: buffer mutations
|
||||
|
||||
internal void
|
||||
txti_reload(TXTI_Handle handle, String8 path)
|
||||
{
|
||||
U64 hash = handle.u64[0];
|
||||
U64 id = handle.u64[1];
|
||||
U64 mut_thread_idx = id%txti_state->mut_thread_count;
|
||||
TXTI_MutThread *mut_thread = &txti_state->mut_threads[mut_thread_idx];
|
||||
OS_MutexScope(mut_thread->msg_mutex)
|
||||
{
|
||||
TXTI_MsgNode *node = push_array(mut_thread->msg_arena, TXTI_MsgNode, 1);
|
||||
TXTI_Msg *msg = &node->v;
|
||||
msg->kind = TXTI_MsgKind_Reload;
|
||||
msg->handle = handle;
|
||||
msg->string = push_str8_copy(mut_thread->msg_arena, path);
|
||||
SLLQueuePush(mut_thread->msg_list.first, mut_thread->msg_list.last, node);
|
||||
mut_thread->msg_list.count += 1;
|
||||
}
|
||||
os_condition_variable_broadcast(mut_thread->msg_cv);
|
||||
}
|
||||
|
||||
internal void
|
||||
txti_append(TXTI_Handle handle, String8 string)
|
||||
{
|
||||
U64 hash = handle.u64[0];
|
||||
U64 id = handle.u64[1];
|
||||
U64 mut_thread_idx = id%txti_state->mut_thread_count;
|
||||
TXTI_MutThread *mut_thread = &txti_state->mut_threads[mut_thread_idx];
|
||||
OS_MutexScope(mut_thread->msg_mutex)
|
||||
{
|
||||
TXTI_MsgNode *node = push_array(mut_thread->msg_arena, TXTI_MsgNode, 1);
|
||||
TXTI_Msg *msg = &node->v;
|
||||
msg->kind = TXTI_MsgKind_Append;
|
||||
msg->handle = handle;
|
||||
msg->string = push_str8_copy(mut_thread->msg_arena, string);
|
||||
SLLQueuePush(mut_thread->msg_list.first, mut_thread->msg_list.last, node);
|
||||
mut_thread->msg_list.count += 1;
|
||||
}
|
||||
os_condition_variable_broadcast(mut_thread->msg_cv);
|
||||
}
|
||||
|
||||
//- rjf: buffer external change detection enabling/disabling
|
||||
|
||||
internal void
|
||||
txti_set_external_change_detection_enabled(B32 enabled)
|
||||
{
|
||||
U64 enabled_u64 = (U64)enabled;
|
||||
ins_atomic_u64_eval_assign(&txti_state->detector_thread_enabled, enabled_u64);
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Mutator Threads
|
||||
|
||||
internal void
|
||||
txti_mut_thread_entry_point(void *p)
|
||||
{
|
||||
U64 mut_thread_idx = (U64)p;
|
||||
ThreadNameF("[txti] mut #%I64u", mut_thread_idx);
|
||||
TXTI_MutThread *mut_thread = &txti_state->mut_threads[mut_thread_idx];
|
||||
for(;;)
|
||||
{
|
||||
//- rjf: begin
|
||||
Temp scratch = scratch_begin(0, 0);
|
||||
|
||||
//- rjf: pull messages
|
||||
TXTI_MsgList msgs = {0};
|
||||
OS_MutexScope(mut_thread->msg_mutex) for(;;)
|
||||
{
|
||||
if(mut_thread->msg_list.count != 0)
|
||||
{
|
||||
msgs = txti_msg_list_deep_copy(scratch.arena, &mut_thread->msg_list);
|
||||
MemoryZeroStruct(&mut_thread->msg_list);
|
||||
arena_clear(mut_thread->msg_arena);
|
||||
break;
|
||||
}
|
||||
os_condition_variable_wait(mut_thread->msg_cv, mut_thread->msg_mutex, max_U64);
|
||||
}
|
||||
|
||||
//- rjf: process msgs
|
||||
for(TXTI_MsgNode *msg_n = msgs.first; msg_n != 0; msg_n = msg_n->next) ProfScope("process msg")
|
||||
{
|
||||
//- rjf: unpack message
|
||||
TXTI_Msg *msg = &msg_n->v;
|
||||
U64 hash = msg->handle.u64[0];
|
||||
U64 id = msg->handle.u64[1];
|
||||
U64 slot_idx = hash%txti_state->entity_map.slots_count;
|
||||
U64 stripe_idx = slot_idx%txti_state->entity_map_stripes.count;
|
||||
TXTI_EntitySlot *slot = &txti_state->entity_map.slots[slot_idx];
|
||||
TXTI_Stripe *stripe = &txti_state->entity_map_stripes.v[stripe_idx];
|
||||
|
||||
//- rjf: load file if we need it
|
||||
B32 load_valid = 0;
|
||||
String8 file_contents = {0};
|
||||
TXT_LangKind lang_kind = TXT_LangKind_Null;
|
||||
U64 timestamp = 0;
|
||||
if(msg->kind == TXTI_MsgKind_Reload) ProfScope("reload file")
|
||||
{
|
||||
FileProperties pre_load_props = os_properties_from_file_path(msg->string);
|
||||
OS_Handle file = os_file_open(OS_AccessFlag_Read|OS_AccessFlag_ShareRead|OS_AccessFlag_ShareWrite, msg->string);
|
||||
file_contents = os_string_from_file_range(scratch.arena, file, r1u64(0, pre_load_props.size));
|
||||
lang_kind = txt_lang_kind_from_extension(str8_skip_last_dot(msg->string));
|
||||
os_file_close(file);
|
||||
FileProperties post_load_props = os_properties_from_file_path(msg->string);
|
||||
load_valid = (post_load_props.modified == pre_load_props.modified);
|
||||
if(load_valid)
|
||||
{
|
||||
timestamp = pre_load_props.modified;
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: nonzero lang kind -> unpack lang info
|
||||
TXT_LangLexFunctionType *lex_function = txt_lex_function_from_lang_kind(lang_kind);
|
||||
|
||||
//- rjf: detect line end kind
|
||||
TXT_LineEndKind line_end_kind = TXT_LineEndKind_Null;
|
||||
if(load_valid)
|
||||
{
|
||||
U64 lf_count = 0;
|
||||
U64 cr_count = 0;
|
||||
for(U64 idx = 0; idx < file_contents.size && idx < 1024; idx += 1)
|
||||
{
|
||||
if(file_contents.str[idx] == '\r')
|
||||
{
|
||||
cr_count += 1;
|
||||
}
|
||||
if(file_contents.str[idx] == '\n')
|
||||
{
|
||||
lf_count += 1;
|
||||
}
|
||||
}
|
||||
if(cr_count >= lf_count/2 && lf_count >= 1)
|
||||
{
|
||||
line_end_kind = TXT_LineEndKind_CRLF;
|
||||
}
|
||||
else if(lf_count >= 1)
|
||||
{
|
||||
line_end_kind = TXT_LineEndKind_LF;
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: obtain initial buffer_apply_gen, reset byte processing counters
|
||||
U64 initial_buffer_apply_gen = 0;
|
||||
ProfScope("obtain initial buffer_apply_gen") OS_MutexScopeR(stripe->rw_mutex)
|
||||
{
|
||||
TXTI_Entity *entity = 0;
|
||||
for(TXTI_Entity *e = slot->first; e != 0; e = e->next)
|
||||
{
|
||||
if(e->id == id)
|
||||
{
|
||||
entity = e;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(entity != 0)
|
||||
{
|
||||
initial_buffer_apply_gen = entity->buffer_apply_gen;
|
||||
if(msg->kind == TXTI_MsgKind_Reload)
|
||||
{
|
||||
ins_atomic_u64_eval_assign(&entity->bytes_processed, 0);
|
||||
ins_atomic_u64_eval_assign(&entity->bytes_to_process, file_contents.size + !!lex_function*file_contents.size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: apply edits
|
||||
ProfScope("apply edits")
|
||||
{
|
||||
for(U64 buffer_apply_idx = 0;
|
||||
buffer_apply_idx < TXTI_ENTITY_BUFFER_COUNT;
|
||||
buffer_apply_idx += 1)
|
||||
ProfScope("apply edit #%i", (int)buffer_apply_idx)
|
||||
{
|
||||
// rjf: last buffer we're going to edit? -> bump buffer_apply_gen,
|
||||
// so that before we touch this last buffer, all readers of this
|
||||
// entity will get the already-modified buffers.
|
||||
if(buffer_apply_idx == TXTI_ENTITY_BUFFER_COUNT-1)
|
||||
{
|
||||
ProfScope("exclusive lock -> buffer swap") OS_MutexScopeW(stripe->rw_mutex)
|
||||
{
|
||||
TXTI_Entity *entity = 0;
|
||||
for(TXTI_Entity *e = slot->first; e != 0; e = e->next)
|
||||
{
|
||||
if(e->id == id)
|
||||
{
|
||||
entity = e;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(entity != 0)
|
||||
{
|
||||
entity->buffer_apply_gen += 1;
|
||||
if(line_end_kind != TXT_LineEndKind_Null)
|
||||
{
|
||||
entity->line_end_kind = line_end_kind;
|
||||
}
|
||||
if(lang_kind != TXT_LangKind_Null)
|
||||
{
|
||||
entity->lang_kind = lang_kind;
|
||||
}
|
||||
if(timestamp != 0)
|
||||
{
|
||||
entity->timestamp = timestamp;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rjf: apply edit to this buffer.
|
||||
//
|
||||
// NOTE(rjf): all edits can apply *with a shared mutex lock*,
|
||||
// because only the mutator thread for this buffer can touch the
|
||||
// non-currently-viewable buffers. we only need to have an
|
||||
// exclusive lock to bump the buffer_apply_gen (to change the
|
||||
// actively viewable buffer).
|
||||
//
|
||||
ProfScope("apply edit") OS_MutexScopeR(stripe->rw_mutex)
|
||||
{
|
||||
TXTI_Entity *entity = 0;
|
||||
for(TXTI_Entity *e = slot->first; e != 0; e = e->next)
|
||||
{
|
||||
if(e->id == id)
|
||||
{
|
||||
entity = e;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(entity != 0)
|
||||
{
|
||||
TXTI_Buffer *buffer = &entity->buffers[(initial_buffer_apply_gen+1+buffer_apply_idx)%TXTI_ENTITY_BUFFER_COUNT];
|
||||
|
||||
// rjf: clear old analysis data
|
||||
arena_clear(buffer->analysis_arena);
|
||||
buffer->lines_count = 0;
|
||||
buffer->lines_ranges = 0;
|
||||
buffer->lines_max_size = 0;
|
||||
MemoryZeroStruct(&buffer->tokens);
|
||||
|
||||
// rjf: perform edit to buffer data
|
||||
switch(msg->kind)
|
||||
{
|
||||
default:{}break;
|
||||
|
||||
// rjf: replace range
|
||||
case TXTI_MsgKind_Append: ProfScope("append")
|
||||
{
|
||||
U8 *append_data_buffer = push_array_no_zero(buffer->data_arena, U8, msg->string.size);
|
||||
MemoryCopy(append_data_buffer, msg->string.str, msg->string.size);
|
||||
buffer->data.size += msg->string.size;
|
||||
if(buffer->data.str == 0 && msg->string.size != 0)
|
||||
{
|
||||
buffer->data.str = append_data_buffer;
|
||||
}
|
||||
}break;
|
||||
|
||||
// rjf: reload from disk
|
||||
case TXTI_MsgKind_Reload: ProfScope("reload")
|
||||
{
|
||||
arena_clear(buffer->data_arena);
|
||||
if(file_contents.size != 0)
|
||||
{
|
||||
buffer->data = push_str8_copy(buffer->data_arena, file_contents);
|
||||
}
|
||||
else
|
||||
{
|
||||
MemoryZeroStruct(&buffer->data);
|
||||
}
|
||||
}break;
|
||||
}
|
||||
|
||||
// rjf: parse & store line range info
|
||||
ProfScope("parse & store line range info")
|
||||
{
|
||||
// rjf: count # of lines
|
||||
U64 line_count = 1;
|
||||
U64 byte_process_start_idx = 0;
|
||||
for(U64 idx = 0; idx < buffer->data.size; idx += 1)
|
||||
{
|
||||
if(buffer_apply_idx == 0 && idx-byte_process_start_idx >= 1000)
|
||||
{
|
||||
ins_atomic_u64_add_eval(&entity->bytes_processed, (idx-byte_process_start_idx));
|
||||
byte_process_start_idx = idx;
|
||||
}
|
||||
if(buffer->data.str[idx] == '\n' || buffer->data.str[idx] == '\r')
|
||||
{
|
||||
line_count += 1;
|
||||
if(buffer->data.str[idx] == '\r')
|
||||
{
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rjf: allocate & store line ranges
|
||||
ProfScope("allocate & store line ranges")
|
||||
{
|
||||
buffer->lines_count = line_count;
|
||||
buffer->lines_ranges = push_array_no_zero(buffer->analysis_arena, Rng1U64, buffer->lines_count);
|
||||
U64 line_idx = 0;
|
||||
U64 line_start_idx = 0;
|
||||
for(U64 idx = 0; idx <= buffer->data.size; idx += 1)
|
||||
{
|
||||
if(idx == buffer->data.size || buffer->data.str[idx] == '\n' || buffer->data.str[idx] == '\r')
|
||||
{
|
||||
Rng1U64 line_range = r1u64(line_start_idx, idx);
|
||||
U64 line_size = dim_1u64(line_range);
|
||||
buffer->lines_ranges[line_idx] = line_range;
|
||||
buffer->lines_max_size = Max(buffer->lines_max_size, line_size);
|
||||
line_idx += 1;
|
||||
line_start_idx = idx+1;
|
||||
if(idx < buffer->data.size && buffer->data.str[idx] == '\r')
|
||||
{
|
||||
line_start_idx += 1;
|
||||
idx += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// rjf: lex file contents
|
||||
if(lex_function != 0) ProfScope("lex text")
|
||||
{
|
||||
buffer->tokens = lex_function(buffer->analysis_arena, buffer_apply_idx == 0 ? &entity->bytes_processed : 0, buffer->data);
|
||||
}
|
||||
|
||||
// rjf: mark final process counter
|
||||
if(buffer_apply_idx == 0)
|
||||
{
|
||||
ins_atomic_u64_eval_assign(&entity->bytes_processed, entity->bytes_to_process);
|
||||
}
|
||||
|
||||
// rjf: mark task completion
|
||||
if(buffer_apply_idx == TXTI_ENTITY_BUFFER_COUNT-1)
|
||||
{
|
||||
ins_atomic_u64_eval_assign(&entity->working_count, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//- rjf: end
|
||||
scratch_end(scratch);
|
||||
}
|
||||
}
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Detector Thread
|
||||
|
||||
internal void
|
||||
txti_detector_thread_entry_point(void *p)
|
||||
{
|
||||
ThreadNameF("[txti] detector");
|
||||
for(;;)
|
||||
{
|
||||
if(ins_atomic_u64_eval(&txti_state->detector_thread_enabled))
|
||||
{
|
||||
U64 slots_per_stripe = txti_state->entity_map.slots_count/txti_state->entity_map_stripes.count;
|
||||
for(U64 stripe_idx = 0; stripe_idx < txti_state->entity_map_stripes.count; stripe_idx += 1)
|
||||
{
|
||||
TXTI_Stripe *stripe = &txti_state->entity_map_stripes.v[stripe_idx];
|
||||
OS_MutexScopeR(stripe->rw_mutex) for(U64 slot_in_stripe_idx = 0; slot_in_stripe_idx < slots_per_stripe; slot_in_stripe_idx += 1)
|
||||
{
|
||||
U64 slot_idx = stripe_idx*slots_per_stripe + slot_in_stripe_idx;
|
||||
TXTI_EntitySlot *slot = &txti_state->entity_map.slots[slot_idx];
|
||||
for(TXTI_Entity *entity = slot->first; entity != 0; entity = entity->next)
|
||||
{
|
||||
FileProperties props = os_properties_from_file_path(entity->path);
|
||||
U64 entity_timestamp = entity->timestamp;
|
||||
if(props.modified != entity_timestamp && ins_atomic_u64_eval(&entity->working_count) == 0)
|
||||
{
|
||||
TXTI_Handle handle = {txti_hash_from_string(entity->path), entity->id};
|
||||
txti_reload(handle, entity->path);
|
||||
ins_atomic_u64_inc_eval(&entity->working_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
os_sleep_milliseconds(100);
|
||||
}
|
||||
}
|
||||
-286
@@ -1,286 +0,0 @@
|
||||
// Copyright (c) 2024 Epic Games Tools
|
||||
// Licensed under the MIT license (https://opensource.org/license/mit/)
|
||||
|
||||
#ifndef TXTI_H
|
||||
#define TXTI_H
|
||||
|
||||
////////////////////////////////
|
||||
//~ NOTE(rjf): Text Info Layer Overview (8/30/2023)
|
||||
//
|
||||
// This layer's purpose is to provide access to a mutable cache of parsed
|
||||
// information about textual data, backed by filesystem contents. This
|
||||
// cache associates unique handles (`TXTI_Handle`) with "entities", where
|
||||
// entities contain information about textual contents of each line, how many
|
||||
// lines of text the entity contains, and the tokenization of each line.
|
||||
//
|
||||
// While it is generally correct for a debugger to only provide read-only UIs
|
||||
// for text viewing (so that the user does not mistakenly modify a buffer and
|
||||
// continue debugging with it, even if the line or symbol info is no longer
|
||||
// reflective of the source code), there are cases where read/write buffers are
|
||||
// useful, and so this layer needs to support that, and the disabling of writes
|
||||
// can remain as a high-level UI decision (rather than a lack of capability of
|
||||
// the debugger). Mutable buffers may be used for editing config files (or
|
||||
// otherwise any files that are not relevant to actively-used debug info),
|
||||
// or debug logs.
|
||||
//
|
||||
// This layer is also responsible for hot-reloading changed files from disk,
|
||||
// *and* for reporting conflicts, if a buffer was mutated within the process,
|
||||
// but was also modified on disk.
|
||||
//
|
||||
// In order to avoid hanging UI while larger files are being edited or lexed,
|
||||
// all buffer loading, mutation, & parsing happen on "mutator threads". These
|
||||
// threads consume messages (`TXTI_Msg`), which command them to reload a file
|
||||
// from disk, or to replace textual ranges in a buffer. After completing those
|
||||
// operations, the buffer is lexed/parsed.
|
||||
//
|
||||
// Entities have *two* buffer data structures -- this allows a mutator thread
|
||||
// to apply edits to one while the other can be read by user threads. For each
|
||||
// editing operation, the mutator threads apply identical operations in a
|
||||
// *rotation-based* order. If the currently-viewable buffer is slot 0, the edit
|
||||
// will first be applied to slot 1, and before edits are reflected in slot 0,
|
||||
// the mutator thread will bump a "buffer mutation counter", such that before
|
||||
// any edits are made to slot 0, the viewable buffer is changed to being within
|
||||
// slot *1*.
|
||||
//
|
||||
// Importantly, entities map to a *unique* mutator thread -- it is not possible
|
||||
// for multiple mutator threads to be attempting to write to the same entity at
|
||||
// the same time, as this could not produce meaningful or coherent results.
|
||||
// This way, all edits to each entity are applied serially.
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Handle Type
|
||||
|
||||
typedef struct TXTI_Handle TXTI_Handle;
|
||||
struct TXTI_Handle
|
||||
{
|
||||
U64 u64[2];
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Buffer Entity Types
|
||||
|
||||
#define TXTI_ENTITY_BUFFER_COUNT 2
|
||||
|
||||
typedef struct TXTI_Buffer TXTI_Buffer;
|
||||
struct TXTI_Buffer
|
||||
{
|
||||
// rjf: arenas
|
||||
Arena *data_arena;
|
||||
Arena *analysis_arena;
|
||||
|
||||
// rjf: raw textual data
|
||||
String8 data;
|
||||
|
||||
// rjf: line range info
|
||||
U64 lines_count;
|
||||
Rng1U64 *lines_ranges;
|
||||
U64 lines_max_size;
|
||||
|
||||
// rjf: tokens
|
||||
TXT_TokenArray tokens;
|
||||
};
|
||||
|
||||
typedef struct TXTI_Entity TXTI_Entity;
|
||||
struct TXTI_Entity
|
||||
{
|
||||
// rjf: top-level info
|
||||
TXTI_Entity *next;
|
||||
String8 path;
|
||||
U64 id;
|
||||
U64 timestamp;
|
||||
U64 mut_gen;
|
||||
|
||||
// rjf: metadata
|
||||
TXT_LineEndKind line_end_kind;
|
||||
TXT_LangKind lang_kind;
|
||||
U64 bytes_processed;
|
||||
U64 bytes_to_process;
|
||||
U64 working_count;
|
||||
|
||||
// rjf: double-buffered mutable text buffers
|
||||
U64 buffer_apply_gen;
|
||||
TXTI_Buffer buffers[TXTI_ENTITY_BUFFER_COUNT];
|
||||
};
|
||||
|
||||
typedef struct TXTI_EntitySlot TXTI_EntitySlot;
|
||||
struct TXTI_EntitySlot
|
||||
{
|
||||
TXTI_Entity *first;
|
||||
TXTI_Entity *last;
|
||||
};
|
||||
|
||||
typedef struct TXTI_EntityMap TXTI_EntityMap;
|
||||
struct TXTI_EntityMap
|
||||
{
|
||||
U64 slots_count;
|
||||
TXTI_EntitySlot *slots;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Striped Access Types
|
||||
|
||||
typedef struct TXTI_Stripe TXTI_Stripe;
|
||||
struct TXTI_Stripe
|
||||
{
|
||||
Arena *arena;
|
||||
OS_Handle cv;
|
||||
OS_Handle rw_mutex;
|
||||
};
|
||||
|
||||
typedef struct TXTI_StripeTable TXTI_StripeTable;
|
||||
struct TXTI_StripeTable
|
||||
{
|
||||
U64 count;
|
||||
TXTI_Stripe *v;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Entity Introspection Result Types
|
||||
|
||||
typedef struct TXTI_BufferInfo TXTI_BufferInfo;
|
||||
struct TXTI_BufferInfo
|
||||
{
|
||||
String8 path;
|
||||
U64 timestamp;
|
||||
TXT_LineEndKind line_end_kind;
|
||||
TXT_LangKind lang_kind;
|
||||
U64 total_line_count;
|
||||
U64 last_line_size;
|
||||
U64 max_line_size;
|
||||
U64 mut_gen;
|
||||
U64 buffer_apply_gen;
|
||||
U64 bytes_processed;
|
||||
U64 bytes_to_process;
|
||||
};
|
||||
|
||||
typedef struct TXTI_Slice TXTI_Slice;
|
||||
struct TXTI_Slice
|
||||
{
|
||||
U64 line_count;
|
||||
String8 *line_text;
|
||||
Rng1U64 *line_ranges;
|
||||
TXT_TokenArray *line_tokens;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: User -> Mutator Thread Messages
|
||||
|
||||
typedef enum TXTI_MsgKind
|
||||
{
|
||||
TXTI_MsgKind_Null,
|
||||
TXTI_MsgKind_Append,
|
||||
TXTI_MsgKind_Reload,
|
||||
TXTI_MsgKind_COUNT
|
||||
}
|
||||
TXTI_MsgKind;
|
||||
|
||||
typedef struct TXTI_Msg TXTI_Msg;
|
||||
struct TXTI_Msg
|
||||
{
|
||||
TXTI_MsgKind kind;
|
||||
TXTI_Handle handle;
|
||||
String8 string;
|
||||
};
|
||||
|
||||
typedef struct TXTI_MsgNode TXTI_MsgNode;
|
||||
struct TXTI_MsgNode
|
||||
{
|
||||
TXTI_MsgNode *next;
|
||||
TXTI_Msg v;
|
||||
};
|
||||
|
||||
typedef struct TXTI_MsgList TXTI_MsgList;
|
||||
struct TXTI_MsgList
|
||||
{
|
||||
TXTI_MsgNode *first;
|
||||
TXTI_MsgNode *last;
|
||||
U64 count;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Central State
|
||||
|
||||
typedef struct TXTI_MutThread TXTI_MutThread;
|
||||
struct TXTI_MutThread
|
||||
{
|
||||
OS_Handle thread;
|
||||
Arena *msg_arena;
|
||||
TXTI_MsgList msg_list;
|
||||
OS_Handle msg_mutex;
|
||||
OS_Handle msg_cv;
|
||||
};
|
||||
|
||||
typedef struct TXTI_State TXTI_State;
|
||||
struct TXTI_State
|
||||
{
|
||||
// rjf: arena
|
||||
Arena *arena;
|
||||
|
||||
// rjf: entities state
|
||||
TXTI_EntityMap entity_map;
|
||||
TXTI_StripeTable entity_map_stripes;
|
||||
U64 entity_id_gen;
|
||||
|
||||
// rjf: mutator threads
|
||||
U64 mut_thread_count;
|
||||
TXTI_MutThread *mut_threads;
|
||||
|
||||
// rjf: detector thread
|
||||
U64 detector_thread_enabled;
|
||||
OS_Handle detector_thread;
|
||||
};
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Globals
|
||||
|
||||
global TXTI_State *txti_state = 0;
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Main Layer Initialization
|
||||
|
||||
internal void txti_init(void);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Basic Helpers
|
||||
|
||||
internal U64 txti_hash_from_string(String8 string);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Message Type Functions
|
||||
|
||||
internal void txti_msg_list_push(Arena *arena, TXTI_MsgList *msgs, TXTI_Msg *msg);
|
||||
internal void txti_msg_list_concat_in_place(TXTI_MsgList *dst, TXTI_MsgList *src);
|
||||
internal TXTI_MsgList txti_msg_list_deep_copy(Arena *arena, TXTI_MsgList *src);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Entities API
|
||||
|
||||
//- rjf: opening entities & correllation w/ path
|
||||
internal TXTI_Handle txti_handle_from_path(String8 path);
|
||||
|
||||
//- rjf: buffer introspection
|
||||
internal TXTI_BufferInfo txti_buffer_info_from_handle(Arena *arena, TXTI_Handle handle);
|
||||
internal TXTI_Slice txti_slice_from_handle_line_range(Arena *arena, TXTI_Handle handle, Rng1S64 line_range);
|
||||
internal String8 txti_string_from_handle_txt_rng(Arena *arena, TXTI_Handle handle, TxtRng range);
|
||||
internal String8 txti_string_from_handle_line_num(Arena *arena, TXTI_Handle handle, S64 line_num);
|
||||
internal Rng1U64 txti_expr_range_from_line_off_range_string_tokens(U64 off, Rng1U64 line_range, String8 line_text, TXT_TokenArray *line_tokens);
|
||||
internal TxtRng txti_expr_range_from_handle_pt(TXTI_Handle handle, TxtPt pt);
|
||||
|
||||
//- rjf: buffer mutations
|
||||
internal void txti_reload(TXTI_Handle handle, String8 path);
|
||||
internal void txti_append(TXTI_Handle handle, String8 string);
|
||||
|
||||
//- rjf: buffer external change detection enabling/disabling
|
||||
internal void txti_set_external_change_detection_enabled(B32 enabled);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Mutator Threads
|
||||
|
||||
internal void txti_mut_thread_entry_point(void *p);
|
||||
|
||||
////////////////////////////////
|
||||
//~ rjf: Detector Thread
|
||||
|
||||
internal void txti_detector_thread_entry_point(void *p);
|
||||
|
||||
#endif //TXTI_H
|
||||
Reference in New Issue
Block a user