1116 lines
40 KiB
C++
1116 lines
40 KiB
C++
/*
|
|
4coder_scope_commands.cpp - A set of commands and helpers relevant for scope level navigation and editing.
|
|
*/
|
|
|
|
// TOP
|
|
|
|
static bool32 parse_statement_down(Application_Links *app, Statement_Parser *parser, Cpp_Token *token_out);
|
|
static float scope_center_threshold = 0.75f;
|
|
|
|
////////////////////////////////
|
|
|
|
static Find_Scope_Token_Type
|
|
find_scope_get_token_type(uint32_t flags, Cpp_Token_Type token_type){
|
|
Find_Scope_Token_Type type = FindScopeTokenType_None;
|
|
if (flags & FindScope_Brace){
|
|
switch (token_type){
|
|
case CPP_TOKEN_BRACE_OPEN:
|
|
{
|
|
type = FindScopeTokenType_Open;
|
|
}break;
|
|
case CPP_TOKEN_BRACE_CLOSE:
|
|
{
|
|
type = FindScopeTokenType_Close;
|
|
}break;
|
|
}
|
|
}
|
|
if (flags & FindScope_Paren){
|
|
switch (token_type){
|
|
case CPP_TOKEN_PARENTHESE_OPEN:
|
|
{
|
|
type = FindScopeTokenType_Open;
|
|
}break;
|
|
case CPP_TOKEN_PARENTHESE_CLOSE:
|
|
{
|
|
type = FindScopeTokenType_Close;
|
|
}break;
|
|
}
|
|
}
|
|
return(type);
|
|
}
|
|
|
|
#if 0
|
|
static bool32
|
|
find_scope_top(Application_Links *app, Buffer_Summary *buffer, int32_t start_pos, uint32_t flags, int32_t *end_pos_out){
|
|
Cpp_Get_Token_Result get_result = {};
|
|
|
|
bool32 success = false;
|
|
int32_t position = 0;
|
|
|
|
if (buffer_get_token_index(app, buffer, start_pos, &get_result)){
|
|
int32_t token_index = get_result.token_index;
|
|
if (flags & FindScope_Parent){
|
|
--token_index;
|
|
if (get_result.in_whitespace){
|
|
++token_index;
|
|
}
|
|
}
|
|
|
|
if (token_index >= 0){
|
|
static const int32_t chunk_cap = 512;
|
|
Cpp_Token chunk[chunk_cap];
|
|
Stream_Tokens stream = {};
|
|
|
|
if (init_stream_tokens(&stream, app, buffer, token_index, chunk, chunk_cap)){int32_t nest_level = 0;
|
|
bool32 still_looping = false;
|
|
do{
|
|
for (; token_index >= stream.start; --token_index){
|
|
Cpp_Token *token = &stream.tokens[token_index];
|
|
Find_Scope_Token_Type type = find_scope_get_token_type(flags, token->type);
|
|
switch (type){
|
|
case FindScopeTokenType_Open:
|
|
{
|
|
if (nest_level == 0){
|
|
success = true;
|
|
position = token->start;
|
|
if (flags & FindScope_EndOfToken){
|
|
position += token->size;
|
|
}
|
|
goto finished;
|
|
}
|
|
else{
|
|
--nest_level;
|
|
}
|
|
}break;
|
|
case FindScopeTokenType_Close:
|
|
{
|
|
++nest_level;
|
|
}break;
|
|
}
|
|
}
|
|
still_looping = backward_stream_tokens(&stream);
|
|
}while(still_looping);
|
|
}
|
|
}
|
|
}
|
|
|
|
finished:;
|
|
*end_pos_out = position;
|
|
return(success);
|
|
}
|
|
#endif
|
|
|
|
static bool32
|
|
find_scope_top(Application_Links *app, Buffer_Summary *buffer, int32_t start_pos, uint32_t flags, int32_t *end_pos_out){
|
|
Cpp_Get_Token_Result get_result = {};
|
|
|
|
bool32 success = false;
|
|
int32_t position = 0;
|
|
|
|
if (buffer_get_token_index(app, buffer, start_pos, &get_result)){
|
|
int32_t token_index = get_result.token_index;
|
|
if (flags & FindScope_Parent){
|
|
--token_index;
|
|
if (get_result.in_whitespace){
|
|
++token_index;
|
|
}
|
|
}
|
|
|
|
if (token_index >= 0){
|
|
Token_Range token_range = buffer_get_token_range(app, buffer->buffer_id);
|
|
|
|
if (token_range.first != 0){
|
|
Token_Iterator token_it = make_token_iterator(token_range, token_index);
|
|
|
|
int32_t nest_level = 0;
|
|
for (Cpp_Token *token = token_iterator_current(&token_it);
|
|
token != 0;
|
|
token = token_iterator_goto_prev(&token_it)){
|
|
Find_Scope_Token_Type type = find_scope_get_token_type(flags, token->type);
|
|
switch (type){
|
|
case FindScopeTokenType_Open:
|
|
{
|
|
if (nest_level == 0){
|
|
success = true;
|
|
position = token->start;
|
|
if (flags & FindScope_EndOfToken){
|
|
position += token->size;
|
|
}
|
|
goto finished;
|
|
}
|
|
else{
|
|
--nest_level;
|
|
}
|
|
}break;
|
|
case FindScopeTokenType_Close:
|
|
{
|
|
++nest_level;
|
|
}break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
finished:;
|
|
*end_pos_out = position;
|
|
return(success);
|
|
}
|
|
|
|
#if 0
|
|
static bool32
|
|
find_scope_bottom(Application_Links *app, Buffer_Summary *buffer, int32_t start_pos, uint32_t flags, int32_t *end_pos_out){
|
|
Cpp_Get_Token_Result get_result = {};
|
|
|
|
bool32 success = false;
|
|
int32_t position = 0;
|
|
|
|
if (buffer_get_token_index(app, buffer, start_pos, &get_result)){
|
|
int32_t token_index = get_result.token_index+1;
|
|
if (flags & FindScope_Parent){
|
|
--token_index;
|
|
if (get_result.in_whitespace){
|
|
++token_index;
|
|
}
|
|
}
|
|
|
|
if (token_index >= 0){
|
|
static const int32_t chunk_cap = 512;
|
|
Cpp_Token chunk[chunk_cap];
|
|
Stream_Tokens stream = {};
|
|
|
|
if (init_stream_tokens(&stream, app, buffer, token_index, chunk, chunk_cap)){
|
|
int32_t nest_level = 0;
|
|
bool32 still_looping = false;
|
|
do{
|
|
for (; token_index < stream.end; ++token_index){
|
|
Cpp_Token *token = &stream.tokens[token_index];
|
|
Find_Scope_Token_Type type = find_scope_get_token_type(flags, token->type);
|
|
switch (type){
|
|
case FindScopeTokenType_Open:
|
|
{
|
|
++nest_level;
|
|
}break;
|
|
case FindScopeTokenType_Close:
|
|
{
|
|
if (nest_level == 0){
|
|
success = true;
|
|
position = token->start;
|
|
if (flags & FindScope_EndOfToken){
|
|
position += token->size;
|
|
}
|
|
goto finished;
|
|
}
|
|
else{
|
|
--nest_level;
|
|
}
|
|
}break;
|
|
}
|
|
}
|
|
still_looping = forward_stream_tokens(&stream);
|
|
}while(still_looping);
|
|
}
|
|
}
|
|
}
|
|
|
|
finished:;
|
|
*end_pos_out = position;
|
|
return(success);
|
|
}
|
|
#endif
|
|
|
|
static bool32
|
|
find_scope_bottom(Application_Links *app, Buffer_Summary *buffer, int32_t start_pos, uint32_t flags, int32_t *end_pos_out){
|
|
Cpp_Get_Token_Result get_result = {};
|
|
|
|
bool32 success = false;
|
|
int32_t position = 0;
|
|
|
|
if (buffer_get_token_index(app, buffer, start_pos, &get_result)){
|
|
int32_t token_index = get_result.token_index + 1;
|
|
if (flags & FindScope_Parent){
|
|
--token_index;
|
|
if (get_result.in_whitespace){
|
|
++token_index;
|
|
}
|
|
}
|
|
|
|
if (token_index >= 0){
|
|
Token_Range token_range = buffer_get_token_range(app, buffer->buffer_id);
|
|
|
|
if (token_range.first != 0){
|
|
Token_Iterator token_it = make_token_iterator(token_range, token_index);
|
|
|
|
int32_t nest_level = 0;
|
|
for (Cpp_Token *token = token_iterator_current(&token_it);
|
|
token != 0;
|
|
token = token_iterator_goto_next(&token_it)){
|
|
Find_Scope_Token_Type type = find_scope_get_token_type(flags, token->type);
|
|
switch (type){
|
|
case FindScopeTokenType_Open:
|
|
{
|
|
++nest_level;
|
|
}break;
|
|
case FindScopeTokenType_Close:
|
|
{
|
|
if (nest_level == 0){
|
|
success = true;
|
|
position = token->start;
|
|
if (flags & FindScope_EndOfToken){
|
|
position += token->size;
|
|
}
|
|
goto finished;
|
|
}
|
|
else{
|
|
--nest_level;
|
|
}
|
|
}break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
finished:;
|
|
*end_pos_out = position;
|
|
return(success);
|
|
}
|
|
|
|
#if 0
|
|
static bool32
|
|
find_next_scope(Application_Links *app, Buffer_Summary *buffer, int32_t start_pos, uint32_t flags, int32_t *end_pos_out){
|
|
Cpp_Get_Token_Result get_result = {};
|
|
|
|
bool32 success = 0;
|
|
int32_t position = 0;
|
|
|
|
if (buffer_get_token_index(app, buffer, start_pos, &get_result)){
|
|
int32_t token_index = get_result.token_index+1;
|
|
|
|
if (token_index >= 0){
|
|
static const int32_t chunk_cap = 512;
|
|
Cpp_Token chunk[chunk_cap];
|
|
Stream_Tokens stream = {};
|
|
|
|
if (init_stream_tokens(&stream, app, buffer, token_index, chunk, chunk_cap)){
|
|
if (flags & FindScope_NextSibling){
|
|
int32_t nest_level = 1;
|
|
|
|
bool32 still_looping = false;
|
|
do{
|
|
for (; token_index < stream.end; ++token_index){
|
|
Cpp_Token *token = &stream.tokens[token_index];
|
|
Find_Scope_Token_Type type = find_scope_get_token_type(flags, token->type);
|
|
switch (type){
|
|
case FindScopeTokenType_Open:
|
|
{
|
|
if (nest_level == 0){
|
|
success = 1;
|
|
position = token->start;
|
|
if (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;
|
|
}
|
|
}
|
|
still_looping = forward_stream_tokens(&stream);
|
|
}while(still_looping);
|
|
}
|
|
else{
|
|
bool32 still_looping = false;
|
|
do{
|
|
for (; token_index < stream.end; ++token_index){
|
|
Cpp_Token *token = &stream.tokens[token_index];
|
|
Find_Scope_Token_Type type = find_scope_get_token_type(flags, token->type);
|
|
if (type == FindScopeTokenType_Open){
|
|
success = 1;
|
|
position = token->start;
|
|
if (flags & FindScope_EndOfToken){
|
|
position += token->size;
|
|
}
|
|
goto finished;
|
|
}
|
|
}
|
|
still_looping = forward_stream_tokens(&stream);
|
|
}while(still_looping);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
finished:;
|
|
*end_pos_out = position;
|
|
return(success);
|
|
}
|
|
#endif
|
|
|
|
static bool32
|
|
find_next_scope(Application_Links *app, Buffer_Summary *buffer, int32_t start_pos, uint32_t flags, int32_t *end_pos_out){
|
|
Cpp_Get_Token_Result get_result = {};
|
|
|
|
bool32 success = 0;
|
|
int32_t position = 0;
|
|
|
|
if (buffer_get_token_index(app, buffer, start_pos, &get_result)){
|
|
int32_t token_index = get_result.token_index+1;
|
|
|
|
if (token_index >= 0){
|
|
Token_Range token_range = buffer_get_token_range(app, buffer->buffer_id);
|
|
|
|
if (token_range.first != 0){
|
|
Token_Iterator token_it = make_token_iterator(token_range, token_index);
|
|
|
|
if ((flags & FindScope_NextSibling) != 0){
|
|
int32_t nest_level = 1;
|
|
for (Cpp_Token *token = token_iterator_current(&token_it);
|
|
token != 0;
|
|
token = token_iterator_goto_next(&token_it)){
|
|
Find_Scope_Token_Type type = find_scope_get_token_type(flags, token->type);
|
|
switch (type){
|
|
case FindScopeTokenType_Open:
|
|
{
|
|
if (nest_level == 0){
|
|
success = 1;
|
|
position = token->start;
|
|
if (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;
|
|
}
|
|
}
|
|
}
|
|
|
|
else{
|
|
for (Cpp_Token *token = token_iterator_current(&token_it);
|
|
token != 0;
|
|
token = token_iterator_goto_next(&token_it)){
|
|
Find_Scope_Token_Type type = find_scope_get_token_type(flags, token->type);
|
|
if (type == FindScopeTokenType_Open){
|
|
success = 1;
|
|
position = token->start;
|
|
if (flags & FindScope_EndOfToken){
|
|
position += token->size;
|
|
}
|
|
goto finished;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
finished:;
|
|
*end_pos_out = position;
|
|
return(success);
|
|
}
|
|
|
|
#if 0
|
|
static bool32
|
|
find_prev_scope(Application_Links *app, Buffer_Summary *buffer, int32_t start_pos, uint32_t flags, int32_t *end_pos_out){
|
|
Cpp_Get_Token_Result get_result = {};
|
|
|
|
bool32 success = 0;
|
|
int32_t position = 0;
|
|
|
|
if (buffer_get_token_index(app, buffer, start_pos, &get_result)){
|
|
int32_t token_index = get_result.token_index-1;
|
|
|
|
if (token_index >= 0){
|
|
static const int32_t chunk_cap = 512;
|
|
Cpp_Token chunk[chunk_cap];
|
|
Stream_Tokens stream = {};
|
|
|
|
if (init_stream_tokens(&stream, app, buffer, token_index, chunk, chunk_cap)){
|
|
if (flags & FindScope_NextSibling){
|
|
int32_t nest_level = -1;
|
|
bool32 still_looping = 0;
|
|
do{
|
|
for (; token_index >= stream.start; --token_index){
|
|
Cpp_Token *token = &stream.tokens[token_index];
|
|
Find_Scope_Token_Type type = find_scope_get_token_type(flags, token->type);
|
|
switch (type){
|
|
case FindScopeTokenType_Open:
|
|
{
|
|
if (nest_level == -1){
|
|
position = start_pos;
|
|
goto finished;
|
|
}
|
|
else if (nest_level == 0){
|
|
success = 1;
|
|
position = token->start;
|
|
if (flags & FindScope_EndOfToken){
|
|
position += token->size;
|
|
}
|
|
goto finished;
|
|
}
|
|
else{
|
|
--nest_level;
|
|
}
|
|
}break;
|
|
case FindScopeTokenType_Close:
|
|
{
|
|
++nest_level;
|
|
}break;
|
|
}
|
|
}
|
|
still_looping = backward_stream_tokens(&stream);
|
|
}while(still_looping);
|
|
}
|
|
else{
|
|
bool32 still_looping = 0;
|
|
do{
|
|
for (; token_index >= stream.start; --token_index){
|
|
Cpp_Token *token = &stream.tokens[token_index];
|
|
Find_Scope_Token_Type type = find_scope_get_token_type(flags, token->type);
|
|
if (type == FindScopeTokenType_Open){
|
|
success = 1;
|
|
position = token->start;
|
|
if (flags & FindScope_EndOfToken){
|
|
position += token->size;
|
|
}
|
|
goto finished;
|
|
}
|
|
}
|
|
still_looping = backward_stream_tokens(&stream);
|
|
}while(still_looping);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
finished:;
|
|
*end_pos_out = position;
|
|
return(success);
|
|
}
|
|
#endif
|
|
|
|
static bool32
|
|
find_prev_scope(Application_Links *app, Buffer_Summary *buffer, int32_t start_pos, uint32_t flags, int32_t *end_pos_out){
|
|
Cpp_Get_Token_Result get_result = {};
|
|
|
|
bool32 success = false;
|
|
int32_t position = 0;
|
|
|
|
if (buffer_get_token_index(app, buffer, start_pos, &get_result)){
|
|
int32_t token_index = get_result.token_index-1;
|
|
|
|
if (token_index >= 0){
|
|
Token_Range token_range = buffer_get_token_range(app, buffer->buffer_id);
|
|
|
|
if (token_range.first != 0){
|
|
Token_Iterator token_it = make_token_iterator(token_range, token_index);
|
|
|
|
if (flags & FindScope_NextSibling){
|
|
int32_t nest_level = -1;
|
|
for (Cpp_Token *token = token_iterator_current(&token_it);
|
|
token != 0;
|
|
token = token_iterator_goto_prev(&token_it)){
|
|
Find_Scope_Token_Type type = find_scope_get_token_type(flags, token->type);
|
|
switch (type){
|
|
case FindScopeTokenType_Open:
|
|
{
|
|
if (nest_level == -1){
|
|
position = start_pos;
|
|
goto finished;
|
|
}
|
|
else if (nest_level == 0){
|
|
success = true;
|
|
position = token->start;
|
|
if (flags & FindScope_EndOfToken){
|
|
position += token->size;
|
|
}
|
|
goto finished;
|
|
}
|
|
else{
|
|
--nest_level;
|
|
}
|
|
}break;
|
|
case FindScopeTokenType_Close:
|
|
{
|
|
++nest_level;
|
|
}break;
|
|
}
|
|
}
|
|
}
|
|
|
|
else{
|
|
for (Cpp_Token *token = token_iterator_current(&token_it);
|
|
token != 0;
|
|
token = token_iterator_goto_prev(&token_it)){
|
|
Find_Scope_Token_Type type = find_scope_get_token_type(flags, token->type);
|
|
if (type == FindScopeTokenType_Open){
|
|
success = true;
|
|
position = token->start;
|
|
if (flags & FindScope_EndOfToken){
|
|
position += token->size;
|
|
}
|
|
goto finished;
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
finished:;
|
|
*end_pos_out = position;
|
|
return(success);
|
|
}
|
|
|
|
static bool32
|
|
find_scope_range(Application_Links *app, Buffer_Summary *buffer, int32_t start_pos, Range *range_out,
|
|
uint32_t flags){
|
|
Range 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;
|
|
return(true);
|
|
}
|
|
}
|
|
return(false);
|
|
}
|
|
|
|
static void
|
|
view_set_to_region(Application_Links *app, View_Summary *view, int32_t major_pos, int32_t minor_pos, float normalized_threshold){
|
|
Range range = make_range(major_pos, minor_pos);
|
|
bool32 bottom_major = false;
|
|
if (major_pos == range.max){
|
|
bottom_major = true;
|
|
}
|
|
|
|
Full_Cursor top = {};
|
|
Full_Cursor bottom = {};
|
|
if (view_compute_cursor(app, view, seek_pos(range.min), &top)){
|
|
if (view_compute_cursor(app, view, seek_pos(range.max), &bottom)){
|
|
float top_y = top.wrapped_y;
|
|
float bottom_y = bottom.wrapped_y;
|
|
if (view->unwrapped_lines){
|
|
top_y = top.unwrapped_y;
|
|
bottom_y = bottom.unwrapped_y;
|
|
}
|
|
|
|
GUI_Scroll_Vars scroll = view->scroll_vars;
|
|
float half_view_height = .5f*(float)(view->file_region.y1 - view->file_region.y0);
|
|
float threshold = normalized_threshold * half_view_height;
|
|
float current_center_y = ((float)scroll.target_y) + half_view_height;
|
|
|
|
if (top_y < current_center_y - threshold || bottom_y > current_center_y + threshold){
|
|
float center_target_y = .5f*(top_y + bottom_y);
|
|
|
|
if (bottom_major){
|
|
if (center_target_y < bottom_y - half_view_height * .9f){
|
|
center_target_y = bottom_y - half_view_height * .9f;
|
|
}
|
|
}
|
|
else{
|
|
if (center_target_y > top_y + half_view_height * .9f){
|
|
center_target_y = top_y + half_view_height * .9f;
|
|
}
|
|
}
|
|
|
|
float target_y = center_target_y - half_view_height;
|
|
if (target_y < 0){
|
|
target_y = 0;
|
|
}
|
|
|
|
scroll.target_y = (int32_t)(target_y);
|
|
view_set_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 '}'.")
|
|
{
|
|
uint32_t access = AccessProtected;
|
|
View_Summary view = get_active_view(app, access);
|
|
Buffer_Summary buffer = get_buffer(app, view.buffer_id, access);
|
|
|
|
Range range = {};
|
|
if (find_scope_range(app, &buffer, view.cursor.pos, &range, FindScope_Brace)){
|
|
view_set_cursor(app, &view, seek_pos(range.first), true);
|
|
view_set_mark(app, &view, seek_pos(range.end));
|
|
view_set_to_region(app, &view, range.first, range.end, scope_center_threshold);
|
|
no_mark_snap_to_cursor(app, view.view_id);
|
|
}
|
|
}
|
|
|
|
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 '}'.")
|
|
{
|
|
uint32_t access = AccessProtected;
|
|
View_Summary view = get_active_view(app, access);
|
|
Buffer_Summary buffer = get_buffer(app, view.buffer_id, access);
|
|
|
|
int32_t start_pos = view.cursor.pos;
|
|
int32_t top = 0;
|
|
int32_t bottom = 0;
|
|
if (find_next_scope(app, &buffer, start_pos, FindScope_Brace, &top)){
|
|
if (find_scope_bottom(app, &buffer, top, FindScope_EndOfToken|FindScope_Brace, &bottom)){
|
|
view_set_cursor(app, &view, seek_pos(top), true);
|
|
view_set_mark(app, &view, seek_pos(bottom));
|
|
view_set_to_region(app, &view, top, bottom, scope_center_threshold);
|
|
no_mark_snap_to_cursor(app, view.view_id);
|
|
}
|
|
}
|
|
}
|
|
|
|
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 '}'.")
|
|
{
|
|
uint32_t access = AccessProtected;
|
|
View_Summary view = get_active_view(app, access);
|
|
Buffer_Summary buffer = get_buffer(app, view.buffer_id, access);
|
|
|
|
int32_t start_pos = view.cursor.pos;
|
|
int32_t top = 0, bottom = 0;
|
|
if (find_prev_scope(app, &buffer, start_pos, FindScope_Brace, &top)){
|
|
if (find_scope_bottom(app, &buffer, top, FindScope_EndOfToken|FindScope_Brace, &bottom)){
|
|
view_set_cursor(app, &view, seek_pos(top), true);
|
|
view_set_mark(app, &view, seek_pos(bottom));
|
|
view_set_to_region(app, &view, top, bottom, scope_center_threshold);
|
|
no_mark_snap_to_cursor(app, view.view_id);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
place_begin_and_end_on_own_lines(Application_Links *app, Partition *scratch, char *begin, char *end){
|
|
View_Summary view = get_active_view(app, AccessOpen);
|
|
Buffer_Summary buffer = get_buffer(app, view.buffer_id, AccessOpen);
|
|
|
|
Range lines = {};
|
|
Range range = get_view_range(&view);
|
|
lines.min = buffer_get_line_number(app, &buffer, range.min);
|
|
lines.max = buffer_get_line_number(app, &buffer, range.max);
|
|
range.min = buffer_get_line_start(app, &buffer, lines.min);
|
|
range.max = buffer_get_line_end(app, &buffer, lines.max);
|
|
|
|
bool32 do_full = (lines.min < lines.max) || (!buffer_line_is_blank(app, &buffer, lines.min));
|
|
|
|
Temp_Memory temp = begin_temp_memory(scratch);
|
|
int32_t begin_len = str_size(begin);
|
|
int32_t end_len = str_size(end);
|
|
|
|
int32_t str_size = begin_len + end_len + 2;
|
|
char *str = push_array(scratch, char, str_size);
|
|
String builder = make_string_cap(str, 0, str_size);
|
|
append(&builder, begin);
|
|
append(&builder, "\n");
|
|
append(&builder, "\n");
|
|
append(&builder, end);
|
|
|
|
if (do_full){
|
|
Buffer_Edit edits[2];
|
|
|
|
int32_t min_adjustment = 0;
|
|
int32_t max_adjustment = 4;
|
|
|
|
if (buffer_line_is_blank(app, &buffer, lines.min)){
|
|
memmove(str + 1, str, begin_len);
|
|
str[0] = '\n';
|
|
++min_adjustment;
|
|
}
|
|
|
|
if (buffer_line_is_blank(app, &buffer, lines.max)){
|
|
memmove(str + begin_len + 1, str + begin_len + 2, end_len);
|
|
str[begin_len + end_len + 1] = '\n';
|
|
--max_adjustment;
|
|
}
|
|
|
|
int32_t min_pos = range.min + min_adjustment;
|
|
int32_t max_pos = range.max + max_adjustment;
|
|
|
|
int32_t cursor_pos = min_pos;
|
|
int32_t mark_pos = max_pos;
|
|
|
|
if (view.cursor.pos > view.mark.pos){
|
|
cursor_pos = max_pos;
|
|
mark_pos = min_pos;
|
|
}
|
|
|
|
edits[0].str_start = 0;
|
|
edits[0].len = begin_len + 1;
|
|
edits[0].start = range.min;
|
|
edits[0].end = range.min;
|
|
|
|
edits[1].str_start = begin_len + 1;
|
|
edits[1].len = end_len + 1;
|
|
edits[1].start = range.max;
|
|
edits[1].end = range.max;
|
|
|
|
buffer_batch_edit(app, &buffer, str, str_size, edits, 2, BatchEdit_Normal);
|
|
|
|
view_set_cursor(app, &view, seek_pos(cursor_pos), true);
|
|
view_set_mark(app, &view, seek_pos(mark_pos));
|
|
}
|
|
else{
|
|
buffer_replace_range(app, &buffer, range.min, range.max, str, str_size);
|
|
int32_t center_pos = range.min + begin_len + 1;
|
|
view_set_cursor(app, &view, seek_pos(center_pos), true);
|
|
view_set_mark(app, &view, seek_pos(center_pos));
|
|
}
|
|
|
|
end_temp_memory(temp);
|
|
}
|
|
|
|
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, &global_part, "{", "}");
|
|
}
|
|
|
|
CUSTOM_COMMAND_SIG(delete_current_scope)
|
|
CUSTOM_DOC("Deletes the braces surrounding the currently selected scope. Leaves the contents within the scope.")
|
|
{
|
|
uint32_t access = AccessOpen;
|
|
View_Summary view = get_active_view(app, access);
|
|
Buffer_Summary buffer = get_buffer(app, view.buffer_id, access);
|
|
|
|
int32_t top = view.cursor.pos;
|
|
int32_t bottom = view.mark.pos;
|
|
|
|
if (top > bottom){
|
|
int32_t x = top;
|
|
top = bottom;
|
|
bottom = x;
|
|
}
|
|
|
|
if (buffer_get_char(app, &buffer, top) == '{' && buffer_get_char(app, &buffer, bottom-1) == '}'){
|
|
int32_t top_len = 1;
|
|
int32_t bottom_len = 1;
|
|
if (buffer_get_char(app, &buffer, top-1) == '\n'){
|
|
top_len = 2;
|
|
}
|
|
if (buffer_get_char(app, &buffer, bottom+1) == '\n'){
|
|
bottom_len = 2;
|
|
}
|
|
|
|
Buffer_Edit edits[2];
|
|
edits[0].str_start = 0;
|
|
edits[0].len = 0;
|
|
edits[0].start = top+1 - top_len;
|
|
edits[0].end = top+1;
|
|
|
|
edits[1].str_start = 0;
|
|
edits[1].len = 0;
|
|
edits[1].start = bottom-1;
|
|
edits[1].end = bottom-1 + bottom_len;
|
|
|
|
buffer_batch_edit(app, &buffer, 0, 0, edits, 2, BatchEdit_Normal);
|
|
}
|
|
}
|
|
|
|
static Cpp_Token*
|
|
parser_next_token(Statement_Parser *parser){
|
|
return(token_iterator_goto_next(&parser->token_iterator));
|
|
}
|
|
|
|
static bool32
|
|
parse_for_down(Application_Links *app, Statement_Parser *parser, Cpp_Token *token_out){
|
|
bool32 success = false;
|
|
Cpp_Token *token = parser_next_token(parser);
|
|
|
|
int32_t paren_level = 0;
|
|
while (token != 0){
|
|
if (!(token->flags & CPP_TFLAG_PP_BODY)){
|
|
switch (token->type){
|
|
case CPP_TOKEN_PARENTHESE_OPEN:
|
|
{
|
|
++paren_level;
|
|
}break;
|
|
|
|
case CPP_TOKEN_PARENTHESE_CLOSE:
|
|
{
|
|
--paren_level;
|
|
if (paren_level == 0){
|
|
success = parse_statement_down(app, parser, token_out);
|
|
goto finished;
|
|
}
|
|
else if (paren_level < 0){
|
|
success = false;
|
|
goto finished;
|
|
}
|
|
}break;
|
|
}
|
|
}
|
|
|
|
token = parser_next_token(parser);
|
|
}
|
|
|
|
finished:;
|
|
return(success);
|
|
}
|
|
|
|
static bool32
|
|
parse_if_down(Application_Links *app, Statement_Parser *parser, Cpp_Token *token_out){
|
|
bool32 success = false;
|
|
Cpp_Token *token = parser_next_token(parser);
|
|
|
|
if (token != 0){
|
|
success = parse_statement_down(app, parser, token_out);
|
|
if (success){
|
|
token = parser_next_token(parser);
|
|
if (token != 0 && token->type == CPP_TOKEN_KEY_CONTROL_FLOW){
|
|
char lexeme[32];
|
|
if (sizeof(lexeme)-1 >= token->size){
|
|
if (buffer_read_range(app, parser->buffer, token->start, token->start + token->size, lexeme)){
|
|
lexeme[token->size] = 0;
|
|
if (match(lexeme, "else")){
|
|
success = parse_statement_down(app, parser, token_out);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return(success);
|
|
}
|
|
|
|
static bool32
|
|
parse_block_down(Application_Links *app, Statement_Parser *parser, Cpp_Token *token_out){
|
|
bool32 success = false;
|
|
Cpp_Token *token = parser_next_token(parser);
|
|
|
|
int32_t nest_level = 0;
|
|
while (token != 0){
|
|
switch (token->type){
|
|
case CPP_TOKEN_BRACE_OPEN:
|
|
{
|
|
++nest_level;
|
|
}break;
|
|
|
|
case CPP_TOKEN_BRACE_CLOSE:
|
|
{
|
|
if (nest_level == 0){
|
|
*token_out = *token;
|
|
success = true;
|
|
goto finished;
|
|
}
|
|
--nest_level;
|
|
}break;
|
|
}
|
|
token = parser_next_token(parser);
|
|
}
|
|
|
|
finished:;
|
|
return(success);
|
|
}
|
|
|
|
static bool32
|
|
parse_statement_down(Application_Links *app, Statement_Parser *parser, Cpp_Token *token_out){
|
|
bool32 success = false;
|
|
Cpp_Token *token = parser_next_token(parser);
|
|
|
|
if (token != 0){
|
|
bool32 not_getting_block = false;
|
|
|
|
do{
|
|
switch (token->type){
|
|
case CPP_TOKEN_BRACE_CLOSE:
|
|
{
|
|
goto finished;
|
|
}break;
|
|
|
|
case CPP_TOKEN_KEY_CONTROL_FLOW:
|
|
{
|
|
char lexeme[32];
|
|
if (sizeof(lexeme)-1 >= token->size){
|
|
if (buffer_read_range(app, parser->buffer, token->start, token->start + token->size, lexeme)){
|
|
lexeme[token->size] = 0;
|
|
if (match(lexeme, "for")){
|
|
success = parse_for_down(app, parser, token_out);
|
|
goto finished;
|
|
}
|
|
else if (match(lexeme, "if")){
|
|
success = parse_if_down(app, parser, token_out);
|
|
goto finished;
|
|
}
|
|
else if (match(lexeme, "else")){
|
|
success = false;
|
|
goto finished;
|
|
}
|
|
}
|
|
}
|
|
}break;
|
|
|
|
case CPP_TOKEN_BRACE_OPEN:
|
|
{
|
|
if (!not_getting_block){
|
|
success = parse_block_down(app, parser, token_out);
|
|
goto finished;
|
|
}
|
|
}break;
|
|
|
|
case CPP_TOKEN_SEMICOLON:
|
|
{
|
|
success = true;
|
|
*token_out = *token;
|
|
goto finished;
|
|
}break;
|
|
|
|
case CPP_TOKEN_EQ:
|
|
{
|
|
not_getting_block = true;
|
|
}break;
|
|
}
|
|
|
|
token = parser_next_token(parser);
|
|
}while(token != 0);
|
|
}
|
|
|
|
finished:;
|
|
return(success);
|
|
}
|
|
|
|
static Statement_Parser
|
|
make_statement_parser(Application_Links *app, Buffer_Summary *buffer, int32_t token_index){
|
|
Statement_Parser parser = {};
|
|
Token_Range token_range = buffer_get_token_range(app, buffer->buffer_id);
|
|
if (token_range.first != 0){
|
|
parser.token_iterator = make_token_iterator(token_range, token_index);
|
|
parser.buffer = buffer;
|
|
}
|
|
return(parser);
|
|
}
|
|
|
|
static bool32
|
|
find_whole_statement_down(Application_Links *app, Buffer_Summary *buffer, int32_t pos, int32_t *start_out, int32_t *end_out){
|
|
bool32 result = false;
|
|
int32_t start = pos;
|
|
int32_t end = start;
|
|
|
|
Cpp_Get_Token_Result get_result = {};
|
|
|
|
if (buffer_get_token_index(app, buffer, pos, &get_result)){
|
|
Statement_Parser parser = make_statement_parser(app, buffer, get_result.token_index);
|
|
if (parser.buffer != 0){
|
|
if (get_result.in_whitespace){
|
|
parser_next_token(&parser);
|
|
}
|
|
|
|
Cpp_Token end_token = {};
|
|
if (parse_statement_down(app, &parser, &end_token)){
|
|
end = end_token.start + end_token.size;
|
|
result = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
*start_out = start;
|
|
*end_out = end;
|
|
return(result);
|
|
}
|
|
|
|
CUSTOM_COMMAND_SIG(scope_absorb_down)
|
|
CUSTOM_DOC("If a scope is currently selected, and a statement or block statement is present below the current scope, the statement is moved into the scope.")
|
|
{
|
|
uint32_t access = AccessOpen;
|
|
View_Summary view = get_active_view(app, access);
|
|
Buffer_Summary buffer = get_buffer(app, view.buffer_id, access);
|
|
|
|
int32_t top = view.cursor.pos;
|
|
int32_t bottom = view.mark.pos;
|
|
|
|
if (top > bottom){
|
|
int32_t x = top;
|
|
top = bottom;
|
|
bottom = x;
|
|
}
|
|
|
|
Partition *part = &global_part;
|
|
|
|
Temp_Memory temp = begin_temp_memory(part);
|
|
if (buffer_get_char(app, &buffer, top) == '{' && buffer_get_char(app, &buffer, bottom-1) == '}'){
|
|
Range range = {};
|
|
if (find_whole_statement_down(app, &buffer, bottom, &range.start, &range.end)){
|
|
char *string_space = push_array(part, char, range.end - range.start);
|
|
buffer_read_range(app, &buffer, range.start, range.end, string_space);
|
|
|
|
String string = make_string(string_space, range.end - range.start);
|
|
string = skip_chop_whitespace(string);
|
|
|
|
int32_t newline_count = 0;
|
|
for (char *ptr = string_space; ptr < string.str; ++ptr){
|
|
if (*ptr == '\n'){
|
|
++newline_count;
|
|
}
|
|
}
|
|
|
|
bool32 extra_newline = false;
|
|
if (newline_count >= 2){
|
|
extra_newline = true;
|
|
}
|
|
|
|
int32_t edit_len = string.size + 1;
|
|
if (extra_newline){
|
|
edit_len += 1;
|
|
}
|
|
|
|
char *edit_str = push_array(part, char, edit_len);
|
|
if (extra_newline){
|
|
edit_str[0] = '\n';
|
|
copy_fast_unsafe(edit_str+1, string);
|
|
edit_str[edit_len-1] = '\n';
|
|
}
|
|
else{
|
|
copy_fast_unsafe(edit_str, string);
|
|
edit_str[edit_len-1] = '\n';
|
|
}
|
|
|
|
Buffer_Edit edits[2];
|
|
edits[0].str_start = 0;
|
|
edits[0].len = edit_len;
|
|
edits[0].start = bottom-1;
|
|
edits[0].end = bottom-1;
|
|
|
|
edits[1].str_start = 0;
|
|
edits[1].len = 0;
|
|
edits[1].start = range.start;
|
|
edits[1].end = range.end;
|
|
|
|
buffer_batch_edit(app, &buffer, edit_str, edit_len, edits, 2, BatchEdit_Normal);
|
|
}
|
|
}
|
|
end_temp_memory(temp);
|
|
|
|
no_mark_snap_to_cursor(app, view.view_id);
|
|
}
|
|
|
|
// BOTTOM
|
|
|