From b84dcf03d746d14dc1313a343c996fbf33dfc989 Mon Sep 17 00:00:00 2001 From: Allen Webster Date: Thu, 22 Sep 2016 20:03:47 -0400 Subject: [PATCH] whitespace virtualization started --- 4cpp_lexer.h | 9 +- 4ed_file_view.cpp | 66 ++++-- buffer/4coder_buffer_abstract.cpp | 327 +++++++++++++++++++----------- buffer/4coder_gap_buffer.cpp | 74 ++++--- 4 files changed, 297 insertions(+), 179 deletions(-) diff --git a/4cpp_lexer.h b/4cpp_lexer.h index f5c1d879..8116cab4 100644 --- a/4cpp_lexer.h +++ b/4cpp_lexer.h @@ -269,13 +269,10 @@ cpp_pp_directive_to_state(Cpp_Token_Type type){ // duff-routine defines #define DrCase(PC) case PC: goto resumespot_##PC -#define DrYield(PC, n) { \ - token_array_out->count = token_i; \ - *S_ptr = S; S_ptr->__pc__ = PC; return(n); resumespot_##PC:; } +#define DrYield(PC, n)\ +{ token_array_out->count = token_i; *S_ptr = S; S_ptr->__pc__ = PC; return(n); resumespot_##PC:; } -#define DrReturn(n) { \ - token_array_out->count = token_i; \ - *S_ptr = S; S_ptr->__pc__ = -1; return(n); } +#define DrReturn(n) { token_array_out->count = token_i; *S_ptr = S; S_ptr->__pc__ = -1; return(n); } FCPP_INTERNAL Cpp_Lex_Result cpp_lex_nonalloc_null_end_no_limit(Cpp_Lex_Data *S_ptr, char *chunk, int32_t size, diff --git a/4ed_file_view.cpp b/4ed_file_view.cpp index 7db81438..8e313cd8 100644 --- a/4ed_file_view.cpp +++ b/4ed_file_view.cpp @@ -4794,7 +4794,6 @@ draw_file_loaded(View *view, i32_Rect rect, b32 is_active, Render_Target *target Style *style = main_style(models); i32 line_height = view->line_height; - f32 clip_w = view_width(view); f32 max_x = view_file_display_width(view); i32 max_y = rect.y1 - rect.y0 + line_height; @@ -4812,18 +4811,16 @@ draw_file_loaded(View *view, i32_Rect rect, b32 is_active, Render_Target *target Temp_Memory temp = begin_temp_memory(part); partition_align(part, 4); + + f32 left_side_space = 0; + i32 max = partition_remaining(part) / sizeof(Buffer_Render_Item); Buffer_Render_Item *items = push_array(part, Buffer_Render_Item, max); i16 font_id = file->settings.font_id; Render_Font *font = get_font_info(models->font_set, font_id)->font; - float *advance_data = 0; - // TODO(allen): Why isn't this "Assert(font);"? - if (font){ - advance_data = font->advance_data; - } + float *advance_data = font->advance_data; - i32 count = 0; Full_Cursor render_cursor = {0}; f32 scroll_x = view->edit_pos->scroll.scroll_x; @@ -4834,18 +4831,55 @@ draw_file_loaded(View *view, i32_Rect rect, b32 is_active, Render_Target *target // to the gui system. scroll_y += view->widget_height; + render_cursor = buffer_get_start_cursor(&file->state.buffer, file->state.wraps, scroll_y, + !file->settings.unwrapped_lines, + max_x, advance_data, (f32)line_height); + + view->edit_pos->scroll_i = render_cursor.pos; + + i32 count = 0; { - render_cursor = buffer_get_start_cursor(&file->state.buffer, file->state.wraps, scroll_y, - !file->settings.unwrapped_lines, - max_x, advance_data, (f32)line_height); + b32 wrapped = !file->settings.unwrapped_lines; - view->edit_pos->scroll_i = render_cursor.pos; + Buffer_Render_Params params; + params.buffer = &file->state.buffer; + params.items = items; + params.max = max; + params.count = &count; + params.port_x = (f32)rect.x0 + left_side_space; + params.port_y = (f32)rect.y0; + params.clip_w = view_width(view) - left_side_space; + params.scroll_x = scroll_x; + params.scroll_y = scroll_y; + params.width = max_x; + params.height = (f32)max_y; + params.start_cursor = render_cursor; + params.wrapped = wrapped; + params.font_height = (f32)line_height; + params.adv = advance_data; + params.virtual_white = 0; + + Buffer_Render_State state = {0}; + Buffer_Render_Stop stop = {0}; + + f32 line_shift = 0; + f32 edge_tolerance = 50.f; + + if (edge_tolerance > params.width){ + edge_tolerance = params.width; + } + + do{ + if (line_shift > params.width - edge_tolerance){ + line_shift = params.width - edge_tolerance; + } + + stop = buffer_render_data(&state, params, line_shift); + switch (stop.status){ + case RenderStatus_NeedLineShift: break; + } + }while(stop.status != RenderStatus_Finished); - buffer_get_render_data(&file->state.buffer, items, max, &count, - (f32)rect.x0, (f32)rect.y0, clip_w, - scroll_x, scroll_y, max_x, (f32)max_y, - render_cursor, !file->settings.unwrapped_lines, - advance_data, (f32)line_height); } Assert(count > 0); diff --git a/buffer/4coder_buffer_abstract.cpp b/buffer/4coder_buffer_abstract.cpp index 094fd115..8688061b 100644 --- a/buffer/4coder_buffer_abstract.cpp +++ b/buffer/4coder_buffer_abstract.cpp @@ -485,7 +485,7 @@ cursor_seek_step(Seek_State *state, Buffer_Seek seek, i32 xy_seek, f32 max_width } } - cursor_seek_step_end: + cursor_seek_step_end:; state->cursor = cursor; state->prev_cursor = prev_cursor; return(result); @@ -694,17 +694,11 @@ typedef struct 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; + i32 shift_amount = state->shift_amount; + i32 i = state->i; + Buffer_Edit *edit = edits + i; + Buffer_Edit *inv_edit = inverse + i; + i32 result = 0; for (; i < count; ++i, ++edit, ++inv_edit){ if (*str_pos + edit->end - edit->start <= max){ @@ -782,141 +776,238 @@ write_render_item(Render_Item_Write write, i32 index, u16 glyphid, u16 flags){ 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){ +// TODO(allen): Reduce the number of parameters. +struct Buffer_Render_Params{ + 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 font_height; + f32 *adv; + b32 virtual_white; +}; + +struct Buffer_Render_State{ + Buffer_Stringify_Type loop; + char *data; + i32 end; + i32 size; + i32 i; - 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; + f32 ch_width; + u8 ch; 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)){ + i32 line; + b32 skipping_whitespace; + + i32 __pc__; +}; + +// duff-routine defines +#define DrCase(PC) case PC: goto resumespot_##PC +#define DrYield(PC, n) { *S_ptr = S; S_ptr->__pc__ = PC; return(n); resumespot_##PC:; } +#define DrReturn(n) { *S_ptr = S; S_ptr->__pc__ = -1; return(n); } + +enum{ + RenderStatus_Finished, + RenderStatus_NeedLineShift +}; + +struct Buffer_Render_Stop{ + u32 status; + i32 line_index; + i32 pos; +}; + +internal_4tech Buffer_Render_Stop +buffer_render_data(Buffer_Render_State *S_ptr, Buffer_Render_Params params, f32 line_shift){ + Buffer_Render_State S = *S_ptr; + Buffer_Render_Stop S_stop; + + i32 size = buffer_size(params.buffer); + f32 shift_x = params.port_x - params.scroll_x; + f32 shift_y = params.port_y - params.scroll_y; + + if (params.wrapped){ + shift_y += params.start_cursor.wrapped_y; + } + else{ + shift_y += params.start_cursor.unwrapped_y; + } + + Buffer_Render_Item *item_end = params.items + params.max; + + switch (S.__pc__){ + DrCase(1); + DrCase(2); + DrCase(3); + } + + S.line = params.start_cursor.line - 1; + + if (params.virtual_white){ + S_stop.status = RenderStatus_NeedLineShift; + S_stop.line_index = S.line; + S_stop.pos = params.start_cursor.pos; + DrYield(1, S_stop); + } + + S.write.item = params.items; + S.write.x = shift_x + line_shift; + S.write.y = shift_y; + S.write.adv = params.adv; + S.write.font_height = params.font_height; + S.write.x_min = params.port_x; + S.write.x_max = params.port_x + params.clip_w; + + if (params.adv){ + for (S.loop = buffer_stringify_loop(params.buffer, params.start_cursor.pos, size); + buffer_stringify_good(&S.loop) && S.write.item < item_end; + buffer_stringify_next(&S.loop)){ - end = loop.size + loop.absolute_pos; - data = loop.data - loop.absolute_pos; + S.end = S.loop.size + S.loop.absolute_pos; + S.data = S.loop.data - S.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); + for (S.i = S.loop.absolute_pos; S.i < S.end; ++S.i){ + S.ch = (uint8_t)S.data[S.i]; + S.ch_width = measure_character(params.adv, S.ch); - if (ch_width + write.x > width + shift_x && wrapped && ch != '\n'){ - write.x = shift_x; - write.y += font_height; + if (S.ch_width + S.write.x > params.width + shift_x && S.ch != '\n' && params.wrapped){ + if (params.virtual_white){ + S_stop.status = RenderStatus_NeedLineShift; + S_stop.line_index = S.line; + S_stop.pos = S.i+1; + DrYield(2, S_stop); + } + + S.write.x = shift_x + line_shift; + S.write.y += params.font_height; } - if (write.y > height + shift_y){ + + if (S.write.y > params.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 (S.ch != ' ' && S.ch != '\t'){ + S.skipping_whitespace = 0; } - if (write.y > height + shift_y){ + if (!S.skipping_whitespace){ + switch (S.ch){ + case '\n': + if (S.write.item < item_end){ + S.write = write_render_item(S.write, S.i, ' ', 0); + + if (params.virtual_white){ + S_stop.status = RenderStatus_NeedLineShift; + S_stop.line_index = S.line+1; + S_stop.pos = S.i+1; + DrYield(3, S_stop); + + S.skipping_whitespace = 1; + } + + ++S.line; + + S.write.x = shift_x + line_shift; + S.write.y += params.font_height; + } + break; + + case '\r': + if (S.write.item < item_end){ + S.write = write_render_item(S.write, S.i, '\\', BRFlag_Special_Character); + + if (S.write.item < item_end){ + S.write = write_render_item(S.write, S.i, 'r', BRFlag_Special_Character); + } + } + break; + + case '\t': + if (S.write.item < item_end){ + f32 new_x = S.write.x + S.ch_width; + S.write = write_render_item(S.write, S.i, ' ', 0); + S.write.x = new_x; + } + break; + + default: + if (S.write.item < item_end){ + if (S.ch >= ' ' && S.ch <= '~'){ + S.write = write_render_item(S.write, S.i, S.ch, 0); + } + else{ + S.write = write_render_item(S.write, S.i, '\\', BRFlag_Special_Character); + + char ch = S.ch; + char C = '0' + (ch / 0x10); + if ((ch / 0x10) > 0x9){ + C = ('A' - 0xA) + (ch / 0x10); + } + + if (S.write.item < item_end){ + S.write = write_render_item(S.write, S.i, C, BRFlag_Special_Character); + } + + ch = (ch % 0x10); + C = '0' + ch; + if (ch > 0x9){ + C = ('A' - 0xA) + ch; + } + + if (S.write.item < item_end){ + S.write = write_render_item(S.write, S.i, C, BRFlag_Special_Character); + } + } + } + break; + } + } + + if (S.write.y > params.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); + if (S.write.y <= params.height + shift_y || S.write.item == params.items){ + if (S.write.item < item_end){ + S.write = write_render_item(S.write, size, ' ', 0); } } } else{ f32 zero = 0; - write.adv = &zero; + S.write.adv = &zero; - if (write.item < item_end){ - write = write_render_item(write, size, 0, 0); + if (S.write.item < item_end){ + S.write = write_render_item(S.write, size, 0, 0); } } - *count = (i32)(write.item - items); - assert_4tech(*count <= max); + *params.count = (i32)(S.write.item - params.items); + assert_4tech(*params.count <= params.max); + + S_stop.status = RenderStatus_Finished; + DrReturn(S_stop); } +#undef DrYield +#undef DrReturn +#undef DrCase + // BOTTOM diff --git a/buffer/4coder_gap_buffer.cpp b/buffer/4coder_gap_buffer.cpp index 5d41ed7b..9a153d9d 100644 --- a/buffer/4coder_gap_buffer.cpp +++ b/buffer/4coder_gap_buffer.cpp @@ -98,80 +98,76 @@ buffer_end_init(Gap_Buffer_Init *init, void *scratch, i32 scratch_size){ return(result); } -typedef struct Gap_Buffer_Stringify_Loop{ +typedef struct Gap_Buffer_Stream{ Gap_Buffer *buffer; - char *data, *base; + char *data; + char *base; i32 absolute_pos; - i32 pos, end; + i32 pos; + i32 end; i32 size; i32 separated; -} Gap_Buffer_Stringify_Loop; +} Gap_Buffer_Stream; -internal_4tech Gap_Buffer_Stringify_Loop -buffer_stringify_loop(Gap_Buffer *buffer, i32 start, i32 end){ - Gap_Buffer_Stringify_Loop result = {0}; +internal_4tech b32 +buffer_stringify_loop(Gap_Buffer_Stream *stream, Gap_Buffer *buffer, i32 start, i32 end){ + b32 result = 0; if (0 <= start && start < end && end <= buffer->size1 + buffer->size2){ - result.buffer = buffer; - result.base = buffer->data; - result.absolute_pos = start; + stream->buffer = buffer; + stream->base = buffer->data; + stream->absolute_pos = start; if (end <= buffer->size1){ - result.end = end; + stream->end = end; } else{ - result.end = end + buffer->gap_size; + stream->end = end + buffer->gap_size; } if (start < buffer->size1){ if (end <= buffer->size1){ - result.separated = 0; + stream->separated = 0; } else{ - result.separated = 1; + stream->separated = 1; } - result.pos = start; + stream->pos = start; } else{ - result.separated = 0; - result.pos = start + buffer->gap_size; + stream->separated = 0; + stream->pos = start + buffer->gap_size; } - if (result.separated){ - result.size = buffer->size1 - start; + if (stream->separated){ + stream->size = buffer->size1 - start; } else{ - result.size = end - start; + stream->size = end - start; } - result.data = buffer->data + result.pos; + stream->data = buffer->data + stream->pos; } return(result); } -inline_4tech i32 -buffer_stringify_good(Gap_Buffer_Stringify_Loop *loop){ - i32 result = (loop->buffer != 0); - return(result); -} - -internal_4tech void -buffer_stringify_next(Gap_Buffer_Stringify_Loop *loop){ +internal_4tech b32 +buffer_stringify_next(Gap_Buffer_Stream *stream){ i32 size1 = 0, temp_end = 0; - if (loop->separated){ - loop->separated = 0; - size1 = loop->buffer->size1; - loop->pos = loop->buffer->gap_size + size1; - loop->absolute_pos = size1; - temp_end = loop->end; + if (stream->separated){ + stream->separated = 0; + size1 = stream->buffer->size1; + stream->pos = stream->buffer->gap_size + size1; + stream->absolute_pos = size1; + temp_end = stream->end; } else{ - loop->buffer = 0; - temp_end = loop->pos; + stream->buffer = 0; + temp_end = stream->pos; } - loop->size = temp_end - loop->pos; - loop->data = loop->base + loop->pos; + stream->size = temp_end - stream->pos; + stream->data = stream->base + stream->pos; } internal_4tech i32