466 lines
17 KiB
C++
466 lines
17 KiB
C++
/*
|
|
4coder_scope_commands.cpp - A set of commands and helpers relevant for scope level navigation and editing.
|
|
*/
|
|
|
|
// TOP
|
|
|
|
static Find_Scope_Token_Type
|
|
find_scope_get_token_type(Find_Scope_Flag flags, Token_Base_Kind kind){
|
|
Find_Scope_Token_Type type = FindScopeTokenType_None;
|
|
if (flags & FindScope_Scope){
|
|
switch (kind){
|
|
case TokenBaseKind_ScopeOpen:
|
|
{
|
|
type = FindScopeTokenType_Open;
|
|
}break;
|
|
case TokenBaseKind_ScopeClose:
|
|
{
|
|
type = FindScopeTokenType_Close;
|
|
}break;
|
|
}
|
|
}
|
|
else if (flags & FindScope_Paren){
|
|
switch (kind){
|
|
case TokenBaseKind_ParentheticalOpen:
|
|
{
|
|
type = FindScopeTokenType_Open;
|
|
}break;
|
|
case TokenBaseKind_ParentheticalClose:
|
|
{
|
|
type = FindScopeTokenType_Close;
|
|
}break;
|
|
}
|
|
}
|
|
return(type);
|
|
}
|
|
|
|
static b32
|
|
find_scope_top(Application_Links *app, Buffer_ID buffer, i64 start_pos, u32 flags, i64 *end_pos_out){
|
|
b32 success = false;
|
|
Token_Array array = get_token_array_from_buffer(app, buffer);
|
|
if (array.tokens != 0){
|
|
i64 position = start_pos;
|
|
i64 token_index = token_index_from_pos(&array, start_pos);
|
|
Token_Iterator_Array it = token_iterator_index(buffer, &array, token_index);
|
|
b32 good_status = true;
|
|
if (HasFlag(flags, FindScope_Parent)){
|
|
good_status = token_it_dec(&it);
|
|
}
|
|
i32 nest_level = 0;
|
|
for (;good_status;){
|
|
Token *token = token_it_read(&it);
|
|
Find_Scope_Token_Type type = find_scope_get_token_type(flags, token->kind);
|
|
switch (type){
|
|
case FindScopeTokenType_Open:
|
|
{
|
|
if (nest_level == 0){
|
|
success = true;
|
|
position = token->pos;
|
|
if (HasFlag(flags, FindScope_EndOfToken)){
|
|
position += token->size;
|
|
}
|
|
goto finished;
|
|
}
|
|
else{
|
|
--nest_level;
|
|
}
|
|
}break;
|
|
case FindScopeTokenType_Close:
|
|
{
|
|
++nest_level;
|
|
}break;
|
|
}
|
|
good_status = token_it_dec(&it);
|
|
}
|
|
finished:;
|
|
*end_pos_out = position;
|
|
}
|
|
return(success);
|
|
}
|
|
|
|
static b32
|
|
find_scope_bottom(Application_Links *app, Buffer_ID buffer, i64 start_pos, u32 flags, i64 *end_pos_out){
|
|
b32 success = false;
|
|
Token_Array array = get_token_array_from_buffer(app, buffer);
|
|
if (array.tokens != 0){
|
|
i64 position = start_pos;
|
|
i64 token_index = token_index_from_pos(&array, start_pos);
|
|
Token_Iterator_Array it = token_iterator_index(buffer, &array, token_index);
|
|
token_it_inc(&it);
|
|
if (HasFlag(flags, FindScope_Parent)){
|
|
token_it_dec(&it);
|
|
}
|
|
b32 good_status = true;
|
|
i32 nest_level = 0;
|
|
for (;good_status;){
|
|
Token *token = token_it_read(&it);
|
|
Find_Scope_Token_Type type = find_scope_get_token_type(flags, token->kind);
|
|
switch (type){
|
|
case FindScopeTokenType_Open:
|
|
{
|
|
++nest_level;
|
|
}break;
|
|
case FindScopeTokenType_Close:
|
|
{
|
|
if (nest_level == 0){
|
|
success = true;
|
|
position = token->pos;
|
|
if (HasFlag(flags, FindScope_EndOfToken)){
|
|
position += token->size;
|
|
}
|
|
goto finished;
|
|
}
|
|
else{
|
|
--nest_level;
|
|
}
|
|
}break;
|
|
}
|
|
good_status = token_it_inc(&it);
|
|
}
|
|
finished:;
|
|
*end_pos_out = position;
|
|
}
|
|
return(success);
|
|
}
|
|
|
|
static b32
|
|
find_next_scope(Application_Links *app, Buffer_ID buffer, i64 start_pos, u32 flags, i64 *end_pos_out){
|
|
b32 success = false;
|
|
Token_Array array = get_token_array_from_buffer(app, buffer);
|
|
if (array.tokens != 0){
|
|
i64 position = start_pos;
|
|
i64 token_index = token_index_from_pos(&array, start_pos);
|
|
Token_Iterator_Array it = token_iterator_index(buffer, &array, token_index);
|
|
token_it_inc(&it);
|
|
if (HasFlag(flags, FindScope_NextSibling)){
|
|
b32 good_status = true;
|
|
i32 nest_level = 1;
|
|
for (;good_status;){
|
|
Token *token = token_it_read(&it);
|
|
Find_Scope_Token_Type type = find_scope_get_token_type(flags, token->kind);
|
|
switch (type){
|
|
case FindScopeTokenType_Open:
|
|
{
|
|
if (nest_level == 0){
|
|
success = true;
|
|
position = token->pos;
|
|
if (HasFlag(flags, FindScope_EndOfToken)){
|
|
position += token->size;
|
|
}
|
|
goto finished;
|
|
}
|
|
else{
|
|
++nest_level;
|
|
}
|
|
}break;
|
|
case FindScopeTokenType_Close:
|
|
{
|
|
--nest_level;
|
|
if (nest_level == -1){
|
|
position = start_pos;
|
|
goto finished;
|
|
}
|
|
}break;
|
|
}
|
|
good_status = token_it_inc(&it);
|
|
}
|
|
}
|
|
else{
|
|
b32 good_status = true;
|
|
for (;good_status;){
|
|
Token *token = token_it_read(&it);
|
|
Find_Scope_Token_Type type = find_scope_get_token_type(flags, token->kind);
|
|
if (type == FindScopeTokenType_Open){
|
|
success = true;
|
|
position = token->pos;
|
|
if (flags & FindScope_EndOfToken){
|
|
position += token->size;
|
|
}
|
|
goto finished;
|
|
}
|
|
good_status = token_it_inc(&it);
|
|
}
|
|
}
|
|
finished:;
|
|
*end_pos_out = position;
|
|
}
|
|
return(success);
|
|
}
|
|
|
|
static b32
|
|
find_prev_scope(Application_Links *app, Buffer_ID buffer, i64 start_pos, u32 flags, i64 *end_pos_out){
|
|
b32 success = false;
|
|
Token_Array array = get_token_array_from_buffer(app, buffer);
|
|
if (array.tokens != 0){
|
|
i64 position = start_pos;
|
|
i64 token_index = token_index_from_pos(&array, start_pos);
|
|
Token_Iterator_Array it = token_iterator_index(buffer, &array, token_index);
|
|
if (HasFlag(flags, FindScope_NextSibling)){
|
|
b32 status_good = token_it_dec(&it);
|
|
i32 nest_level = -1;
|
|
for (;status_good;){
|
|
Token *token = token_it_read(&it);
|
|
Find_Scope_Token_Type type = find_scope_get_token_type(flags, token->kind);
|
|
switch (type){
|
|
case FindScopeTokenType_Open:
|
|
{
|
|
if (nest_level == -1){
|
|
position = start_pos;
|
|
goto finished;
|
|
}
|
|
else if (nest_level == 0){
|
|
success = true;
|
|
position = token->pos;
|
|
if (HasFlag(flags, FindScope_EndOfToken)){
|
|
position += token->size;
|
|
}
|
|
goto finished;
|
|
}
|
|
else{
|
|
--nest_level;
|
|
}
|
|
}break;
|
|
case FindScopeTokenType_Close:
|
|
{
|
|
++nest_level;
|
|
}break;
|
|
}
|
|
status_good = token_it_dec(&it);
|
|
}
|
|
}
|
|
else{
|
|
b32 status_good = token_it_dec(&it);
|
|
for (;status_good;){
|
|
Token *token = token_it_read(&it);
|
|
Find_Scope_Token_Type type = find_scope_get_token_type(flags, token->kind);
|
|
if (type == FindScopeTokenType_Open){
|
|
success = true;
|
|
position = token->pos;
|
|
if (HasFlag(flags, FindScope_EndOfToken)){
|
|
position += token->size;
|
|
}
|
|
goto finished;
|
|
}
|
|
status_good = token_it_dec(&it);
|
|
}
|
|
}
|
|
finished:;
|
|
*end_pos_out = position;
|
|
}
|
|
return(success);
|
|
}
|
|
|
|
static b32
|
|
find_scope_range(Application_Links *app, Buffer_ID buffer, i64 start_pos, Range_i64 *range_out, u32 flags){
|
|
b32 result = false;
|
|
Range_i64 range = {};
|
|
if (find_scope_top(app, buffer, start_pos, FindScope_Parent|flags, &range.start)){
|
|
if (find_scope_bottom(app, buffer, start_pos, FindScope_Parent|FindScope_EndOfToken|flags, &range.end)){
|
|
*range_out = range;
|
|
result = true;
|
|
}
|
|
}
|
|
return(result);
|
|
}
|
|
|
|
static void
|
|
view_set_to_region(Application_Links *app, View_ID view, i64 major_pos, i64 minor_pos){
|
|
Range_i64 range = Ii64(major_pos, minor_pos);
|
|
b32 bottom_major = false;
|
|
if (major_pos == range.max){
|
|
bottom_major = true;
|
|
}
|
|
|
|
Buffer_Cursor top = view_compute_cursor(app, view, seek_pos(range.min));
|
|
if (top.line > 0){
|
|
Buffer_Cursor bottom = view_compute_cursor(app, view, seek_pos(range.max));
|
|
if (bottom.line > 0){
|
|
Rect_f32 region = view_get_buffer_region(app, view);
|
|
f32 view_height = rect_height(region);
|
|
f32 skirt_height = view_height*.1f;
|
|
Interval_f32 acceptable_y = If32(skirt_height, view_height*.9f);
|
|
|
|
f32 target_height = view_line_y_difference(app, view, bottom.line, top.line);
|
|
|
|
if (target_height > view_height){
|
|
i64 major_line = bottom.line;
|
|
if (range.min == major_pos){
|
|
major_line = top.line;
|
|
}
|
|
|
|
Buffer_Scroll scroll = view_get_buffer_scroll(app, view);
|
|
scroll.target.line_number = major_line;
|
|
scroll.target.pixel_shift.y = -skirt_height;
|
|
view_set_buffer_scroll(app, view, scroll);
|
|
}
|
|
else{
|
|
Buffer_Scroll scroll = view_get_buffer_scroll(app, view);
|
|
Vec2_f32 top_p = view_relative_xy_of_pos(app, view, scroll.position.line_number, range.min);
|
|
top_p -= scroll.position.pixel_shift;
|
|
if (top_p.y < acceptable_y.min){
|
|
scroll.target.line_number = top.line;
|
|
scroll.target.pixel_shift.y = -skirt_height;
|
|
view_set_buffer_scroll(app, view, scroll);
|
|
}
|
|
else{
|
|
Vec2_f32 bot_p = view_relative_xy_of_pos(app, view, scroll.position.line_number, range.max);
|
|
bot_p -= scroll.position.pixel_shift;
|
|
if (bot_p.y > acceptable_y.max){
|
|
scroll.target.line_number = bottom.line;
|
|
scroll.target.pixel_shift.y = skirt_height - view_height;
|
|
view_set_buffer_scroll(app, view, scroll);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
CUSTOM_COMMAND_SIG(select_surrounding_scope)
|
|
CUSTOM_DOC("Finds the scope enclosed by '{' '}' surrounding the cursor and puts the cursor and mark on the '{' and '}'.")
|
|
{
|
|
View_ID view = get_active_view(app, AccessProtected);
|
|
Buffer_ID buffer = view_get_buffer(app, view, AccessProtected);
|
|
i64 pos = view_get_cursor_pos(app, view);
|
|
Range_i64 range = {};
|
|
if (find_scope_range(app, buffer, pos, &range, FindScope_Scope)){
|
|
view_set_cursor_and_preferred_x(app, view, seek_pos(range.first));
|
|
view_set_mark(app, view, seek_pos(range.end));
|
|
view_set_to_region(app, view, range.first, range.end);
|
|
no_mark_snap_to_cursor(app, view);
|
|
}
|
|
}
|
|
|
|
CUSTOM_COMMAND_SIG(select_next_scope_absolute)
|
|
CUSTOM_DOC("Finds the first scope started by '{' after the cursor and puts the cursor and mark on the '{' and '}'.")
|
|
{
|
|
View_ID view = get_active_view(app, AccessProtected);
|
|
Buffer_ID buffer = view_get_buffer(app, view, AccessProtected);
|
|
i64 pos = view_get_cursor_pos(app, view);
|
|
i64 start_pos = pos;
|
|
i64 top = 0;
|
|
i64 bottom = 0;
|
|
if (find_next_scope(app, buffer, start_pos, FindScope_Scope, &top)){
|
|
if (find_scope_bottom(app, buffer, top, FindScope_EndOfToken|FindScope_Scope, &bottom)){
|
|
view_set_cursor_and_preferred_x(app, view, seek_pos(top));
|
|
view_set_mark(app, view, seek_pos(bottom));
|
|
view_set_to_region(app, view, top, bottom);
|
|
no_mark_snap_to_cursor(app, view);
|
|
}
|
|
}
|
|
}
|
|
|
|
CUSTOM_COMMAND_SIG(select_prev_scope_absolute)
|
|
CUSTOM_DOC("Finds the first scope started by '{' before the cursor and puts the cursor and mark on the '{' and '}'.")
|
|
{
|
|
View_ID view = get_active_view(app, AccessProtected);
|
|
Buffer_ID buffer = view_get_buffer(app, view, AccessProtected);
|
|
i64 pos = view_get_cursor_pos(app, view);
|
|
i64 start_pos = pos;
|
|
i64 top = 0;
|
|
i64 bottom = 0;
|
|
if (find_prev_scope(app, buffer, start_pos, FindScope_Scope, &top)){
|
|
if (find_scope_bottom(app, buffer, top, FindScope_EndOfToken|FindScope_Scope, &bottom)){
|
|
view_set_cursor_and_preferred_x(app, view, seek_pos(top));
|
|
view_set_mark(app, view, seek_pos(bottom));
|
|
view_set_to_region(app, view, top, bottom);
|
|
no_mark_snap_to_cursor(app, view);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
place_begin_and_end_on_own_lines(Application_Links *app, char *begin, char *end){
|
|
View_ID view = get_active_view(app, AccessOpen);
|
|
Buffer_ID buffer = view_get_buffer(app, view, AccessOpen);
|
|
|
|
Range_i64 range = get_view_range(app, view);
|
|
Range_i64 lines = get_line_range_from_pos_range(app, buffer, range);
|
|
range = get_pos_range_from_line_range(app, buffer, lines);
|
|
|
|
Scratch_Block scratch(app);
|
|
|
|
b32 min_line_blank = line_is_valid_and_blank(app, buffer, lines.min);
|
|
b32 max_line_blank = line_is_valid_and_blank(app, buffer, lines.max);
|
|
|
|
if ((lines.min < lines.max) || (!min_line_blank)){
|
|
String_Const_u8 begin_str = {};
|
|
String_Const_u8 end_str = {};
|
|
|
|
i64 min_adjustment = 0;
|
|
i64 max_adjustment = 0;
|
|
|
|
if (min_line_blank){
|
|
begin_str = push_u8_stringf(scratch, "\n%s", begin);
|
|
min_adjustment += 1;
|
|
}
|
|
else{
|
|
begin_str = push_u8_stringf(scratch, "%s\n", begin);
|
|
}
|
|
if (max_line_blank){
|
|
end_str = push_u8_stringf(scratch, "%s\n", end);
|
|
}
|
|
else{
|
|
end_str = push_u8_stringf(scratch, "\n%s", end);
|
|
max_adjustment += 1;
|
|
}
|
|
|
|
max_adjustment += begin_str.size;
|
|
Range_i64 new_pos = Ii64(range.min + min_adjustment, range.max + max_adjustment);
|
|
|
|
History_Group group = history_group_begin(app, buffer);
|
|
buffer_replace_range(app, buffer, Ii64(range.min), begin_str);
|
|
buffer_replace_range(app, buffer, Ii64(range.max + begin_str.size), end_str);
|
|
history_group_end(group);
|
|
|
|
set_view_range(app, view, new_pos);
|
|
}
|
|
else{
|
|
String_Const_u8 str = push_u8_stringf(scratch, "%s\n\n%s", begin, end);
|
|
buffer_replace_range(app, buffer, range, str);
|
|
i64 center_pos = range.min + cstring_length(begin) + 1;
|
|
view_set_cursor_and_preferred_x(app, view, seek_pos(center_pos));
|
|
view_set_mark(app, view, seek_pos(center_pos));
|
|
}
|
|
}
|
|
|
|
CUSTOM_COMMAND_SIG(place_in_scope)
|
|
CUSTOM_DOC("Wraps the code contained in the range between cursor and mark with a new curly brace scope.")
|
|
{
|
|
place_begin_and_end_on_own_lines(app, "{", "}");
|
|
}
|
|
|
|
CUSTOM_COMMAND_SIG(delete_current_scope)
|
|
CUSTOM_DOC("Deletes the braces surrounding the currently selected scope. Leaves the contents within the scope.")
|
|
{
|
|
View_ID view = get_active_view(app, AccessOpen);
|
|
Buffer_ID buffer = view_get_buffer(app, view, AccessOpen);
|
|
|
|
Range_i64 range = get_view_range(app, view);
|
|
if (buffer_get_char(app, buffer, range.min) == '{' &&
|
|
buffer_get_char(app, buffer, range.max - 1) == '}'){
|
|
i32 top_len = 1;
|
|
i32 bot_len = 1;
|
|
if (buffer_get_char(app, buffer, range.min - 1) == '\n'){
|
|
top_len = 2;
|
|
}
|
|
if (buffer_get_char(app, buffer, range.max + 1) == '\n'){
|
|
bot_len = 2;
|
|
}
|
|
|
|
Batch_Edit batch_first = {};
|
|
Batch_Edit batch_last = {};
|
|
|
|
batch_first.edit.text = SCu8();
|
|
batch_first.edit.range = Ii64(range.min + 1 - top_len, range.min + 1);
|
|
batch_first.next = &batch_last;
|
|
batch_last.edit.text = SCu8();
|
|
batch_last.edit.range = Ii64((i32)(range.max - 1), (i32)(range.max - 1 + bot_len));
|
|
|
|
buffer_batch_edit(app, buffer, &batch_first);
|
|
}
|
|
}
|
|
|
|
// BOTTOM
|
|
|