2025-07-10 14:08:27 +00:00
///////////////////////////////////////////////////////////////////////////
// Begin Buffer
///////////////////////////////////////////////////////////////////////////
BUFFER_HOOK_SIG ( custom_begin_buffer ) {
ProfileScope ( app , " begin buffer " ) ;
Scratch_Block scratch ( app ) ;
2025-07-10 15:53:54 +00:00
Tree_Sitter_Language_Definition * language = tree_sitter_language_for_buffer ( app , buffer_id ) ;
bool treat_as_code = language ! = 0 ;
if ( treat_as_code ) tree_sitter_begin_buffer ( app , buffer_id ) ;
2025-07-10 14:08:27 +00:00
String_ID file_map_id = vars_save_string_lit ( " keys_file " ) ;
String_ID code_map_id = vars_save_string_lit ( " keys_code " ) ;
2025-07-10 15:53:54 +00:00
Command_Map_ID map_id = ( treat_as_code ) ? ( code_map_id ) : ( file_map_id ) ;
2025-07-10 14:08:27 +00:00
Managed_Scope scope = buffer_get_managed_scope ( app , buffer_id ) ;
Command_Map_ID * map_id_ptr = scope_attachment ( app , scope , buffer_map_id , Command_Map_ID ) ;
* map_id_ptr = map_id ;
Line_Ending_Kind setting = guess_line_ending_kind_from_buffer ( app , buffer_id ) ;
Line_Ending_Kind * eol_setting = scope_attachment ( app , scope , buffer_eol_setting , Line_Ending_Kind ) ;
* eol_setting = setting ;
// NOTE(allen): Decide buffer settings
b32 wrap_lines = true ;
b32 use_lexer = false ;
2025-07-10 15:53:54 +00:00
if ( treat_as_code ) {
2025-07-10 14:08:27 +00:00
wrap_lines = def_get_config_b32 ( vars_save_string_lit ( " enable_code_wrapping " ) ) ;
// TODO(PS): @Remove - consider removing the lexer for now? later, replace in favor of tree-sitter
use_lexer = true ;
}
2025-07-10 15:53:54 +00:00
if ( treat_as_code )
2025-07-10 14:08:27 +00:00
{
Async_Task * parse_task = scope_attachment ( app , scope , buffer_tree_sitter_parse_task_id , Async_Task ) ;
* parse_task = async_task_no_dep ( & global_async_system , tree_sitter_parse_async , make_data_struct ( & buffer_id ) ) ;
}
String_Const_u8 buffer_name = push_buffer_base_name ( app , scratch , buffer_id ) ;
if ( buffer_name . size > 0 & & buffer_name . str [ 0 ] = = ' * ' & & buffer_name . str [ buffer_name . size - 1 ] = = ' * ' ) {
wrap_lines = def_get_config_b32 ( vars_save_string_lit ( " enable_output_wrapping " ) ) ;
}
if ( use_lexer ) {
ProfileBlock ( app , " begin buffer kick off lexer " ) ;
Async_Task * lex_task_ptr = scope_attachment ( app , scope , buffer_lex_task , Async_Task ) ;
* lex_task_ptr = async_task_no_dep ( & global_async_system , do_full_lex_async , make_data_struct ( & buffer_id ) ) ;
}
{
b32 * wrap_lines_ptr = scope_attachment ( app , scope , buffer_wrap_lines , b32 ) ;
* wrap_lines_ptr = wrap_lines ;
}
if ( use_lexer ) {
buffer_set_layout ( app , buffer_id , layout_virt_indent_index_generic ) ;
}
else {
2025-07-10 15:53:54 +00:00
if ( treat_as_code ) {
2025-07-10 14:08:27 +00:00
buffer_set_layout ( app , buffer_id , layout_virt_indent_literal_generic ) ;
}
else {
buffer_set_layout ( app , buffer_id , layout_generic ) ;
}
}
// no meaning for return
return ( 0 ) ;
}
///////////////////////////////////////////////////////////////////////////
// End Buffer
///////////////////////////////////////////////////////////////////////////
BUFFER_HOOK_SIG ( custom_end_buffer ) {
Marker_List * list = get_marker_list_for_buffer ( buffer_id ) ;
if ( list ! = 0 ) delete_marker_list ( list ) ;
tree_sitter_end_buffer ( app , buffer_id ) ;
default_end_buffer ( app , buffer_id ) ;
return ( 0 ) ;
}
2025-07-10 15:53:54 +00:00
2025-07-10 16:14:35 +00:00
///////////////////////////////////////////////////////////////////////////
// Buffer Edit Range
///////////////////////////////////////////////////////////////////////////
BUFFER_EDIT_RANGE_SIG ( custom_buffer_edit_range ) {
// buffer_id, new_range, original_size
ProfileScope ( app , " default edit range " ) ;
Range_i64 old_range = Ii64 ( old_cursor_range . min . pos , old_cursor_range . max . pos ) ;
buffer_shift_fade_ranges ( buffer_id , old_range . max , ( new_range . max - old_range . max ) ) ;
{
code_index_lock ( ) ;
Code_Index_File * file = code_index_get_file ( buffer_id ) ;
if ( file ! = 0 ) code_index_shift ( file , old_range , range_size ( new_range ) ) ;
code_index_unlock ( ) ;
}
i64 insert_size = range_size ( new_range ) ;
i64 text_shift = replace_range_shift ( old_range , insert_size ) ;
Scratch_Block scratch ( app ) ;
Managed_Scope scope = buffer_get_managed_scope ( app , buffer_id ) ;
{ // Tree Sitter
Buffer_Tree_Sitter_Data * tree_data = scope_attachment ( app , scope , buffer_tree_sitter_data_id , Buffer_Tree_Sitter_Data ) ;
if ( tree_data - > tree )
{
Async_Task * tree_sitter_parse_task = scope_attachment ( app , scope , buffer_tree_sitter_parse_task_id , Async_Task ) ;
if ( async_task_is_running_or_pending ( & global_async_system , * tree_sitter_parse_task ) )
{
// NOTE(jack): We dont want to wait for ts_tree delete, as it is slow for large files
// (noticable spikes when it fires in 4coder_base_types.cpp ~200Kb)
// And if we restart the next task before it attempts the delete we may be able to
// avoid the delete entirely as the reference count may have increased from the next
// parse attempt
async_task_cancel_nowait ( app , & global_async_system , * tree_sitter_parse_task ) ;
* tree_sitter_parse_task = 0 ;
}
i64 new_end_line = get_line_number_from_pos ( app , buffer_id , new_range . end ) ;
i64 new_end_pos = new_range . end - get_line_start_pos ( app , buffer_id , new_end_line ) ;
TSInputEdit edit ;
edit . start_byte = ( u32 ) old_range . start ;
edit . old_end_byte = ( u32 ) old_range . end ;
edit . new_end_byte = ( u32 ) new_range . end ;
edit . start_point = {
( u32 ) old_cursor_range . start . line ,
( u32 ) old_cursor_range . start . col
} ;
edit . old_end_point = {
( u32 ) old_cursor_range . end . line ,
( u32 ) old_cursor_range . end . col
} ;
// TODO(PS): jack says this works but looks wrong???
edit . new_end_point = {
( u32 ) new_end_line - 1 ,
( u32 ) new_end_pos + 1 ,
} ;
ts_tree_edit ( tree_data - > tree , & edit ) ;
* tree_sitter_parse_task = async_task_no_dep ( & global_async_system , tree_sitter_parse_async , make_data_struct ( & buffer_id ) ) ;
}
}
Async_Task * lex_task_ptr = scope_attachment ( app , scope , buffer_lex_task , Async_Task ) ;
Base_Allocator * allocator = managed_scope_allocator ( app , scope ) ;
b32 do_full_relex = false ;
if ( async_task_is_running_or_pending ( & global_async_system , * lex_task_ptr ) )
{
async_task_cancel ( app , & global_async_system , * lex_task_ptr ) ;
buffer_unmark_as_modified ( buffer_id ) ;
do_full_relex = true ;
* lex_task_ptr = 0 ;
}
Token_Array * ptr = scope_attachment ( app , scope , attachment_tokens , Token_Array ) ;
if ( ptr ! = 0 & & ptr - > tokens ! = 0 )
{
ProfileBlockNamed ( app , " attempt resync " , profile_attempt_resync ) ;
i64 token_index_first = token_relex_first ( ptr , old_range . first , 1 ) ;
i64 token_index_resync_guess =
token_relex_resync ( ptr , old_range . one_past_last , 16 ) ;
if ( token_index_resync_guess - token_index_first > = 4000 )
{
do_full_relex = true ;
}
else
{
Token * token_first = ptr - > tokens + token_index_first ;
Token * token_resync = ptr - > tokens + token_index_resync_guess ;
Range_i64 relex_range = Ii64 ( token_first - > pos , token_resync - > pos + token_resync - > size + text_shift ) ;
String_Const_u8 partial_text = push_buffer_range ( app , scratch , buffer_id , relex_range ) ;
Token_List relex_list = lex_full_input_cpp ( scratch , partial_text ) ;
if ( relex_range . one_past_last < buffer_get_size ( app , buffer_id ) )
{
token_drop_eof ( & relex_list ) ;
}
Token_Relex relex = token_relex ( relex_list , relex_range . first - text_shift , ptr - > tokens , token_index_first , token_index_resync_guess ) ;
ProfileCloseNow ( profile_attempt_resync ) ;
if ( ! relex . successful_resync )
{
do_full_relex = true ;
}
else
{
ProfileBlock ( app , " apply resync " ) ;
i64 token_index_resync = relex . first_resync_index ;
Range_i64 head = Ii64 ( 0 , token_index_first ) ;
Range_i64 replaced = Ii64 ( token_index_first , token_index_resync ) ;
Range_i64 tail = Ii64 ( token_index_resync , ptr - > count ) ;
i64 resynced_count = ( token_index_resync_guess + 1 ) - token_index_resync ;
i64 relexed_count = relex_list . total_count - resynced_count ;
i64 tail_shift = relexed_count - ( token_index_resync - token_index_first ) ;
i64 new_tokens_count = ptr - > count + tail_shift ;
Token * new_tokens = base_array ( allocator , Token , new_tokens_count ) ;
Token * old_tokens = ptr - > tokens ;
block_copy_array_shift ( new_tokens , old_tokens , head , 0 ) ;
token_fill_memory_from_list ( new_tokens + replaced . first , & relex_list , relexed_count ) ;
for ( i64 i = 0 , index = replaced . first ; i < relexed_count ; i + = 1 , index + = 1 )
{
new_tokens [ index ] . pos + = relex_range . first ;
}
for ( i64 i = tail . first ; i < tail . one_past_last ; i + = 1 )
{
old_tokens [ i ] . pos + = text_shift ;
}
block_copy_array_shift ( new_tokens , ptr - > tokens , tail , tail_shift ) ;
base_free ( allocator , ptr - > tokens ) ;
ptr - > tokens = new_tokens ;
ptr - > count = new_tokens_count ;
ptr - > max = new_tokens_count ;
buffer_mark_as_modified ( buffer_id ) ;
}
}
}
if ( do_full_relex )
{
* lex_task_ptr = async_task_no_dep ( & global_async_system , do_full_lex_async ,
make_data_struct ( & buffer_id ) ) ;
}
loco_on_buffer_edit ( app , buffer_id , old_range , new_range ) ;
// no meaning for return
return ( 0 ) ;
}
2025-07-10 15:53:54 +00:00
///////////////////////////////////////////////////////////////////////////
// Render Buffer
///////////////////////////////////////////////////////////////////////////
function void custom_render_buffer (
Application_Links * app ,
View_ID view_id ,
Face_ID face_id ,
Buffer_ID buffer ,
Text_Layout_ID text_layout_id ,
Rect_f32 rect
) {
ProfileScope ( app , " render buffer " ) ;
Scratch_Block scratch ( app ) ;
View_ID active_view = get_active_view ( app , Access_Always ) ;
b32 is_active_view = ( active_view = = view_id ) ;
Rect_f32 prev_clip = draw_set_clip ( app , rect ) ;
Range_i64 visible_range = text_layout_get_visible_range ( app , text_layout_id ) ;
// NOTE(allen): Cursor shape
Face_Metrics metrics = get_face_metrics ( app , face_id ) ;
u64 cursor_roundness_100 = def_get_config_u64 ( app , vars_save_string_lit ( " cursor_roundness " ) ) ;
f32 cursor_roundness = metrics . normal_advance * cursor_roundness_100 * 0.01f ;
f32 mark_thickness = ( f32 ) def_get_config_u64 ( app , vars_save_string_lit ( " mark_thickness " ) ) ;
// NOTE(allen): Token colorizing
Managed_Scope buffer_scope = buffer_get_managed_scope ( app , buffer ) ;
Buffer_Tree_Sitter_Data * tree_data = scope_attachment ( app , buffer_scope , buffer_tree_sitter_data_id , Buffer_Tree_Sitter_Data ) ;
TSTree * tree = tree_sitter_buffer_get_tree_copy ( tree_data ) ;
Token_Array token_array = get_token_array_from_buffer ( app , buffer ) ;
paint_text_color_fcolor ( app , text_layout_id , visible_range , fcolor_id ( defcolor_text_default ) ) ; // will get overridden by lang-specific token coloring below
if ( token_array . tokens ! = 0 & & tree )
{
if ( use_tree_sitter_token_coloring )
{
draw_tree_sitter_node_colors ( app , text_layout_id , buffer ) ;
}
else
{
draw_cpp_token_colors ( app , text_layout_id , & token_array ) ;
}
}
i64 cursor_pos = view_correct_cursor ( app , view_id ) ;
view_correct_mark ( app , view_id ) ;
// NOTE(allen): Scope highlight
b32 use_scope_highlight = def_get_config_b32 ( vars_save_string_lit ( " use_scope_highlight " ) ) ;
if ( use_scope_highlight ) {
Color_Array colors = finalize_color_array ( defcolor_back_cycle ) ;
draw_scope_highlight ( app , buffer , text_layout_id , cursor_pos , colors . vals , colors . count ) ;
}
// NOTE(PS): QOL Column
if ( qol_col_cursor . pos > = 0 ) {
Buffer_Seek seek = seek_line_col ( qol_col_cursor . line , qol_col_cursor . col ) ;
Buffer_Cursor cursor = buffer_compute_cursor ( app , buffer , seek ) ;
Rect_f32 col_rect = text_layout_character_on_screen ( app , text_layout_id , cursor . pos ) ;
if ( col_rect . x1 > 0.f ) {
col_rect . y0 = rect . y0 ;
col_rect . y1 = rect . y1 ;
draw_rectangle_fcolor ( app , col_rect , 0.f , fcolor_id ( defcolor_highlight_cursor_line ) ) ;
}
}
b32 use_error_highlight = def_get_config_b32 ( vars_save_string_lit ( " use_error_highlight " ) ) ;
b32 use_jump_highlight = def_get_config_b32 ( vars_save_string_lit ( " use_jump_highlight " ) ) ;
if ( use_error_highlight | | use_jump_highlight ) {
// NOTE(allen): Error highlight
String_Const_u8 name = string_u8_litexpr ( " *compilation* " ) ;
Buffer_ID compilation_buffer = get_buffer_by_name ( app , name , Access_Always ) ;
if ( use_error_highlight ) {
draw_jump_highlights ( app , buffer , text_layout_id , compilation_buffer ,
fcolor_id ( defcolor_highlight_junk ) ) ;
}
// NOTE(allen): Search highlight
if ( use_jump_highlight ) {
Buffer_ID jump_buffer = get_locked_jump_buffer ( app ) ;
if ( jump_buffer ! = compilation_buffer ) {
draw_jump_highlights ( app , buffer , text_layout_id , jump_buffer ,
fcolor_id ( defcolor_highlight_white ) ) ;
}
}
}
// NOTE(allen): Color parens
b32 use_paren_helper = def_get_config_b32 ( vars_save_string_lit ( " use_paren_helper " ) ) ;
if ( use_paren_helper ) {
Color_Array colors = finalize_color_array ( defcolor_text_cycle ) ;
draw_paren_highlight ( app , buffer , text_layout_id , cursor_pos , colors . vals , colors . count ) ;
}
// NOTE(allen): Line highlight
b32 highlight_line_at_cursor = def_get_config_b32 ( vars_save_string_lit ( " highlight_line_at_cursor " ) ) ;
if ( highlight_line_at_cursor & & is_active_view ) {
i64 line_number = get_line_number_from_pos ( app , buffer , cursor_pos ) ;
draw_line_highlight ( app , text_layout_id , line_number , fcolor_id ( defcolor_highlight_cursor_line ) ) ;
}
// NOTE(allen): Whitespace highlight
b64 show_whitespace = false ;
view_get_setting ( app , view_id , ViewSetting_ShowWhitespace , & show_whitespace ) ;
if ( show_whitespace ) {
if ( token_array . tokens = = 0 ) {
draw_whitespace_highlight ( app , buffer , text_layout_id , cursor_roundness ) ;
}
else {
draw_whitespace_highlight ( app , text_layout_id , & token_array , cursor_roundness ) ;
}
}
// NOTE(allen): Cursor
switch ( fcoder_mode ) {
case FCoderMode_Original :
{
draw_original_4coder_style_cursor_mark_highlight ( app , view_id , is_active_view , buffer , text_layout_id , cursor_roundness , mark_thickness ) ;
} break ;
case FCoderMode_NotepadLike :
{
draw_notepad_style_cursor_highlight ( app , view_id , buffer , text_layout_id , cursor_roundness ) ;
} break ;
}
// NOTE(allen): Fade ranges
paint_fade_ranges ( app , text_layout_id , buffer ) ;
// NOTE(allen): put the actual text on the actual screen
draw_text_layout_default ( app , text_layout_id ) ;
draw_set_clip ( app , prev_clip ) ;
}
function void
custom_render_caller ( Application_Links * app , Frame_Info frame_info , View_ID view_id ) {
ProfileScope ( app , " default render caller " ) ;
View_ID active_view = get_active_view ( app , Access_Always ) ;
b32 is_active_view = ( active_view = = view_id ) ;
Rect_f32 region = draw_background_and_margin ( app , view_id , is_active_view ) ;
Rect_f32 prev_clip = draw_set_clip ( app , region ) ;
Buffer_ID buffer = view_get_buffer ( app , view_id , Access_Always ) ;
Face_ID face_id = get_face_id ( app , buffer ) ;
Face_Metrics face_metrics = get_face_metrics ( app , face_id ) ;
f32 line_height = face_metrics . line_height ;
f32 digit_advance = face_metrics . decimal_digit_advance ;
// NOTE(allen): file bar
b64 showing_file_bar = false ;
if ( view_get_setting ( app , view_id , ViewSetting_ShowFileBar , & showing_file_bar ) & & showing_file_bar ) {
Rect_f32_Pair pair = layout_file_bar_on_top ( region , line_height ) ;
draw_file_bar ( app , view_id , buffer , face_id , pair . min ) ;
region = pair . max ;
}
Buffer_Scroll scroll = view_get_buffer_scroll ( app , view_id ) ;
Buffer_Point_Delta_Result delta = delta_apply ( app , view_id ,
frame_info . animation_dt , scroll ) ;
if ( ! block_match_struct ( & scroll . position , & delta . point ) ) {
block_copy_struct ( & scroll . position , & delta . point ) ;
view_set_buffer_scroll ( app , view_id , scroll , SetBufferScroll_NoCursorChange ) ;
}
if ( delta . still_animating ) {
animate_in_n_milliseconds ( app , 0 ) ;
}
// NOTE(allen): query bars
region = default_draw_query_bars ( app , region , view_id , face_id ) ;
// NOTE(allen): FPS hud
if ( show_fps_hud ) {
Rect_f32_Pair pair = layout_fps_hud_on_bottom ( region , line_height ) ;
draw_fps_hud ( app , frame_info , face_id , pair . max ) ;
region = pair . min ;
animate_in_n_milliseconds ( app , 1000 ) ;
}
// NOTE(allen): layout line numbers
b32 show_line_number_margins = def_get_config_b32 ( vars_save_string_lit ( " show_line_number_margins " ) ) ;
Rect_f32 line_number_rect = { } ;
if ( show_line_number_margins ) {
Rect_f32_Pair pair = layout_line_number_margin ( app , buffer , region , digit_advance ) ;
line_number_rect = pair . min ;
region = pair . max ;
}
// NOTE(allen): begin buffer render
Buffer_Point buffer_point = scroll . position ;
Text_Layout_ID text_layout_id = text_layout_create ( app , buffer , region , buffer_point ) ;
// NOTE(allen): draw line numbers
if ( show_line_number_margins ) {
draw_line_number_margin ( app , view_id , buffer , face_id , text_layout_id , line_number_rect ) ;
}
// NOTE(allen): draw the buffer
custom_render_buffer ( app , view_id , face_id , buffer , text_layout_id , region ) ;
loco_render_buffer ( app , view_id , face_id , buffer , text_layout_id , region , frame_info ) ;
text_layout_free ( app , text_layout_id ) ;
draw_set_clip ( app , prev_clip ) ;
}