2017-01-23 06:19:43 +00:00
/*
2019-10-03 20:54:13 +00:00
4 coder_auto_indent . cpp - Commands for automatic indentation .
2017-01-23 06:19:43 +00:00
*/
// TOP
2019-09-02 18:59:36 +00:00
internal Batch_Edit *
2019-10-03 20:54:13 +00:00
make_batch_from_indentations ( Application_Links * app , Arena * arena , Buffer_ID buffer , Range_i64 lines , i64 * indentations , Indent_Flag flags , i32 tab_width ) {
2021-05-31 23:08:54 +00:00
i64 * shifted_indentations = indentations - lines . first ;
Batch_Edit * batch_first = 0 ;
Batch_Edit * batch_last = 0 ;
for ( i64 line_number = lines . first ;
line_number < = lines . max ;
+ + line_number ) {
i64 line_start_pos = get_line_start_pos ( app , buffer , line_number ) ;
Indent_Info indent_info = get_indent_info_line_number_and_start ( app , buffer , line_number , line_start_pos , tab_width ) ;
i64 correct_indentation = shifted_indentations [ line_number ] ;
if ( indent_info . is_blank & & HasFlag ( flags , Indent_ClearLine ) ) {
correct_indentation = 0 ;
}
if ( correct_indentation < = - 1 ) {
correct_indentation = indent_info . indent_pos ;
}
if ( correct_indentation ! = indent_info . indent_pos ) {
u64 str_size = 0 ;
u8 * str = 0 ;
if ( HasFlag ( flags , Indent_UseTab ) ) {
i64 tab_count = correct_indentation / tab_width ;
i64 indent = tab_count * tab_width ;
i64 space_count = correct_indentation - indent ;
str_size = tab_count + space_count ;
str = push_array ( arena , u8 , str_size ) ;
block_fill_u8 ( str , tab_count , ' \t ' ) ;
block_fill_u8 ( str + tab_count , space_count , ' ' ) ;
}
else {
str_size = correct_indentation ;
str = push_array ( arena , u8 , str_size ) ;
block_fill_u8 ( str , str_size , ' ' ) ;
}
Batch_Edit * batch = push_array ( arena , Batch_Edit , 1 ) ;
sll_queue_push ( batch_first , batch_last , batch ) ;
batch - > edit . text = SCu8 ( str , str_size ) ;
batch - > edit . range = Ii64 ( line_start_pos , indent_info . first_char_pos ) ;
}
}
return ( batch_first ) ;
2016-09-08 00:32:31 +00:00
}
2019-06-09 21:05:57 +00:00
internal void
2019-10-03 20:54:13 +00:00
set_line_indents ( Application_Links * app , Arena * arena , Buffer_ID buffer , Range_i64 lines , i64 * indentations , Indent_Flag flags , i32 tab_width ) {
2021-05-31 23:08:54 +00:00
Batch_Edit * batch = make_batch_from_indentations ( app , arena , buffer , lines , indentations , flags , tab_width ) ;
if ( batch ! = 0 ) {
buffer_batch_edit ( app , buffer , batch ) ;
}
2016-09-09 01:02:51 +00:00
}
2019-09-04 05:31:35 +00:00
internal Token *
2019-10-03 20:54:13 +00:00
find_anchor_token ( Application_Links * app , Buffer_ID buffer , Token_Array * tokens , i64 invalid_line ) {
2021-05-31 23:08:54 +00:00
ProfileScope ( app , " find anchor token " ) ;
Token * result = 0 ;
if ( tokens ! = 0 & & tokens - > tokens ! = 0 ) {
result = tokens - > tokens ;
i64 invalid_pos = get_line_start_pos ( app , buffer , invalid_line ) ;
i32 scope_counter = 0 ;
i32 paren_counter = 0 ;
Token * token = tokens - > tokens ;
for ( ; ; token + = 1 ) {
if ( token - > pos + token - > size > invalid_pos ) {
break ;
}
if ( ! HasFlag ( token - > flags , TokenBaseFlag_PreprocessorBody ) ) {
if ( scope_counter = = 0 & & paren_counter = = 0 ) {
result = token ;
}
switch ( token - > kind ) {
case TokenBaseKind_ScopeOpen :
{
scope_counter + = 1 ;
} break ;
case TokenBaseKind_ScopeClose :
{
paren_counter = 0 ;
if ( scope_counter > 0 ) {
scope_counter - = 1 ;
}
} break ;
case TokenBaseKind_ParentheticalOpen :
{
paren_counter + = 1 ;
} break ;
case TokenBaseKind_ParentheticalClose :
{
if ( paren_counter > 0 ) {
paren_counter - = 1 ;
}
} break ;
}
}
}
}
return ( result ) ;
2016-09-08 00:32:31 +00:00
}
2019-10-03 20:54:13 +00:00
internal Nest *
indent__new_nest ( Arena * arena , Nest_Alloc * alloc ) {
2021-05-31 23:08:54 +00:00
Nest * new_nest = alloc - > free_nest ;
if ( new_nest = = 0 ) {
new_nest = push_array ( arena , Nest , 1 ) ;
}
else {
sll_stack_pop ( alloc - > free_nest ) ;
}
return ( new_nest ) ;
2019-10-03 20:54:13 +00:00
}
internal void
indent__free_nest ( Nest_Alloc * alloc , Nest * nest ) {
2021-05-31 23:08:54 +00:00
sll_stack_push ( alloc - > free_nest , nest ) ;
2016-09-09 01:02:51 +00:00
}
2019-10-12 04:10:56 +00:00
internal b32
indent__unfinished_statement ( Token * token , Nest * current_nest ) {
2021-05-31 23:08:54 +00:00
b32 result = false ;
if ( current_nest ! = 0 & & current_nest - > kind = = TokenBaseKind_ScopeOpen ) {
result = true ;
switch ( token - > kind ) {
case TokenBaseKind_ScopeOpen :
case TokenBaseKind_ScopeClose :
case TokenBaseKind_StatementClose :
{
result = false ;
} break ;
}
if ( HasFlag ( token - > flags , TokenBaseFlag_PreprocessorBody ) ) {
result = false ;
}
}
return ( result ) ;
2019-10-12 04:10:56 +00:00
}
2020-01-12 00:27:34 +00:00
function void
line_indent_cache_update ( Application_Links * app , Buffer_ID buffer , i32 tab_width , Indent_Line_Cache * line_cache ) {
2021-05-31 23:08:54 +00:00
if ( line_cache - > line_number_for_cached_indent ! = line_cache - > where_token_starts ) {
ProfileScope ( app , " get indent info " ) ;
line_cache - > line_number_for_cached_indent = line_cache - > where_token_starts ;
line_cache - > start_pos = get_line_start_pos ( app , buffer , line_cache - > where_token_starts ) ;
Range_i64 range = Ii64 ( line_cache - > start_pos , line_cache - > one_past_last_pos ) ;
line_cache - > indent_info = get_indent_info_range ( app , buffer , range , tab_width ) ;
}
2020-01-12 00:27:34 +00:00
}
2019-06-20 23:43:27 +00:00
internal i64 *
2019-10-03 20:54:13 +00:00
get_indentation_array ( Application_Links * app , Arena * arena , Buffer_ID buffer , Range_i64 lines , Indent_Flag flags , i32 tab_width , i32 indent_width ) {
2021-05-31 23:08:54 +00:00
ProfileScope ( app , " get indentation array " ) ;
i64 count = lines . max - lines . min + 1 ;
i64 * indentations = push_array ( arena , i64 , count ) ;
i64 * shifted_indentations = indentations - lines . first ;
block_fill_u64 ( indentations , sizeof ( * indentations ) * count , ( u64 ) ( - 1 ) ) ;
2020-02-09 10:22:35 +00:00
#if 0
2021-05-31 23:08:54 +00:00
Managed_Scope scope = buffer_get_managed_scope ( app , buffer ) ;
Token_Array * tokens = scope_attachment ( app , scope , attachment_tokens , Token_Array ) ;
2020-02-09 10:22:35 +00:00
# endif
2021-05-31 23:08:54 +00:00
Token_Array token_array = get_token_array_from_buffer ( app , buffer ) ;
Token_Array * tokens = & token_array ;
i64 anchor_line = clamp_bot ( 1 , lines . first - 1 ) ;
Token * anchor_token = find_anchor_token ( app , buffer , tokens , anchor_line ) ;
if ( anchor_token ! = 0 & &
anchor_token > = tokens - > tokens & &
anchor_token < tokens - > tokens + tokens - > count ) {
i64 line = get_line_number_from_pos ( app , buffer , anchor_token - > pos ) ;
line = clamp_top ( line , lines . first ) ;
Token_Iterator_Array token_it = token_iterator ( 0 , tokens , anchor_token ) ;
Scratch_Block scratch ( app , arena ) ;
Nest * nest = 0 ;
Nest_Alloc nest_alloc = { } ;
i64 line_last_indented = line - 1 ;
i64 last_indent = 0 ;
i64 actual_indent = 0 ;
b32 in_unfinished_statement = false ;
Indent_Line_Cache line_cache = { } ;
for ( ; ; ) {
Token * token = token_it_read ( & token_it ) ;
if ( line_cache . where_token_starts = = 0 | |
token - > pos > = line_cache . one_past_last_pos ) {
ProfileScope ( app , " get line number " ) ;
line_cache . where_token_starts = get_line_number_from_pos ( app , buffer , token - > pos ) ;
line_cache . one_past_last_pos = get_line_end_pos ( app , buffer , line_cache . where_token_starts ) ;
}
i64 current_indent = 0 ;
if ( nest ! = 0 ) {
current_indent = nest - > indent ;
}
i64 this_indent = current_indent ;
i64 following_indent = current_indent ;
b32 shift_by_actual_indent = false ;
b32 ignore_unfinished_statement = false ;
if ( HasFlag ( token - > flags , TokenBaseFlag_PreprocessorBody ) ) {
this_indent = 0 ;
}
else {
switch ( token - > kind ) {
case TokenBaseKind_ScopeOpen :
{
Nest * new_nest = indent__new_nest ( arena , & nest_alloc ) ;
sll_stack_push ( nest , new_nest ) ;
nest - > kind = TokenBaseKind_ScopeOpen ;
nest - > indent = current_indent + indent_width ;
following_indent = nest - > indent ;
ignore_unfinished_statement = true ;
} break ;
case TokenBaseKind_ScopeClose :
{
for ( ; nest ! = 0 & & nest - > kind ! = TokenBaseKind_ScopeOpen ; ) {
Nest * n = nest ;
sll_stack_pop ( nest ) ;
indent__free_nest ( & nest_alloc , n ) ;
}
if ( nest ! = 0 & & nest - > kind = = TokenBaseKind_ScopeOpen ) {
Nest * n = nest ;
sll_stack_pop ( nest ) ;
indent__free_nest ( & nest_alloc , n ) ;
}
this_indent = 0 ;
if ( nest ! = 0 ) {
this_indent = nest - > indent ;
}
following_indent = this_indent ;
ignore_unfinished_statement = true ;
} break ;
case TokenBaseKind_ParentheticalOpen :
{
Nest * new_nest = indent__new_nest ( arena , & nest_alloc ) ;
sll_stack_push ( nest , new_nest ) ;
nest - > kind = TokenBaseKind_ParentheticalOpen ;
line_indent_cache_update ( app , buffer , tab_width , & line_cache ) ;
nest - > indent = ( token - > pos - line_cache . indent_info . first_char_pos ) + 1 ;
following_indent = nest - > indent ;
shift_by_actual_indent = true ;
ignore_unfinished_statement = true ;
} break ;
case TokenBaseKind_ParentheticalClose :
{
if ( nest ! = 0 & & nest - > kind = = TokenBaseKind_ParentheticalOpen ) {
Nest * n = nest ;
sll_stack_pop ( nest ) ;
indent__free_nest ( & nest_alloc , n ) ;
}
following_indent = 0 ;
if ( nest ! = 0 ) {
following_indent = nest - > indent ;
}
//ignore_unfinished_statement = true;
} break ;
}
if ( token - > sub_kind = = TokenCppKind_BlockComment | |
token - > sub_kind = = TokenCppKind_LiteralStringRaw ) {
ignore_unfinished_statement = true ;
}
if ( in_unfinished_statement & & ! ignore_unfinished_statement ) {
this_indent + = indent_width ;
}
}
2019-10-03 20:54:13 +00:00
# define EMIT(N) \
2020-05-01 13:52:22 +00:00
Stmnt ( if ( lines . first < = line_it ) { shifted_indentations [ line_it ] = N ; } \
if ( line_it = = lines . end ) { goto finished ; } \
actual_indent = N ; )
2021-05-31 23:08:54 +00:00
i64 line_it = line_last_indented ;
if ( lines . first < = line_cache . where_token_starts ) {
for ( ; line_it < line_cache . where_token_starts ; ) {
line_it + = 1 ;
if ( line_it = = line_cache . where_token_starts ) {
EMIT ( this_indent ) ;
}
else {
EMIT ( last_indent ) ;
}
}
}
else {
actual_indent = this_indent ;
line_it = line_cache . where_token_starts ;
}
i64 line_where_token_ends = get_line_number_from_pos ( app , buffer , token - > pos + token - > size ) ;
if ( lines . first < = line_where_token_ends ) {
line_indent_cache_update ( app , buffer , tab_width , & line_cache ) ;
i64 line_where_token_starts_shift = this_indent - line_cache . indent_info . indent_pos ;
for ( ; line_it < line_where_token_ends ; ) {
line_it + = 1 ;
i64 line_it_start_pos = get_line_start_pos ( app , buffer , line_it ) ;
Indent_Info line_it_indent_info = get_indent_info_line_number_and_start ( app , buffer , line_it , line_it_start_pos , tab_width ) ;
i64 new_indent = line_it_indent_info . indent_pos + line_where_token_starts_shift ;
new_indent = clamp_bot ( 0 , new_indent ) ;
EMIT ( new_indent ) ;
}
}
else {
line_it = line_where_token_ends ;
}
2019-10-03 20:54:13 +00:00
# undef EMIT
2021-05-31 23:08:54 +00:00
if ( shift_by_actual_indent ) {
nest - > indent + = actual_indent ;
following_indent + = actual_indent ;
}
if ( token - > kind ! = TokenBaseKind_Comment ) {
in_unfinished_statement = indent__unfinished_statement ( token , nest ) ;
if ( in_unfinished_statement ) {
following_indent + = indent_width ;
}
}
last_indent = following_indent ;
line_last_indented = line_it ;
if ( ! token_it_inc_non_whitespace ( & token_it ) ) {
break ;
}
}
}
finished : ;
return ( indentations ) ;
2016-09-10 15:22:25 +00:00
}
2019-06-09 21:05:57 +00:00
internal b32
2019-10-03 20:54:13 +00:00
auto_indent_buffer ( Application_Links * app , Buffer_ID buffer , Range_i64 pos , Indent_Flag flags , i32 tab_width , i32 indent_width ) {
2021-05-31 23:08:54 +00:00
ProfileScope ( app , " auto indent buffer " ) ;
Token_Array token_array = get_token_array_from_buffer ( app , buffer ) ;
Token_Array * tokens = & token_array ;
b32 result = false ;
if ( tokens - > tokens ! = 0 ) {
result = true ;
Scratch_Block scratch ( app ) ;
Range_i64 line_numbers = { } ;
if ( HasFlag ( flags , Indent_FullTokens ) ) {
i32 safety_counter = 0 ;
for ( ; ; ) {
Range_i64 expanded = enclose_tokens ( app , buffer , pos ) ;
expanded = enclose_whole_lines ( app , buffer , expanded ) ;
if ( expanded = = pos ) {
break ;
}
pos = expanded ;
safety_counter + = 1 ;
if ( safety_counter = = 20 ) {
pos = buffer_range ( app , buffer ) ;
break ;
}
}
}
line_numbers = get_line_range_from_pos_range ( app , buffer , pos ) ;
i64 * indentations = get_indentation_array ( app , scratch , buffer , line_numbers , flags , tab_width , indent_width ) ;
set_line_indents ( app , scratch , buffer , line_numbers , indentations , flags , tab_width ) ;
}
return ( result ) ;
2016-09-08 00:32:31 +00:00
}
2019-10-03 20:54:13 +00:00
function void
auto_indent_buffer ( Application_Links * app , Buffer_ID buffer , Range_i64 pos , Indent_Flag flags ) {
2021-05-31 23:08:54 +00:00
i32 indent_width = ( i32 ) def_get_config_u64 ( app , vars_save_string_lit ( " indent_width " ) ) ;
i32 tab_width = ( i32 ) def_get_config_u64 ( app , vars_save_string_lit ( " default_tab_width " ) ) ;
tab_width = clamp_bot ( 1 , tab_width ) ;
AddFlag ( flags , Indent_FullTokens ) ;
b32 indent_with_tabs = def_get_config_b32 ( vars_save_string_lit ( " indent_with_tabs " ) ) ;
if ( indent_with_tabs ) {
AddFlag ( flags , Indent_UseTab ) ;
}
auto_indent_buffer ( app , buffer , pos , flags , indent_width , tab_width ) ;
2019-10-03 20:54:13 +00:00
}
2017-01-23 06:19:43 +00:00
2019-10-03 20:54:13 +00:00
function void
auto_indent_buffer ( Application_Links * app , Buffer_ID buffer , Range_i64 pos ) {
2021-05-31 23:08:54 +00:00
auto_indent_buffer ( app , buffer , pos , 0 ) ;
2019-10-03 20:54:13 +00:00
}
2018-05-28 05:30:31 +00:00
2019-10-03 20:54:13 +00:00
////////////////////////////////
2018-05-28 05:30:31 +00:00
2019-10-10 18:21:47 +00:00
CUSTOM_COMMAND_SIG ( auto_indent_whole_file )
2017-11-15 23:57:21 +00:00
CUSTOM_DOC ( " Audo-indents the entire current buffer. " )
{
2021-05-31 23:08:54 +00:00
View_ID view = get_active_view ( app , Access_ReadWriteVisible ) ;
Buffer_ID buffer = view_get_buffer ( app , view , Access_ReadWriteVisible ) ;
i64 buffer_size = buffer_get_size ( app , buffer ) ;
auto_indent_buffer ( app , buffer , Ii64 ( 0 , buffer_size ) ) ;
2017-01-23 06:19:43 +00:00
}
2019-10-10 18:21:47 +00:00
CUSTOM_COMMAND_SIG ( auto_indent_line_at_cursor )
2017-11-15 23:57:21 +00:00
CUSTOM_DOC ( " Auto-indents the line on which the cursor sits. " )
{
2021-05-31 23:08:54 +00:00
View_ID view = get_active_view ( app , Access_ReadWriteVisible ) ;
Buffer_ID buffer = view_get_buffer ( app , view , Access_ReadWriteVisible ) ;
i64 pos = view_get_cursor_pos ( app , view ) ;
auto_indent_buffer ( app , buffer , Ii64 ( pos ) ) ;
move_past_lead_whitespace ( app , view , buffer ) ;
2017-01-23 06:19:43 +00:00
}
2019-10-10 18:21:47 +00:00
CUSTOM_COMMAND_SIG ( auto_indent_range )
2017-11-15 23:57:21 +00:00
CUSTOM_DOC ( " Auto-indents the range between the cursor and the mark. " )
{
2021-05-31 23:08:54 +00:00
View_ID view = get_active_view ( app , Access_ReadWriteVisible ) ;
Buffer_ID buffer = view_get_buffer ( app , view , Access_ReadWriteVisible ) ;
Range_i64 range = get_view_range ( app , view ) ;
auto_indent_buffer ( app , buffer , range ) ;
move_past_lead_whitespace ( app , view , buffer ) ;
2017-01-23 06:19:43 +00:00
}
2019-10-10 18:21:47 +00:00
CUSTOM_COMMAND_SIG ( write_text_and_auto_indent )
CUSTOM_DOC ( " Inserts text and auto-indents the line on which the cursor sits if any of the text contains 'layout punctuation' such as ;:{}()[]# and new lines. " )
2017-11-15 23:57:21 +00:00
{
2021-05-31 23:08:54 +00:00
ProfileScope ( app , " write and auto indent " ) ;
User_Input in = get_current_input ( app ) ;
String_Const_u8 insert = to_writable ( & in ) ;
if ( insert . str ! = 0 & & insert . size > 0 ) {
b32 do_auto_indent = false ;
for ( u64 i = 0 ; ! do_auto_indent & & i < insert . size ; i + = 1 ) {
switch ( insert . str [ i ] ) {
case ' ; ' : case ' : ' :
case ' { ' : case ' } ' :
case ' ( ' : case ' ) ' :
case ' [ ' : case ' ] ' :
case ' # ' :
case ' \n ' : case ' \t ' :
{
do_auto_indent = true ;
} break ;
}
}
if ( do_auto_indent ) {
View_ID view = get_active_view ( app , Access_ReadWriteVisible ) ;
Buffer_ID buffer = view_get_buffer ( app , view , Access_ReadWriteVisible ) ;
Range_i64 pos = { } ;
if ( view_has_highlighted_range ( app , view ) ) {
pos = get_view_range ( app , view ) ;
}
else {
pos . min = pos . max = view_get_cursor_pos ( app , view ) ;
}
write_text_input ( app ) ;
i64 end_pos = view_get_cursor_pos ( app , view ) ;
pos . min = Min ( pos . min , end_pos ) ;
pos . max = Max ( pos . max , end_pos ) ;
auto_indent_buffer ( app , buffer , pos , 0 ) ;
move_past_lead_whitespace ( app , view , buffer ) ;
}
else {
write_text_input ( app ) ;
}
}
2017-01-23 06:19:43 +00:00
}
// BOTTOM