From 0da0013df61e0328844817c0345be2f9447016a3 Mon Sep 17 00:00:00 2001 From: Allen Webster Date: Sat, 24 Sep 2016 20:13:24 -0400 Subject: [PATCH] basic apparent character positioning working --- 4coder_API.html | 4 +- 4coder_default_include.cpp | 26 +++---- 4coder_types.h | 2 - 4ed_file_view.cpp | 29 ++++---- buffer/4coder_buffer_abstract.cpp | 120 +++++++++++++++++++++++------- buffer/4coder_shared.cpp | 35 +++------ 6 files changed, 131 insertions(+), 85 deletions(-) diff --git a/4coder_API.html b/4coder_API.html index d6fc6004..4276e789 100644 --- a/4coder_API.html +++ b/4coder_API.html @@ -181,10 +181,10 @@ for concisely creating Buffer_Seek structs. They can be found in 4coder_buffer_ and keeping the preferred x.

x
The x coordinate for xy type seeks.

y
The y coordinate for xy type seeks.

line
The line number of a line-character type seek.

character
The character number of a line-character type seek.

See Also
Buffer_Seek_Type
4coder_Buffer_Positioning_System

§3.4.35: Full_Cursor

struct Full_Cursor {
int32_t pos;
int32_t character_pos;
int32_t line;
int32_t character;
float unwrapped_x;
float unwrapped_y;
float wrapped_x;
float wrapped_y;
};
Description
Full_Cursor describes the position of a cursor in every buffer coordinate system supported by 4coder. This cursor type requires that the buffer is associated with a view to give the x/y values meaning.

Fields
pos
This field contains the cursor's position in absolute byte index positioning.

character_pos
This field contains the cursor's position in apparent character index positioning.

line
This field contains the number of the line where the cursor is located. This field is one based.

character
This field contains the number of the character from the beginninf of the line - where the cursor is located. This field is one based.

unwrapped_x
This field contains the x position measured with unwrapped lines.

unwrapped_y
This field contains the y position measured with unwrapped lines.

wrapped_x
This field contains the x position measured with wrapped lines.

wrapped_y
This field contains the y position measured with wrapped lines.

See Also
4coder_Buffer_Positioning_System

§3.4.36: Partial_Cursor

struct Partial_Cursor {
int32_t pos;
int32_t character_pos;
int32_t line;
int32_t character;
};
Description
Partial_Cursor describes the position of a cursor in all of + where the cursor is located. This field is one based.

unwrapped_x
This field contains the x position measured with unwrapped lines.

unwrapped_y
This field contains the y position measured with unwrapped lines.

wrapped_x
This field contains the x position measured with wrapped lines.

wrapped_y
This field contains the y position measured with wrapped lines.

See Also
4coder_Buffer_Positioning_System

§3.4.36: Partial_Cursor

struct Partial_Cursor {
int32_t pos;
int32_t line;
int32_t character;
};
Description
Partial_Cursor describes the position of a cursor in all of the coordinate systems that a invariant to the View. In other words the coordinate systems available here can be used on a buffer that is -not currently associated with a View.

Fields
pos
This field contains the cursor's position in absolute byte index positioning.

character_pos
This field contains the cursor's position in apparent character index positioning.

line
This field contains the number of the character from the beginninf of the line +not currently associated with a View.

Fields
pos
This field contains the cursor's position in absolute byte index positioning.

line
This field contains the number of the character from the beginninf of the line where the cursor is located. This field is one based.

character
This field contains the number of the column where the cursor is located. This field is one based.

See Also
4coder_Buffer_Positioning_System

§3.4.37: Buffer_Edit

struct Buffer_Edit {
int32_t str_start;
int32_t len;
int32_t start;
int32_t end;
};
Description
Buffer_Edit describes a range of a buffer and string to replace that range. A Buffer_Edit has to be paired with a string that contains the actual text that will be replaced into the buffer.

Fields
str_start
The str_start field specifies the first character in the accompanying string that corresponds with this edit.

len
The len field specifies the length of the string being written into the buffer.

start
The start field specifies the start of the range in the buffer to replace in absolute position.

end
The end field specifies one past the end of the range in the buffer to replace in absolute position.


§3.4.38: Buffer_Summary

struct Buffer_Summary {
bool32 exists;
bool32 ready;
int32_t buffer_id;
Access_Flag lock_flags;
int32_t size;
int32_t line_count;
char * file_name;
int32_t file_name_len;
char * buffer_name;
int32_t buffer_name_len;
Dirty_State dirty;
bool32 is_lexed;
bool32 tokens_are_ready;
int32_t map_id;
bool32 unwrapped_lines;
};
Description
Buffer_Summary acts as a handle to a buffer and describes the state of the buffer.

Fields
exists
This field indicates whether the Buffer_Summary describes a buffer that is open in 4coder. diff --git a/4coder_default_include.cpp b/4coder_default_include.cpp index 421248a6..9c144d4d 100644 --- a/4coder_default_include.cpp +++ b/4coder_default_include.cpp @@ -63,13 +63,13 @@ view_open_file(Application_Links *app, char *filename, int32_t filename_len, int32_t never_new){ - int32_t result = false; + int32_t result = 0; if (view){ Buffer_Summary buffer = {0}; if (open_file(app, &buffer, filename, filename_len, false, never_new)){ view_set_buffer(app, view, buffer.buffer_id, 0); - result = true; + result = 1; } } @@ -744,8 +744,8 @@ CUSTOM_COMMAND_SIG(delete_char){ int32_t start = view.cursor.pos; - Partial_Cursor cursor; - buffer_compute_cursor(app, &buffer, seek_character_pos(view.cursor.character_pos+1), &cursor); + Full_Cursor cursor; + view_compute_cursor(app, &view, seek_character_pos(view.cursor.character_pos+1), &cursor); int32_t end = cursor.pos; if (0 <= start && start < buffer.size){ @@ -760,8 +760,8 @@ CUSTOM_COMMAND_SIG(backspace_char){ int32_t end = view.cursor.pos; - Partial_Cursor cursor; - buffer_compute_cursor(app, &buffer, seek_character_pos(view.cursor.character_pos-1), &cursor); + Full_Cursor cursor; + view_compute_cursor(app, &view, seek_character_pos(view.cursor.character_pos-1), &cursor); int32_t start = cursor.pos; if (0 < end && end <= buffer.size){ @@ -858,10 +858,7 @@ CUSTOM_COMMAND_SIG(click_set_cursor){ Mouse_State mouse = get_mouse_state(app); float rx = 0, ry = 0; if (get_relative_xy(&view, mouse.x, mouse.y, &rx, &ry)){ - view_set_cursor(app, &view, - seek_xy(rx, ry, true, - view.unwrapped_lines), - true); + view_set_cursor(app, &view, seek_xy(rx, ry, 1, view.unwrapped_lines), 1); } } @@ -872,10 +869,7 @@ CUSTOM_COMMAND_SIG(click_set_mark){ Mouse_State mouse = get_mouse_state(app); float rx = 0, ry = 0; if (get_relative_xy(&view, mouse.x, mouse.y, &rx, &ry)){ - view_set_mark(app, &view, - seek_xy(rx, ry, true, - view.unwrapped_lines) - ); + view_set_mark(app, &view, seek_xy(rx, ry, 1, view.unwrapped_lines)); } } @@ -887,9 +881,7 @@ move_vertical(Application_Links *app, float line_multiplier){ float new_y = get_view_y(view) + line_multiplier*view.line_height; float x = view.preferred_x; - view_set_cursor(app, &view, - seek_xy(x, new_y, false, view.unwrapped_lines), - false); + view_set_cursor(app, &view, seek_xy(x, new_y, 0, view.unwrapped_lines), 0); } CUSTOM_COMMAND_SIG(move_up){ diff --git a/4coder_types.h b/4coder_types.h index 690012ae..72e923e3 100644 --- a/4coder_types.h +++ b/4coder_types.h @@ -575,8 +575,6 @@ DOC_SEE(4coder_Buffer_Positioning_System) */ struct Partial_Cursor{ /* DOC(This field contains the cursor's position in absolute byte index positioning.) */ int32_t pos; - /* DOC(This field contains the cursor's position in apparent character index positioning.) */ - int32_t character_pos; /* DOC(This field contains the number of the character from the beginninf of the line where the cursor is located. This field is one based.) */ int32_t line; diff --git a/4ed_file_view.cpp b/4ed_file_view.cpp index d6c46db4..49ba9110 100644 --- a/4ed_file_view.cpp +++ b/4ed_file_view.cpp @@ -9,6 +9,8 @@ // TOP +#define VWHITE 1 + internal i32 get_or_add_map_index(Models *models, i32 mapid){ i32 result; @@ -391,17 +393,18 @@ view_compute_cursor(View *view, Buffer_Seek seek){ Render_Font *font = get_font_info(models->font_set, file->settings.font_id)->font; Buffer_Cursor_Seek_Params params; - params.buffer = &file->state.buffer; - params.seek = seek; - params.width = view_file_display_width(view); - params.font_height = (f32)font->height; - params.adv = font->advance_data; - params.wraps = file->state.wraps; - params.virtual_white = 0; + params.buffer = &file->state.buffer; + params.seek = seek; + params.width = view_file_display_width(view); + params.font_height = (f32)font->height; + params.adv = font->advance_data; + params.wraps = file->state.wraps; + params.character_starts = file->state.character_starts; + params.virtual_white = VWHITE; Buffer_Cursor_Seek_State state = {0}; - Full_Cursor result; - Buffer_Layout_Stop stop; + Full_Cursor result = {0}; + Buffer_Layout_Stop stop = {0}; f32 edge_tolerance = 50.f; if (edge_tolerance > params.width){ @@ -930,7 +933,7 @@ file_allocate_character_starts_as_needed(General_Memory *general, Editing_File * internal void file_measure_character_starts(Models *models, Editing_File *file){ file_allocate_character_starts_as_needed(&models->mem.general, file); - buffer_measure_character_starts(&file->state.buffer, file->state.character_starts, 0, 1); + buffer_measure_character_starts(&file->state.buffer, file->state.character_starts, 0, VWHITE); file_update_cursor_positions(models, file); } @@ -2056,7 +2059,7 @@ file_do_single_edit(System_Functions *system, // TODO(allen): write the remeasurement version file_allocate_character_starts_as_needed(general, file); - buffer_measure_character_starts(buffer, file->state.character_starts, 0, 1); + buffer_measure_character_starts(buffer, file->state.character_starts, 0, VWHITE); file_allocate_wraps_as_needed(general, file); buffer_remeasure_wrap_y(buffer, line_start, line_end, line_shift, @@ -4852,10 +4855,10 @@ draw_file_loaded(View *view, i32_Rect rect, b32 is_active, Render_Target *target params.wrapped = wrapped; params.font_height = (f32)line_height; params.adv = advance_data; - params.virtual_white = 0; + params.virtual_white = VWHITE; Buffer_Render_State state = {0}; - Buffer_Layout_Stop stop; + Buffer_Layout_Stop stop = {0}; f32 edge_tolerance = 50.f; if (edge_tolerance > params.width){ diff --git a/buffer/4coder_buffer_abstract.cpp b/buffer/4coder_buffer_abstract.cpp index d3d776bb..4f55bec4 100644 --- a/buffer/4coder_buffer_abstract.cpp +++ b/buffer/4coder_buffer_abstract.cpp @@ -140,8 +140,14 @@ buffer_measure_character_starts(Buffer_Type *buffer, i32 *character_starts, i32 i32 line_index = 0; i32 character_index = 0; + b32 skipping_whitespace = 0; + character_starts[line_index++] = character_index; + if (virtual_whitespace){ + skipping_whitespace = 1; + } + if (buffer_stringify_loop(&stream, buffer, i, size)){ b32 still_looping = 0; do{ @@ -150,9 +156,18 @@ buffer_measure_character_starts(Buffer_Type *buffer, i32 *character_starts, i32 if (ch == '\n'){ ++character_index; character_starts[line_index++] = character_index; + if (virtual_whitespace){ + skipping_whitespace = 1; + } } else{ - ++character_index; + if (ch != ' ' && ch != '\t'){ + skipping_whitespace = 0; + } + + if (!skipping_whitespace){ + ++character_index; + } } } still_looping = buffer_stringify_next(&stream); @@ -403,25 +418,54 @@ buffer_get_line_index(Buffer_Type *buffer, i32 pos){ return(result); } +// TODO(allen): Try to merge this with the other line start binary search. 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; +buffer_get_line_index_from_character_pos(i32 *character_starts, i32 pos, i32 l_bound, i32 u_bound){ + i32 start = l_bound, end = u_bound; + i32 i = 0; + for (;;){ - i = (start + end) / 2; - if (wraps[i]+font_height <= y) start = i; - else if (wraps[i] > y) end = i; + i = (start + end) >> 1; + if (character_starts[i] < pos){ + start = i; + } + else if (character_starts[i] > pos){ + end = i; + } else{ - result = i; + start = i; break; } - if (start >= end - 1){ - result = start; + assert_4tech(start < end); + if (start == end - 1){ break; } } - return(result); + + return(start); +} + +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 = l_bound, end = u_bound; + i32 i = 0; + for (;;){ + i = (start + end) / 2; + if (wraps[i]+font_height <= y){ + start = i; + } + else if (wraps[i] > y){ + end = i; + } + else{ + break; + } + if (start >= end - 1){ + i = start; + break; + } + } + return(i); } internal_4tech Partial_Cursor @@ -481,6 +525,7 @@ struct Buffer_Cursor_Seek_Params{ f32 font_height; f32 *adv; f32 *wraps; + i32 *character_starts; b32 virtual_white; }; @@ -528,6 +573,7 @@ buffer_cursor_seek(Buffer_Cursor_Seek_State *S_ptr, Buffer_Cursor_Seek_Params pa S.size = buffer_size(params.buffer); // Get cursor hint + i32 line_index = 0; switch (params.seek.type){ case buffer_seek_pos: { @@ -538,52 +584,67 @@ buffer_cursor_seek(Buffer_Cursor_Seek_State *S_ptr, Buffer_Cursor_Seek_Params pa params.seek.pos = 0; } - i32 line_index = buffer_get_line_index_range(params.buffer, params.seek.pos, 0, params.buffer->line_count); - S.cursor = make_cursor_hint(line_index, params.buffer->line_starts, params.wraps, params.font_height); + line_index = buffer_get_line_index(params.buffer, params.seek.pos); }break; case buffer_seek_character_pos: { - NotImplemented; + i32 line_count = params.buffer->line_count; + i32 max_character = params.character_starts[line_count] - 1; + if (params.seek.pos > max_character){ + params.seek.pos = max_character; + } + if (params.seek.pos < 0){ + params.seek.pos = 0; + } + + line_index = buffer_get_line_index_from_character_pos(params.character_starts, params.seek.pos, + 0, params.buffer->line_count); }break; case buffer_seek_line_char: { - i32 line_index = params.seek.line - 1; + line_index = params.seek.line - 1; if (line_index >= params.buffer->line_count){ line_index = params.buffer->line_count - 1; } if (line_index < 0){ line_index = 0; } - - S.cursor = make_cursor_hint(line_index, params.buffer->line_starts, params.wraps, params.font_height); }break; case buffer_seek_unwrapped_xy: { - i32 line_index = (i32)(params.seek.y / params.font_height); + line_index = (i32)(params.seek.y / params.font_height); if (line_index >= params.buffer->line_count){ line_index = params.buffer->line_count - 1; } if (line_index < 0){ line_index = 0; } - - S.cursor = make_cursor_hint(line_index, params.buffer->line_starts, params.wraps, params.font_height); }break; case buffer_seek_wrapped_xy: { - i32 line_index = buffer_get_line_index_from_wrapped_y(params.wraps, params.seek.y, - params.font_height, 0, params.buffer->line_count); - - S.cursor = make_cursor_hint(line_index, params.buffer->line_starts, params.wraps, params.font_height); + line_index = buffer_get_line_index_from_wrapped_y(params.wraps, params.seek.y, + params.font_height, 0, params.buffer->line_count); }break; default: InvalidCodePath; } + // Build the cursor hint + { + S.cursor.pos = params.buffer->line_starts[line_index]; + S.cursor.character_pos = params.character_starts[line_index]; + S.cursor.line = line_index + 1; + S.cursor.character = 1; + S.cursor.unwrapped_y = (f32)(line_index * params.font_height); + S.cursor.unwrapped_x = 0; + S.cursor.wrapped_y = params.wraps[line_index]; + S.cursor.wrapped_x = 0; + } + // Get the initial line shift. // Adjust the non-screen based coordinates to point to the first // non-virtual character of the line. @@ -674,6 +735,7 @@ buffer_cursor_seek(Buffer_Cursor_Seek_State *S_ptr, Buffer_Cursor_Seek_Params pa switch (S.ch){ case '\n': { + ++S.cursor.character_pos; ++S.cursor.line; S.cursor.unwrapped_y += params.font_height; S.cursor.wrapped_y += params.font_height; @@ -714,6 +776,14 @@ buffer_cursor_seek(Buffer_Cursor_Seek_State *S_ptr, Buffer_Cursor_Seek_Params pa goto buffer_cursor_seek_end; }break; + case buffer_seek_character_pos: + { + if (S.cursor.character_pos > params.seek.pos){ + S.cursor = S.prev_cursor; + goto buffer_cursor_seek_end; + }break; + }break; + case buffer_seek_wrapped_xy: { x = S.cursor.wrapped_x; px = S.prev_cursor.wrapped_x; diff --git a/buffer/4coder_shared.cpp b/buffer/4coder_shared.cpp index 3cc2b609..c80c3f94 100644 --- a/buffer/4coder_shared.cpp +++ b/buffer/4coder_shared.cpp @@ -82,21 +82,9 @@ typedef struct Buffer_Batch_State{ i32 shift_total; } Buffer_Batch_State; -inline_4tech Full_Cursor -make_cursor_hint(i32 line_index, i32 *starts, f32 *wrap_ys, f32 font_height){ - Full_Cursor hint; - hint.pos = starts[line_index]; - hint.line = line_index + 1; - hint.character = 1; - hint.unwrapped_y = (f32)(line_index * font_height); - hint.unwrapped_x = 0; - hint.wrapped_y = wrap_ys[line_index]; - hint.wrapped_x = 0; - return(hint); -} - typedef struct Cursor_With_Index{ - i32 pos, index; + i32 pos; + i32 index; } Cursor_With_Index; inline_4tech void @@ -110,12 +98,9 @@ write_cursor_with_index(Cursor_With_Index *positions, i32 *count, i32 pos){ internal_4tech void buffer_quick_sort_cursors(Cursor_With_Index *positions, i32 start, i32 pivot){ - i32 i, mid; - i32 pivot_pos; - - mid = start; - pivot_pos = positions[pivot].pos; - for (i = mid; i < pivot; ++i){ + i32 mid = start; + i32 pivot_pos = positions[pivot].pos; + for (i32 i = mid; i < pivot; ++i){ if (positions[i].pos < pivot_pos){ CursorSwap__(positions[mid], positions[i]); ++mid; @@ -127,14 +112,12 @@ buffer_quick_sort_cursors(Cursor_With_Index *positions, i32 start, i32 pivot){ if (mid + 1 < pivot) buffer_quick_sort_cursors(positions, mid + 1, pivot); } +// TODO(allen): Rewrite this without being a dumbass. internal_4tech void buffer_quick_unsort_cursors(Cursor_With_Index *positions, i32 start, i32 pivot){ - i32 i, mid; - i32 pivot_index; - - mid = start; - pivot_index = positions[pivot].index; - for (i = mid; i < pivot; ++i){ + i32 mid = start; + i32 pivot_index = positions[pivot].index; + for (i32 i = mid; i < pivot; ++i){ if (positions[i].index < pivot_index){ CursorSwap__(positions[mid], positions[i]); ++mid;