From d12c5ec2e81d465bb3d0ed425d9892541380dc3b Mon Sep 17 00:00:00 2001 From: Ryan Fleury Date: Tue, 25 Jun 2024 11:09:02 -0700 Subject: [PATCH] first pass at mutable text layer, which allows debugger-produced/controlled buffers to be fed into text visualization systems; start pulling out code view into single path, which will be used for disassembly/source/output --- src/base/base_entry_point.c | 3 + src/df/core/df_core.c | 4 + src/df/core/df_core.mdesk | 3 - src/df/core/generated/df_core.meta.c | 15 +- src/df/core/generated/df_core.meta.h | 11 +- src/df/gfx/df_gfx.c | 27 +- src/df/gfx/df_gfx.h | 1 - src/df/gfx/df_gfx.mdesk | 3 +- src/df/gfx/df_views.c | 737 ++++++++++++++++++++++++++- src/df/gfx/df_views.h | 7 + src/df/gfx/generated/df_gfx.meta.c | 14 +- src/df/gfx/generated/df_gfx.meta.h | 9 +- src/mutable_text/mutable_text.c | 142 ++++++ src/mutable_text/mutable_text.h | 96 ++++ src/raddbg/raddbg_main.c | 2 + 15 files changed, 1022 insertions(+), 52 deletions(-) create mode 100644 src/mutable_text/mutable_text.c create mode 100644 src/mutable_text/mutable_text.h diff --git a/src/base/base_entry_point.c b/src/base/base_entry_point.c index 3c4b9926..7a0e5db6 100644 --- a/src/base/base_entry_point.c +++ b/src/base/base_entry_point.c @@ -33,6 +33,9 @@ main_thread_base_entry_point(void (*entry_point)(CmdLine *cmdline), char **argum #if defined(TEXT_CACHE_H) && !defined(TXT_INIT_MANUAL) txt_init(); #endif +#if defined(MUTABLE_TEXT_H) && !defined(MTX_INIT_MANUAL) + mtx_init(); +#endif #if defined(DASM_CACHE_H) && !defined(DASM_INIT_MANUAL) dasm_init(); #endif diff --git a/src/df/core/df_core.c b/src/df/core/df_core.c index 48c05da7..2a75b09f 100644 --- a/src/df/core/df_core.c +++ b/src/df/core/df_core.c @@ -7129,6 +7129,9 @@ df_core_begin_frame(Arena *arena, DF_CmdList *cmds, F32 dt) case CTRL_EventKind_DebugString: { + //MTX_Op op = {r1u64(max_U64, max_U64), event->string}; + //mtx_push_op(u128_zero(), op); +#if 1 String8 string = event->string; DF_Entity *root = df_entity_root(); DF_Entity *thread = df_entity_from_ctrl_handle(event->machine_id, event->entity); @@ -7146,6 +7149,7 @@ df_core_begin_frame(Arena *arena, DF_CmdList *cmds, F32 dt) txti_append(thread_log_handle, string); txti_append(process_log_handle, string); txti_append(machine_log_handle, string); +#endif }break; case CTRL_EventKind_ThreadName: diff --git a/src/df/core/df_core.mdesk b/src/df/core/df_core.mdesk index ee4fd76d..331ac04c 100644 --- a/src/df/core/df_core.mdesk +++ b/src/df/core/df_core.mdesk @@ -30,9 +30,6 @@ DF_EntityKindTable: //- rjf: auto view rules {AutoViewRule auto_view_rule 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 "Label" Binoculars "Auto View Rule" } - //- rjf: text attachments - {FlashMarker flash_marker 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 "Label" Null "Flash Marker" } - //- rjf: watch pins {WatchPin watch_pin 1 0 0 1 0 0 1 0 0 0 0 0 0 0 0 1 1 "Expression" Pin "Watch Pin" } diff --git a/src/df/core/generated/df_core.meta.c b/src/df/core/generated/df_core.meta.c index b0c3d6ef..b8e5b2cc 100644 --- a/src/df/core/generated/df_core.meta.c +++ b/src/df/core/generated/df_core.meta.c @@ -32,7 +32,7 @@ Rng1U64 df_g_cmd_param_slot_range_table[24] = {OffsetOf(DF_CmdParams, inline_unwind_index), OffsetOf(DF_CmdParams, inline_unwind_index) + sizeof(U64)}, }; -DF_IconKind df_g_entity_kind_icon_kind_table[26] = +DF_IconKind df_g_entity_kind_icon_kind_table[25] = { DF_IconKind_Null, DF_IconKind_Null, @@ -40,7 +40,6 @@ DF_IconKind_Machine, DF_IconKind_FileOutline, DF_IconKind_FileOutline, DF_IconKind_Binoculars, -DF_IconKind_Null, DF_IconKind_Pin, DF_IconKind_CircleFilled, DF_IconKind_CircleFilled, @@ -62,7 +61,7 @@ DF_IconKind_Null, DF_IconKind_Null, }; -String8 df_g_entity_kind_display_string_table[26] = +String8 df_g_entity_kind_display_string_table[25] = { str8_lit_comp("Nil"), str8_lit_comp("Root"), @@ -70,7 +69,6 @@ str8_lit_comp("Machine"), str8_lit_comp("File"), str8_lit_comp("Override File Link"), str8_lit_comp("Auto View Rule"), -str8_lit_comp("Flash Marker"), str8_lit_comp("Watch Pin"), str8_lit_comp("Breakpoint"), str8_lit_comp("Condition"), @@ -92,7 +90,7 @@ str8_lit_comp("Conversion Failure"), str8_lit_comp("EndedProcess"), }; -String8 df_g_entity_kind_name_label_table[26] = +String8 df_g_entity_kind_name_label_table[25] = { str8_lit_comp("Label"), str8_lit_comp("Label"), @@ -100,7 +98,6 @@ str8_lit_comp("Label"), str8_lit_comp("Label"), str8_lit_comp("Label"), str8_lit_comp("Label"), -str8_lit_comp("Label"), str8_lit_comp("Expression"), str8_lit_comp("Label"), str8_lit_comp("Expression"), @@ -122,7 +119,7 @@ str8_lit_comp("Label"), str8_lit_comp("Label"), }; -DF_EntityKindFlags df_g_entity_kind_flags_table[26] = +DF_EntityKindFlags df_g_entity_kind_flags_table[25] = { (0*DF_EntityKindFlag_LeafMutationUserConfig | 0*DF_EntityKindFlag_LeafMutationProjectConfig | 0*DF_EntityKindFlag_LeafMutationSoftHalt | 0*DF_EntityKindFlag_LeafMutationDebugInfoMap | 0*DF_EntityKindFlag_TreeMutationUserConfig | 0*DF_EntityKindFlag_TreeMutationProjectConfig | 0*DF_EntityKindFlag_TreeMutationSoftHalt | 0*DF_EntityKindFlag_TreeMutationDebugInfoMap | 0*DF_EntityKindFlag_NameIsCode | 0*DF_EntityKindFlag_UserDefinedLifetime), (0*DF_EntityKindFlag_LeafMutationUserConfig | 0*DF_EntityKindFlag_LeafMutationProjectConfig | 0*DF_EntityKindFlag_LeafMutationSoftHalt | 0*DF_EntityKindFlag_LeafMutationDebugInfoMap | 0*DF_EntityKindFlag_TreeMutationUserConfig | 0*DF_EntityKindFlag_TreeMutationProjectConfig | 0*DF_EntityKindFlag_TreeMutationSoftHalt | 0*DF_EntityKindFlag_TreeMutationDebugInfoMap | 0*DF_EntityKindFlag_NameIsCode | 0*DF_EntityKindFlag_UserDefinedLifetime), @@ -130,7 +127,6 @@ DF_EntityKindFlags df_g_entity_kind_flags_table[26] = (0*DF_EntityKindFlag_LeafMutationUserConfig | 0*DF_EntityKindFlag_LeafMutationProjectConfig | 0*DF_EntityKindFlag_LeafMutationSoftHalt | 0*DF_EntityKindFlag_LeafMutationDebugInfoMap | 0*DF_EntityKindFlag_TreeMutationUserConfig | 0*DF_EntityKindFlag_TreeMutationProjectConfig | 0*DF_EntityKindFlag_TreeMutationSoftHalt | 0*DF_EntityKindFlag_TreeMutationDebugInfoMap | 0*DF_EntityKindFlag_NameIsCode | 0*DF_EntityKindFlag_UserDefinedLifetime), (1*DF_EntityKindFlag_LeafMutationUserConfig | 0*DF_EntityKindFlag_LeafMutationProjectConfig | 0*DF_EntityKindFlag_LeafMutationSoftHalt | 0*DF_EntityKindFlag_LeafMutationDebugInfoMap | 0*DF_EntityKindFlag_TreeMutationUserConfig | 0*DF_EntityKindFlag_TreeMutationProjectConfig | 0*DF_EntityKindFlag_TreeMutationSoftHalt | 0*DF_EntityKindFlag_TreeMutationDebugInfoMap | 0*DF_EntityKindFlag_NameIsCode | 0*DF_EntityKindFlag_UserDefinedLifetime), (0*DF_EntityKindFlag_LeafMutationUserConfig | 0*DF_EntityKindFlag_LeafMutationProjectConfig | 0*DF_EntityKindFlag_LeafMutationSoftHalt | 0*DF_EntityKindFlag_LeafMutationDebugInfoMap | 0*DF_EntityKindFlag_TreeMutationUserConfig | 0*DF_EntityKindFlag_TreeMutationProjectConfig | 0*DF_EntityKindFlag_TreeMutationSoftHalt | 0*DF_EntityKindFlag_TreeMutationDebugInfoMap | 0*DF_EntityKindFlag_NameIsCode | 1*DF_EntityKindFlag_UserDefinedLifetime), -(0*DF_EntityKindFlag_LeafMutationUserConfig | 0*DF_EntityKindFlag_LeafMutationProjectConfig | 0*DF_EntityKindFlag_LeafMutationSoftHalt | 0*DF_EntityKindFlag_LeafMutationDebugInfoMap | 0*DF_EntityKindFlag_TreeMutationUserConfig | 0*DF_EntityKindFlag_TreeMutationProjectConfig | 0*DF_EntityKindFlag_TreeMutationSoftHalt | 0*DF_EntityKindFlag_TreeMutationDebugInfoMap | 0*DF_EntityKindFlag_NameIsCode | 0*DF_EntityKindFlag_UserDefinedLifetime), (0*DF_EntityKindFlag_LeafMutationUserConfig | 0*DF_EntityKindFlag_LeafMutationProjectConfig | 0*DF_EntityKindFlag_LeafMutationSoftHalt | 0*DF_EntityKindFlag_LeafMutationDebugInfoMap | 0*DF_EntityKindFlag_TreeMutationUserConfig | 0*DF_EntityKindFlag_TreeMutationProjectConfig | 0*DF_EntityKindFlag_TreeMutationSoftHalt | 0*DF_EntityKindFlag_TreeMutationDebugInfoMap | 1*DF_EntityKindFlag_NameIsCode | 1*DF_EntityKindFlag_UserDefinedLifetime), (0*DF_EntityKindFlag_LeafMutationUserConfig | 1*DF_EntityKindFlag_LeafMutationProjectConfig | 1*DF_EntityKindFlag_LeafMutationSoftHalt | 0*DF_EntityKindFlag_LeafMutationDebugInfoMap | 0*DF_EntityKindFlag_TreeMutationUserConfig | 0*DF_EntityKindFlag_TreeMutationProjectConfig | 0*DF_EntityKindFlag_TreeMutationSoftHalt | 0*DF_EntityKindFlag_TreeMutationDebugInfoMap | 0*DF_EntityKindFlag_NameIsCode | 1*DF_EntityKindFlag_UserDefinedLifetime), (0*DF_EntityKindFlag_LeafMutationUserConfig | 1*DF_EntityKindFlag_LeafMutationProjectConfig | 1*DF_EntityKindFlag_LeafMutationSoftHalt | 0*DF_EntityKindFlag_LeafMutationDebugInfoMap | 0*DF_EntityKindFlag_TreeMutationUserConfig | 1*DF_EntityKindFlag_TreeMutationProjectConfig | 0*DF_EntityKindFlag_TreeMutationSoftHalt | 0*DF_EntityKindFlag_TreeMutationDebugInfoMap | 1*DF_EntityKindFlag_NameIsCode | 1*DF_EntityKindFlag_UserDefinedLifetime), @@ -152,7 +148,7 @@ DF_EntityKindFlags df_g_entity_kind_flags_table[26] = (0*DF_EntityKindFlag_LeafMutationUserConfig | 0*DF_EntityKindFlag_LeafMutationProjectConfig | 0*DF_EntityKindFlag_LeafMutationSoftHalt | 0*DF_EntityKindFlag_LeafMutationDebugInfoMap | 0*DF_EntityKindFlag_TreeMutationUserConfig | 0*DF_EntityKindFlag_TreeMutationProjectConfig | 0*DF_EntityKindFlag_TreeMutationSoftHalt | 0*DF_EntityKindFlag_TreeMutationDebugInfoMap | 0*DF_EntityKindFlag_NameIsCode | 0*DF_EntityKindFlag_UserDefinedLifetime), }; -DF_EntityOpFlags df_g_entity_kind_op_flags_table[26] = +DF_EntityOpFlags df_g_entity_kind_op_flags_table[25] = { (0*DF_EntityOpFlag_Delete) | (0*DF_EntityOpFlag_Freeze) | (0*DF_EntityOpFlag_Edit) | (0*DF_EntityOpFlag_Rename) | (0*DF_EntityOpFlag_Enable) | (0*DF_EntityOpFlag_Condition) | (0*DF_EntityOpFlag_Duplicate), (0*DF_EntityOpFlag_Delete) | (0*DF_EntityOpFlag_Freeze) | (0*DF_EntityOpFlag_Edit) | (0*DF_EntityOpFlag_Rename) | (0*DF_EntityOpFlag_Enable) | (0*DF_EntityOpFlag_Condition) | (0*DF_EntityOpFlag_Duplicate), @@ -160,7 +156,6 @@ DF_EntityOpFlags df_g_entity_kind_op_flags_table[26] = (0*DF_EntityOpFlag_Delete) | (0*DF_EntityOpFlag_Freeze) | (0*DF_EntityOpFlag_Edit) | (0*DF_EntityOpFlag_Rename) | (0*DF_EntityOpFlag_Enable) | (0*DF_EntityOpFlag_Condition) | (0*DF_EntityOpFlag_Duplicate), (0*DF_EntityOpFlag_Delete) | (0*DF_EntityOpFlag_Freeze) | (0*DF_EntityOpFlag_Edit) | (0*DF_EntityOpFlag_Rename) | (0*DF_EntityOpFlag_Enable) | (0*DF_EntityOpFlag_Condition) | (0*DF_EntityOpFlag_Duplicate), (0*DF_EntityOpFlag_Delete) | (0*DF_EntityOpFlag_Freeze) | (0*DF_EntityOpFlag_Edit) | (0*DF_EntityOpFlag_Rename) | (0*DF_EntityOpFlag_Enable) | (0*DF_EntityOpFlag_Condition) | (0*DF_EntityOpFlag_Duplicate), -(0*DF_EntityOpFlag_Delete) | (0*DF_EntityOpFlag_Freeze) | (0*DF_EntityOpFlag_Edit) | (0*DF_EntityOpFlag_Rename) | (0*DF_EntityOpFlag_Enable) | (0*DF_EntityOpFlag_Condition) | (0*DF_EntityOpFlag_Duplicate), (1*DF_EntityOpFlag_Delete) | (0*DF_EntityOpFlag_Freeze) | (0*DF_EntityOpFlag_Edit) | (1*DF_EntityOpFlag_Rename) | (0*DF_EntityOpFlag_Enable) | (0*DF_EntityOpFlag_Condition) | (1*DF_EntityOpFlag_Duplicate), (1*DF_EntityOpFlag_Delete) | (0*DF_EntityOpFlag_Freeze) | (0*DF_EntityOpFlag_Edit) | (1*DF_EntityOpFlag_Rename) | (1*DF_EntityOpFlag_Enable) | (1*DF_EntityOpFlag_Condition) | (1*DF_EntityOpFlag_Duplicate), (0*DF_EntityOpFlag_Delete) | (0*DF_EntityOpFlag_Freeze) | (0*DF_EntityOpFlag_Edit) | (0*DF_EntityOpFlag_Rename) | (0*DF_EntityOpFlag_Enable) | (0*DF_EntityOpFlag_Condition) | (0*DF_EntityOpFlag_Duplicate), diff --git a/src/df/core/generated/df_core.meta.h b/src/df/core/generated/df_core.meta.h index 9f857b6e..722ecbb4 100644 --- a/src/df/core/generated/df_core.meta.h +++ b/src/df/core/generated/df_core.meta.h @@ -23,7 +23,6 @@ DF_EntityKind_Machine, DF_EntityKind_File, DF_EntityKind_OverrideFileLink, DF_EntityKind_AutoViewRule, -DF_EntityKind_FlashMarker, DF_EntityKind_WatchPin, DF_EntityKind_Breakpoint, DF_EntityKind_Condition, @@ -1532,11 +1531,11 @@ struct {B32 *value_ptr; String8 name;} DEV_toggle_table[] = }; C_LINKAGE_BEGIN extern Rng1U64 df_g_cmd_param_slot_range_table[24]; -extern DF_IconKind df_g_entity_kind_icon_kind_table[26]; -extern String8 df_g_entity_kind_display_string_table[26]; -extern String8 df_g_entity_kind_name_label_table[26]; -extern DF_EntityKindFlags df_g_entity_kind_flags_table[26]; -extern DF_EntityOpFlags df_g_entity_kind_op_flags_table[26]; +extern DF_IconKind df_g_entity_kind_icon_kind_table[25]; +extern String8 df_g_entity_kind_display_string_table[25]; +extern String8 df_g_entity_kind_name_label_table[25]; +extern DF_EntityKindFlags df_g_entity_kind_flags_table[25]; +extern DF_EntityOpFlags df_g_entity_kind_op_flags_table[25]; extern String8 df_g_cfg_src_string_table[4]; extern DF_CoreCmdKind df_g_cfg_src_load_cmd_kind_table[4]; extern DF_CoreCmdKind df_g_cfg_src_write_cmd_kind_table[4]; diff --git a/src/df/gfx/df_gfx.c b/src/df/gfx/df_gfx.c index 392a6d17..e967dace 100644 --- a/src/df/gfx/df_gfx.c +++ b/src/df/gfx/df_gfx.c @@ -11737,21 +11737,6 @@ df_code_slice(DF_Window *ws, DF_CtrlCtx *ctrl_ctx, EVAL_ParseCtx *parse_ctx, DF_ SLLQueuePush(first_txt_rng_color_pair, last_txt_rng_color_pair, n); } - // rjf: push for flash ranges - for(DF_EntityNode *n = params->flash_ranges.first; n != 0; n = n->next) - { - DF_Entity *flash_range = n->entity; - if(flash_range->flags & DF_EntityFlag_HasTextPoint && - flash_range->flags & DF_EntityFlag_HasTextPointAlt) - { - TxtRngColorPairNode *pair = push_array(scratch.arena, TxtRngColorPairNode, 1); - pair->rng = txt_rng(flash_range->text_point, flash_range->text_point_alt); - pair->color = df_rgba_from_entity(flash_range); - pair->color.w *= ClampTop(flash_range->life_left, 1.f); - SLLQueuePush(first_txt_rng_color_pair, last_txt_rng_color_pair, pair); - } - } - // rjf: push for ctrlified mouse expr if(ctrlified && !txt_pt_match(result.mouse_expr_rng.max, result.mouse_expr_rng.min)) { @@ -14252,6 +14237,18 @@ df_gfx_begin_frame(Arena *arena, DF_CmdList *cmds) df_gfx_state->cfg_palettes[DF_PaletteCode_DropSiteOverlay].text = current->colors[DF_ThemeColor_DropSiteOverlay]; df_gfx_state->cfg_palettes[DF_PaletteCode_DropSiteOverlay].text_weak = current->colors[DF_ThemeColor_DropSiteOverlay]; df_gfx_state->cfg_palettes[DF_PaletteCode_DropSiteOverlay].border = current->colors[DF_ThemeColor_DropSiteOverlay]; + if(df_setting_val_from_code(DF_SettingCode_OpaqueBackgrounds).s32) + { + for(EachEnumVal(DF_PaletteCode, code)) + { + if(df_gfx_state->cfg_palettes[code].background.x != 0 || + df_gfx_state->cfg_palettes[code].background.y != 0 || + df_gfx_state->cfg_palettes[code].background.z != 0) + { + df_gfx_state->cfg_palettes[code].background.w = 1; + } + } + } } //- rjf: animate alive-transitions for entities diff --git a/src/df/gfx/df_gfx.h b/src/df/gfx/df_gfx.h index 8d059dd9..cb710dfc 100644 --- a/src/df/gfx/df_gfx.h +++ b/src/df/gfx/df_gfx.h @@ -458,7 +458,6 @@ struct DF_CodeSliceParams F32 catchall_margin_width_px; F32 line_num_width_px; F32 line_text_max_width_px; - DF_EntityList flash_ranges; F32 margin_float_off_px; }; diff --git a/src/df/gfx/df_gfx.mdesk b/src/df/gfx/df_gfx.mdesk index 3f42d688..87a1e8a4 100644 --- a/src/df/gfx/df_gfx.mdesk +++ b/src/df/gfx/df_gfx.mdesk @@ -236,7 +236,7 @@ DF_GfxViewTable: { Types "types" "Types" Null Binoculars 0 0 1 0 1 1 1 1 "Nearly identical to `Watch`, but automatically filled with all types within the selected thread's module. View rules can be edited, like in `Watch`, but unlike `Watch`, expressions cannot be edited or added to the table." } { Procedures "procedures" "Procedures" Null Binoculars 0 0 1 0 1 1 1 1 "Nearly identical to `Watch`, but automatically filled with all procedures within the selected thread's module. View rules can be edited, like in `Watch`, but unlike `Watch`, expressions cannot be edited or added to the table." } { Output "output" "Output" Null List 0 0 1 0 0 0 0 1 "Displays textual output from the selected thread's containing process." } - { Memory "memory" "Memory" Null Grid 0 0 1 1 0 0 0 1 "A familiar hex-editor-like interface for viewing memory of attached processes." } + { Memory "memory" "Memory" Null Grid 0 0 1 0 0 0 0 1 "A familiar hex-editor-like interface for viewing memory of attached processes." } { Breakpoints "breakpoints" "Breakpoints" Null CircleFilled 0 0 1 0 1 0 1 1 "Displays a table of all breakpoints, containing information about each breakpoint's name, location, and hit count. Also contains per-breakpoint controls for enabling, deleting, or editing each breakpoint. For more information on breakpoints and their features, read the 'Breakpoints' section." } { WatchPins "watch_pins" "Watch Pins" Null Pin 0 0 1 0 1 1 1 1 "Displays a table of all watch pins (watched expressions, like those found in `Watch`, but instead of being within a table, being pinned to some source code location, like breakpoints). This table contains each pin's name, location, and controls for editing or deleting each pin." } { ExceptionFilters "exception_filters" "Exception Filters" Null Gear 0 0 1 0 1 0 1 1 "An interface which controls whether or not the debugger will halt attached processes upon encountering specific exception codes for the first time." } @@ -625,6 +625,7 @@ DF_SettingTable: {MenuAnimations menu_animations "Menu Animations" 1 0 1 } {ScrollingAnimations scrolling_animations "Scrolling Animations" 1 0 1 } {BackgroundBlur background_blur "Background Blur" 1 0 1 } + {OpaqueBackgrounds opaque_backgrounds "Opaque Backgrounds" 0 0 1 } {TabWidth tab_width "Tab Width" 4 0 32 } } diff --git a/src/df/gfx/df_views.c b/src/df/gfx/df_views.c index 61e79457..a3dde09e 100644 --- a/src/df/gfx/df_views.c +++ b/src/df/gfx/df_views.c @@ -369,6 +369,718 @@ df_entity_lister_item_array_sort_by_strength__in_place(DF_EntityListerItemArray quick_sort(array.v, array.count, sizeof(DF_EntityListerItem), df_qsort_compare_entity_lister__strength); } +//////////////////////////////// +//~ rjf: Code Views + +internal void +df_code_view_init(DF_CodeViewState *cv, DF_View *view) +{ + if(cv->initialized == 0) + { + cv->initialized = 1; + cv->cursor = cv->mark = txt_pt(1, 1); + cv->preferred_column = 1; + cv->find_text_arena = df_view_push_arena_ext(view); + } + df_view_equip_loading_info(view, 1, 0, 0); + view->loading_t = view->loading_t_target = 1.f; +} + +internal void +df_code_view_cmds(DF_Window *ws, DF_Panel *panel, DF_View *view, DF_CodeViewState *cv, DF_CmdList *cmds, U128 key, TXT_LangKind lang_kind) +{ + for(DF_CmdNode *n = cmds->first; n != 0; n = n->next) + { + DF_Cmd *cmd = &n->cmd; + + // rjf: mismatched window/panel => skip + if(df_window_from_handle(cmd->params.window) != ws || + df_panel_from_handle(cmd->params.panel) != panel) + { + continue; + } + + // rjf: process + DF_CoreCmdKind core_cmd_kind = df_core_cmd_kind_from_string(cmd->spec->info.string); + switch(core_cmd_kind) + { + default: break; + case DF_CoreCmdKind_GoToLine: + { + cv->goto_line_num = cmd->params.text_point.line; + }break; + case DF_CoreCmdKind_CenterCursor: + { + cv->center_cursor = 1; + }break; + case DF_CoreCmdKind_ContainCursor: + { + cv->contain_cursor = 1; + }break; + case DF_CoreCmdKind_FindTextForward: + { + arena_clear(cv->find_text_arena); + cv->find_text_fwd = push_str8_copy(cv->find_text_arena, cmd->params.string); + }break; + case DF_CoreCmdKind_FindTextBackward: + { + arena_clear(cv->find_text_arena); + cv->find_text_bwd = push_str8_copy(cv->find_text_arena, cmd->params.string); + }break; + case DF_CoreCmdKind_GoToNameAtCursor: + { + Temp scratch = scratch_begin(0, 0); + TXT_Scope *txt_scope = txt_scope_open(); + HS_Scope *hs_scope = hs_scope_open(); + { + // rjf: unpack entity info + U128 hash = {0}; + TXT_TextInfo text_info = txt_text_info_from_key_lang(txt_scope, key, lang_kind, &hash); + String8 data = hs_data_from_hash(hs_scope, hash); + + // rjf: determine expression range + Rng1U64 expr_range = {0}; + { + TxtRng selection_range = txt_rng(cv->cursor, cv->mark); + if(txt_pt_match(selection_range.min, selection_range.max)) + { + expr_range = txt_expr_off_range_from_info_data_pt(&text_info, data, cv->cursor); + } + else + { + expr_range = r1u64(txt_off_from_info_pt(&text_info, selection_range.min), txt_off_from_info_pt(&text_info, selection_range.max)); + } + } + + // rjf: expression range -> text + String8 expr_text = str8_substr(data, expr_range); + + // rjf: go to name + DF_CmdParams params = df_cmd_params_from_view(ws, panel, view); + params.string = expr_text; + df_cmd_params_mark_slot(¶ms, DF_CmdParamSlot_String); + df_push_cmd__root(¶ms, df_cmd_spec_from_core_cmd_kind(DF_CoreCmdKind_GoToName)); + } + hs_scope_close(hs_scope); + txt_scope_close(txt_scope); + scratch_end(scratch); + }break; + case DF_CoreCmdKind_ToggleWatchExpressionAtCursor: + { + Temp scratch = scratch_begin(0, 0); + TXT_Scope *txt_scope = txt_scope_open(); + HS_Scope *hs_scope = hs_scope_open(); + { + // rjf: unpack entity info + U128 hash = {0}; + TXT_TextInfo text_info = txt_text_info_from_key_lang(txt_scope, key, lang_kind, &hash); + String8 data = hs_data_from_hash(hs_scope, hash); + + // rjf: determine expression range + Rng1U64 expr_range = {0}; + { + TxtRng selection_range = txt_rng(cv->cursor, cv->mark); + if(txt_pt_match(selection_range.min, selection_range.max)) + { + expr_range = txt_expr_off_range_from_info_data_pt(&text_info, data, cv->cursor); + } + else + { + expr_range = r1u64(txt_off_from_info_pt(&text_info, selection_range.min), txt_off_from_info_pt(&text_info, selection_range.max)); + } + } + + // rjf: expression range -> text + String8 expr_text = str8_substr(data, expr_range); + + // rjf: toggle watch expr + DF_CmdParams params = df_cmd_params_from_view(ws, panel, view); + params.string = expr_text; + df_cmd_params_mark_slot(¶ms, DF_CmdParamSlot_String); + df_push_cmd__root(¶ms, df_cmd_spec_from_core_cmd_kind(DF_CoreCmdKind_ToggleWatchExpression)); + scratch_end(scratch); + } + hs_scope_close(hs_scope); + txt_scope_close(txt_scope); + scratch_end(scratch); + }break; + } + } +} + +internal void +df_code_view_build(DF_Window *ws, DF_Panel *panel, DF_View *view, DF_CodeViewState *cv, Rng2F32 rect, U128 key, TXT_LangKind lang_kind) +{ + ProfBeginFunction(); + Temp scratch = scratch_begin(0, 0); + HS_Scope *hs_scope = hs_scope_open(); + DI_Scope *di_scope = di_scope_open(); + TXT_Scope *txt_scope = txt_scope_open(); + + ////////////////////////////// + //- rjf: extract invariants + // + DF_CtrlCtx ctrl_ctx = df_ctrl_ctx_from_view(ws, view); + F_Tag code_font = df_font_from_slot(DF_FontSlot_Code); + F32 code_font_size = df_font_size_from_slot(ws, DF_FontSlot_Code); + F32 code_tab_size = f_column_size_from_tag_size(code_font, code_font_size)*df_setting_val_from_code(DF_SettingCode_TabWidth).s32; + F_Metrics code_font_metrics = f_metrics_from_tag_size(code_font, code_font_size); + F32 code_line_height = ceil_f32(f_line_height_from_metrics(&code_font_metrics) * 1.5f); + F32 big_glyph_advance = f_dim_from_tag_size_string(code_font, code_font_size, 0, 0, str8_lit("H")).x; + Vec2F32 panel_box_dim = dim_2f32(rect); + Vec2F32 bottom_bar_dim = {panel_box_dim.x, ui_em(1.8f, 0).value}; + F32 scroll_bar_dim = floor_f32(ui_top_font_size()*1.5f); + Vec2F32 code_area_dim = v2f32(panel_box_dim.x - scroll_bar_dim, panel_box_dim.y - scroll_bar_dim - bottom_bar_dim.y); + S64 num_possible_visible_lines = (S64)(code_area_dim.y/code_line_height)+1; + + ////////////////////////////// + //- rjf: unpack ctrl ctx & make parse ctx + // + DF_Entity *thread = df_entity_from_handle(ctrl_ctx.thread); + U64 unwind_count = ctrl_ctx.unwind_count; + U64 rip_vaddr = df_query_cached_rip_from_thread_unwind(thread, unwind_count); + DF_Entity *process = df_entity_ancestor_from_kind(thread, DF_EntityKind_Process); + EVAL_ParseCtx parse_ctx = df_eval_parse_ctx_from_process_vaddr(di_scope, process, rip_vaddr); + + ////////////////////////////// + //- rjf: unpack text info + // + U128 hash = {0}; + TXT_TextInfo text_info = txt_text_info_from_key_lang(txt_scope, key, lang_kind, &hash); + String8 data = hs_data_from_hash(hs_scope, hash); + B32 text_info_is_ready = (text_info.lines_count != 0); + + ////////////////////////////// + //- rjf: buffer is pending -> equip view with loading information + // + if(!text_info_is_ready) + { + df_view_equip_loading_info(view, 1, text_info.bytes_processed, text_info.bytes_to_process); + } + + ////////////////////////////// + //- rjf: determine visible line range / count + // + Rng1S64 visible_line_num_range = r1s64(view->scroll_pos.y.idx + (S64)(view->scroll_pos.y.off) + 1 - !!(view->scroll_pos.y.off < 0), + view->scroll_pos.y.idx + (S64)(view->scroll_pos.y.off) + 1 + num_possible_visible_lines); + Rng1S64 target_visible_line_num_range = r1s64(view->scroll_pos.y.idx + 1, + view->scroll_pos.y.idx + 1 + num_possible_visible_lines); + U64 visible_line_count = 0; + { + visible_line_num_range.min = Clamp(1, visible_line_num_range.min, (S64)text_info.lines_count); + visible_line_num_range.max = Clamp(1, visible_line_num_range.max, (S64)text_info.lines_count); + visible_line_num_range.min = Max(1, visible_line_num_range.min); + visible_line_num_range.max = Max(1, visible_line_num_range.max); + target_visible_line_num_range.min = Clamp(1, target_visible_line_num_range.min, (S64)text_info.lines_count); + target_visible_line_num_range.max = Clamp(1, target_visible_line_num_range.max, (S64)text_info.lines_count); + target_visible_line_num_range.min = Max(1, target_visible_line_num_range.min); + target_visible_line_num_range.max = Max(1, target_visible_line_num_range.max); + visible_line_count = (U64)dim_1s64(visible_line_num_range)+1; + } + + ////////////////////////////// + //- rjf: calculate scroll bounds + // + S64 line_size_x = 0; + Rng1S64 scroll_idx_rng[Axis2_COUNT] = {0}; + { + line_size_x = (text_info.lines_max_size*big_glyph_advance*3)/2; + line_size_x = ClampBot(line_size_x, (S64)big_glyph_advance*120); + line_size_x = ClampBot(line_size_x, (S64)code_area_dim.x); + scroll_idx_rng[Axis2_X] = r1s64(0, line_size_x-(S64)code_area_dim.x); + scroll_idx_rng[Axis2_Y] = r1s64(0, (S64)text_info.lines_count-1); + } + + ////////////////////////////// + //- rjf: calculate line-range-dependent info + // + F32 priority_margin_width_px = big_glyph_advance*3.5f; + F32 catchall_margin_width_px = big_glyph_advance*3.5f; + F32 line_num_width_px = big_glyph_advance * (log10(visible_line_num_range.max) + 3); + TXT_LineTokensSlice slice = txt_line_tokens_slice_from_info_data_line_range(scratch.arena, &text_info, data, visible_line_num_range); + + ////////////////////////////// + //- rjf: get active search query + // + String8 search_query = {0}; + Side search_query_side = Side_Invalid; + B32 search_query_is_active = 0; + { + DF_CoreCmdKind query_cmd_kind = df_core_cmd_kind_from_string(ws->query_cmd_spec->info.string); + if(query_cmd_kind == DF_CoreCmdKind_FindTextForward || + query_cmd_kind == DF_CoreCmdKind_FindTextBackward) + { + search_query = str8(ws->query_view_stack_top->query_buffer, ws->query_view_stack_top->query_string_size); + search_query_is_active = 1; + search_query_side = (query_cmd_kind == DF_CoreCmdKind_FindTextForward) ? Side_Max : Side_Min; + } + } + + ////////////////////////////// + //- rjf: prepare code slice info bundle, for the viewable region of text + // + DF_CodeSliceParams code_slice_params = {0}; + if(text_info_is_ready) + { + // rjf: fill basics + code_slice_params.flags = DF_CodeSliceFlag_PriorityMargin|DF_CodeSliceFlag_CatchallMargin|DF_CodeSliceFlag_LineNums|DF_CodeSliceFlag_Clickable; + code_slice_params.line_num_range = visible_line_num_range; + code_slice_params.line_text = push_array(scratch.arena, String8, visible_line_count); + code_slice_params.line_ranges = push_array(scratch.arena, Rng1U64, visible_line_count); + code_slice_params.line_tokens = push_array(scratch.arena, TXT_TokenArray, visible_line_count); + code_slice_params.line_bps = push_array(scratch.arena, DF_EntityList, visible_line_count); + code_slice_params.line_ips = push_array(scratch.arena, DF_EntityList, visible_line_count); + code_slice_params.line_pins = push_array(scratch.arena, DF_EntityList, visible_line_count); + code_slice_params.line_dasm2src = push_array(scratch.arena, DF_TextLineDasm2SrcInfoList, visible_line_count); + code_slice_params.line_src2dasm = push_array(scratch.arena, DF_TextLineSrc2DasmInfoList, visible_line_count); + code_slice_params.font = code_font; + code_slice_params.font_size = code_font_size; + code_slice_params.tab_size = code_tab_size; + code_slice_params.line_height_px = code_line_height; + code_slice_params.search_query = search_query; + code_slice_params.priority_margin_width_px = priority_margin_width_px; + code_slice_params.catchall_margin_width_px = catchall_margin_width_px; + code_slice_params.line_num_width_px = line_num_width_px; + code_slice_params.line_text_max_width_px = (F32)line_size_x; + code_slice_params.margin_float_off_px = view->scroll_pos.x.idx + view->scroll_pos.x.off; + + // rjf: fill text info + { + S64 line_num = visible_line_num_range.min; + U64 line_idx = visible_line_num_range.min-1; + for(U64 visible_line_idx = 0; visible_line_idx < visible_line_count; visible_line_idx += 1, line_idx += 1, line_num += 1) + { + code_slice_params.line_text[visible_line_idx] = str8_substr(data, text_info.lines_ranges[line_idx]); + code_slice_params.line_ranges[visible_line_idx] = text_info.lines_ranges[line_idx]; + code_slice_params.line_tokens[visible_line_idx] = slice.line_tokens[visible_line_idx]; + } + } + } + + ////////////////////////////// + //- rjf: build container + // + UI_Box *container_box = &ui_g_nil_box; + if(text_info_is_ready) + { + ui_set_next_pref_width(ui_px(code_area_dim.x, 1)); + ui_set_next_pref_height(ui_px(code_area_dim.y, 1)); + ui_set_next_child_layout_axis(Axis2_Y); + container_box = ui_build_box_from_stringf(UI_BoxFlag_Clip| + UI_BoxFlag_Scroll| + UI_BoxFlag_AllowOverflowX| + UI_BoxFlag_AllowOverflowY, + "###code_area_%p", view); + } + + ////////////////////////////// + //- rjf: cancelled search query -> center cursor + // + if(!search_query_is_active && cv->drifted_for_search) + { + cv->drifted_for_search = 0; + cv->center_cursor = 1; + } + + ////////////////////////////// + //- rjf: do searching operations + // + if(text_info_is_ready) + { + //- rjf: find text (forward) + if(cv->find_text_fwd.size != 0) + { + Temp scratch = scratch_begin(0, 0); + B32 found = 0; + B32 first = 1; + S64 line_num_start = cv->cursor.line; + S64 line_num_last = (S64)text_info.lines_count; + for(S64 line_num = line_num_start;; first = 0) + { + // rjf: pop scratch + temp_end(scratch); + + // rjf: gather line info + String8 line_string = str8_substr(data, text_info.lines_ranges[line_num-1]); + U64 search_start = 0; + if(cv->cursor.line == line_num && first) + { + search_start = cv->cursor.column; + } + + // rjf: search string + U64 needle_pos = str8_find_needle(line_string, search_start, cv->find_text_fwd, StringMatchFlag_CaseInsensitive); + if(needle_pos < line_string.size) + { + cv->cursor.line = line_num; + cv->cursor.column = needle_pos+1; + cv->mark = cv->cursor; + found = 1; + break; + } + + // rjf: break if circled back around to cursor + else if(line_num == line_num_start && !first) + { + break; + } + + // rjf: increment + line_num += 1; + if(line_num > line_num_last) + { + line_num = 1; + } + } + cv->center_cursor = found; + if(found == 0) + { + DF_CmdParams params = df_cmd_params_from_view(ws, panel, view); + params.string = push_str8f(scratch.arena, "Could not find \"%S\"", cv->find_text_fwd); + df_cmd_params_mark_slot(¶ms, DF_CmdParamSlot_String); + df_push_cmd__root(¶ms, df_cmd_spec_from_core_cmd_kind(DF_CoreCmdKind_Error)); + } + scratch_end(scratch); + } + + //- rjf: find text (backward) + if(cv->find_text_bwd.size != 0) + { + Temp scratch = scratch_begin(0, 0); + B32 found = 0; + B32 first = 1; + S64 line_num_start = cv->cursor.line; + S64 line_num_last = (S64)text_info.lines_count; + for(S64 line_num = line_num_start;; first = 0) + { + // rjf: pop scratch + temp_end(scratch); + + // rjf: gather line info + String8 line_string = str8_substr(data, text_info.lines_ranges[line_num-1]); + if(cv->cursor.line == line_num && first) + { + line_string = str8_prefix(line_string, cv->cursor.column-1); + } + + // rjf: search string + U64 next_needle_pos = line_string.size; + for(U64 needle_pos = 0; needle_pos < line_string.size;) + { + needle_pos = str8_find_needle(line_string, needle_pos, cv->find_text_bwd, StringMatchFlag_CaseInsensitive); + if(needle_pos < line_string.size) + { + next_needle_pos = needle_pos; + needle_pos += 1; + } + } + if(next_needle_pos < line_string.size) + { + cv->cursor.line = line_num; + cv->cursor.column = next_needle_pos+1; + cv->mark = cv->cursor; + found = 1; + break; + } + + // rjf: break if circled back around to cursor line + else if(line_num == line_num_start && !first) + { + break; + } + + // rjf: increment + line_num -= 1; + if(line_num == 0) + { + line_num = line_num_last; + } + } + cv->center_cursor = found; + if(found == 0) + { + DF_CmdParams params = df_cmd_params_from_view(ws, panel, view); + params.string = push_str8f(scratch.arena, "Could not find \"%S\"", cv->find_text_bwd); + df_cmd_params_mark_slot(¶ms, DF_CmdParamSlot_String); + df_push_cmd__root(¶ms, df_cmd_spec_from_core_cmd_kind(DF_CoreCmdKind_Error)); + } + scratch_end(scratch); + } + + MemoryZeroStruct(&cv->find_text_fwd); + MemoryZeroStruct(&cv->find_text_bwd); + arena_clear(cv->find_text_arena); + } + + ////////////////////////////// + //- rjf: do goto line + // + if(text_info_is_ready) if(cv->goto_line_num != 0) + { + S64 line_num = cv->goto_line_num; + cv->goto_line_num = 0; + line_num = Clamp(1, line_num, text_info.lines_count); + cv->cursor = cv->mark = txt_pt(line_num, 1); + cv->center_cursor = !cv->contain_cursor || (line_num < target_visible_line_num_range.min+4 || target_visible_line_num_range.max-4 < line_num); + } + + ////////////////////////////// + //- rjf: do keyboard interaction + // + B32 snap[Axis2_COUNT] = {0}; + UI_Focus(UI_FocusKind_On) + { + if(ui_is_focus_active() && text_info_is_ready && visible_line_num_range.max >= visible_line_num_range.min) + { + snap[Axis2_X] = snap[Axis2_Y] = df_do_txt_controls(&text_info, data, ClampBot(num_possible_visible_lines, 10) - 10, &cv->cursor, &cv->mark, &cv->preferred_column); + } + } + + ////////////////////////////// + //- rjf: build container contents + // + if(text_info_is_ready) UI_Parent(container_box) + { + //- rjf: build fractional space + container_box->view_off.x = container_box->view_off_target.x = view->scroll_pos.x.idx + view->scroll_pos.x.off; + container_box->view_off.y = container_box->view_off_target.y = code_line_height*mod_f32(view->scroll_pos.y.off, 1.f) + code_line_height*(view->scroll_pos.y.off < 0) - code_line_height*(view->scroll_pos.y.off == -1.f && view->scroll_pos.y.idx == 1); + + //- rjf: build code slice + DF_CodeSliceSignal sig = {0}; + UI_Focus(UI_FocusKind_On) + { + sig = df_code_slicef(ws, &ctrl_ctx, &parse_ctx, &code_slice_params, &cv->cursor, &cv->mark, &cv->preferred_column, "txt_view_%p", view); + } + + //- rjf: press code slice? -> focus panel + if(ui_pressed(sig.base)) + { + DF_CmdParams p = df_cmd_params_from_panel(ws, panel); + df_push_cmd__root(&p, df_cmd_spec_from_core_cmd_kind(DF_CoreCmdKind_FocusPanel)); + } + + //- rjf: dragging & outside region? -> contain cursor + if(ui_dragging(sig.base) && sig.base.event_flags == 0) + { + if(!contains_2f32(sig.base.box->rect, ui_mouse())) + { + cv->contain_cursor = 1; + } + else + { + snap[Axis2_X] = 1; + } + } + + //- rjf: ctrl+pressed? -> go to name + if(ui_pressed(sig.base) && sig.base.event_flags & OS_EventFlag_Ctrl) + { + ui_kill_action(); + DF_CmdParams params = df_cmd_params_from_view(ws, panel, view); + params.string = txt_string_from_info_data_txt_rng(&text_info, data, sig.mouse_expr_rng); + df_cmd_params_mark_slot(¶ms, DF_CmdParamSlot_String); + df_push_cmd__root(¶ms, df_cmd_spec_from_core_cmd_kind(DF_CoreCmdKind_GoToName)); + } + + //- rjf: copy text + if(!txt_pt_match(sig.copy_range.min, sig.copy_range.max)) + { + String8 text = txt_string_from_info_data_txt_rng(&text_info, data, sig.copy_range); + os_set_clipboard_text(text); + } + + //- rjf: selected text on single line, no query? -> set search text + if(!txt_pt_match(cv->cursor, cv->mark) && cv->cursor.line == cv->mark.line && search_query.size == 0) + { + String8 text = txt_string_from_info_data_txt_rng(&text_info, data, txt_rng(cv->cursor, cv->mark)); + df_set_search_string(text); + } + + //- rjf: toggle cursor watch + if(sig.toggle_cursor_watch) + { + DF_CmdParams params = df_cmd_params_from_view(ws, panel, view); + df_push_cmd__root(¶ms, df_cmd_spec_from_core_cmd_kind(DF_CoreCmdKind_ToggleWatchExpressionAtCursor)); + } + + //- rjf: set next statement + if(sig.set_next_statement_line_num != 0 && contains_1s64(visible_line_num_range, sig.set_next_statement_line_num)) + { + DF_CmdParams params = df_cmd_params_from_view(ws, panel, view); + params.text_point = txt_pt(sig.set_next_statement_line_num, 1); + df_push_cmd__root(¶ms, df_cmd_spec_from_core_cmd_kind(DF_CoreCmdKind_SetNextStatement)); + } + + //- rjf: go to disasm + if(sig.goto_disasm_line_num != 0 && contains_1s64(visible_line_num_range, sig.goto_disasm_line_num)) + { + U64 line_idx = (sig.goto_disasm_line_num-visible_line_num_range.min); + DF_TextLineSrc2DasmInfoList *src2dasm_list = &code_slice_params.line_src2dasm[line_idx]; + if(src2dasm_list->first != 0) + { + Rng1U64 voff_rng = src2dasm_list->first->v.voff_range; + DI_Key dbgi_key = src2dasm_list->first->v.dbgi_key; + DF_EntityList possible_modules = df_modules_from_dbgi_key(scratch.arena, &dbgi_key); + DF_Entity *thread = df_entity_from_handle(ctrl_ctx.thread); + DF_Entity *thread_dst_module = df_module_from_thread_candidates(thread, &possible_modules); + DF_Entity *process = df_entity_ancestor_from_kind(thread, DF_EntityKind_Process); + DF_Entity *module = thread_dst_module; + if(df_entity_is_nil(module)) + { + module = df_first_entity_from_list(&possible_modules); + } + U64 voff = voff_rng.min; + if(!df_entity_is_nil(module) && voff != 0) + { + DF_CmdParams params = df_cmd_params_from_window(ws); + params.entity = df_handle_from_entity(process); + params.vaddr = df_vaddr_from_voff(module, voff); + df_cmd_params_mark_slot(¶ms, DF_CmdParamSlot_Entity); + df_cmd_params_mark_slot(¶ms, DF_CmdParamSlot_VirtualAddr); + df_push_cmd__root(¶ms, df_cmd_spec_from_core_cmd_kind(DF_CoreCmdKind_FindCodeLocation)); + } + } + } + } + + ////////////////////////////// + //- rjf: apply post-build view snapping rules + // + if(text_info_is_ready) + { + // rjf: contain => snap + if(cv->contain_cursor) + { + cv->contain_cursor = 0; + snap[Axis2_X] = 1; + snap[Axis2_Y] = 1; + } + + // rjf: center cursor + if(cv->center_cursor) + { + cv->center_cursor = 0; + String8 cursor_line = str8_substr(data, text_info.lines_ranges[cv->cursor.line-1]); + F32 cursor_advance = f_dim_from_tag_size_string(code_font, code_font_size, 0, code_tab_size, str8_prefix(cursor_line, cv->cursor.column-1)).x; + + // rjf: scroll x + { + S64 new_idx = (S64)(cursor_advance - code_area_dim.x/2); + new_idx = Clamp(scroll_idx_rng[Axis2_X].min, new_idx, scroll_idx_rng[Axis2_X].max); + ui_scroll_pt_target_idx(&view->scroll_pos.x, new_idx); + snap[Axis2_X] = 0; + } + + // rjf: scroll y + { + S64 new_idx = (cv->cursor.line-1) - num_possible_visible_lines/2 + 2; + new_idx = Clamp(scroll_idx_rng[Axis2_Y].min, new_idx, scroll_idx_rng[Axis2_Y].max); + ui_scroll_pt_target_idx(&view->scroll_pos.y, new_idx); + snap[Axis2_Y] = 0; + } + } + + // rjf: snap in X + if(snap[Axis2_X]) + { + String8 cursor_line = str8_substr(data, text_info.lines_ranges[cv->cursor.line-1]); + S64 cursor_off = (S64)(f_dim_from_tag_size_string(code_font, code_font_size, 0, code_tab_size, str8_prefix(cursor_line, cv->cursor.column-1)).x + priority_margin_width_px + catchall_margin_width_px + line_num_width_px); + Rng1S64 visible_pixel_range = + { + view->scroll_pos.x.idx, + view->scroll_pos.x.idx + (S64)code_area_dim.x, + }; + Rng1S64 cursor_pixel_range = + { + cursor_off - (S64)(big_glyph_advance*4) - (S64)(priority_margin_width_px + catchall_margin_width_px + line_num_width_px), + cursor_off + (S64)(big_glyph_advance*4), + }; + S64 min_delta = Min(0, cursor_pixel_range.min - visible_pixel_range.min); + S64 max_delta = Max(0, cursor_pixel_range.max - visible_pixel_range.max); + S64 new_idx = view->scroll_pos.x.idx+min_delta+max_delta; + new_idx = Clamp(scroll_idx_rng[Axis2_X].min, new_idx, scroll_idx_rng[Axis2_X].max); + ui_scroll_pt_target_idx(&view->scroll_pos.x, new_idx); + } + + // rjf: snap in Y + if(snap[Axis2_Y]) + { + Rng1S64 cursor_visibility_range = r1s64(cv->cursor.line-4, cv->cursor.line+4); + cursor_visibility_range.min = ClampBot(0, cursor_visibility_range.min); + cursor_visibility_range.max = ClampBot(0, cursor_visibility_range.max); + S64 min_delta = Min(0, cursor_visibility_range.min-(target_visible_line_num_range.min)); + S64 max_delta = Max(0, cursor_visibility_range.max-(target_visible_line_num_range.min+num_possible_visible_lines)); + S64 new_idx = view->scroll_pos.y.idx+min_delta+max_delta; + new_idx = Clamp(0, new_idx, (S64)text_info.lines_count-1); + ui_scroll_pt_target_idx(&view->scroll_pos.y, new_idx); + } + } + + ////////////////////////////// + //- rjf: build horizontal scroll bar + // + if(text_info_is_ready) + { + ui_set_next_fixed_x(0); + ui_set_next_fixed_y(code_area_dim.y); + ui_set_next_fixed_width(panel_box_dim.x - scroll_bar_dim); + ui_set_next_fixed_height(scroll_bar_dim); + { + view->scroll_pos.x = ui_scroll_bar(Axis2_X, + ui_px(scroll_bar_dim, 1.f), + view->scroll_pos.x, + scroll_idx_rng[Axis2_X], + (S64)code_area_dim.x); + } + } + + ////////////////////////////// + //- rjf: build vertical scroll bar + // + if(text_info_is_ready) + { + ui_set_next_fixed_x(code_area_dim.x); + ui_set_next_fixed_y(0); + ui_set_next_fixed_width(scroll_bar_dim); + ui_set_next_fixed_height(panel_box_dim.y - bottom_bar_dim.y - scroll_bar_dim); + { + view->scroll_pos.y = ui_scroll_bar(Axis2_Y, + ui_px(scroll_bar_dim, 1.f), + view->scroll_pos.y, + scroll_idx_rng[Axis2_Y], + num_possible_visible_lines); + } + } + + ////////////////////////////// + //- rjf: top-level container interaction (scrolling) + // + if(text_info_is_ready) + { + UI_Signal sig = ui_signal_from_box(container_box); + if(sig.scroll.x != 0) + { + S64 new_idx = view->scroll_pos.x.idx+sig.scroll.x*big_glyph_advance; + new_idx = clamp_1s64(scroll_idx_rng[Axis2_X], new_idx); + ui_scroll_pt_target_idx(&view->scroll_pos.x, new_idx); + } + if(sig.scroll.y != 0) + { + S64 new_idx = view->scroll_pos.y.idx + sig.scroll.y; + new_idx = clamp_1s64(scroll_idx_rng[Axis2_Y], new_idx); + ui_scroll_pt_target_idx(&view->scroll_pos.y, new_idx); + } + ui_scroll_pt_clamp_idx(&view->scroll_pos.x, scroll_idx_rng[Axis2_X]); + ui_scroll_pt_clamp_idx(&view->scroll_pos.y, scroll_idx_rng[Axis2_Y]); + } + + txt_scope_close(txt_scope); + di_scope_close(di_scope); + hs_scope_close(hs_scope); + scratch_end(scratch); + ProfEnd(); +} + //////////////////////////////// //~ rjf: Watch Views @@ -5888,7 +6600,6 @@ DF_VIEW_UI_FUNCTION_DEF(Code) code_slice_params.catchall_margin_width_px = catchall_margin_width_px; code_slice_params.line_num_width_px = line_num_width_px; code_slice_params.line_text_max_width_px = (F32)line_size_x; - code_slice_params.flash_ranges = df_push_entity_child_list_with_kind(scratch.arena, entity, DF_EntityKind_FlashMarker); code_slice_params.margin_float_off_px = view->scroll_pos.x.idx + view->scroll_pos.x.off; // rjf: fill text info @@ -6224,9 +6935,16 @@ DF_VIEW_UI_FUNCTION_DEF(Code) } //- rjf: dragging & outside region? -> contain cursor - if(ui_dragging(sig.base) && !contains_2f32(sig.base.box->rect, ui_mouse()) && sig.base.event_flags == 0) + if(ui_dragging(sig.base) && sig.base.event_flags == 0) { - tv->contain_cursor = 1; + if(!contains_2f32(sig.base.box->rect, ui_mouse())) + { + tv->contain_cursor = 1; + } + else + { + snap[Axis2_X] = 1; + } } //- rjf: ctrl+pressed? -> go to name @@ -6983,7 +7701,6 @@ DF_VIEW_UI_FUNCTION_DEF(Disassembly) code_slice_params.catchall_margin_width_px = catchall_margin_width_px; code_slice_params.line_num_width_px = line_num_width_px; code_slice_params.line_text_max_width_px = (F32)line_size_x; - code_slice_params.flash_ranges = df_push_entity_child_list_with_kind(scratch.arena, process, DF_EntityKind_FlashMarker); code_slice_params.margin_float_off_px = view->scroll_pos.x.idx + view->scroll_pos.x.off; di_key_list_push(scratch.arena, &code_slice_params.relevant_dbgi_keys, &dbgi_key); @@ -7165,9 +7882,16 @@ DF_VIEW_UI_FUNCTION_DEF(Disassembly) } //- rjf: dragging & outside region? -> contain cursor - if(ui_dragging(sig.base) && !contains_2f32(sig.base.box->rect, ui_mouse()) && sig.base.event_flags == 0) + if(ui_dragging(sig.base) && sig.base.event_flags == 0) { - dv->contain_cursor = 1; + if(!contains_2f32(sig.base.box->rect, ui_mouse())) + { + dv->contain_cursor = 1; + } + else + { + snap[Axis2_X] = 1; + } } //- rjf: clicked margin? -> place breakpoint @@ -7823,7 +8547,6 @@ DF_VIEW_UI_FUNCTION_DEF(Output) code_slice_params.catchall_margin_width_px = 0.f; code_slice_params.line_num_width_px = line_num_width_px; code_slice_params.line_text_max_width_px = (F32)line_size_x; - code_slice_params.flash_ranges = df_push_entity_child_list_with_kind(scratch.arena, entity, DF_EntityKind_FlashMarker); code_slice_params.margin_float_off_px = view->scroll_pos.x.idx + view->scroll_pos.x.off; } diff --git a/src/df/gfx/df_views.h b/src/df/gfx/df_views.h index 80af1789..22c374ee 100644 --- a/src/df/gfx/df_views.h +++ b/src/df/gfx/df_views.h @@ -467,6 +467,13 @@ internal DF_EntityListerItemList df_entity_lister_item_list_from_needle(Arena *a internal DF_EntityListerItemArray df_entity_lister_item_array_from_list(Arena *arena, DF_EntityListerItemList list); internal void df_entity_lister_item_array_sort_by_strength__in_place(DF_EntityListerItemArray array); +//////////////////////////////// +//~ rjf: Code Views + +internal void df_code_view_init(DF_CodeViewState *cv, DF_View *view); +internal void df_code_view_cmds(DF_Window *ws, DF_Panel *panel, DF_View *view, DF_CodeViewState *cv, DF_CmdList *cmds, U128 key, TXT_LangKind lang_kind); +internal void df_code_view_build(DF_Window *ws, DF_Panel *panel, DF_View *view, DF_CodeViewState *cv, Rng2F32 rect, U128 key, TXT_LangKind lang_kind); + //////////////////////////////// //~ rjf: Watch Views diff --git a/src/df/gfx/generated/df_gfx.meta.c b/src/df/gfx/generated/df_gfx.meta.c index f9029467..07261700 100644 --- a/src/df/gfx/generated/df_gfx.meta.c +++ b/src/df/gfx/generated/df_gfx.meta.c @@ -159,7 +159,7 @@ DF_ViewSpecInfo df_g_gfx_view_kind_spec_info_table[31] = {(0|0*DF_ViewSpecFlag_ParameterizedByEntity|0*DF_ViewSpecFlag_ProjectSpecific|1*DF_ViewSpecFlag_CanSerialize|0*DF_ViewSpecFlag_CanSerializeEntityPath|1*DF_ViewSpecFlag_CanFilter|1*DF_ViewSpecFlag_FilterIsCode|1*DF_ViewSpecFlag_TypingAutomaticallyFilters), str8_lit_comp("types"), str8_lit_comp("Types"), DF_NameKind_Null, DF_IconKind_Binoculars, DF_VIEW_SETUP_FUNCTION_NAME(Types), DF_VIEW_STRING_FROM_STATE_FUNCTION_NAME(Types), DF_VIEW_CMD_FUNCTION_NAME(Types), DF_VIEW_UI_FUNCTION_NAME(Types)}, {(0|0*DF_ViewSpecFlag_ParameterizedByEntity|0*DF_ViewSpecFlag_ProjectSpecific|1*DF_ViewSpecFlag_CanSerialize|0*DF_ViewSpecFlag_CanSerializeEntityPath|1*DF_ViewSpecFlag_CanFilter|1*DF_ViewSpecFlag_FilterIsCode|1*DF_ViewSpecFlag_TypingAutomaticallyFilters), str8_lit_comp("procedures"), str8_lit_comp("Procedures"), DF_NameKind_Null, DF_IconKind_Binoculars, DF_VIEW_SETUP_FUNCTION_NAME(Procedures), DF_VIEW_STRING_FROM_STATE_FUNCTION_NAME(Procedures), DF_VIEW_CMD_FUNCTION_NAME(Procedures), DF_VIEW_UI_FUNCTION_NAME(Procedures)}, {(0|0*DF_ViewSpecFlag_ParameterizedByEntity|0*DF_ViewSpecFlag_ProjectSpecific|1*DF_ViewSpecFlag_CanSerialize|0*DF_ViewSpecFlag_CanSerializeEntityPath|0*DF_ViewSpecFlag_CanFilter|0*DF_ViewSpecFlag_FilterIsCode|0*DF_ViewSpecFlag_TypingAutomaticallyFilters), str8_lit_comp("output"), str8_lit_comp("Output"), DF_NameKind_Null, DF_IconKind_List, DF_VIEW_SETUP_FUNCTION_NAME(Output), DF_VIEW_STRING_FROM_STATE_FUNCTION_NAME(Output), DF_VIEW_CMD_FUNCTION_NAME(Output), DF_VIEW_UI_FUNCTION_NAME(Output)}, -{(0|0*DF_ViewSpecFlag_ParameterizedByEntity|0*DF_ViewSpecFlag_ProjectSpecific|1*DF_ViewSpecFlag_CanSerialize|1*DF_ViewSpecFlag_CanSerializeEntityPath|0*DF_ViewSpecFlag_CanFilter|0*DF_ViewSpecFlag_FilterIsCode|0*DF_ViewSpecFlag_TypingAutomaticallyFilters), str8_lit_comp("memory"), str8_lit_comp("Memory"), DF_NameKind_Null, DF_IconKind_Grid, DF_VIEW_SETUP_FUNCTION_NAME(Memory), DF_VIEW_STRING_FROM_STATE_FUNCTION_NAME(Memory), DF_VIEW_CMD_FUNCTION_NAME(Memory), DF_VIEW_UI_FUNCTION_NAME(Memory)}, +{(0|0*DF_ViewSpecFlag_ParameterizedByEntity|0*DF_ViewSpecFlag_ProjectSpecific|1*DF_ViewSpecFlag_CanSerialize|0*DF_ViewSpecFlag_CanSerializeEntityPath|0*DF_ViewSpecFlag_CanFilter|0*DF_ViewSpecFlag_FilterIsCode|0*DF_ViewSpecFlag_TypingAutomaticallyFilters), str8_lit_comp("memory"), str8_lit_comp("Memory"), DF_NameKind_Null, DF_IconKind_Grid, DF_VIEW_SETUP_FUNCTION_NAME(Memory), DF_VIEW_STRING_FROM_STATE_FUNCTION_NAME(Memory), DF_VIEW_CMD_FUNCTION_NAME(Memory), DF_VIEW_UI_FUNCTION_NAME(Memory)}, {(0|0*DF_ViewSpecFlag_ParameterizedByEntity|0*DF_ViewSpecFlag_ProjectSpecific|1*DF_ViewSpecFlag_CanSerialize|0*DF_ViewSpecFlag_CanSerializeEntityPath|1*DF_ViewSpecFlag_CanFilter|0*DF_ViewSpecFlag_FilterIsCode|1*DF_ViewSpecFlag_TypingAutomaticallyFilters), str8_lit_comp("breakpoints"), str8_lit_comp("Breakpoints"), DF_NameKind_Null, DF_IconKind_CircleFilled, DF_VIEW_SETUP_FUNCTION_NAME(Breakpoints), DF_VIEW_STRING_FROM_STATE_FUNCTION_NAME(Breakpoints), DF_VIEW_CMD_FUNCTION_NAME(Breakpoints), DF_VIEW_UI_FUNCTION_NAME(Breakpoints)}, {(0|0*DF_ViewSpecFlag_ParameterizedByEntity|0*DF_ViewSpecFlag_ProjectSpecific|1*DF_ViewSpecFlag_CanSerialize|0*DF_ViewSpecFlag_CanSerializeEntityPath|1*DF_ViewSpecFlag_CanFilter|1*DF_ViewSpecFlag_FilterIsCode|1*DF_ViewSpecFlag_TypingAutomaticallyFilters), str8_lit_comp("watch_pins"), str8_lit_comp("Watch Pins"), DF_NameKind_Null, DF_IconKind_Pin, DF_VIEW_SETUP_FUNCTION_NAME(WatchPins), DF_VIEW_STRING_FROM_STATE_FUNCTION_NAME(WatchPins), DF_VIEW_CMD_FUNCTION_NAME(WatchPins), DF_VIEW_UI_FUNCTION_NAME(WatchPins)}, {(0|0*DF_ViewSpecFlag_ParameterizedByEntity|0*DF_ViewSpecFlag_ProjectSpecific|1*DF_ViewSpecFlag_CanSerialize|0*DF_ViewSpecFlag_CanSerializeEntityPath|1*DF_ViewSpecFlag_CanFilter|0*DF_ViewSpecFlag_FilterIsCode|1*DF_ViewSpecFlag_TypingAutomaticallyFilters), str8_lit_comp("exception_filters"), str8_lit_comp("Exception Filters"), DF_NameKind_Null, DF_IconKind_Gear, DF_VIEW_SETUP_FUNCTION_NAME(ExceptionFilters), DF_VIEW_STRING_FROM_STATE_FUNCTION_NAME(ExceptionFilters), DF_VIEW_CMD_FUNCTION_NAME(ExceptionFilters), DF_VIEW_UI_FUNCTION_NAME(ExceptionFilters)}, @@ -1201,7 +1201,7 @@ str8_lit_comp("thread_error"), str8_lit_comp("breakpoint"), }; -String8 df_g_setting_code_display_string_table[8] = +String8 df_g_setting_code_display_string_table[9] = { str8_lit_comp("Hover Animations"), str8_lit_comp("Press Animations"), @@ -1210,10 +1210,11 @@ str8_lit_comp("Tooltip Animations"), str8_lit_comp("Menu Animations"), str8_lit_comp("Scrolling Animations"), str8_lit_comp("Background Blur"), +str8_lit_comp("Opaque Backgrounds"), str8_lit_comp("Tab Width"), }; -String8 df_g_setting_code_lower_string_table[8] = +String8 df_g_setting_code_lower_string_table[9] = { str8_lit_comp("hover_animations"), str8_lit_comp("press_animations"), @@ -1222,10 +1223,11 @@ str8_lit_comp("tooltip_animations"), str8_lit_comp("menu_animations"), str8_lit_comp("scrolling_animations"), str8_lit_comp("background_blur"), +str8_lit_comp("opaque_backgrounds"), str8_lit_comp("tab_width"), }; -DF_SettingVal df_g_setting_code_default_val_table[8] = +DF_SettingVal df_g_setting_code_default_val_table[9] = { {1, 1}, {1, 1}, @@ -1234,10 +1236,11 @@ DF_SettingVal df_g_setting_code_default_val_table[8] = {1, 1}, {1, 1}, {1, 1}, +{1, 0}, {1, 4}, }; -Rng1S32 df_g_setting_code_s32_range_table[8] = +Rng1S32 df_g_setting_code_s32_range_table[9] = { {0, 1}, {0, 1}, @@ -1246,6 +1249,7 @@ Rng1S32 df_g_setting_code_s32_range_table[8] = {0, 1}, {0, 1}, {0, 1}, +{0, 1}, {0, 32}, }; diff --git a/src/df/gfx/generated/df_gfx.meta.h b/src/df/gfx/generated/df_gfx.meta.h index f84ec142..f0292711 100644 --- a/src/df/gfx/generated/df_gfx.meta.h +++ b/src/df/gfx/generated/df_gfx.meta.h @@ -145,6 +145,7 @@ DF_SettingCode_TooltipAnimations, DF_SettingCode_MenuAnimations, DF_SettingCode_ScrollingAnimations, DF_SettingCode_BackgroundBlur, +DF_SettingCode_OpaqueBackgrounds, DF_SettingCode_TabWidth, DF_SettingCode_COUNT, } DF_SettingCode; @@ -332,10 +333,10 @@ extern Vec4F32 df_g_theme_preset_colors__far_manager[75]; extern Vec4F32* df_g_theme_preset_colors_table[9]; extern String8 df_g_theme_color_display_string_table[75]; extern String8 df_g_theme_color_cfg_string_table[75]; -extern String8 df_g_setting_code_display_string_table[8]; -extern String8 df_g_setting_code_lower_string_table[8]; -extern DF_SettingVal df_g_setting_code_default_val_table[8]; -extern Rng1S32 df_g_setting_code_s32_range_table[8]; +extern String8 df_g_setting_code_display_string_table[9]; +extern String8 df_g_setting_code_lower_string_table[9]; +extern DF_SettingVal df_g_setting_code_default_val_table[9]; +extern Rng1S32 df_g_setting_code_s32_range_table[9]; read_only global U8 df_g_icon_font_bytes__data[] = { 0x00,0x01,0x00,0x00,0x00,0x0f,0x00,0x80,0x00,0x03,0x00,0x70,0x47,0x53,0x55,0x42,0x20,0x8b,0x25,0x7a,0x00,0x00,0x00,0xfc,0x00,0x00,0x00,0x54,0x4f,0x53,0x2f,0x32,0x56,0x44,0x49,0xa0,0x00,0x00,0x01,0x50,0x00,0x00,0x00,0x60,0x63,0x6d,0x61,0x70,0x2a,0x09,0xe2,0xc2,0x00,0x00,0x01,0xb0,0x00,0x00,0x05,0xec,0x63,0x76,0x74,0x20, diff --git a/src/mutable_text/mutable_text.c b/src/mutable_text/mutable_text.c new file mode 100644 index 00000000..92f2c0ed --- /dev/null +++ b/src/mutable_text/mutable_text.c @@ -0,0 +1,142 @@ +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +//////////////////////////////// +//~ rjf: Main Layer Initialization + +internal void +mtx_init(void) +{ + Arena *arena = arena_alloc(); + mtx_shared = push_array(arena, MTX_Shared, 1); + mtx_shared->arena = arena; + mtx_shared->slots_count = 256; + mtx_shared->stripes_count = Min(mtx_shared->slots_count, os_logical_core_count()); + mtx_shared->slots = push_array(arena, MTX_Slot, mtx_shared->slots_count); + mtx_shared->stripes = push_array(arena, MTX_Stripe, mtx_shared->stripes_count); + for(U64 idx = 0; idx < mtx_shared->stripes_count; idx += 1) + { + mtx_shared->stripes[idx].arena = arena_alloc(); + mtx_shared->stripes[idx].rw_mutex = os_rw_mutex_alloc(); + } + mtx_shared->mut_threads_count = Min(os_logical_core_count(), 4); + mtx_shared->mut_threads = push_array(arena, MTX_MutThread, mtx_shared->mut_threads_count); + for(U64 idx = 0; idx < mtx_shared->mut_threads_count; idx += 1) + { + mtx_shared->mut_threads[idx].ring_size = KB(64); + mtx_shared->mut_threads[idx].ring_base = push_array_no_zero(arena, U8, mtx_shared->mut_threads[idx].ring_size); + mtx_shared->mut_threads[idx].cv = os_condition_variable_alloc(); + mtx_shared->mut_threads[idx].mutex = os_mutex_alloc(); + mtx_shared->mut_threads[idx].thread = os_launch_thread(mtx_mut_thread__entry_point, &mtx_shared->mut_threads[idx], 0); + } +} + +//////////////////////////////// +//~ rjf: Buffer Operations + +internal void +mtx_push_op(U128 buffer_key, MTX_Op op) +{ + MTX_MutThread *thread = &mtx_shared->mut_threads[buffer_key.u64[1]%mtx_shared->mut_threads_count]; + mtx_enqueue_op(thread, buffer_key, op); +} + +//////////////////////////////// +//~ rjf: Mutation Threads + +internal void +mtx_enqueue_op(MTX_MutThread *thread, U128 buffer_key, MTX_Op op) +{ + // TODO(rjf): if op.replace is too big, need to split into multiple edits + OS_MutexScope(thread->mutex) for(;;) + { + U64 unconsumed_size = thread->ring_write_pos - thread->ring_read_pos; + U64 available_size = thread->ring_size - unconsumed_size; + if(available_size >= sizeof(buffer_key) + sizeof(op.range) + sizeof(op.replace.size) + op.replace.size) + { + thread->ring_write_pos += ring_write_struct(thread->ring_base, thread->ring_size, thread->ring_write_pos, &buffer_key); + thread->ring_write_pos += ring_write_struct(thread->ring_base, thread->ring_size, thread->ring_write_pos, &op.range); + thread->ring_write_pos += ring_write_struct(thread->ring_base, thread->ring_size, thread->ring_write_pos, &op.replace.size); + thread->ring_write_pos += ring_write(thread->ring_base, thread->ring_size, thread->ring_write_pos, op.replace.str, op.replace.size); + thread->ring_write_pos += 7; + thread->ring_write_pos -= thread->ring_write_pos%8; + break; + } + os_condition_variable_wait(thread->cv, thread->mutex, max_U64); + } + os_condition_variable_broadcast(thread->cv); +} + +internal void +mtx_dequeue_op(Arena *arena, MTX_MutThread *thread, U128 *buffer_key_out, MTX_Op *op_out) +{ + OS_MutexScope(thread->mutex) for(;;) + { + U64 unconsumed_size = thread->ring_write_pos - thread->ring_read_pos; + if(unconsumed_size >= sizeof(*buffer_key_out) + sizeof(op_out->range) + sizeof(op_out->replace.size)) + { + thread->ring_read_pos += ring_read_struct(thread->ring_base, thread->ring_size, thread->ring_read_pos, buffer_key_out); + thread->ring_read_pos += ring_read_struct(thread->ring_base, thread->ring_size, thread->ring_read_pos, &op_out->range); + thread->ring_read_pos += ring_read_struct(thread->ring_base, thread->ring_size, thread->ring_read_pos, &op_out->replace.size); + op_out->replace.str = push_array_no_zero(arena, U8, op_out->replace.size); + thread->ring_read_pos += ring_read(thread->ring_base, thread->ring_size, thread->ring_read_pos, op_out->replace.str, op_out->replace.size); + thread->ring_read_pos += 7; + thread->ring_read_pos -= thread->ring_read_pos%8; + break; + } + os_condition_variable_wait(thread->cv, thread->mutex, max_U64); + } + os_condition_variable_broadcast(thread->cv); +} + +internal void +mtx_mut_thread__entry_point(void *p) +{ + MTX_MutThread *mut_thread = (MTX_MutThread *)p; + ThreadNameF("[mtx] mut thread #%I64u", (U64)(mut_thread - mtx_shared->mut_threads)); + for(;;) + { + Temp scratch = scratch_begin(0, 0); + HS_Scope *hs_scope = hs_scope_open(); + + //- rjf: get next op + U128 buffer_key = {0}; + MTX_Op op = {0}; + mtx_dequeue_op(scratch.arena, mut_thread, &buffer_key, &op); + + //- rjf: get buffer's current data + U128 hash = hs_hash_from_key(buffer_key, 0); + String8 data = hs_data_from_hash(hs_scope, hash); + + //- rjf: clamp op by data + op.range.min = Min(op.range.min, data.size); + op.range.max = Min(op.range.max, data.size); + + //- 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); + 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)); + if(pre_replace_data.size != 0) + { + MemoryCopy(new_data_base+0, pre_replace_data.str, pre_replace_data.size); + } + if(op.replace.size != 0) + { + MemoryCopy(new_data_base+pre_replace_data.size, op.replace.str, op.replace.size); + } + if(post_replace_data.size != 0) + { + MemoryCopy(new_data_base+pre_replace_data.size+op.replace.size, post_replace_data.str, post_replace_data.size); + } + String8 new_data = str8(new_data_base, new_data_size); + hs_submit_data(buffer_key, &arena, new_data); + } + + hs_scope_close(hs_scope); + scratch_end(scratch); + } +} diff --git a/src/mutable_text/mutable_text.h b/src/mutable_text/mutable_text.h new file mode 100644 index 00000000..716faa6d --- /dev/null +++ b/src/mutable_text/mutable_text.h @@ -0,0 +1,96 @@ +// Copyright (c) 2024 Epic Games Tools +// Licensed under the MIT license (https://opensource.org/license/mit/) + +#ifndef MUTABLE_TEXT_H +#define MUTABLE_TEXT_H + +//////////////////////////////// +//~ rjf: Cache Types + +typedef struct MTX_Node MTX_Node; +struct MTX_Node +{ + MTX_Node *next; + MTX_Node *prev; + U128 key; +}; + +typedef struct MTX_Slot MTX_Slot; +struct MTX_Slot +{ + MTX_Node *first; + MTX_Node *last; +}; + +typedef struct MTX_Stripe MTX_Stripe; +struct MTX_Stripe +{ + Arena *arena; + MTX_Node *free_node; + OS_Handle rw_mutex; +}; + +//////////////////////////////// +//~ rjf: Mutation Thread Types + +typedef struct MTX_Op MTX_Op; +struct MTX_Op +{ + Rng1U64 range; + String8 replace; +}; + +typedef struct MTX_MutThread MTX_MutThread; +struct MTX_MutThread +{ + U64 ring_size; + U8 *ring_base; + U64 ring_read_pos; + U64 ring_write_pos; + OS_Handle cv; + OS_Handle mutex; + OS_Handle thread; +}; + +//////////////////////////////// +//~ rjf: Shared State + +typedef struct MTX_Shared MTX_Shared; +struct MTX_Shared +{ + Arena *arena; + + // rjf: buffer cache + U64 slots_count; + U64 stripes_count; + MTX_Slot *slots; + MTX_Stripe *stripes; + + // rjf: mut threads + U64 mut_threads_count; + MTX_MutThread *mut_threads; +}; + +//////////////////////////////// +//~ rjf: Globals + +global MTX_Shared *mtx_shared = 0; + +//////////////////////////////// +//~ rjf: Main Layer Initialization + +internal void mtx_init(void); + +//////////////////////////////// +//~ rjf: Buffer Operations + +internal void mtx_push_op(U128 buffer_key, MTX_Op op); + +//////////////////////////////// +//~ rjf: Mutation Threads + +internal void mtx_enqueue_op(MTX_MutThread *thread, U128 buffer_key, MTX_Op op); +internal void mtx_dequeue_op(Arena *arena, MTX_MutThread *thread, U128 *buffer_key_out, MTX_Op *op_out); +internal void mtx_mut_thread__entry_point(void *p); + +#endif // MUTABLE_TEXT_H diff --git a/src/raddbg/raddbg_main.c b/src/raddbg/raddbg_main.c index 024d0aa4..f68eb1b4 100644 --- a/src/raddbg/raddbg_main.c +++ b/src/raddbg/raddbg_main.c @@ -36,6 +36,7 @@ #include "hash_store/hash_store.h" #include "file_stream/file_stream.h" #include "text_cache/text_cache.h" +#include "mutable_text/mutable_text.h" #include "path/path.h" #include "txti/txti.h" #include "coff/coff.h" @@ -76,6 +77,7 @@ #include "hash_store/hash_store.c" #include "file_stream/file_stream.c" #include "text_cache/text_cache.c" +#include "mutable_text/mutable_text.c" #include "path/path.c" #include "txti/txti.c" #include "coff/coff.c"