From 582faa4bdfed7e71da9b8593cbc8dca0e3ad9308 Mon Sep 17 00:00:00 2001 From: Allen Webster Date: Wed, 21 Sep 2016 18:34:19 -0400 Subject: [PATCH] new wrap rule; cleaning up some init stuff --- 4ed.cpp | 162 +-- 4ed_api_implementation.cpp | 17 +- 4ed_file_view.cpp | 81 +- TODO.txt | 14 +- buffer/4coder_buffer_abstract.cpp | 1929 +++++++++++++++-------------- 5 files changed, 1109 insertions(+), 1094 deletions(-) diff --git a/4ed.cpp b/4ed.cpp index a29d11db..298b5429 100644 --- a/4ed.cpp +++ b/4ed.cpp @@ -39,9 +39,11 @@ typedef struct Command_Data{ System_Functions *system; Live_Views *live_set; +#if 0 // TODO(allen): eliminate this shit yo! Panel *panel; View *view; +#endif i32 screen_width, screen_height; Key_Event_Data key; @@ -246,12 +248,23 @@ do_feedback_message(System_Functions *system, Models *models, String value, b32 #define USE_MODELS(n) Models *n = command->models #define USE_VARS(n) App_Vars *n = command->vars -#define USE_PANEL(n) Panel *n = command->panel -#define USE_VIEW(n) View *n = command->view #define USE_FILE(n,v) Editing_File *n = (v)->file_data.file -#define REQ_OPEN_VIEW(n) View *n = command->panel->view; if (view_lock_level(n) > LockLevel_Open) return -#define REQ_READABLE_VIEW(n) View *n = command->panel->view; if (view_lock_level(n) > LockLevel_Protected) return + +#define USE_PANEL(n) Panel *n = 0;{ \ + i32 panel_index = command->models->layout.active_panel; \ + n = command->models->layout.panels + panel_index; \ +} + +#define USE_VIEW(n) View *n = 0;{ \ + i32 panel_index = command->models->layout.active_panel; \ + Panel *panel = command->models->layout.panels + panel_index; \ + n = panel->view; \ +} + +#define REQ_OPEN_VIEW(n) USE_VIEW(n); if (view_lock_level(n) > LockLevel_Open) return + +#define REQ_READABLE_VIEW(n) USE_VIEW(n); if (view_lock_level(n) > LockLevel_Protected) return #define REQ_FILE(n,v) Editing_File *n = (v)->file_data.file; if (!n) return #define REQ_FILE_HISTORY(n,v) Editing_File *n = (v)->file_data.file; if (!n || !n->state.undo.undo.edits) return @@ -710,12 +723,6 @@ COMMAND_DECL(user_callback){ if (binding.custom) binding.custom(&models->app_links); } -internal void -update_command_data(App_Vars *vars, Command_Data *cmd){ - cmd->panel = cmd->models->layout.panels + cmd->models->layout.active_panel; - cmd->view = cmd->panel->view; -} - globalvar Command_Function command_table[cmdid_count]; #include "4ed_api_implementation.cpp" @@ -728,9 +735,9 @@ struct Command_In{ internal void command_caller(Coroutine *coroutine){ Command_In *cmd_in = (Command_In*)coroutine->in; - Command_Data *cmd = cmd_in->cmd; - Models *models = cmd->models; - View *view = cmd->view; + Command_Data *command = cmd_in->cmd; + Models *models = command->models; + USE_VIEW(view); view->next_mode = view_mode_zero(); if (models->command_caller){ @@ -745,7 +752,7 @@ command_caller(Coroutine *coroutine){ } } else{ - cmd_in->bind.function(cmd->system, cmd, cmd_in->bind); + cmd_in->bind.function(command->system, command, cmd_in->bind); } view->mode = view->next_mode; } @@ -1673,6 +1680,42 @@ App_Init_Sig(app_init){ models->palette = push_array(partition, u32, models->palette_size); // NOTE(allen): init first panel + Command_Data *cmd = &vars->command_data; + + cmd->models = models; + cmd->vars = vars; + cmd->system = system; + cmd->live_set = &vars->live_set; + + cmd->screen_width = target->width; + cmd->screen_height = target->height; + + cmd->key = null_key_event_data; + + General_Memory *general = &models->mem.general; + + String init_files[] = { + make_lit_string("*messages*"), + make_lit_string("*scratch*"), + }; + + Editing_File **init_file_ptrs[] = { + &models->message_buffer, + &models->scratch_buffer, + }; + + for (i32 i = 0; i < ArrayCount(init_files); ++i){ + String name = init_files[i]; + Editing_File *file = working_set_alloc_always(&models->working_set, general); + buffer_bind_name(general, &models->working_set, file, name); + init_read_only_file(system, models, file); + file->settings.never_kill = 1; + file->settings.unimportant = 1; + file->settings.unwrapped_lines = 1; + + *init_file_ptrs[i] = file; + } + Panel_And_ID p = layout_alloc_panel(&models->layout); panel_make_empty(system, vars, p.panel); models->layout.active_panel = p.id; @@ -1811,22 +1854,11 @@ App_Step_Sig(app_step){ i32 current_width = target->width; i32 current_height = target->height; - Panel *panel = 0, *used_panels = &models->layout.used_sentinel; - View *view = 0; - models->layout.full_width = current_width; models->layout.full_height = current_height; if (prev_width != current_width || prev_height != current_height){ layout_refit(&models->layout, prev_width, prev_height); - - for (dll_items(panel, used_panels)){ - view = panel->view; - Assert(view); - // TODO(allen): All responses to a panel changing size should - // be handled in the same place. - view_change_size(&models->mem.general, view); - } } } @@ -1985,9 +2017,6 @@ App_Step_Sig(app_step){ cmd->system = system; cmd->live_set = &vars->live_set; - cmd->panel = models->layout.panels + models->layout.active_panel; - cmd->view = cmd->panel->view; - cmd->screen_width = target->width; cmd->screen_height = target->height; @@ -2024,30 +2053,6 @@ App_Step_Sig(app_step){ } #endif - General_Memory *general = &models->mem.general; - - { - Editing_File *file = working_set_alloc_always(&models->working_set, general); - buffer_bind_name(general, &models->working_set, file, make_lit_string("*messages*")); - init_read_only_file(system, models, file); - file->settings.never_kill = 1; - file->settings.unimportant = 1; - file->settings.unwrapped_lines = 1; - - models->message_buffer = file; - } - - { - Editing_File *file = working_set_alloc_always(&models->working_set, general); - buffer_bind_name(general, &models->working_set, file, make_lit_string("*scratch*")); - init_normal_file(system, models, file, 0, 0); - file->settings.never_kill = 1; - file->settings.unimportant = 1; - file->settings.unwrapped_lines = 1; - - models->scratch_buffer = file; - } - if (models->hooks[hook_start]){ models->hooks[hook_start](&models->app_links); } @@ -2101,11 +2106,6 @@ App_Step_Sig(app_step){ panel = panel->next; } - for (;i < models->layout.panel_count; ++i, panel = panel->next){ - view_set_file(panel->view, models->scratch_buffer, models); - view_show_file(panel->view); - } - panel = models->layout.used_sentinel.next; for (i = 0; i < models->settings.init_files_count; ++i, panel = panel->next){ Assert(panel->view->file_data.file != 0); @@ -2129,7 +2129,8 @@ App_Step_Sig(app_step){ if (there_is_unsaved){ Coroutine *command_coroutine = models->command_coroutine; - View *view = cmd->view; + Command_Data *command = cmd; + USE_VIEW(view); for (i32 i = 0; i < 128 && command_coroutine; ++i){ User_Input user_in = {0}; @@ -2208,7 +2209,8 @@ App_Step_Sig(app_step){ for (i32 key_i = 0; key_i < key_data.count; ++key_i){ Key_Event_Data key = get_single_key(&key_data, key_i); - View *view = cmd->view; + Command_Data *command = cmd; + USE_VIEW(view); b32 pass_in = 0; cmd->key = key; @@ -2265,7 +2267,8 @@ App_Step_Sig(app_step){ // NOTE(allen): Mouse input to command coroutine if (models->command_coroutine != 0 && (get_flags & EventOnMouse)){ - View *view = cmd->view; + Command_Data *command = cmd; + USE_VIEW(view); b32 pass_in = 0; User_Input user_in; @@ -2335,8 +2338,6 @@ App_Step_Sig(app_step){ } } - update_command_data(vars, cmd); - // NOTE(allen): pass raw input to the panels Input_Summary dead_input = {}; dead_input.mouse.x = input->mouse.x; @@ -2354,15 +2355,18 @@ App_Step_Sig(app_step){ { Panel *panel = 0, *used_panels = 0; - View *view = 0, *active_view = 0; + View *view = 0; b32 active = 0; Input_Summary summary = {0}; - active_view = cmd->panel->view; + Command_Data *command = cmd; + USE_VIEW(active_view); + USE_PANEL(active_panel); + used_panels = &models->layout.used_sentinel; for (dll_items(panel, used_panels)){ view = panel->view; - active = (panel == cmd->panel); + active = (panel == active_panel); summary = (active)?(active_input):(dead_input); view->changed_context_in_step = 0; @@ -2382,7 +2386,7 @@ App_Step_Sig(app_step){ } if (view->changed_context_in_step == 0){ - active = (panel == cmd->panel); + active = (panel == active_panel); summary = (active)?(active_input):(dead_input); if (panel == mouse_panel && !input->mouse.out_of_window){ summary.mouse = mouse_state; @@ -2443,8 +2447,6 @@ App_Step_Sig(app_step){ } } - update_command_data(vars, cmd); - // NOTE(allen): command execution { Key_Summary key_data = get_key_data(&vars->available_input); @@ -2460,11 +2462,15 @@ App_Step_Sig(app_step){ Key_Event_Data key = get_single_key(&key_data, key_i); cmd->key = key; - View *view = cmd->view; + Command_Data *command = cmd; + USE_VIEW(view); + Assert(view); + + Command_Map *map = view->map; + if (map == 0){ + map = &models->map_top; + } - Command_Map *map = 0; - if (view) map = view->map; - if (map == 0) map = &models->map_top; Command_Binding cmd_bind = map_extract_recursive(map, key); if (cmd_bind.function){ @@ -2514,8 +2520,6 @@ App_Step_Sig(app_step){ } } - update_command_data(vars, cmd); - // NOTE(allen): pass consumption data to debug { Debug_Data *debug = &models->debug; @@ -2742,8 +2746,6 @@ App_Step_Sig(app_step){ models->layout.active_panel = (i32)(mouse_panel - models->layout.panels); } - update_command_data(vars, cmd); - end_temp_memory(param_stack_temp); // NOTE(allen): on the first frame there should be no scrolling @@ -2774,6 +2776,10 @@ App_Step_Sig(app_step){ target->clip_top = -1; draw_push_clip(target, rect_from_target(target)); + Command_Data *command = cmd; + USE_PANEL(active_panel); + USE_VIEW(active_view); + // NOTE(allen): render the panels Panel *panel, *used_panels; used_panels = &models->layout.used_sentinel; @@ -2784,7 +2790,7 @@ App_Step_Sig(app_step){ View *view = panel->view; Style *style = main_style(models); - b32 active = (panel == cmd->panel); + b32 active = (panel == active_panel); u32 back_color = style->main.back_color; draw_rectangle(target, full, back_color); @@ -2803,7 +2809,7 @@ App_Step_Sig(app_step){ } } - do_render_file_view(system, view, scroll_vars, cmd->view, + do_render_file_view(system, view, scroll_vars, active_view, panel->inner, active, target, &dead_input); draw_pop_clip(target); diff --git a/4ed_api_implementation.cpp b/4ed_api_implementation.cpp index 97051334..5b342c55 100644 --- a/4ed_api_implementation.cpp +++ b/4ed_api_implementation.cpp @@ -171,8 +171,6 @@ DOC_SEE(Command_ID) binding.function = function; if (function) function(cmd->system, cmd, binding); - update_command_data(cmd->vars, cmd); - result = true; } else{ @@ -1243,8 +1241,6 @@ DOC_SEE(View_Split_Position) fill_view_summary(&result, split.panel->view, cmd); } - update_command_data(cmd->vars, cmd); - return(result); } @@ -1370,8 +1366,6 @@ DOC_SEE(get_active_view) Panel *panel = vptr->panel; models->layout.active_panel = (i32)(panel - models->layout.panels); - - update_command_data(cmd->vars, cmd); } return(result); @@ -1807,11 +1801,9 @@ will reflect the change. Since the bar stops showing when the command exits the only use for this call is in an interactive command that makes calls to get_user_input. ) */{ - Command_Data *cmd = (Command_Data*)app->cmd_context; + Command_Data *command = (Command_Data*)app->cmd_context; + USE_VIEW(vptr); Query_Slot *slot = 0; - View *vptr; - - vptr = cmd->view; slot = alloc_query_slot(&vptr->query_set); slot->query_bar = bar; @@ -1826,9 +1818,8 @@ DOC_PARAM(bar, This parameter should be a bar pointer of a currently active quer DOC_PARAM(flags, This parameter is not currently used and should be 0 for now.) DOC(Stops showing the particular query bar specified by the bar parameter.) */{ - Command_Data *cmd = (Command_Data*)app->cmd_context; - View *vptr; - vptr = cmd->view; + Command_Data *command = (Command_Data*)app->cmd_context; + USE_VIEW(vptr); free_query_slot(&vptr->query_set, bar); } diff --git a/4ed_file_view.cpp b/4ed_file_view.cpp index 349df600..1a931290 100644 --- a/4ed_file_view.cpp +++ b/4ed_file_view.cpp @@ -944,7 +944,7 @@ file_grow_starts_widths_as_needed(General_Memory *general, Buffer_Type *buffer, internal void file_measure_starts_widths(System_Functions *system, General_Memory *general, - Buffer_Type *buffer, float *advance_data){ + Buffer_Type *buffer, f32 *advance_data){ if (!buffer->line_starts){ i32 max = buffer->line_max = Kbytes(1); buffer->line_starts = (i32*)general_memory_allocate(general, max*sizeof(i32)); @@ -991,10 +991,9 @@ file_measure_starts_widths(System_Functions *system, General_Memory *general, } internal void -view_measure_wraps(General_Memory *general, View *view){ - Buffer_Type *buffer; - - buffer = &view->file_data.file->state.buffer; +view_measure_wraps(Models *models, General_Memory *general, View *view){ + Editing_File *file = view->file_data.file; + Buffer_Type *buffer = &file->state.buffer; i32 line_count = buffer->line_count; if (view->file_data.line_max < line_count){ @@ -1009,9 +1008,13 @@ view_measure_wraps(General_Memory *general, View *view){ } } + Render_Font *font = get_font_info(models->font_set, file->settings.font_id)->font; + f32 *adv = font->advance_data; + f32 line_height = (f32)view->line_height; f32 max_width = view_file_display_width(view); - buffer_measure_wrap_y(buffer, view->file_data.line_wrap_y, line_height, max_width); + buffer_measure_wrap_y(buffer, view->file_data.line_wrap_y, line_height, + adv, max_width); view->file_data.line_count = line_count; } @@ -1703,6 +1706,8 @@ update_view_line_height(Models *models, View *view, i16 font_id){ internal void view_set_file(View *view, Editing_File *file, Models *models){ + Assert(file); + if (view->file_data.file != 0){ touch_file(&models->working_set, view->file_data.file); } @@ -1717,21 +1722,16 @@ view_set_file(View *view, Editing_File *file, Models *models){ file_view_nullify_file(view); view->file_data.file = file; - if (file){ - view->file_data.unwrapped_lines = file->settings.unwrapped_lines; - - edit_pos = edit_pos_get_new(file, view->persistent.id); - view->edit_pos = edit_pos; - - if (file_is_ready(file)){ - view_measure_wraps(&models->mem.general, view); - } - - update_view_line_height(models, view, file->settings.font_id); - } - else{ - update_view_line_height(models, view, models->global_font.font_id); + view->file_data.unwrapped_lines = file->settings.unwrapped_lines; + + edit_pos = edit_pos_get_new(file, view->persistent.id); + view->edit_pos = edit_pos; + + if (file_is_ready(file)){ + view_measure_wraps(models, &models->mem.general, view); } + + update_view_line_height(models, view, file->settings.font_id); } struct Relative_Scrolling{ @@ -1995,25 +1995,26 @@ struct Cursor_Fix_Descriptor{ }; internal void -file_edit_cursor_fix(System_Functions *system, - Partition *part, General_Memory *general, +file_edit_cursor_fix(System_Functions *system, Models *models, Editing_File *file, Editing_Layout *layout, Cursor_Fix_Descriptor desc, i32 *shift_out){ + Partition *part = &models->mem.part; + General_Memory *general = &models->mem.general; + Temp_Memory cursor_temp = begin_temp_memory(part); i32 cursor_max = layout->panel_max_count * 2; Cursor_With_Index *cursors = push_array(part, Cursor_With_Index, cursor_max); i32 cursor_count = 0; - View *view; - Panel *panel, *used_panels; - used_panels = &layout->used_sentinel; - + View *view = 0; + Panel *panel, *used_panels = &layout->used_sentinel; for (dll_items(panel, used_panels)){ view = panel->view; if (view->file_data.file == file){ - view_measure_wraps(general, view); + // TODO(allen): Is this a good place for this ???? + view_measure_wraps(models, general, view); Assert(view->edit_pos); write_cursor_with_index(cursors, &cursor_count, view->edit_pos->cursor.pos); write_cursor_with_index(cursors, &cursor_count, view->edit_pos->mark); @@ -2142,7 +2143,7 @@ file_do_single_edit(System_Functions *system, for (dll_items(panel, used_panels)){ View *view = panel->view; if (view->file_data.file == file){ - view_measure_wraps(general, view); + view_measure_wraps(models, general, view); } } @@ -2152,7 +2153,7 @@ file_do_single_edit(System_Functions *system, desc.end = end; desc.shift_amount = shift_amount; - file_edit_cursor_fix(system, part, general, file, layout, desc, 0); + file_edit_cursor_fix(system, models, file, layout, desc, 0); // NOTE(allen): token fixing if (file->settings.tokens_exist){ @@ -2219,7 +2220,7 @@ file_do_batch_edit(System_Functions *system, Models *models, Editing_File *file, desc.batch = batch; desc.batch_size = batch_size; - file_edit_cursor_fix(system, part, general, file, layout, desc, &shift_total); + file_edit_cursor_fix(system, models, file, layout, desc, &shift_total); } // NOTE(allen): token fixing @@ -2626,13 +2627,16 @@ style_get_color(Style *style, Cpp_Token token){ return result; } +// TODO(allen): What's the deal with this function? Can we +// just pass models in... by the way is this even being called? internal void remeasure_file_view(System_Functions *system, View *view){ if (file_is_ready(view->file_data.file)){ Assert(view->edit_pos); Relative_Scrolling relative = view_get_relative_scrolling(view); - view_measure_wraps(&view->persistent.models->mem.general, view); + Models *models = view->persistent.models; + view_measure_wraps(models, &models->mem.general, view); if (view->file_data.show_temp_highlight == 0){ view_cursor_move(view, view->edit_pos->cursor.pos); } @@ -2749,7 +2753,7 @@ init_normal_file(System_Functions *system, Models *models, Editing_File *file, for (View_Iter iter = file_view_iter_init(&models->layout, file, 0); file_view_iter_good(iter); iter = file_view_iter_next(iter)){ - view_measure_wraps(general, iter.view); + view_measure_wraps(models, general, iter.view); } } @@ -2767,7 +2771,7 @@ init_read_only_file(System_Functions *system, Models *models, Editing_File *file for (View_Iter iter = file_view_iter_init(&models->layout, file, 0); file_view_iter_good(iter); iter = file_view_iter_next(iter)){ - view_measure_wraps(general, iter.view); + view_measure_wraps(models, general, iter.view); } } @@ -5600,17 +5604,6 @@ file_view_free_buffers(View *view){ view->gui_mem = 0; } -inline void -view_change_size(General_Memory *general, View *view){ - if (view->file_data.file){ - Assert(view->edit_pos); - view_measure_wraps(general, view); - Full_Cursor cursor = - view_compute_cursor_from_pos(view, view->edit_pos->cursor.pos); - view_set_cursor(view, cursor, 0, view->file_data.unwrapped_lines); - } -} - internal View_And_ID live_set_alloc_view(Live_Views *live_set, Panel *panel, Models *models){ View_And_ID result = {}; diff --git a/TODO.txt b/TODO.txt index 15f90bb8..83ad76f9 100644 --- a/TODO.txt +++ b/TODO.txt @@ -133,6 +133,7 @@ ; [X] use strange theme ; [X] cuber's return to previous buffer idea +; Token related upgrades ; [X] tokens in the custom API ; [X] token seeking on custom side ; [X] auto indent on the custom side @@ -151,6 +152,13 @@ ; [] command for setting wrap positions in views directly ; [] ability to see the wrap position as a number/line and adjust graphically +; buffer behavior cleanup +; [X] show all characters as \# if they can't be rendered +; [] get the navigation working correctly around multi-glyph characters +; [] binary buffer mode +; [] unicode buffer mode +; [] support full length unicode file names + ; [] miblo's various number editors ; [] user file bar string ; [] API docs as text file @@ -177,12 +185,6 @@ ; [] control the file opening/start hook relationship better ; [] get keyboard state on launch -; buffer behavior cleanup -; [X] show all characters as \# if they can't be rendered -; [] get the navigation working correctly around multi-glyph characters -; [] binary buffer mode -; [] support full length unicode file names - ; meta programming system ; [X] condense system into single meta compiler ; [] formalize the rewriter for the 4coder_string.h so it can be used for other single header libs diff --git a/buffer/4coder_buffer_abstract.cpp b/buffer/4coder_buffer_abstract.cpp index c736fa64..3f12d303 100644 --- a/buffer/4coder_buffer_abstract.cpp +++ b/buffer/4coder_buffer_abstract.cpp @@ -1,953 +1,976 @@ -/* - * Mr. 4th Dimention - Allen Webster - * - * 24.10.2015 - * - * Buffer features based on the stringify loop, - * and other abstract buffer features. - * - */ - -// TOP - -#define Buffer_Init_Type cat_4tech(Buffer_Type, _Init) -#define Buffer_Stringify_Type cat_4tech(Buffer_Type, _Stringify_Loop) - -inline_4tech void -buffer_stringify(Buffer_Type *buffer, i32 start, i32 end, char *out){ - for (Buffer_Stringify_Type loop = buffer_stringify_loop(buffer, start, end); - buffer_stringify_good(&loop); - buffer_stringify_next(&loop)){ - memcpy_4tech(out, loop.data, loop.size); - out += loop.size; - } -} - -internal_4tech i32 -buffer_convert_out(Buffer_Type *buffer, char *dest, i32 max){ - Buffer_Stringify_Type loop; - i32 size, out_size, pos, result; - - size = buffer_size(buffer); - assert_4tech(size + buffer->line_count <= max); - - pos = 0; - for (loop = buffer_stringify_loop(buffer, 0, size); - buffer_stringify_good(&loop); - buffer_stringify_next(&loop)){ - result = eol_convert_out(dest + pos, max - pos, loop.data, loop.size, &out_size); - assert_4tech(result); - pos += out_size; - } - - return(pos); -} - -internal_4tech i32 -buffer_count_newlines(Buffer_Type *buffer, i32 start, i32 end){ - Buffer_Stringify_Type loop; - i32 i; - i32 count; - - assert_4tech(0 <= start); - assert_4tech(start <= end); - assert_4tech(end <= buffer_size(buffer)); - - count = 0; - - for (loop = buffer_stringify_loop(buffer, start, end); - buffer_stringify_good(&loop); - buffer_stringify_next(&loop)){ - for (i = 0; i < loop.size; ++i){ - count += (loop.data[i] == '\n'); - } - } - - return(count); -} - -typedef struct Buffer_Measure_Starts{ - i32 i; - i32 count; - i32 start; - f32 width; -} Buffer_Measure_Starts; - -internal_4tech i32 -buffer_measure_starts_widths_(Buffer_Measure_Starts *state, Buffer_Type *buffer, f32 *advance_data){ - Buffer_Stringify_Type loop; - i32 *start_ptr, *start_end; - f32 *width_ptr; - debug_4tech(i32 widths_max); - debug_4tech(i32 max); - char *data; - i32 size, end; - f32 width; - i32 start, i; - i32 result; - char ch; - - size = buffer_size(buffer); - - debug_4tech(max = buffer->line_max); - debug_4tech(widths_max = buffer->widths_max); - assert_4tech(max == widths_max); - - result = 1; - - i = state->i; - start = state->start; - width = state->width; - - start_ptr = buffer->line_starts + state->count; - width_ptr = buffer->line_widths + state->count; - start_end = buffer->line_starts + buffer->line_max; - - for (loop = buffer_stringify_loop(buffer, i, size); - buffer_stringify_good(&loop); - buffer_stringify_next(&loop)){ - end = loop.size + loop.absolute_pos; - data = loop.data - loop.absolute_pos; - for (; i < end; ++i){ - ch = data[i]; - if (ch == '\n'){ - if (start_ptr == start_end) goto buffer_measure_starts_widths_end; - - *width_ptr++ = width; - *start_ptr++ = start; - start = i + 1; - width = 0; - } - else{ - width += measure_character(advance_data, ch); - } - } - } - - assert_4tech(i == size); - - if (start_ptr == start_end) goto buffer_measure_starts_widths_end; - *start_ptr++ = start; - *width_ptr++ = 0; - result = 0; - - buffer_measure_starts_widths_end: - state->i = i; - state->count = (i32)(start_ptr - buffer->line_starts); - state->start = start; - state->width = width; - - return(result); -} - -internal_4tech i32 -buffer_measure_starts_zero_widths_(Buffer_Measure_Starts *state, Buffer_Type *buffer){ - Buffer_Stringify_Type loop; - i32 *start_ptr, *start_end; - f32 *width_ptr; - debug_4tech(i32 widths_max); - debug_4tech(i32 max); - char *data; - i32 size, end; - i32 start, i; - i32 result; - char ch; - - size = buffer_size(buffer); - - debug_4tech(max = buffer->line_max); - debug_4tech(widths_max = buffer->widths_max); - assert_4tech(max == widths_max); - - result = 1; - - i = state->i; - start = state->start; - - start_ptr = buffer->line_starts + state->count; - width_ptr = buffer->line_widths + state->count; - start_end = buffer->line_starts + buffer->line_max; - - for (loop = buffer_stringify_loop(buffer, i, size); - buffer_stringify_good(&loop); - buffer_stringify_next(&loop)){ - end = loop.size + loop.absolute_pos; - data = loop.data - loop.absolute_pos; - for (; i < end; ++i){ - ch = data[i]; - if (ch == '\n'){ - if (start_ptr == start_end) goto buffer_measure_starts_zero_widths_end; - - *width_ptr++ = 0; - *start_ptr++ = start; - start = i + 1; - } - } - } - - assert_4tech(i == size); - - if (start_ptr == start_end) goto buffer_measure_starts_zero_widths_end; - *start_ptr++ = start; - *width_ptr++ = 0; - result = 0; - - buffer_measure_starts_zero_widths_end: - state->i = i; - state->count = (i32)(start_ptr - buffer->line_starts); - state->start = start; - - return(result); -} - -internal_4tech i32 -buffer_measure_starts_widths(Buffer_Measure_Starts *state, Buffer_Type *buffer, f32 *advance_data){ - i32 result = 0; - - if (advance_data){ - result = buffer_measure_starts_widths_(state, buffer, advance_data); - } - else{ - result = buffer_measure_starts_zero_widths_(state, buffer); - } - - return(result); -} - -internal_4tech void -buffer_remeasure_starts(Buffer_Type *buffer, i32 line_start, i32 line_end, i32 line_shift, i32 text_shift){ - Buffer_Stringify_Type loop; - i32 *starts = buffer->line_starts; - i32 line_count = buffer->line_count; - char *data = 0; - i32 size = 0, end = 0; - i32 line_i = 0, char_i = 0, start = 0; - - assert_4tech(0 <= line_start); - assert_4tech(line_start <= line_end); - assert_4tech(line_end < line_count); - assert_4tech(line_count + line_shift <= buffer->line_max); - - ++line_end; - if (text_shift != 0){ - line_i = line_end; - starts += line_i; - for (; line_i < line_count; ++line_i, ++starts){ - *starts += text_shift; - } - starts = buffer->line_starts; - } - - if (line_shift != 0){ - memmove_4tech(starts + line_end + line_shift, starts + line_end, - sizeof(i32)*(line_count - line_end)); - line_count += line_shift; - } - - line_end += line_shift; - size = buffer_size(buffer); - char_i = starts[line_start]; - line_i = line_start; - start = char_i; - - for (loop = buffer_stringify_loop(buffer, char_i, size); - buffer_stringify_good(&loop); - buffer_stringify_next(&loop)){ - end = loop.size + loop.absolute_pos; - data = loop.data - loop.absolute_pos; - for (; char_i < end; ++char_i){ - if (data[char_i] == '\n'){ - starts[line_i++] = start; - start = char_i + 1; - if (line_i >= line_end && start == starts[line_i]) goto buffer_remeasure_starts_end; - } - } - } - - if (char_i == size){ - starts[line_i++] = start; - } - - buffer_remeasure_starts_end: - assert_4tech(line_count >= 1); - buffer->line_count = line_count; -} - -internal_4tech void -buffer_remeasure_widths(Buffer_Type *buffer, f32 *advance_data, - i32 line_start, i32 line_end, i32 line_shift){ - Buffer_Stringify_Type loop; - i32 *starts = buffer->line_starts; - f32 *widths = buffer->line_widths; - i32 line_count = buffer->line_count; - i32 widths_count = buffer->widths_count; - char *data = 0; - i32 size = 0, end = 0; - i32 i = 0, j = 0; - f32 width = 0; - char ch = 0; - - assert_4tech(0 <= line_start); - assert_4tech(line_start <= line_end); - assert_4tech(line_count <= buffer->widths_max); - - ++line_end; - if (line_shift != 0){ - memmove_4tech(widths + line_end + line_shift, widths + line_end, - sizeof(f32)*(widths_count - line_end)); - } - buffer->widths_count = line_count; - - line_end += line_shift; - i = line_start; - j = starts[i]; - - if (line_end == line_count){ - size = buffer_size(buffer); - } - else{ - size = starts[line_end]; - } - - width = 0; - for (loop = buffer_stringify_loop(buffer, j, size); - buffer_stringify_good(&loop); - buffer_stringify_next(&loop)){ - end = loop.size + loop.absolute_pos; - data = loop.data - loop.absolute_pos; - - for (; j < end; ++j){ - ch = data[j]; - if (ch == '\n'){ - widths[i] = width; - ++i; - assert_4tech(j + 1 == starts[i]); - width = 0; - } - else{ - width += measure_character(advance_data, ch); - } - } - } - - if (j == buffer_size(buffer)){ - widths[i] = width; - assert_4tech(i+1 == line_count); - } -} - -internal_4tech void -buffer_measure_wrap_y(Buffer_Type *buffer, f32 *wraps, - f32 font_height, f32 max_width){ - f32 *widths = buffer->line_widths; - f32 y_pos = 0; - i32 line_count = buffer->line_count; - i32 i = 0; - - for (; i < line_count; ++i){ - wraps[i] = y_pos; - if (widths[i] == 0){ - y_pos += font_height; - } - else{ - y_pos += font_height*ceil_4tech(widths[i]/max_width); - } - } -} - -internal_4tech i32 -buffer_get_line_index_range(Buffer_Type *buffer, i32 pos, i32 l_bound, i32 u_bound){ - i32 *lines = buffer->line_starts; - i32 start = l_bound, end = u_bound; - i32 i = 0; - - assert_4tech(0 <= l_bound); - assert_4tech(l_bound <= u_bound); - assert_4tech(u_bound <= buffer->line_count); - - assert_4tech(lines != 0); - - for (;;){ - i = (start + end) >> 1; - if (lines[i] < pos){ - start = i; - } - else if (lines[i] > pos){ - end = i; - } - else{ - start = i; - break; - } - assert_4tech(start < end); - if (start == end - 1){ - break; - } - } - - return(start); -} - -inline_4tech i32 -buffer_get_line_index(Buffer_Type *buffer, i32 pos){ - i32 result = buffer_get_line_index_range(buffer, pos, 0, buffer->line_count); - return(result); -} - -internal_4tech i32 -buffer_get_line_index_from_wrapped_y(f32 *wraps, f32 y, f32 font_height, i32 l_bound, i32 u_bound){ - i32 start, end, i, result; - start = l_bound; - end = u_bound; - for (;;){ - i = (start + end) / 2; - if (wraps[i]+font_height <= y) start = i; - else if (wraps[i] > y) end = i; - else{ - result = i; - break; - } - if (start >= end - 1){ - result = start; - break; - } - } - return(result); -} - -typedef struct Seek_State{ - Full_Cursor cursor; - Full_Cursor prev_cursor; -} Seek_State; - -internal_4tech i32 -cursor_seek_step(Seek_State *state, Buffer_Seek seek, i32 xy_seek, f32 max_width, - f32 font_height, f32 *adv, i32 size, uint8_t ch){ - Full_Cursor cursor = state->cursor; - Full_Cursor prev_cursor = cursor; - f32 ch_width; - i32 result = 1; - f32 x, px, y; - - switch (ch){ - case '\n': - ++cursor.line; - cursor.unwrapped_y += font_height; - cursor.wrapped_y += font_height; - cursor.character = 1; - cursor.unwrapped_x = 0; - cursor.wrapped_x = 0; - break; - - default: - ++cursor.character; - ch_width = adv[ch]; - - if (cursor.wrapped_x + ch_width >= max_width){ - cursor.wrapped_y += font_height; - cursor.wrapped_x = 0; - prev_cursor = cursor; - } - - cursor.unwrapped_x += ch_width; - cursor.wrapped_x += ch_width; - break; - } - - ++cursor.pos; - - if (cursor.pos > size){ - cursor = prev_cursor; - result = 0; - goto cursor_seek_step_end; - } - - x = y = px = 0; - - switch (seek.type){ - case buffer_seek_pos: - if (cursor.pos > seek.pos){ - cursor = prev_cursor; - result = 0; - goto cursor_seek_step_end; - }break; - - case buffer_seek_wrapped_xy: - x = cursor.wrapped_x; px = prev_cursor.wrapped_x; - y = cursor.wrapped_y; break; - - case buffer_seek_unwrapped_xy: - x = cursor.unwrapped_x; px = prev_cursor.unwrapped_x; - y = cursor.unwrapped_y; break; - - case buffer_seek_line_char: - if (cursor.line == seek.line && cursor.character >= seek.character){ - result = 0; - goto cursor_seek_step_end; - } - else if (cursor.line > seek.line){ - cursor = prev_cursor; - result = 0; - goto cursor_seek_step_end; - }break; - } - - if (xy_seek){ - if (y > seek.y){ - cursor = prev_cursor; - result = 0; - goto cursor_seek_step_end; - } - - if (y > seek.y - font_height && x >= seek.x){ - if (!seek.round_down){ - if (ch != '\n' && (seek.x - px) < (x - seek.x)) cursor = prev_cursor; - result = 0; - goto cursor_seek_step_end; - } - - if (x > seek.x){ - cursor = prev_cursor; - result = 0; - goto cursor_seek_step_end; - } - } - } - - cursor_seek_step_end: - state->cursor = cursor; - state->prev_cursor = prev_cursor; - return(result); -} - -internal_4tech Full_Cursor -buffer_cursor_seek(Buffer_Type *buffer, Buffer_Seek seek, f32 max_width, - f32 font_height, f32 *adv, Full_Cursor cursor){ - Buffer_Stringify_Type loop; - char *data; - i32 size, end; - i32 i; - i32 result; - - Seek_State state; - i32 xy_seek; - - state.cursor = cursor; - - switch(seek.type){ - case buffer_seek_pos: - if (cursor.pos >= seek.pos) goto buffer_cursor_seek_end; - break; - - case buffer_seek_wrapped_xy: - if (seek.x == 0 && cursor.wrapped_y >= seek.y) goto buffer_cursor_seek_end; - break; - - case buffer_seek_unwrapped_xy: - if (seek.x == 0 && cursor.unwrapped_y >= seek.y) goto buffer_cursor_seek_end; - break; - - case buffer_seek_line_char: - if (cursor.line >= seek.line && cursor.character >= seek.character) goto buffer_cursor_seek_end; - break; - } - - if (adv){ - size = buffer_size(buffer); - xy_seek = (seek.type == buffer_seek_wrapped_xy || seek.type == buffer_seek_unwrapped_xy); - - result = 1; - i = cursor.pos; - for (loop = buffer_stringify_loop(buffer, i, size); - buffer_stringify_good(&loop); - buffer_stringify_next(&loop)){ - end = loop.size + loop.absolute_pos; - data = loop.data - loop.absolute_pos; - for (; i < end; ++i){ - result = cursor_seek_step(&state, seek, xy_seek, max_width, - font_height, adv, size, data[i]); - if (!result) goto buffer_cursor_seek_end; - } - } - if (result){ - result = cursor_seek_step(&state, seek, xy_seek, max_width, - font_height, adv, size, 0); - assert_4tech(result == 0); - } - } - - buffer_cursor_seek_end: - return(state.cursor); -} - -internal_4tech Partial_Cursor -buffer_partial_from_pos(Buffer_Type *buffer, i32 pos){ - Partial_Cursor result = {0}; - - int32_t size = buffer_size(buffer); - if (pos > size){ - pos = size; - } - if (pos < 0){ - pos = 0; - } - - i32 line_index = buffer_get_line_index_range(buffer, pos, 0, buffer->line_count); - result.pos = pos; - result.line = line_index+1; - result.character = pos - buffer->line_starts[line_index] + 1; - - return(result); -} - -internal_4tech Full_Cursor -buffer_cursor_from_pos(Buffer_Type *buffer, i32 pos, f32 *wraps, - f32 max_width, f32 font_height, f32 *adv){ - Full_Cursor result; - i32 line_index; - - int32_t size = buffer_size(buffer); - if (pos > size){ - pos = size; - } - if (pos < 0){ - pos = 0; - } - - line_index = buffer_get_line_index_range(buffer, pos, 0, buffer->line_count); - result = make_cursor_hint(line_index, buffer->line_starts, wraps, font_height); - result = buffer_cursor_seek(buffer, seek_pos(pos), max_width, font_height, adv, result); - - return(result); -} - -internal_4tech Partial_Cursor -buffer_partial_from_line_character(Buffer_Type *buffer, i32 line, i32 character){ - Partial_Cursor result = {0}; - - i32 line_index = line - 1; - if (line_index >= buffer->line_count) line_index = buffer->line_count - 1; - if (line_index < 0) line_index = 0; - - int32_t size = buffer_size(buffer); - i32 this_start = buffer->line_starts[line_index]; - i32 max_character = (size-this_start) + 1; - if (line_index+1 < buffer->line_count){ - i32 next_start = buffer->line_starts[line_index+1]; - max_character = (next_start-this_start); - } - - if (character <= 0) character = 1; - if (character > max_character) character = max_character; - - result.pos = this_start + character - 1; - result.line = line_index+1; - result.character = character; - - return(result); -} - -internal_4tech Full_Cursor -buffer_cursor_from_line_character(Buffer_Type *buffer, i32 line, i32 character, f32 *wraps, - f32 max_width, f32 font_height, f32 *adv){ - Full_Cursor result = {0}; - - i32 line_index = line - 1; - if (line_index >= buffer->line_count) line_index = buffer->line_count - 1; - if (line_index < 0) line_index = 0; - - result = make_cursor_hint(line_index, buffer->line_starts, wraps, font_height); - result = buffer_cursor_seek(buffer, seek_line_char(line, character), - max_width, font_height, adv, result); - - return(result); -} - -internal_4tech Full_Cursor -buffer_cursor_from_unwrapped_xy(Buffer_Type *buffer, f32 x, f32 y, i32 round_down, f32 *wraps, - f32 max_width, f32 font_height, f32 *adv){ - Full_Cursor result; - i32 line_index; - - line_index = (i32)(y / font_height); - if (line_index >= buffer->line_count) line_index = buffer->line_count - 1; - if (line_index < 0) line_index = 0; - - result = make_cursor_hint(line_index, buffer->line_starts, wraps, font_height); - result = buffer_cursor_seek(buffer, seek_unwrapped_xy(x, y, round_down), - max_width, font_height, adv, result); - - return(result); -} - -internal_4tech Full_Cursor -buffer_cursor_from_wrapped_xy(Buffer_Type *buffer, f32 x, f32 y, i32 round_down, f32 *wraps, - f32 max_width, f32 font_height, f32 *adv){ - Full_Cursor result; - i32 line_index; - - line_index = buffer_get_line_index_from_wrapped_y(wraps, y, font_height, 0, buffer->line_count); - result = make_cursor_hint(line_index, buffer->line_starts, wraps, font_height); - result = buffer_cursor_seek(buffer, seek_wrapped_xy(x, y, round_down), - max_width, font_height, adv, result); - - return(result); -} - -internal_4tech void -buffer_invert_edit_shift(Buffer_Type *buffer, Buffer_Edit edit, Buffer_Edit *inverse, char *strings, - i32 *str_pos, i32 max, i32 shift_amount){ - i32 pos = *str_pos; - i32 len = edit.end - edit.start; - assert_4tech(pos >= 0); - assert_4tech(pos + len <= max); - *str_pos = pos + len; - - inverse->str_start = pos; - inverse->len = len; - inverse->start = edit.start + shift_amount; - inverse->end = edit.start + edit.len + shift_amount; - buffer_stringify(buffer, edit.start, edit.end, strings + pos); -} - -inline_4tech void -buffer_invert_edit(Buffer_Type *buffer, Buffer_Edit edit, Buffer_Edit *inverse, char *strings, - i32 *str_pos, i32 max){ - buffer_invert_edit_shift(buffer, edit, inverse, strings, str_pos, max, 0); -} - -typedef struct Buffer_Invert_Batch{ - i32 i; - i32 shift_amount; - i32 len; -} Buffer_Invert_Batch; - -internal_4tech i32 -buffer_invert_batch(Buffer_Invert_Batch *state, Buffer_Type *buffer, Buffer_Edit *edits, i32 count, - Buffer_Edit *inverse, char *strings, i32 *str_pos, i32 max){ - Buffer_Edit *edit, *inv_edit; - i32 shift_amount; - i32 result; - i32 i; - - result = 0; - i = state->i; - shift_amount = state->shift_amount; - - edit = edits + i; - inv_edit = inverse + i; - - for (; i < count; ++i, ++edit, ++inv_edit){ - if (*str_pos + edit->end - edit->start <= max){ - buffer_invert_edit_shift(buffer, *edit, inv_edit, strings, str_pos, max, shift_amount); - shift_amount += (edit->len - (edit->end - edit->start)); - } - else{ - result = 1; - state->len = edit->end - edit->start; - } - } - - state->i = i; - state->shift_amount = shift_amount; - - return(result); -} - -internal_4tech Full_Cursor -buffer_get_start_cursor(Buffer_Type *buffer, f32 *wraps, f32 scroll_y, - i32 wrapped, f32 width, f32 *adv, f32 font_height){ - Full_Cursor result; - - if (wrapped){ - result = buffer_cursor_from_wrapped_xy(buffer, 0, scroll_y, 0, wraps, - width, font_height, adv); - } - else{ - result = buffer_cursor_from_unwrapped_xy(buffer, 0, scroll_y, 0, wraps, - width, font_height, adv); - } - - return(result); -} - -enum Buffer_Render_Flag{ - BRFlag_Special_Character = (1 << 0) -}; - -typedef struct Buffer_Render_Item{ - i32 index; - u16 glyphid; - u16 flags; - f32 x0, y0; - f32 x1, y1; -} Buffer_Render_Item; - -typedef struct Render_Item_Write{ - Buffer_Render_Item *item; - f32 x, y; - f32 *adv; - f32 font_height; - f32 x_min; - f32 x_max; -} Render_Item_Write; - -inline_4tech Render_Item_Write -write_render_item(Render_Item_Write write, i32 index, u16 glyphid, u16 flags){ - f32 ch_width = measure_character(write.adv, (char)glyphid); - - if (write.x <= write.x_max && write.x + ch_width >= write.x_min){ - write.item->index = index; - write.item->glyphid = glyphid; - write.item->flags = flags; - write.item->x0 = write.x; - write.item->y0 = write.y; - write.item->x1 = write.x + ch_width; - write.item->y1 = write.y + write.font_height; - - ++write.item; - } - - write.x += ch_width; - - return(write); -} - -internal_4tech void -buffer_get_render_data(Buffer_Type *buffer, Buffer_Render_Item *items, i32 max, i32 *count, - f32 port_x, f32 port_y, f32 clip_w, - f32 scroll_x, f32 scroll_y, f32 width, f32 height, - Full_Cursor start_cursor, - i32 wrapped, f32 *adv, f32 font_height){ - - Buffer_Stringify_Type loop = {0}; - char *data = 0; - i32 end = 0; - - i32 size = buffer_size(buffer); - f32 shift_x = port_x - scroll_x, shift_y = port_y - scroll_y; - - if (wrapped){ - shift_y += start_cursor.wrapped_y; - } - else{ - shift_y += start_cursor.unwrapped_y; - } - - Buffer_Render_Item *item_end = items + max; - - Render_Item_Write write; - write.item = items; - write.x = shift_x; - write.y = shift_y; - write.adv = adv; - write.font_height = font_height; - write.x_min = port_x; - write.x_max = port_x + clip_w; - - if (adv){ - for (loop = buffer_stringify_loop(buffer, start_cursor.pos, size); - buffer_stringify_good(&loop) && write.item < item_end; - buffer_stringify_next(&loop)){ - - end = loop.size + loop.absolute_pos; - data = loop.data - loop.absolute_pos; - - for (i32 i = loop.absolute_pos; i < end; ++i){ - u8 ch = (uint8_t)data[i]; - f32 ch_width = measure_character(adv, ch); - - if (ch_width + write.x > width + shift_x && wrapped && ch != '\n'){ - write.x = shift_x; - write.y += font_height; - } - if (write.y > height + shift_y){ - goto buffer_get_render_data_end; - } - - switch (ch){ - case '\n': - if (write.item < item_end){ - write = write_render_item(write, i, ' ', 0); - write.x = shift_x; - write.y += font_height; - } - break; - - case '\r': - if (write.item < item_end){ - write = write_render_item(write, i, '\\', BRFlag_Special_Character); - - if (write.item < item_end){ - write = write_render_item(write, i, 'r', BRFlag_Special_Character); - } - } - break; - - case '\t': - if (write.item < item_end){ - f32 new_x = write.x + ch_width; - write = write_render_item(write, i, ' ', 0); - write.x = new_x; - } - break; - - default: - if (write.item < item_end){ - if (ch >= ' ' && ch <= '~'){ - write = write_render_item(write, i, ch, 0); - } - else{ - write = write_render_item(write, i, '\\', BRFlag_Special_Character); - - char C = '0' + (ch / 0x10); - if ((ch / 0x10) > 0x9){ - C = ('A' - 0xA) + (ch / 0x10); - } - - if (write.item < item_end){ - write = write_render_item(write, i, C, BRFlag_Special_Character); - } - - ch = (ch % 0x10); - C = '0' + ch; - if (ch > 0x9){ - C = ('A' - 0xA) + ch; - } - - if (write.item < item_end){ - write = write_render_item(write, i, C, BRFlag_Special_Character); - } - } - } - break; - } - - if (write.y > height + shift_y){ - goto buffer_get_render_data_end; - } - } - } - - buffer_get_render_data_end:; - if (write.y <= height + shift_y || write.item == items){ - if (write.item < item_end){ - write_render_item(write, size, ' ', 0); - } - } - } - else{ - f32 zero = 0; - write.adv = &zero; - - if (write.item < item_end){ - write_render_item(write, size, 0, 0); - } - } - - *count = (i32)(write.item - items); - assert_4tech(*count <= max); -} - -// BOTTOM - +/* + * Mr. 4th Dimention - Allen Webster + * + * 24.10.2015 + * + * Buffer features based on the stringify loop, + * and other abstract buffer features. + * + */ + +// TOP + +#define Buffer_Init_Type cat_4tech(Buffer_Type, _Init) +#define Buffer_Stringify_Type cat_4tech(Buffer_Type, _Stringify_Loop) + +inline_4tech void +buffer_stringify(Buffer_Type *buffer, i32 start, i32 end, char *out){ + for (Buffer_Stringify_Type loop = buffer_stringify_loop(buffer, start, end); + buffer_stringify_good(&loop); + buffer_stringify_next(&loop)){ + memcpy_4tech(out, loop.data, loop.size); + out += loop.size; + } +} + +internal_4tech i32 +buffer_convert_out(Buffer_Type *buffer, char *dest, i32 max){ + Buffer_Stringify_Type loop; + i32 size, out_size, pos, result; + + size = buffer_size(buffer); + assert_4tech(size + buffer->line_count <= max); + + pos = 0; + for (loop = buffer_stringify_loop(buffer, 0, size); + buffer_stringify_good(&loop); + buffer_stringify_next(&loop)){ + result = eol_convert_out(dest + pos, max - pos, loop.data, loop.size, &out_size); + assert_4tech(result); + pos += out_size; + } + + return(pos); +} + +internal_4tech i32 +buffer_count_newlines(Buffer_Type *buffer, i32 start, i32 end){ + Buffer_Stringify_Type loop; + i32 i; + i32 count; + + assert_4tech(0 <= start); + assert_4tech(start <= end); + assert_4tech(end <= buffer_size(buffer)); + + count = 0; + + for (loop = buffer_stringify_loop(buffer, start, end); + buffer_stringify_good(&loop); + buffer_stringify_next(&loop)){ + for (i = 0; i < loop.size; ++i){ + count += (loop.data[i] == '\n'); + } + } + + return(count); +} + +typedef struct Buffer_Measure_Starts{ + i32 i; + i32 count; + i32 start; + f32 width; +} Buffer_Measure_Starts; + +// TODO(allen): Rewrite this with a duff routine +internal_4tech i32 +buffer_measure_starts_widths_(Buffer_Measure_Starts *state, Buffer_Type *buffer, f32 *advance_data){ + Buffer_Stringify_Type loop = {0}; + char *data = 0; + i32 end = 0; + i32 size = buffer_size(buffer); + f32 width = state->width; + i32 start = state->start, i = state->i; + i32 *start_ptr = buffer->line_starts + state->count; + i32 *start_end = buffer->line_starts + buffer->line_max; + f32 *width_ptr = buffer->line_widths + state->count; + i32 result = 1; + char ch = 0; + + debug_4tech(i32 widths_max = buffer->widths_max); + debug_4tech(i32 max = buffer->line_max); + assert_4tech(max == widths_max); + + for (loop = buffer_stringify_loop(buffer, i, size); + buffer_stringify_good(&loop); + buffer_stringify_next(&loop)){ + end = loop.size + loop.absolute_pos; + data = loop.data - loop.absolute_pos; + for (; i < end; ++i){ + ch = data[i]; + if (ch == '\n'){ + if (start_ptr == start_end){ + goto buffer_measure_starts_widths_end; + } + + *width_ptr++ = width; + *start_ptr++ = start; + start = i + 1; + width = 0; + } + else{ + width += measure_character(advance_data, ch); + } + } + } + + assert_4tech(i == size); + + if (start_ptr == start_end){ + goto buffer_measure_starts_widths_end; + } + *start_ptr++ = start; + *width_ptr++ = 0; + result = 0; + + buffer_measure_starts_widths_end:; + state->i = i; + state->count = (i32)(start_ptr - buffer->line_starts); + state->start = start; + state->width = width; + + return(result); +} + +internal_4tech i32 +buffer_measure_starts_zero_widths_(Buffer_Measure_Starts *state, Buffer_Type *buffer){ + Buffer_Stringify_Type loop; + i32 *start_ptr, *start_end; + f32 *width_ptr; + debug_4tech(i32 widths_max); + debug_4tech(i32 max); + char *data; + i32 size, end; + i32 start, i; + i32 result; + char ch; + + size = buffer_size(buffer); + + debug_4tech(max = buffer->line_max); + debug_4tech(widths_max = buffer->widths_max); + assert_4tech(max == widths_max); + + result = 1; + + i = state->i; + start = state->start; + + start_ptr = buffer->line_starts + state->count; + width_ptr = buffer->line_widths + state->count; + start_end = buffer->line_starts + buffer->line_max; + + for (loop = buffer_stringify_loop(buffer, i, size); + buffer_stringify_good(&loop); + buffer_stringify_next(&loop)){ + end = loop.size + loop.absolute_pos; + data = loop.data - loop.absolute_pos; + for (; i < end; ++i){ + ch = data[i]; + if (ch == '\n'){ + if (start_ptr == start_end) goto buffer_measure_starts_zero_widths_end; + + *width_ptr++ = 0; + *start_ptr++ = start; + start = i + 1; + } + } + } + + assert_4tech(i == size); + + if (start_ptr == start_end) goto buffer_measure_starts_zero_widths_end; + *start_ptr++ = start; + *width_ptr++ = 0; + result = 0; + + buffer_measure_starts_zero_widths_end: + state->i = i; + state->count = (i32)(start_ptr - buffer->line_starts); + state->start = start; + + return(result); +} + +internal_4tech i32 +buffer_measure_starts_widths(Buffer_Measure_Starts *state, Buffer_Type *buffer, f32 *advance_data){ + i32 result = 0; + + if (advance_data){ + result = buffer_measure_starts_widths_(state, buffer, advance_data); + } + else{ + result = buffer_measure_starts_zero_widths_(state, buffer); + } + + return(result); +} + +internal_4tech void +buffer_remeasure_starts(Buffer_Type *buffer, i32 line_start, i32 line_end, i32 line_shift, i32 text_shift){ + Buffer_Stringify_Type loop; + i32 *starts = buffer->line_starts; + i32 line_count = buffer->line_count; + char *data = 0; + i32 size = 0, end = 0; + i32 line_i = 0, char_i = 0, start = 0; + + assert_4tech(0 <= line_start); + assert_4tech(line_start <= line_end); + assert_4tech(line_end < line_count); + assert_4tech(line_count + line_shift <= buffer->line_max); + + ++line_end; + if (text_shift != 0){ + line_i = line_end; + starts += line_i; + for (; line_i < line_count; ++line_i, ++starts){ + *starts += text_shift; + } + starts = buffer->line_starts; + } + + if (line_shift != 0){ + memmove_4tech(starts + line_end + line_shift, starts + line_end, + sizeof(i32)*(line_count - line_end)); + line_count += line_shift; + } + + line_end += line_shift; + size = buffer_size(buffer); + char_i = starts[line_start]; + line_i = line_start; + start = char_i; + + for (loop = buffer_stringify_loop(buffer, char_i, size); + buffer_stringify_good(&loop); + buffer_stringify_next(&loop)){ + end = loop.size + loop.absolute_pos; + data = loop.data - loop.absolute_pos; + for (; char_i < end; ++char_i){ + if (data[char_i] == '\n'){ + starts[line_i++] = start; + start = char_i + 1; + if (line_i >= line_end && start == starts[line_i]) goto buffer_remeasure_starts_end; + } + } + } + + if (char_i == size){ + starts[line_i++] = start; + } + + buffer_remeasure_starts_end: + assert_4tech(line_count >= 1); + buffer->line_count = line_count; +} + +internal_4tech void +buffer_remeasure_widths(Buffer_Type *buffer, f32 *advance_data, + i32 line_start, i32 line_end, i32 line_shift){ + Buffer_Stringify_Type loop; + i32 *starts = buffer->line_starts; + f32 *widths = buffer->line_widths; + i32 line_count = buffer->line_count; + i32 widths_count = buffer->widths_count; + char *data = 0; + i32 size = 0, end = 0; + i32 i = 0, j = 0; + f32 width = 0; + char ch = 0; + + assert_4tech(0 <= line_start); + assert_4tech(line_start <= line_end); + assert_4tech(line_count <= buffer->widths_max); + + ++line_end; + if (line_shift != 0){ + memmove_4tech(widths + line_end + line_shift, widths + line_end, + sizeof(f32)*(widths_count - line_end)); + } + buffer->widths_count = line_count; + + line_end += line_shift; + i = line_start; + j = starts[i]; + + if (line_end == line_count){ + size = buffer_size(buffer); + } + else{ + size = starts[line_end]; + } + + width = 0; + for (loop = buffer_stringify_loop(buffer, j, size); + buffer_stringify_good(&loop); + buffer_stringify_next(&loop)){ + end = loop.size + loop.absolute_pos; + data = loop.data - loop.absolute_pos; + + for (; j < end; ++j){ + ch = data[j]; + if (ch == '\n'){ + widths[i] = width; + ++i; + assert_4tech(j + 1 == starts[i]); + width = 0; + } + else{ + width += measure_character(advance_data, ch); + } + } + } + + if (j == buffer_size(buffer)){ + widths[i] = width; + assert_4tech(i+1 == line_count); + } +} + +internal_4tech void +buffer_measure_wrap_y(Buffer_Type *buffer, f32 *wraps, + f32 font_height, f32 *adv, f32 max_width){ + Buffer_Stringify_Type loop = {0}; + i32 size = buffer_size(buffer); + char *data = 0; + i32 end = 0; + i32 i = 0; + + i32 wrap_index = 0; + f32 last_wrap = 0.f; + f32 current_wrap = 0.f; + + f32 x = 0.f; + f32 current_adv = 0.f; + + u8 ch = 0; + + for (loop = buffer_stringify_loop(buffer, i, size); + buffer_stringify_good(&loop); + buffer_stringify_next(&loop)){ + end = loop.size + loop.absolute_pos; + data = loop.data - loop.absolute_pos; + for (; i < end; ++i){ + ch = (u8)data[i]; + if (ch == '\n'){ + wraps[wrap_index++] = last_wrap; + current_wrap += font_height; + last_wrap = current_wrap; + x = 0.f; + } + else{ + current_adv = adv[ch]; + if (x + current_adv > max_width){ + current_wrap += font_height; + x = current_adv; + } + else{ + x += current_adv; + } + } + } + } + + wraps[wrap_index++] = last_wrap; + + assert_4tech(wrap_index == buffer->line_count); +} + +internal_4tech i32 +buffer_get_line_index_range(Buffer_Type *buffer, i32 pos, i32 l_bound, i32 u_bound){ + i32 *lines = buffer->line_starts; + i32 start = l_bound, end = u_bound; + i32 i = 0; + + assert_4tech(0 <= l_bound); + assert_4tech(l_bound <= u_bound); + assert_4tech(u_bound <= buffer->line_count); + + assert_4tech(lines != 0); + + for (;;){ + i = (start + end) >> 1; + if (lines[i] < pos){ + start = i; + } + else if (lines[i] > pos){ + end = i; + } + else{ + start = i; + break; + } + assert_4tech(start < end); + if (start == end - 1){ + break; + } + } + + return(start); +} + +inline_4tech i32 +buffer_get_line_index(Buffer_Type *buffer, i32 pos){ + i32 result = buffer_get_line_index_range(buffer, pos, 0, buffer->line_count); + return(result); +} + +internal_4tech i32 +buffer_get_line_index_from_wrapped_y(f32 *wraps, f32 y, f32 font_height, i32 l_bound, i32 u_bound){ + i32 start, end, i, result; + start = l_bound; + end = u_bound; + for (;;){ + i = (start + end) / 2; + if (wraps[i]+font_height <= y) start = i; + else if (wraps[i] > y) end = i; + else{ + result = i; + break; + } + if (start >= end - 1){ + result = start; + break; + } + } + return(result); +} + +typedef struct Seek_State{ + Full_Cursor cursor; + Full_Cursor prev_cursor; +} Seek_State; + +internal_4tech i32 +cursor_seek_step(Seek_State *state, Buffer_Seek seek, i32 xy_seek, f32 max_width, + f32 font_height, f32 *adv, i32 size, uint8_t ch){ + Full_Cursor cursor = state->cursor; + Full_Cursor prev_cursor = cursor; + f32 ch_width; + i32 result = 1; + f32 x, px, y; + + switch (ch){ + case '\n': + ++cursor.line; + cursor.unwrapped_y += font_height; + cursor.wrapped_y += font_height; + cursor.character = 1; + cursor.unwrapped_x = 0; + cursor.wrapped_x = 0; + break; + + default: + ++cursor.character; + ch_width = adv[ch]; + + if (cursor.wrapped_x + ch_width >= max_width){ + cursor.wrapped_y += font_height; + cursor.wrapped_x = 0; + prev_cursor = cursor; + } + + cursor.unwrapped_x += ch_width; + cursor.wrapped_x += ch_width; + break; + } + + ++cursor.pos; + + if (cursor.pos > size){ + cursor = prev_cursor; + result = 0; + goto cursor_seek_step_end; + } + + x = y = px = 0; + + switch (seek.type){ + case buffer_seek_pos: + if (cursor.pos > seek.pos){ + cursor = prev_cursor; + result = 0; + goto cursor_seek_step_end; + }break; + + case buffer_seek_wrapped_xy: + x = cursor.wrapped_x; px = prev_cursor.wrapped_x; + y = cursor.wrapped_y; break; + + case buffer_seek_unwrapped_xy: + x = cursor.unwrapped_x; px = prev_cursor.unwrapped_x; + y = cursor.unwrapped_y; break; + + case buffer_seek_line_char: + if (cursor.line == seek.line && cursor.character >= seek.character){ + result = 0; + goto cursor_seek_step_end; + } + else if (cursor.line > seek.line){ + cursor = prev_cursor; + result = 0; + goto cursor_seek_step_end; + }break; + } + + if (xy_seek){ + if (y > seek.y){ + cursor = prev_cursor; + result = 0; + goto cursor_seek_step_end; + } + + if (y > seek.y - font_height && x >= seek.x){ + if (!seek.round_down){ + if (ch != '\n' && (seek.x - px) < (x - seek.x)) cursor = prev_cursor; + result = 0; + goto cursor_seek_step_end; + } + + if (x > seek.x){ + cursor = prev_cursor; + result = 0; + goto cursor_seek_step_end; + } + } + } + + cursor_seek_step_end: + state->cursor = cursor; + state->prev_cursor = prev_cursor; + return(result); +} + +internal_4tech Full_Cursor +buffer_cursor_seek(Buffer_Type *buffer, Buffer_Seek seek, f32 max_width, + f32 font_height, f32 *adv, Full_Cursor cursor){ + Buffer_Stringify_Type loop; + char *data; + i32 size, end; + i32 i; + i32 result; + + Seek_State state; + i32 xy_seek; + + state.cursor = cursor; + + switch(seek.type){ + case buffer_seek_pos: + if (cursor.pos >= seek.pos) goto buffer_cursor_seek_end; + break; + + case buffer_seek_wrapped_xy: + if (seek.x == 0 && cursor.wrapped_y >= seek.y) goto buffer_cursor_seek_end; + break; + + case buffer_seek_unwrapped_xy: + if (seek.x == 0 && cursor.unwrapped_y >= seek.y) goto buffer_cursor_seek_end; + break; + + case buffer_seek_line_char: + if (cursor.line >= seek.line && cursor.character >= seek.character) goto buffer_cursor_seek_end; + break; + } + + if (adv){ + size = buffer_size(buffer); + xy_seek = (seek.type == buffer_seek_wrapped_xy || seek.type == buffer_seek_unwrapped_xy); + + result = 1; + i = cursor.pos; + for (loop = buffer_stringify_loop(buffer, i, size); + buffer_stringify_good(&loop); + buffer_stringify_next(&loop)){ + end = loop.size + loop.absolute_pos; + data = loop.data - loop.absolute_pos; + for (; i < end; ++i){ + result = cursor_seek_step(&state, seek, xy_seek, max_width, + font_height, adv, size, data[i]); + if (!result) goto buffer_cursor_seek_end; + } + } + if (result){ + result = cursor_seek_step(&state, seek, xy_seek, max_width, + font_height, adv, size, 0); + assert_4tech(result == 0); + } + } + + buffer_cursor_seek_end: + return(state.cursor); +} + +internal_4tech Partial_Cursor +buffer_partial_from_pos(Buffer_Type *buffer, i32 pos){ + Partial_Cursor result = {0}; + + int32_t size = buffer_size(buffer); + if (pos > size){ + pos = size; + } + if (pos < 0){ + pos = 0; + } + + i32 line_index = buffer_get_line_index_range(buffer, pos, 0, buffer->line_count); + result.pos = pos; + result.line = line_index+1; + result.character = pos - buffer->line_starts[line_index] + 1; + + return(result); +} + +internal_4tech Full_Cursor +buffer_cursor_from_pos(Buffer_Type *buffer, i32 pos, f32 *wraps, + f32 max_width, f32 font_height, f32 *adv){ + Full_Cursor result; + i32 line_index; + + int32_t size = buffer_size(buffer); + if (pos > size){ + pos = size; + } + if (pos < 0){ + pos = 0; + } + + line_index = buffer_get_line_index_range(buffer, pos, 0, buffer->line_count); + result = make_cursor_hint(line_index, buffer->line_starts, wraps, font_height); + result = buffer_cursor_seek(buffer, seek_pos(pos), max_width, font_height, adv, result); + + return(result); +} + +internal_4tech Partial_Cursor +buffer_partial_from_line_character(Buffer_Type *buffer, i32 line, i32 character){ + Partial_Cursor result = {0}; + + i32 line_index = line - 1; + if (line_index >= buffer->line_count) line_index = buffer->line_count - 1; + if (line_index < 0) line_index = 0; + + int32_t size = buffer_size(buffer); + i32 this_start = buffer->line_starts[line_index]; + i32 max_character = (size-this_start) + 1; + if (line_index+1 < buffer->line_count){ + i32 next_start = buffer->line_starts[line_index+1]; + max_character = (next_start-this_start); + } + + if (character <= 0) character = 1; + if (character > max_character) character = max_character; + + result.pos = this_start + character - 1; + result.line = line_index+1; + result.character = character; + + return(result); +} + +internal_4tech Full_Cursor +buffer_cursor_from_line_character(Buffer_Type *buffer, i32 line, i32 character, f32 *wraps, + f32 max_width, f32 font_height, f32 *adv){ + Full_Cursor result = {0}; + + i32 line_index = line - 1; + if (line_index >= buffer->line_count) line_index = buffer->line_count - 1; + if (line_index < 0) line_index = 0; + + result = make_cursor_hint(line_index, buffer->line_starts, wraps, font_height); + result = buffer_cursor_seek(buffer, seek_line_char(line, character), + max_width, font_height, adv, result); + + return(result); +} + +internal_4tech Full_Cursor +buffer_cursor_from_unwrapped_xy(Buffer_Type *buffer, f32 x, f32 y, i32 round_down, f32 *wraps, + f32 max_width, f32 font_height, f32 *adv){ + Full_Cursor result; + i32 line_index; + + line_index = (i32)(y / font_height); + if (line_index >= buffer->line_count) line_index = buffer->line_count - 1; + if (line_index < 0) line_index = 0; + + result = make_cursor_hint(line_index, buffer->line_starts, wraps, font_height); + result = buffer_cursor_seek(buffer, seek_unwrapped_xy(x, y, round_down), + max_width, font_height, adv, result); + + return(result); +} + +internal_4tech Full_Cursor +buffer_cursor_from_wrapped_xy(Buffer_Type *buffer, f32 x, f32 y, i32 round_down, f32 *wraps, + f32 max_width, f32 font_height, f32 *adv){ + Full_Cursor result; + i32 line_index; + + line_index = buffer_get_line_index_from_wrapped_y(wraps, y, font_height, 0, buffer->line_count); + result = make_cursor_hint(line_index, buffer->line_starts, wraps, font_height); + result = buffer_cursor_seek(buffer, seek_wrapped_xy(x, y, round_down), + max_width, font_height, adv, result); + + return(result); +} + +internal_4tech void +buffer_invert_edit_shift(Buffer_Type *buffer, Buffer_Edit edit, Buffer_Edit *inverse, char *strings, + i32 *str_pos, i32 max, i32 shift_amount){ + i32 pos = *str_pos; + i32 len = edit.end - edit.start; + assert_4tech(pos >= 0); + assert_4tech(pos + len <= max); + *str_pos = pos + len; + + inverse->str_start = pos; + inverse->len = len; + inverse->start = edit.start + shift_amount; + inverse->end = edit.start + edit.len + shift_amount; + buffer_stringify(buffer, edit.start, edit.end, strings + pos); +} + +inline_4tech void +buffer_invert_edit(Buffer_Type *buffer, Buffer_Edit edit, Buffer_Edit *inverse, char *strings, + i32 *str_pos, i32 max){ + buffer_invert_edit_shift(buffer, edit, inverse, strings, str_pos, max, 0); +} + +typedef struct Buffer_Invert_Batch{ + i32 i; + i32 shift_amount; + i32 len; +} Buffer_Invert_Batch; + +internal_4tech i32 +buffer_invert_batch(Buffer_Invert_Batch *state, Buffer_Type *buffer, Buffer_Edit *edits, i32 count, + Buffer_Edit *inverse, char *strings, i32 *str_pos, i32 max){ + Buffer_Edit *edit, *inv_edit; + i32 shift_amount; + i32 result; + i32 i; + + result = 0; + i = state->i; + shift_amount = state->shift_amount; + + edit = edits + i; + inv_edit = inverse + i; + + for (; i < count; ++i, ++edit, ++inv_edit){ + if (*str_pos + edit->end - edit->start <= max){ + buffer_invert_edit_shift(buffer, *edit, inv_edit, strings, str_pos, max, shift_amount); + shift_amount += (edit->len - (edit->end - edit->start)); + } + else{ + result = 1; + state->len = edit->end - edit->start; + } + } + + state->i = i; + state->shift_amount = shift_amount; + + return(result); +} + +internal_4tech Full_Cursor +buffer_get_start_cursor(Buffer_Type *buffer, f32 *wraps, f32 scroll_y, + i32 wrapped, f32 width, f32 *adv, f32 font_height){ + Full_Cursor result; + + if (wrapped){ + result = buffer_cursor_from_wrapped_xy(buffer, 0, scroll_y, 0, wraps, + width, font_height, adv); + } + else{ + result = buffer_cursor_from_unwrapped_xy(buffer, 0, scroll_y, 0, wraps, + width, font_height, adv); + } + + return(result); +} + +enum Buffer_Render_Flag{ + BRFlag_Special_Character = (1 << 0) +}; + +typedef struct Buffer_Render_Item{ + i32 index; + u16 glyphid; + u16 flags; + f32 x0, y0; + f32 x1, y1; +} Buffer_Render_Item; + +typedef struct Render_Item_Write{ + Buffer_Render_Item *item; + f32 x, y; + f32 *adv; + f32 font_height; + f32 x_min; + f32 x_max; +} Render_Item_Write; + +inline_4tech Render_Item_Write +write_render_item(Render_Item_Write write, i32 index, u16 glyphid, u16 flags){ + f32 ch_width = measure_character(write.adv, (char)glyphid); + + if (write.x <= write.x_max && write.x + ch_width >= write.x_min){ + write.item->index = index; + write.item->glyphid = glyphid; + write.item->flags = flags; + write.item->x0 = write.x; + write.item->y0 = write.y; + write.item->x1 = write.x + ch_width; + write.item->y1 = write.y + write.font_height; + + ++write.item; + } + + write.x += ch_width; + + return(write); +} + +internal_4tech void +buffer_get_render_data(Buffer_Type *buffer, Buffer_Render_Item *items, i32 max, i32 *count, + f32 port_x, f32 port_y, f32 clip_w, + f32 scroll_x, f32 scroll_y, f32 width, f32 height, + Full_Cursor start_cursor, + i32 wrapped, f32 *adv, f32 font_height){ + + Buffer_Stringify_Type loop = {0}; + char *data = 0; + i32 end = 0; + + i32 size = buffer_size(buffer); + f32 shift_x = port_x - scroll_x, shift_y = port_y - scroll_y; + + if (wrapped){ + shift_y += start_cursor.wrapped_y; + } + else{ + shift_y += start_cursor.unwrapped_y; + } + + Buffer_Render_Item *item_end = items + max; + + Render_Item_Write write; + write.item = items; + write.x = shift_x; + write.y = shift_y; + write.adv = adv; + write.font_height = font_height; + write.x_min = port_x; + write.x_max = port_x + clip_w; + + if (adv){ + for (loop = buffer_stringify_loop(buffer, start_cursor.pos, size); + buffer_stringify_good(&loop) && write.item < item_end; + buffer_stringify_next(&loop)){ + + end = loop.size + loop.absolute_pos; + data = loop.data - loop.absolute_pos; + + for (i32 i = loop.absolute_pos; i < end; ++i){ + u8 ch = (uint8_t)data[i]; + f32 ch_width = measure_character(adv, ch); + + if (ch_width + write.x > width + shift_x && wrapped && ch != '\n'){ + write.x = shift_x; + write.y += font_height; + } + if (write.y > height + shift_y){ + goto buffer_get_render_data_end; + } + + switch (ch){ + case '\n': + if (write.item < item_end){ + write = write_render_item(write, i, ' ', 0); + write.x = shift_x; + write.y += font_height; + } + break; + + case '\r': + if (write.item < item_end){ + write = write_render_item(write, i, '\\', BRFlag_Special_Character); + + if (write.item < item_end){ + write = write_render_item(write, i, 'r', BRFlag_Special_Character); + } + } + break; + + case '\t': + if (write.item < item_end){ + f32 new_x = write.x + ch_width; + write = write_render_item(write, i, ' ', 0); + write.x = new_x; + } + break; + + default: + if (write.item < item_end){ + if (ch >= ' ' && ch <= '~'){ + write = write_render_item(write, i, ch, 0); + } + else{ + write = write_render_item(write, i, '\\', BRFlag_Special_Character); + + char C = '0' + (ch / 0x10); + if ((ch / 0x10) > 0x9){ + C = ('A' - 0xA) + (ch / 0x10); + } + + if (write.item < item_end){ + write = write_render_item(write, i, C, BRFlag_Special_Character); + } + + ch = (ch % 0x10); + C = '0' + ch; + if (ch > 0x9){ + C = ('A' - 0xA) + ch; + } + + if (write.item < item_end){ + write = write_render_item(write, i, C, BRFlag_Special_Character); + } + } + } + break; + } + + if (write.y > height + shift_y){ + goto buffer_get_render_data_end; + } + } + } + + buffer_get_render_data_end:; + if (write.y <= height + shift_y || write.item == items){ + if (write.item < item_end){ + write = write_render_item(write, size, ' ', 0); + } + } + } + else{ + f32 zero = 0; + write.adv = &zero; + + if (write.item < item_end){ + write = write_render_item(write, size, 0, 0); + } + } + + *count = (i32)(write.item - items); + assert_4tech(*count <= max); +} + +// BOTTOM +