dialectic API for line wrap measurements
parent
3cfb743a12
commit
980cb9e477
|
@ -955,8 +955,40 @@ file_allocate_wraps_as_needed(General_Memory *general, Editing_File *file){
|
||||||
internal void
|
internal void
|
||||||
file_measure_wraps(Models *models, Editing_File *file, f32 font_height, f32 *adv){
|
file_measure_wraps(Models *models, Editing_File *file, f32 font_height, f32 *adv){
|
||||||
file_allocate_wraps_as_needed(&models->mem.general, file);
|
file_allocate_wraps_as_needed(&models->mem.general, file);
|
||||||
buffer_measure_wrap_y(&file->state.buffer, file->state.wraps,
|
|
||||||
font_height, adv, (f32)file->settings.display_width);
|
Buffer_Measure_Wrap_Params params;
|
||||||
|
params.buffer = &file->state.buffer;
|
||||||
|
params.wraps = file->state.wraps;
|
||||||
|
params.font_height = font_height;
|
||||||
|
params.adv = adv;
|
||||||
|
params.width = (f32)file->settings.display_width;
|
||||||
|
params.virtual_white = VWHITE;
|
||||||
|
|
||||||
|
Buffer_Measure_Wrap_State state = {0};
|
||||||
|
Buffer_Layout_Stop stop = {0};
|
||||||
|
|
||||||
|
f32 edge_tolerance = 50.f;
|
||||||
|
if (edge_tolerance > params.width){
|
||||||
|
edge_tolerance = params.width;
|
||||||
|
}
|
||||||
|
|
||||||
|
f32 line_shift = 0.f;
|
||||||
|
do{
|
||||||
|
f32 this_line_shift = line_shift;
|
||||||
|
if (this_line_shift > params.width - edge_tolerance){
|
||||||
|
this_line_shift = params.width - edge_tolerance;
|
||||||
|
}
|
||||||
|
|
||||||
|
stop = buffer_measure_wrap_y(&state, params, this_line_shift);
|
||||||
|
switch (stop.status){
|
||||||
|
case BLStatus_NeedWrapLineShift:
|
||||||
|
case BLStatus_NeedLineShift:
|
||||||
|
line_shift = (stop.line_index%4)*9.f;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}while(stop.status != BLStatus_Finished);
|
||||||
|
|
||||||
|
buffer_measure_wrap_y(&state, params, 0.f);
|
||||||
file_update_cursor_positions(models, file);
|
file_update_cursor_positions(models, file);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2065,15 +2097,19 @@ file_do_single_edit(System_Functions *system,
|
||||||
file_grow_starts_as_needed(general, buffer, line_shift);
|
file_grow_starts_as_needed(general, buffer, line_shift);
|
||||||
buffer_remeasure_starts(buffer, line_start, line_end, line_shift, shift_amount);
|
buffer_remeasure_starts(buffer, line_start, line_end, line_shift, shift_amount);
|
||||||
|
|
||||||
// TODO(allen): write the remeasurement version
|
|
||||||
file_allocate_character_starts_as_needed(general, file);
|
file_allocate_character_starts_as_needed(general, file);
|
||||||
buffer_remeasure_character_starts(buffer, line_start, line_end, line_shift,
|
buffer_remeasure_character_starts(buffer, line_start, line_end, line_shift,
|
||||||
file->state.character_starts, 0, VWHITE);
|
file->state.character_starts, 0, VWHITE);
|
||||||
|
|
||||||
|
// TODO(allen): Redo this as some sort of dialectic API
|
||||||
|
#if 0
|
||||||
file_allocate_wraps_as_needed(general, file);
|
file_allocate_wraps_as_needed(general, file);
|
||||||
buffer_remeasure_wrap_y(buffer, line_start, line_end, line_shift,
|
buffer_remeasure_wrap_y(buffer, line_start, line_end, line_shift,
|
||||||
file->state.wraps, (f32)font->height, font->advance_data,
|
file->state.wraps, (f32)font->height, font->advance_data,
|
||||||
(f32)file->settings.display_width);
|
(f32)file->settings.display_width);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
file_measure_wraps(models, file, (f32)font->height, font->advance_data);
|
||||||
|
|
||||||
// NOTE(allen): cursor fixing
|
// NOTE(allen): cursor fixing
|
||||||
Cursor_Fix_Descriptor desc = {};
|
Cursor_Fix_Descriptor desc = {};
|
||||||
|
|
23
TODO.txt
23
TODO.txt
|
@ -86,6 +86,15 @@
|
||||||
; [] view fails to follow cursor after deleting long line
|
; [] view fails to follow cursor after deleting long line
|
||||||
;
|
;
|
||||||
|
|
||||||
|
; BEFORE I SHIP
|
||||||
|
;
|
||||||
|
; [] ad hoc call for setting up/down keys for interactive screens
|
||||||
|
; [] miblo's various number editors
|
||||||
|
; [] API docs have duplicate ids?
|
||||||
|
; [] query buffer font info
|
||||||
|
; [] option to not open *messages* every startup
|
||||||
|
;
|
||||||
|
|
||||||
; TODOS
|
; TODOS
|
||||||
; [X] success message when compiler works
|
; [X] success message when compiler works
|
||||||
; [X] auto-complete
|
; [X] auto-complete
|
||||||
|
@ -132,6 +141,7 @@
|
||||||
; [X] why are command line files not loading any more?
|
; [X] why are command line files not loading any more?
|
||||||
; [X] use strange theme
|
; [X] use strange theme
|
||||||
; [X] cuber's return to previous buffer idea
|
; [X] cuber's return to previous buffer idea
|
||||||
|
; [X] find matches for current identifier
|
||||||
|
|
||||||
; Token related upgrades
|
; Token related upgrades
|
||||||
; [X] tokens in the custom API
|
; [X] tokens in the custom API
|
||||||
|
@ -155,7 +165,7 @@
|
||||||
; [] word level wrapping
|
; [] word level wrapping
|
||||||
; [] code level wrapping
|
; [] code level wrapping
|
||||||
|
|
||||||
; buffer behavior cleanup
|
; Buffer behavior cleanup
|
||||||
; [X] show all characters as \# if they can't be rendered
|
; [X] show all characters as \# if they can't be rendered
|
||||||
; [X] get the navigation working correctly around multi-glyph characters
|
; [X] get the navigation working correctly around multi-glyph characters
|
||||||
; [] wrap remeasuring routine for local edits
|
; [] wrap remeasuring routine for local edits
|
||||||
|
@ -165,21 +175,17 @@
|
||||||
; [] unicode buffer mode
|
; [] unicode buffer mode
|
||||||
; [] support full length unicode file names
|
; [] support full length unicode file names
|
||||||
|
|
||||||
; [] miblo's various number editors
|
|
||||||
; [] user file bar string
|
; [] user file bar string
|
||||||
; [] API docs as text file
|
; [] API docs as text file
|
||||||
; [] read only files
|
; [] read only files
|
||||||
; [] option to hide hidden files
|
; [] option to hide hidden files
|
||||||
; [] control over how mouse effects panel focus
|
; [] control over how mouse effects panel focus
|
||||||
; [] option to not open *messages* every startup
|
|
||||||
;
|
|
||||||
; [] query buffer font info
|
|
||||||
; [] option to break buffer name ties by adding parent directories instead of <#>
|
; [] option to break buffer name ties by adding parent directories instead of <#>
|
||||||
; [] undo groups
|
; [] undo groups
|
||||||
; [] cursor/scroll groups
|
; [] cursor/scroll groups
|
||||||
; [] double binding warnings
|
; [] double binding warnings
|
||||||
;
|
|
||||||
;
|
|
||||||
; [] the "main_4coder" experiment
|
; [] the "main_4coder" experiment
|
||||||
; [] real multi-line editing
|
; [] real multi-line editing
|
||||||
; [] multi-cursor editing
|
; [] multi-cursor editing
|
||||||
|
@ -188,10 +194,8 @@
|
||||||
|
|
||||||
; [] simple text based project file
|
; [] simple text based project file
|
||||||
; [] system commands bound to <ctrl #> in project file
|
; [] system commands bound to <ctrl #> in project file
|
||||||
; [] find matches for current identifier
|
|
||||||
; [] ability to save and reopen the window state
|
; [] ability to save and reopen the window state
|
||||||
|
|
||||||
; [] API docs have duplicate ids?
|
|
||||||
; [] introduce custom command line arguments
|
; [] introduce custom command line arguments
|
||||||
; [] control the file opening/start hook relationship better
|
; [] control the file opening/start hook relationship better
|
||||||
; [] get keyboard state on launch
|
; [] get keyboard state on launch
|
||||||
|
@ -222,6 +226,7 @@
|
||||||
; [X] wave search
|
; [X] wave search
|
||||||
; [] optimize search
|
; [] optimize search
|
||||||
; [] smarter isearch behavior
|
; [] smarter isearch behavior
|
||||||
|
; [] cleanup word complete so it may be case-insensitive
|
||||||
;
|
;
|
||||||
|
|
||||||
; theme related business
|
; theme related business
|
||||||
|
|
|
@ -130,7 +130,7 @@ buffer_measure_starts(Buffer_Measure_Starts *state, Buffer_Type *buffer){
|
||||||
}
|
}
|
||||||
|
|
||||||
internal_4tech void
|
internal_4tech void
|
||||||
buffer_measure_character_starts(Buffer_Type *buffer, i32 *character_starts, i32 mode, i32 virtual_whitespace){
|
buffer_measure_character_starts(Buffer_Type *buffer, i32 *character_starts, i32 mode, i32 virtual_white){
|
||||||
assert_4tech(mode == 0);
|
assert_4tech(mode == 0);
|
||||||
|
|
||||||
Buffer_Stream_Type stream = {0};
|
Buffer_Stream_Type stream = {0};
|
||||||
|
@ -144,7 +144,7 @@ buffer_measure_character_starts(Buffer_Type *buffer, i32 *character_starts, i32
|
||||||
|
|
||||||
character_starts[line_index++] = character_index;
|
character_starts[line_index++] = character_index;
|
||||||
|
|
||||||
if (virtual_whitespace){
|
if (virtual_white){
|
||||||
skipping_whitespace = 1;
|
skipping_whitespace = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,7 +158,7 @@ buffer_measure_character_starts(Buffer_Type *buffer, i32 *character_starts, i32
|
||||||
if (ch == '\n'){
|
if (ch == '\n'){
|
||||||
++character_index;
|
++character_index;
|
||||||
character_starts[line_index++] = character_index;
|
character_starts[line_index++] = character_index;
|
||||||
if (virtual_whitespace){
|
if (virtual_white){
|
||||||
skipping_whitespace = 1;
|
skipping_whitespace = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -179,50 +179,142 @@ buffer_measure_character_starts(Buffer_Type *buffer, i32 *character_starts, i32
|
||||||
assert_4tech(line_index-1 == buffer->line_count);
|
assert_4tech(line_index-1 == buffer->line_count);
|
||||||
}
|
}
|
||||||
|
|
||||||
internal_4tech void
|
struct Buffer_Measure_Wrap_Params{
|
||||||
buffer_measure_wrap_y(Buffer_Type *buffer, f32 *wraps, f32 font_height, f32 *adv, f32 max_width){
|
Buffer_Type *buffer;
|
||||||
Buffer_Stream_Type stream = {0};
|
f32 *wraps;
|
||||||
i32 i = 0;
|
f32 font_height;
|
||||||
i32 size = buffer_size(buffer);
|
f32 *adv;
|
||||||
|
f32 width;
|
||||||
|
b32 virtual_white;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Buffer_Measure_Wrap_State{
|
||||||
|
Buffer_Stream_Type stream;
|
||||||
|
i32 i;
|
||||||
|
i32 size;
|
||||||
|
i32 wrap_index;
|
||||||
|
|
||||||
i32 wrap_index = 0;
|
f32 current_wrap;
|
||||||
f32 current_wrap = 0.f;
|
f32 current_adv;
|
||||||
|
f32 x;
|
||||||
|
|
||||||
f32 x = 0.f;
|
b32 skipping_whitespace;
|
||||||
|
|
||||||
wraps[wrap_index++] = current_wrap;
|
b32 still_looping;
|
||||||
|
|
||||||
if (buffer_stringify_loop(&stream, buffer, i, size)){
|
u8 ch;
|
||||||
b32 still_looping = 0;
|
|
||||||
|
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{
|
||||||
|
BLStatus_Finished,
|
||||||
|
BLStatus_NeedWrapLineShift,
|
||||||
|
BLStatus_NeedLineShift
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Buffer_Layout_Stop{
|
||||||
|
u32 status;
|
||||||
|
i32 line_index;
|
||||||
|
i32 pos;
|
||||||
|
};
|
||||||
|
|
||||||
|
internal_4tech Buffer_Layout_Stop
|
||||||
|
buffer_measure_wrap_y(Buffer_Measure_Wrap_State *S_ptr, Buffer_Measure_Wrap_Params params, f32 line_shift){
|
||||||
|
Buffer_Measure_Wrap_State S = *S_ptr;
|
||||||
|
Buffer_Layout_Stop S_stop;
|
||||||
|
|
||||||
|
S.size = buffer_size(params.buffer);
|
||||||
|
|
||||||
|
switch (S.__pc__){
|
||||||
|
DrCase(1);
|
||||||
|
DrCase(2);
|
||||||
|
DrCase(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params.virtual_white){
|
||||||
|
S_stop.status = BLStatus_NeedLineShift;
|
||||||
|
S_stop.line_index = S.wrap_index;
|
||||||
|
S_stop.pos = S.i;
|
||||||
|
DrYield(1, S_stop);
|
||||||
|
}
|
||||||
|
|
||||||
|
S.x = line_shift;
|
||||||
|
params.wraps[S.wrap_index++] = 0;
|
||||||
|
|
||||||
|
if (params.virtual_white){
|
||||||
|
S.skipping_whitespace = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buffer_stringify_loop(&S.stream, params.buffer, S.i, S.size)){
|
||||||
|
S.still_looping = 0;
|
||||||
do{
|
do{
|
||||||
for (; i < stream.end; ++i){
|
for (; S.i < S.stream.end; ++S.i){
|
||||||
u8 ch = (u8)stream.data[i];
|
S.ch = (u8)S.stream.data[S.i];
|
||||||
if (ch == '\n'){
|
if (S.ch == '\n'){
|
||||||
current_wrap += font_height;
|
S.current_wrap += params.font_height;
|
||||||
wraps[wrap_index++] = current_wrap;
|
params.wraps[S.wrap_index++] = S.current_wrap;
|
||||||
x = 0.f;
|
|
||||||
|
if (params.virtual_white){
|
||||||
|
S_stop.status = BLStatus_NeedLineShift;
|
||||||
|
S_stop.line_index = S.wrap_index - 1;
|
||||||
|
S_stop.pos = S.i+1;
|
||||||
|
DrYield(2, S_stop);
|
||||||
|
}
|
||||||
|
|
||||||
|
S.x = line_shift;
|
||||||
|
|
||||||
|
if (params.virtual_white){
|
||||||
|
S.skipping_whitespace = 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else{
|
else{
|
||||||
f32 current_adv = adv[ch];
|
if (S.ch != ' ' && S.ch != '\t'){
|
||||||
if (x + current_adv > max_width){
|
S.skipping_whitespace = 0;
|
||||||
current_wrap += font_height;
|
|
||||||
x = current_adv;
|
|
||||||
}
|
}
|
||||||
else{
|
|
||||||
x += current_adv;
|
if (!S.skipping_whitespace){
|
||||||
|
S.current_adv = params.adv[S.ch];
|
||||||
|
if (S.x + S.current_adv > params.width){
|
||||||
|
S.current_wrap += params.font_height;
|
||||||
|
|
||||||
|
if (params.virtual_white){
|
||||||
|
S_stop.status = BLStatus_NeedWrapLineShift;
|
||||||
|
S_stop.line_index = S.wrap_index - 1;
|
||||||
|
S_stop.pos = S.i;
|
||||||
|
DrYield(3, S_stop);
|
||||||
|
}
|
||||||
|
|
||||||
|
S.x = line_shift + S.current_adv;
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
S.x += S.current_adv;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
still_looping = buffer_stringify_next(&stream);
|
S.still_looping = buffer_stringify_next(&S.stream);
|
||||||
}while(still_looping);
|
}while(S.still_looping);
|
||||||
}
|
}
|
||||||
|
|
||||||
current_wrap += font_height;
|
S.current_wrap += params.font_height;
|
||||||
wraps[wrap_index++] = current_wrap;
|
params.wraps[S.wrap_index++] = S.current_wrap;
|
||||||
|
|
||||||
assert_4tech(wrap_index-1 == buffer->line_count);
|
assert_4tech(S.wrap_index-1 == params.buffer->line_count);
|
||||||
|
|
||||||
|
S_stop.status = BLStatus_Finished;
|
||||||
|
DrReturn(S_stop);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#undef DrCase
|
||||||
|
#undef DrYield
|
||||||
|
#undef DrReturn
|
||||||
|
|
||||||
internal_4tech void
|
internal_4tech void
|
||||||
buffer_remeasure_starts(Buffer_Type *buffer, i32 line_start, i32 line_end, i32 line_shift, i32 text_shift){
|
buffer_remeasure_starts(Buffer_Type *buffer, i32 line_start, i32 line_end, i32 line_shift, i32 text_shift){
|
||||||
i32 *starts = buffer->line_starts;
|
i32 *starts = buffer->line_starts;
|
||||||
|
@ -628,6 +720,8 @@ struct Buffer_Cursor_Seek_State{
|
||||||
i32 i;
|
i32 i;
|
||||||
i32 size;
|
i32 size;
|
||||||
b32 xy_seek;
|
b32 xy_seek;
|
||||||
|
|
||||||
|
f32 ch_width;
|
||||||
u8 ch;
|
u8 ch;
|
||||||
|
|
||||||
i32 __pc__;
|
i32 __pc__;
|
||||||
|
@ -638,18 +732,6 @@ struct Buffer_Cursor_Seek_State{
|
||||||
#define DrYield(PC, n) { *S_ptr = S; S_ptr->__pc__ = PC; return(n); 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); }
|
#define DrReturn(n) { *S_ptr = S; S_ptr->__pc__ = -1; return(n); }
|
||||||
|
|
||||||
enum{
|
|
||||||
BLStatus_Finished,
|
|
||||||
BLStatus_NeedWrapLineShift,
|
|
||||||
BLStatus_NeedLineShift
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Buffer_Layout_Stop{
|
|
||||||
u32 status;
|
|
||||||
i32 line_index;
|
|
||||||
i32 pos;
|
|
||||||
};
|
|
||||||
|
|
||||||
internal_4tech Buffer_Layout_Stop
|
internal_4tech Buffer_Layout_Stop
|
||||||
buffer_cursor_seek(Buffer_Cursor_Seek_State *S_ptr, Buffer_Cursor_Seek_Params params,
|
buffer_cursor_seek(Buffer_Cursor_Seek_State *S_ptr, Buffer_Cursor_Seek_Params params,
|
||||||
f32 line_shift, Full_Cursor *cursor_out){
|
f32 line_shift, Full_Cursor *cursor_out){
|
||||||
|
@ -658,6 +740,8 @@ buffer_cursor_seek(Buffer_Cursor_Seek_State *S_ptr, Buffer_Cursor_Seek_Params pa
|
||||||
|
|
||||||
switch (S.__pc__){
|
switch (S.__pc__){
|
||||||
DrCase(1);
|
DrCase(1);
|
||||||
|
DrCase(2);
|
||||||
|
DrCase(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
S.xy_seek = (params.seek.type == buffer_seek_wrapped_xy || params.seek.type == buffer_seek_unwrapped_xy);
|
S.xy_seek = (params.seek.type == buffer_seek_wrapped_xy || params.seek.type == buffer_seek_unwrapped_xy);
|
||||||
|
@ -832,23 +916,39 @@ buffer_cursor_seek(Buffer_Cursor_Seek_State *S_ptr, Buffer_Cursor_Seek_Params pa
|
||||||
S.cursor.wrapped_y += params.font_height;
|
S.cursor.wrapped_y += params.font_height;
|
||||||
S.cursor.character = 1;
|
S.cursor.character = 1;
|
||||||
S.cursor.unwrapped_x = 0;
|
S.cursor.unwrapped_x = 0;
|
||||||
S.cursor.wrapped_x = 0;
|
|
||||||
|
if (params.virtual_white){
|
||||||
|
S_stop.status = BLStatus_NeedLineShift;
|
||||||
|
S_stop.line_index = S.cursor.line-1;
|
||||||
|
S_stop.pos = S.cursor.pos+1;
|
||||||
|
DrYield(2, S_stop);
|
||||||
|
}
|
||||||
|
|
||||||
|
S.cursor.wrapped_x = line_shift;
|
||||||
}break;
|
}break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
{
|
{
|
||||||
f32 ch_width = params.adv[S.ch];
|
S.ch_width = params.adv[S.ch];
|
||||||
|
|
||||||
if (S.cursor.wrapped_x + ch_width > params.width){
|
if (S.cursor.wrapped_x + S.ch_width > params.width){
|
||||||
S.cursor.wrapped_y += params.font_height;
|
S.cursor.wrapped_y += params.font_height;
|
||||||
S.cursor.wrapped_x = 0;
|
|
||||||
|
if (params.virtual_white){
|
||||||
|
S_stop.status = BLStatus_NeedWrapLineShift;
|
||||||
|
S_stop.line_index = S.cursor.line-1;
|
||||||
|
S_stop.pos = S.cursor.pos;
|
||||||
|
DrYield(3, S_stop);
|
||||||
|
}
|
||||||
|
|
||||||
|
S.cursor.wrapped_x = line_shift;
|
||||||
S.prev_cursor = S.cursor;
|
S.prev_cursor = S.cursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
++S.cursor.character_pos;
|
++S.cursor.character_pos;
|
||||||
++S.cursor.character;
|
++S.cursor.character;
|
||||||
S.cursor.unwrapped_x += ch_width;
|
S.cursor.unwrapped_x += S.ch_width;
|
||||||
S.cursor.wrapped_x += ch_width;
|
S.cursor.wrapped_x += S.ch_width;
|
||||||
}break;
|
}break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -930,9 +1030,9 @@ buffer_cursor_seek(Buffer_Cursor_Seek_State *S_ptr, Buffer_Cursor_Seek_Params pa
|
||||||
DrReturn(S_stop);
|
DrReturn(S_stop);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#undef DrCase
|
||||||
#undef DrYield
|
#undef DrYield
|
||||||
#undef DrReturn
|
#undef DrReturn
|
||||||
#undef DrCase
|
|
||||||
|
|
||||||
internal_4tech void
|
internal_4tech void
|
||||||
buffer_invert_edit_shift(Buffer_Type *buffer, Buffer_Edit edit, Buffer_Edit *inverse, char *strings,
|
buffer_invert_edit_shift(Buffer_Type *buffer, Buffer_Edit edit, Buffer_Edit *inverse, char *strings,
|
||||||
|
|
Loading…
Reference in New Issue