/* 4coder_code_index.cpp - Generic code indexing system for layout, definition jumps, etc. */ // TOP global Code_Index global_code_index = {}; //////////////////////////////// // NOTE(allen): Lookups // TODO(allen): accelerator for these nest lookups? // Looks like the only one I ever actually use is the file one, not the array one. function Code_Index_Nest* code_index_get_nest_(Code_Index_Nest_Ptr_Array *array, i64 pos){ Code_Index_Nest *result = 0; i32 count = array->count; Code_Index_Nest **nest_ptrs = array->ptrs; for (i32 i = 0; i < count; i += 1){ Code_Index_Nest *nest = nest_ptrs[i]; if (nest->open.max <= pos && pos <= nest->close.min){ Code_Index_Nest *sub_nest = code_index_get_nest_(&nest->nest_array, pos); if (sub_nest != 0){ result = sub_nest; } else{ result = nest; } break; } } return(result); } function Code_Index_Nest* code_index_get_nest(Code_Index_File *file, i64 pos){ return(code_index_get_nest_(&file->nest_array, pos)); } function Code_Index_Note_List* code_index__list_from_string(String_Const_u8 string){ u64 hash = table_hash_u8(string.str, string.size); Code_Index_Note_List *result = &global_code_index.name_hash[hash % ArrayCount(global_code_index.name_hash)]; return(result); } function Code_Index_Note* code_index_note_from_string(String_Const_u8 string){ Code_Index_Note_List *list = code_index__list_from_string(string); Code_Index_Note *result = 0; for (Code_Index_Note *node = list->first; node != 0; node = node->next_in_hash){ if (string_match(string, node->text)){ result = node; break; } } return(result); } //////////////////////////////// // NOTE(allen): Global Code Index function void code_index_init(void){ global_code_index.mutex = system_mutex_make(); global_code_index.node_arena = make_arena_system(KB(4)); global_code_index.buffer_to_index_file = make_table_u64_u64(global_code_index.node_arena.base_allocator, 500); } function Code_Index_File_Storage* code_index__alloc_storage(void){ Code_Index_File_Storage *result = global_code_index.free_storage; if (result == 0){ result = push_array_zero(&global_code_index.node_arena, Code_Index_File_Storage, 1); } else{ sll_stack_pop(global_code_index.free_storage); } zdll_push_back(global_code_index.storage_first, global_code_index.storage_last, result); global_code_index.storage_count += 1; return(result); } function void code_index__free_storage(Code_Index_File_Storage *storage){ zdll_remove(global_code_index.storage_first, global_code_index.storage_last, storage); global_code_index.storage_count -= 1; sll_stack_push(global_code_index.free_storage, storage); } function void code_index_push_nest(Code_Index_Nest_List *list, Code_Index_Nest *nest){ sll_queue_push(list->first, list->last, nest); list->count += 1; } function Code_Index_Nest_Ptr_Array code_index_nest_ptr_array_from_list(Arena *arena, Code_Index_Nest_List *list){ Code_Index_Nest_Ptr_Array array = {}; array.ptrs = push_array_zero(arena, Code_Index_Nest*, list->count); array.count = list->count; i32 counter = 0; for (Code_Index_Nest *node = list->first; node != 0; node = node->next){ array.ptrs[counter] = node; counter += 1; } return(array); } function Code_Index_Note_Ptr_Array code_index_note_ptr_array_from_list(Arena *arena, Code_Index_Note_List *list){ Code_Index_Note_Ptr_Array array = {}; array.ptrs = push_array_zero(arena, Code_Index_Note*, list->count); array.count = list->count; i32 counter = 0; for (Code_Index_Note *node = list->first; node != 0; node = node->next){ array.ptrs[counter] = node; counter += 1; } return(array); } function void code_index_lock(void){ system_mutex_acquire(global_code_index.mutex); } function void code_index_unlock(void){ system_mutex_release(global_code_index.mutex); } function void code_index__hash_file(Code_Index_File *file){ for (Code_Index_Note *node = file->note_list.first; node != 0; node = node->next){ Code_Index_Note_List *list = code_index__list_from_string(node->text); zdll_push_back_NP_(list->first, list->last, node, next_in_hash, prev_in_hash); list->count += 1; } } function void code_index__clear_file(Code_Index_File *file){ for (Code_Index_Note *node = file->note_list.first; node != 0; node = node->next){ Code_Index_Note_List *list = code_index__list_from_string(node->text); zdll_remove_NP_(list->first, list->last, node, next_in_hash, prev_in_hash); list->count -= 1; } } function void code_index_set_file(Buffer_ID buffer, Arena arena, Code_Index_File *index){ Code_Index_File_Storage *storage = 0; Table_Lookup lookup = table_lookup(&global_code_index.buffer_to_index_file, buffer); if (lookup.found_match){ u64 val = 0; table_read(&global_code_index.buffer_to_index_file, lookup, &val); storage = (Code_Index_File_Storage*)IntAsPtr(val); code_index__clear_file(storage->file); linalloc_clear(&storage->arena); } else{ storage = code_index__alloc_storage(); table_insert(&global_code_index.buffer_to_index_file, buffer, (u64)PtrAsInt(storage)); } storage->arena = arena; storage->file = index; code_index__hash_file(index); } function void code_index_erase_file(Buffer_ID buffer){ Table_Lookup lookup = table_lookup(&global_code_index.buffer_to_index_file, buffer); if (lookup.found_match){ u64 val = 0; table_read(&global_code_index.buffer_to_index_file, lookup, &val); Code_Index_File_Storage *storage = (Code_Index_File_Storage*)IntAsPtr(val); code_index__clear_file(storage->file); linalloc_clear(&storage->arena); table_erase(&global_code_index.buffer_to_index_file, lookup); code_index__free_storage(storage); } } function Code_Index_File* code_index_get_file(Buffer_ID buffer){ Code_Index_File *result = 0; Table_Lookup lookup = table_lookup(&global_code_index.buffer_to_index_file, buffer); if (lookup.found_match){ u64 val = 0; table_read(&global_code_index.buffer_to_index_file, lookup, &val); Code_Index_File_Storage *storage = (Code_Index_File_Storage*)IntAsPtr(val); result = storage->file; } return(result); } function void index_shift(i64 *ptr, Range_i64 old_range, u64 new_size){ i64 i = *ptr; if (old_range.min <= i && i < old_range.max){ *ptr = old_range.first; } else if (old_range.max <= i){ *ptr = i + new_size - (old_range.max - old_range.min); } } function void code_index_shift(Code_Index_Nest_Ptr_Array *array, Range_i64 old_range, u64 new_size){ i32 count = array->count; Code_Index_Nest **nest_ptr = array->ptrs; for (i32 i = 0; i < count; i += 1, nest_ptr += 1){ Code_Index_Nest *nest = *nest_ptr; index_shift(&nest->open.min, old_range, new_size); index_shift(&nest->open.max, old_range, new_size); if (nest->is_closed){ index_shift(&nest->close.min, old_range, new_size); index_shift(&nest->close.max, old_range, new_size); } code_index_shift(&nest->nest_array, old_range, new_size); } } function void code_index_shift(Code_Index_File *file, Range_i64 old_range, u64 new_size){ code_index_shift(&file->nest_array, old_range, new_size); } //////////////////////////////// // NOTE(allen): Parser Helpers function void generic_parse_inc(Generic_Parse_State *state){ if (!token_it_inc_all(&state->it)){ state->finished = true; } } function void generic_parse_skip_soft_tokens(Code_Index_File *index, Generic_Parse_State *state){ Token *token = token_it_read(&state->it); for (;token != 0 && !state->finished;){ if (state->in_preprocessor && !HasFlag(token->flags, TokenBaseFlag_PreprocessorBody)){ break; } if (token->kind == TokenBaseKind_Comment){ state->handle_comment(state->app, state->arena, index, token, state->contents); } else if (token->kind == TokenBaseKind_Whitespace){ Range_i64 range = Ii64(token); u8 *ptr = state->contents.str + range.one_past_last - 1; for (i64 i = range.one_past_last - 1; i >= range.first; i -= 1, ptr -= 1){ if (*ptr == '\n'){ state->prev_line_start = ptr + 1; break; } } } else{ break; } generic_parse_inc(state); token = token_it_read(&state->it); } } function void generic_parse_init(Application_Links *app, Arena *arena, String_Const_u8 contents, Token_Array *tokens, Generic_Parse_Comment_Function *handle_comment, Generic_Parse_State *state){ state->app = app; state->arena = arena; state->contents = contents; state->it = token_iterator(0, tokens); state->handle_comment = handle_comment; state->prev_line_start = contents.str; } //////////////////////////////// // NOTE(allen): Parser #if 0 /* // NOTE(allen): grammar syntax (X) = X X Y = X and then Y X? = zero or one X $X = check for X but don't consume [X] = zero or more Xs X | Y = either X or Y * = anything that does not match previous options in a X | Y | ... chain * - X = anything that does not match X or previous options in a Y | Z | ... chain = a token of type X "X" = literally the string "X" X{Y} = X with flag Y // NOTE(allen): grammar of code index parse file: [preprocessor | scope | parens | function | type | * - ] preprocessor: [scope | parens | stmnt]{pp-body} scope: [preprocessor | scope | parens | * - ] paren: [preprocessor | scope | parens | * - ] stmnt-close-pattern: | | | | | stmnt: [type | * - stmnt-close-pattern] stmnt-close-pattern type: struct | union | enum | typedef struct: "struct" $(";" | "{") union: "union" $(";" | "{") enum: "enum" $(";" | "{") typedef: "typedef" [* - ( (";" | "("))] $(";" | "(") function: >"(" ["(" ")" | * - ("(" | ")")] ")" ("{" | ";") */ #endif function Code_Index_Note* index_new_note(Code_Index_File *index, Generic_Parse_State *state, Range_i64 range, Code_Index_Note_Kind kind, Code_Index_Nest *parent){ Code_Index_Note *result = push_array(state->arena, Code_Index_Note, 1); sll_queue_push(index->note_list.first, index->note_list.last, result); index->note_list.count += 1; result->note_kind = kind; result->pos = range; result->text = push_string_copy(state->arena, string_substring(state->contents, range)); result->file = index; result->parent = parent; return(result); } function void cpp_parse_type_structure(Code_Index_File *index, Generic_Parse_State *state, Code_Index_Nest *parent){ generic_parse_inc(state); generic_parse_skip_soft_tokens(index, state); if (state->finished){ return; } Token *token = token_it_read(&state->it); if (token != 0 && token->kind == TokenBaseKind_Identifier){ generic_parse_inc(state); generic_parse_skip_soft_tokens(index, state); Token *peek = token_it_read(&state->it); if (peek != 0 && peek->kind == TokenBaseKind_StatementClose || peek->kind == TokenBaseKind_ScopeOpen){ index_new_note(index, state, Ii64(token), CodeIndexNote_Type, parent); } } } function void cpp_parse_type_def(Code_Index_File *index, Generic_Parse_State *state, Code_Index_Nest *parent){ generic_parse_inc(state); generic_parse_skip_soft_tokens(index, state); for (;;){ b32 did_advance = false; Token *token = token_it_read(&state->it); if (token == 0 || state->finished){ break; } if (token->kind == TokenBaseKind_Identifier){ generic_parse_inc(state); generic_parse_skip_soft_tokens(index, state); did_advance = true; Token *peek = token_it_read(&state->it); if (peek != 0 && peek->kind == TokenBaseKind_StatementClose || peek->kind == TokenBaseKind_ParentheticalOpen){ index_new_note(index, state, Ii64(token), CodeIndexNote_Type, parent); break; } } else if (token->kind == TokenBaseKind_StatementClose || token->kind == TokenBaseKind_ScopeOpen || token->kind == TokenBaseKind_ScopeClose || token->kind == TokenBaseKind_ScopeOpen || token->kind == TokenBaseKind_ScopeClose){ break; } else if (token->kind == TokenBaseKind_Keyword){ String_Const_u8 lexeme = string_substring(state->contents, Ii64(token)); if (string_match(lexeme, string_u8_litexpr("struct")) || string_match(lexeme, string_u8_litexpr("union")) || string_match(lexeme, string_u8_litexpr("enum"))){ break; } } if (!did_advance){ generic_parse_inc(state); generic_parse_skip_soft_tokens(index, state); } } } function void cpp_parse_function(Code_Index_File *index, Generic_Parse_State *state, Code_Index_Nest *parent){ Token *token = token_it_read(&state->it); generic_parse_inc(state); generic_parse_skip_soft_tokens(index, state); if (state->finished){ return; } Token *peek = token_it_read(&state->it); Token *reset_point = peek; if (peek != 0 && peek->sub_kind == TokenCppKind_ParenOp){ b32 at_paren_close = false; i32 paren_nest_level = 0; for (; peek != 0;){ generic_parse_inc(state); generic_parse_skip_soft_tokens(index, state); peek = token_it_read(&state->it); if (peek == 0 || state->finished){ break; } if (peek->kind == TokenBaseKind_ParentheticalOpen){ paren_nest_level += 1; } else if (peek->kind == TokenBaseKind_ParentheticalClose){ if (paren_nest_level > 0){ paren_nest_level -= 1; } else{ at_paren_close = true; break; } } } if (at_paren_close){ generic_parse_inc(state); generic_parse_skip_soft_tokens(index, state); peek = token_it_read(&state->it); if (peek != 0 && peek->kind == TokenBaseKind_ScopeOpen || peek->kind == TokenBaseKind_StatementClose){ index_new_note(index, state, Ii64(token), CodeIndexNote_Function, parent); } } } state->it = token_iterator(state->it.user_id, state->it.tokens, state->it.count, reset_point); } function Code_Index_Nest* generic_parse_statement(Code_Index_File *index, Generic_Parse_State *state); function Code_Index_Nest* generic_parse_preprocessor(Code_Index_File *index, Generic_Parse_State *state); function Code_Index_Nest* generic_parse_scope(Code_Index_File *index, Generic_Parse_State *state); function Code_Index_Nest* generic_parse_paren(Code_Index_File *index, Generic_Parse_State *state); function Code_Index_Nest* generic_parse_statement(Code_Index_File *index, Generic_Parse_State *state){ Token *token = token_it_read(&state->it); Code_Index_Nest *result = push_array_zero(state->arena, Code_Index_Nest, 1); result->kind = CodeIndexNest_Statement; result->open = Ii64(token->pos); result->close = Ii64(max_i64); result->file = index; state->in_statement = true; for (;;){ generic_parse_skip_soft_tokens(index, state); token = token_it_read(&state->it); if (token == 0 || state->finished){ break; } if (state->in_preprocessor){ if (!HasFlag(token->flags, TokenBaseFlag_PreprocessorBody) || token->kind == TokenBaseKind_Preprocessor){ result->is_closed = true; result->close = Ii64(token->pos); break; } } else{ if (token->kind == TokenBaseKind_Preprocessor){ result->is_closed = true; result->close = Ii64(token->pos); break; } } if (token->kind == TokenBaseKind_ScopeOpen || token->kind == TokenBaseKind_ScopeClose || token->kind == TokenBaseKind_ParentheticalOpen){ result->is_closed = true; result->close = Ii64(token->pos); break; } if (token->kind == TokenBaseKind_StatementClose){ result->is_closed = true; result->close = Ii64(token); generic_parse_inc(state); break; } generic_parse_inc(state); } state->in_statement = false; return(result); } function Code_Index_Nest* generic_parse_preprocessor(Code_Index_File *index, Generic_Parse_State *state){ Token *token = token_it_read(&state->it); Code_Index_Nest *result = push_array_zero(state->arena, Code_Index_Nest, 1); result->kind = CodeIndexNest_Preprocessor; result->open = Ii64(token->pos); result->close = Ii64(max_i64); result->file = index; state->in_preprocessor = true; b32 potential_macro = false; if (state->do_cpp_parse){ if (token->sub_kind == TokenCppKind_PPDefine){ potential_macro = true; } } generic_parse_inc(state); for (;;){ generic_parse_skip_soft_tokens(index, state); token = token_it_read(&state->it); if (token == 0 || state->finished){ break; } if (!HasFlag(token->flags, TokenBaseFlag_PreprocessorBody) || token->kind == TokenBaseKind_Preprocessor){ result->is_closed = true; result->close = Ii64(token->pos); break; } if (state->do_cpp_parse && potential_macro){ if (token->sub_kind == TokenCppKind_Identifier){ index_new_note(index, state, Ii64(token), CodeIndexNote_Macro, result); } potential_macro = false; } if (token->kind == TokenBaseKind_ScopeOpen){ Code_Index_Nest *nest = generic_parse_scope(index, state); nest->parent = result; code_index_push_nest(&result->nest_list, nest); continue; } if (token->kind == TokenBaseKind_ParentheticalOpen){ Code_Index_Nest *nest = generic_parse_paren(index, state); nest->parent = result; code_index_push_nest(&result->nest_list, nest); continue; } generic_parse_inc(state); } result->nest_array = code_index_nest_ptr_array_from_list(state->arena, &result->nest_list); state->in_preprocessor = false; return(result); } function Code_Index_Nest* generic_parse_scope(Code_Index_File *index, Generic_Parse_State *state){ Token *token = token_it_read(&state->it); Code_Index_Nest *result = push_array_zero(state->arena, Code_Index_Nest, 1); result->kind = CodeIndexNest_Scope; result->open = Ii64(token); result->close = Ii64(max_i64); result->file = index; state->scope_counter += 1; generic_parse_inc(state); for (;;){ generic_parse_skip_soft_tokens(index, state); token = token_it_read(&state->it); if (token == 0 || state->finished){ break; } if (state->in_preprocessor){ if (!HasFlag(token->flags, TokenBaseFlag_PreprocessorBody) || token->kind == TokenBaseKind_Preprocessor){ break; } } else{ if (token->kind == TokenBaseKind_Preprocessor){ Code_Index_Nest *nest = generic_parse_preprocessor(index, state); code_index_push_nest(&index->nest_list, nest); continue; } } if (token->kind == TokenBaseKind_ScopeClose){ result->is_closed = true; result->close = Ii64(token); generic_parse_inc(state); break; } if (token->kind == TokenBaseKind_ScopeOpen){ Code_Index_Nest *nest = generic_parse_scope(index, state); nest->parent = result; code_index_push_nest(&result->nest_list, nest); continue; } if (token->kind == TokenBaseKind_ParentheticalClose){ generic_parse_inc(state); continue; } if (token->kind == TokenBaseKind_ParentheticalOpen){ Code_Index_Nest *nest = generic_parse_paren(index, state); nest->parent = result; code_index_push_nest(&result->nest_list, nest); // NOTE(allen): after a parenthetical group we consider ourselves immediately // transitioning into a statement nest = generic_parse_statement(index, state); nest->parent = result; code_index_push_nest(&result->nest_list, nest); continue; } { Code_Index_Nest *nest = generic_parse_statement(index, state); nest->parent = result; code_index_push_nest(&result->nest_list, nest); } } result->nest_array = code_index_nest_ptr_array_from_list(state->arena, &result->nest_list); state->scope_counter -= 1; return(result); } function Code_Index_Nest* generic_parse_paren(Code_Index_File *index, Generic_Parse_State *state){ Token *token = token_it_read(&state->it); Code_Index_Nest *result = push_array_zero(state->arena, Code_Index_Nest, 1); result->kind = CodeIndexNest_Paren; result->open = Ii64(token); result->close = Ii64(max_i64); result->file = index; i64 manifested_characters_on_line = 0; { u8 *ptr = state->prev_line_start; u8 *end_ptr = state->contents.str + token->pos; // NOTE(allen): Initial whitespace for (;ptr < end_ptr; ptr += 1){ if (!character_is_whitespace(*ptr)){ break; } } // NOTE(allen): Manifested characters manifested_characters_on_line = (i64)(end_ptr - ptr) + token->size; } state->paren_counter += 1; generic_parse_inc(state); for (;;){ generic_parse_skip_soft_tokens(index, state); token = token_it_read(&state->it); if (token == 0 || state->finished){ break; } if (state->in_preprocessor){ if (!HasFlag(token->flags, TokenBaseFlag_PreprocessorBody) || token->kind == TokenBaseKind_Preprocessor){ break; } } else{ if (token->kind == TokenBaseKind_Preprocessor){ Code_Index_Nest *nest = generic_parse_preprocessor(index, state); code_index_push_nest(&index->nest_list, nest); continue; } } if (token->kind == TokenBaseKind_ParentheticalClose){ result->is_closed = true; result->close = Ii64(token); generic_parse_inc(state); break; } if (token->kind == TokenBaseKind_ScopeClose){ break; } if (token->kind == TokenBaseKind_ScopeOpen){ Code_Index_Nest *nest = generic_parse_scope(index, state); nest->parent = result; code_index_push_nest(&result->nest_list, nest); continue; } if (token->kind == TokenBaseKind_ParentheticalOpen){ Code_Index_Nest *nest = generic_parse_paren(index, state); nest->parent = result; code_index_push_nest(&result->nest_list, nest); continue; } generic_parse_inc(state); } result->nest_array = code_index_nest_ptr_array_from_list(state->arena, &result->nest_list); state->paren_counter -= 1; return(result); } function b32 generic_parse_full_input_breaks(Code_Index_File *index, Generic_Parse_State *state, i32 limit){ b32 result = false; i64 first_index = token_it_index(&state->it); i64 one_past_last_index = first_index + limit; for (;;){ generic_parse_skip_soft_tokens(index, state); Token *token = token_it_read(&state->it); if (token == 0 || state->finished){ result = true; break; } if (token->kind == TokenBaseKind_Preprocessor){ Code_Index_Nest *nest = generic_parse_preprocessor(index, state); code_index_push_nest(&index->nest_list, nest); } else if (token->kind == TokenBaseKind_ScopeOpen){ Code_Index_Nest *nest = generic_parse_scope(index, state); code_index_push_nest(&index->nest_list, nest); } else if (token->kind == TokenBaseKind_ParentheticalOpen){ Code_Index_Nest *nest = generic_parse_paren(index, state); code_index_push_nest(&index->nest_list, nest); } else if (state->do_cpp_parse){ if (token->sub_kind == TokenCppKind_Struct || token->sub_kind == TokenCppKind_Union || token->sub_kind == TokenCppKind_Enum){ cpp_parse_type_structure(index, state, 0); } else if (token->sub_kind == TokenCppKind_Typedef){ cpp_parse_type_def(index, state, 0); } else if (token->sub_kind == TokenCppKind_Identifier){ cpp_parse_function(index, state, 0); } else{ generic_parse_inc(state); } } else{ generic_parse_inc(state); } i64 index = token_it_index(&state->it); if (index >= one_past_last_index){ token = token_it_read(&state->it); if (token == 0){ result = true; } break; } } if (result){ index->nest_array = code_index_nest_ptr_array_from_list(state->arena, &index->nest_list); index->note_array = code_index_note_ptr_array_from_list(state->arena, &index->note_list); } return(result); } //////////////////////////////// // NOTE(allen): Not sure function void default_comment_index(Application_Links *app, Arena *arena, Code_Index_File *index, Token *token, String_Const_u8 contents){ } function void generic_parse_init(Application_Links *app, Arena *arena, String_Const_u8 contents, Token_Array *tokens, Generic_Parse_State *state){ generic_parse_init(app, arena, contents, tokens, default_comment_index, state); } //////////////////////////////// // NOTE(allen): Virtual Whitespace Layout function Token_Pair layout_token_pair(Token_Array *tokens, i64 pos){ Token_Pair result = {}; Token_Iterator_Array it = token_iterator_pos(0, tokens, pos); Token *b = token_it_read(&it); if (b != 0){ if (b->kind == TokenBaseKind_Whitespace){ token_it_inc_non_whitespace(&it); b = token_it_read(&it); } } token_it_dec_non_whitespace(&it); Token *a = token_it_read(&it); if (a != 0){ result.a = *a; } if (b != 0){ result.b = *b; } return(result); } function f32 layout_index_x_shift(Application_Links *app, Layout_Reflex *reflex, Code_Index_Nest *nest, i64 pos, f32 regular_indent, b32 *unresolved_dependence){ f32 result = 0.f; if (nest != 0){ switch (nest->kind){ case CodeIndexNest_Scope: case CodeIndexNest_Preprocessor: { result = layout_index_x_shift(app, reflex, nest->parent, pos, regular_indent, unresolved_dependence); if (nest->open.min < pos && nest->open.max <= pos && (!nest->is_closed || pos < nest->close.min)){ result += regular_indent; } }break; case CodeIndexNest_Statement: { result = layout_index_x_shift(app, reflex, nest->parent, pos, regular_indent, unresolved_dependence); if (nest->open.min < pos && nest->open.max <= pos && (!nest->is_closed || pos < nest->close.min)){ result += regular_indent; } }break; case CodeIndexNest_Paren: { Rect_f32 box = layout_reflex_get_rect(app, reflex, nest->open.max - 1, unresolved_dependence); result = box.x1; }break; } } return(result); } function f32 layout_index_x_shift(Application_Links *app, Layout_Reflex *reflex, Code_Index_Nest *nest, i64 pos, f32 regular_indent){ b32 ignore; return(layout_index_x_shift(app, reflex, nest, pos, regular_indent, &ignore)); } function f32 layout_index_x_shift(Application_Links *app, Layout_Reflex *reflex, Code_Index_File *file, i64 pos, f32 regular_indent, b32 *unresolved_dependence){ f32 indent = 0; Code_Index_Nest *nest = code_index_get_nest(file, pos); if (nest != 0){ indent = layout_index_x_shift(app, reflex, nest, pos, regular_indent, unresolved_dependence); } return(indent); } function f32 layout_index_x_shift(Application_Links *app, Layout_Reflex *reflex, Code_Index_File *file, i64 pos, f32 regular_indent){ b32 ignore; return(layout_index_x_shift(app, reflex, file, pos, regular_indent, &ignore)); } function void layout_index__emit_chunk(LefRig_TopBot_Layout_Vars *pos_vars, Face_ID face, Arena *arena, u8 *text_str, i64 range_first, u8 *ptr, u8 *end, Layout_Item_List *list){ for (;ptr < end;){ Character_Consume_Result consume = utf8_consume(ptr, (u64)(end - ptr)); if (consume.codepoint != '\r'){ i64 index = layout_index_from_ptr(ptr, text_str, range_first); if (consume.codepoint != max_u32){ lr_tb_write(pos_vars, face, arena, list, index, consume.codepoint); } else{ lr_tb_write_byte(pos_vars, face, arena, list, index, *ptr); } } ptr += consume.inc; } } function i32 layout_token_score_wrap_token(Token_Pair *pair, Token_Cpp_Kind kind){ i32 result = 0; if (pair->a.sub_kind != kind && pair->b.sub_kind == kind){ result -= 1; } else if (pair->a.sub_kind == kind && pair->b.sub_kind != kind){ result += 1; } return(result); } function Layout_Item_List layout_index__inner(Application_Links *app, Arena *arena, Buffer_ID buffer, Range_i64 range, Face_ID face, f32 width, Code_Index_File *file, Layout_Wrap_Kind kind){ Scratch_Block scratch(app, arena); Token_Array tokens = get_token_array_from_buffer(app, buffer); Token_Array *tokens_ptr = &tokens; Layout_Item_List list = get_empty_item_list(range); String_Const_u8 text = push_buffer_range(app, scratch, buffer, range); Face_Advance_Map advance_map = get_face_advance_map(app, face); Face_Metrics metrics = get_face_metrics(app, face); f32 tab_width = (f32)def_get_config_u64(app, vars_save_string_lit("default_tab_width")); tab_width = clamp_bot(1, tab_width); LefRig_TopBot_Layout_Vars pos_vars = get_lr_tb_layout_vars(&advance_map, &metrics, tab_width, width); u64 vw_indent = def_get_config_u64(app, vars_save_string_lit("virtual_whitespace_regular_indent")); f32 regular_indent = metrics.space_advance*vw_indent; f32 wrap_align_x = width - metrics.normal_advance; Layout_Reflex reflex = get_layout_reflex(&list, buffer, width, face); if (text.size == 0){ lr_tb_write_blank(&pos_vars, face, arena, &list, range.start); } else{ b32 first_of_the_line = true; Newline_Layout_Vars newline_vars = get_newline_layout_vars(); u8 *ptr = text.str; u8 *end_ptr = ptr + text.size; u8 *word_ptr = ptr; u8 *pending_wrap_ptr = ptr; f32 pending_wrap_x = 0.f; i32 pending_wrap_paren_nest_count = 0; i32 pending_wrap_token_score = 0; f32 pending_wrap_accumulated_w = 0.f; start: if (ptr == end_ptr){ i64 index = layout_index_from_ptr(ptr, text.str, range.first); f32 shift = layout_index_x_shift(app, &reflex, file, index, regular_indent); lr_tb_advance_x_without_item(&pos_vars, shift); goto finish; } if (!character_is_whitespace(*ptr)){ i64 index = layout_index_from_ptr(ptr, text.str, range.first); f32 shift = layout_index_x_shift(app, &reflex, file, index, regular_indent); lr_tb_advance_x_without_item(&pos_vars, shift); goto consuming_non_whitespace; } { for (;ptr < end_ptr; ptr += 1){ if (!character_is_whitespace(*ptr)){ pending_wrap_ptr = ptr; word_ptr = ptr; i64 index = layout_index_from_ptr(ptr, text.str, range.first); f32 shift = layout_index_x_shift(app, &reflex, file, index, regular_indent); lr_tb_advance_x_without_item(&pos_vars, shift); goto consuming_non_whitespace; } if (*ptr == '\r'){ i64 index = layout_index_from_ptr(ptr, text.str, range.first); newline_layout_consume_CR(&newline_vars, index); } else if (*ptr == '\n'){ pending_wrap_ptr = ptr; i64 index = layout_index_from_ptr(ptr, text.str, range.first); f32 shift = layout_index_x_shift(app, &reflex, file, index, regular_indent); lr_tb_advance_x_without_item(&pos_vars, shift); goto consuming_normal_whitespace; } } if (ptr == end_ptr){ pending_wrap_ptr = ptr; i64 index = layout_index_from_ptr(ptr - 1, text.str, range.first); f32 shift = layout_index_x_shift(app, &reflex, file, index, regular_indent); lr_tb_advance_x_without_item(&pos_vars, shift); goto finish; } } consuming_non_whitespace: { for (;ptr <= end_ptr; ptr += 1){ if (ptr == end_ptr || character_is_whitespace(*ptr)){ break; } } // NOTE(allen): measure this word newline_layout_consume_default(&newline_vars); String_Const_u8 word = SCu8(word_ptr, ptr); u8 *word_end = ptr; { f32 word_advance = 0.f; ptr = word.str; for (;ptr < word_end;){ Character_Consume_Result consume = utf8_consume(ptr, (u64)(word_end - ptr)); if (consume.codepoint != max_u32){ word_advance += lr_tb_advance(&pos_vars, face, consume.codepoint); } else{ word_advance += lr_tb_advance_byte(&pos_vars); } ptr += consume.inc; } pending_wrap_accumulated_w += word_advance; } if (!first_of_the_line && (kind == Layout_Wrapped) && lr_tb_crosses_width(&pos_vars, pending_wrap_accumulated_w)){ i64 index = layout_index_from_ptr(pending_wrap_ptr, text.str, range.first); lr_tb_align_rightward(&pos_vars, wrap_align_x); lr_tb_write_ghost(&pos_vars, face, arena, &list, index, '\\'); lr_tb_next_line(&pos_vars); #if 0 f32 shift = layout_index_x_shift(app, &reflex, file, index, regular_indent); lr_tb_advance_x_without_item(&pos_vars, shift); #endif ptr = pending_wrap_ptr; pending_wrap_accumulated_w = 0.f; first_of_the_line = true; goto start; } } consuming_normal_whitespace: for (; ptr < end_ptr; ptr += 1){ if (!character_is_whitespace(*ptr)){ u8 *new_wrap_ptr = ptr; i64 index = layout_index_from_ptr(new_wrap_ptr, text.str, range.first); Code_Index_Nest *new_wrap_nest = code_index_get_nest(file, index); b32 invalid_wrap_x = false; f32 new_wrap_x = layout_index_x_shift(app, &reflex, new_wrap_nest, index, regular_indent, &invalid_wrap_x); if (invalid_wrap_x){ new_wrap_x = max_f32; } i32 new_wrap_paren_nest_count = 0; for (Code_Index_Nest *nest = new_wrap_nest; nest != 0; nest = nest->parent){ if (nest->kind == CodeIndexNest_Paren){ new_wrap_paren_nest_count += 1; } } Token_Pair new_wrap_token_pair = layout_token_pair(tokens_ptr, index); // TODO(allen): pull out the token scoring part and make it replacable for other // language's token based wrap scoring needs. i32 token_score = 0; if (new_wrap_token_pair.a.kind == TokenBaseKind_Keyword){ if (new_wrap_token_pair.b.kind == TokenBaseKind_ParentheticalOpen || new_wrap_token_pair.b.kind == TokenBaseKind_Keyword){ token_score -= 2; } } token_score += layout_token_score_wrap_token(&new_wrap_token_pair, TokenCppKind_Eq); token_score += layout_token_score_wrap_token(&new_wrap_token_pair, TokenCppKind_PlusEq); token_score += layout_token_score_wrap_token(&new_wrap_token_pair, TokenCppKind_MinusEq); token_score += layout_token_score_wrap_token(&new_wrap_token_pair, TokenCppKind_StarEq); token_score += layout_token_score_wrap_token(&new_wrap_token_pair, TokenCppKind_DivEq); token_score += layout_token_score_wrap_token(&new_wrap_token_pair, TokenCppKind_ModEq); token_score += layout_token_score_wrap_token(&new_wrap_token_pair, TokenCppKind_LeftLeftEq); token_score += layout_token_score_wrap_token(&new_wrap_token_pair, TokenCppKind_RightRightEq); token_score += layout_token_score_wrap_token(&new_wrap_token_pair, TokenCppKind_Comma); token_score += layout_token_score_wrap_token(&new_wrap_token_pair, TokenCppKind_AndAnd); token_score += layout_token_score_wrap_token(&new_wrap_token_pair, TokenCppKind_OrOr); token_score += layout_token_score_wrap_token(&new_wrap_token_pair, TokenCppKind_Ternary); token_score += layout_token_score_wrap_token(&new_wrap_token_pair, TokenCppKind_Colon); token_score += layout_token_score_wrap_token(&new_wrap_token_pair, TokenCppKind_Semicolon); i32 new_wrap_token_score = token_score; b32 new_wrap_ptr_is_better = false; if (first_of_the_line){ new_wrap_ptr_is_better = true; } else{ if (new_wrap_token_score > pending_wrap_token_score){ new_wrap_ptr_is_better = true; } else if (new_wrap_token_score == pending_wrap_token_score){ f32 new_score = new_wrap_paren_nest_count*10.f + new_wrap_x; f32 old_score = pending_wrap_paren_nest_count*10.f + pending_wrap_x + metrics.normal_advance*4.f + pending_wrap_accumulated_w*0.5f; if (new_score < old_score){ new_wrap_ptr_is_better = true; } } } if (new_wrap_ptr_is_better){ layout_index__emit_chunk(&pos_vars, face, arena, text.str, range.first, pending_wrap_ptr, new_wrap_ptr, &list); first_of_the_line = false; pending_wrap_ptr = new_wrap_ptr; pending_wrap_paren_nest_count = new_wrap_paren_nest_count; pending_wrap_x = layout_index_x_shift(app, &reflex, new_wrap_nest, index, regular_indent); pending_wrap_paren_nest_count = new_wrap_paren_nest_count; pending_wrap_token_score = new_wrap_token_score; pending_wrap_accumulated_w = 0.f; } word_ptr = ptr; goto consuming_non_whitespace; } i64 index = layout_index_from_ptr(ptr, text.str, range.first); switch (*ptr){ default: { newline_layout_consume_default(&newline_vars); pending_wrap_accumulated_w += lr_tb_advance(&pos_vars, face, *ptr); }break; case '\r': { newline_layout_consume_CR(&newline_vars, index); }break; case '\n': { layout_index__emit_chunk(&pos_vars, face, arena, text.str, range.first, pending_wrap_ptr, ptr, &list); pending_wrap_ptr = ptr + 1; pending_wrap_accumulated_w = 0.f; u64 newline_index = newline_layout_consume_LF(&newline_vars, index); lr_tb_write_blank(&pos_vars, face, arena, &list, newline_index); lr_tb_next_line(&pos_vars); first_of_the_line = true; ptr += 1; goto start; }break; } } finish: if (newline_layout_consume_finish(&newline_vars)){ layout_index__emit_chunk(&pos_vars, face, arena, text.str, range.first, pending_wrap_ptr, ptr, &list); i64 index = layout_index_from_ptr(ptr, text.str, range.first); lr_tb_write_blank(&pos_vars, face, arena, &list, index); } } layout_item_list_finish(&list, -pos_vars.line_to_text_shift); return(list); } function Layout_Item_List layout_virt_indent_index(Application_Links *app, Arena *arena, Buffer_ID buffer, Range_i64 range, Face_ID face, f32 width, Layout_Wrap_Kind kind){ Layout_Item_List result = {}; b32 enable_virtual_whitespace = def_get_config_b32(vars_save_string_lit("enable_virtual_whitespace")); if (enable_virtual_whitespace){ code_index_lock(); Code_Index_File *file = code_index_get_file(buffer); if (file != 0){ result = layout_index__inner(app, arena, buffer, range, face, width, file, kind); } code_index_unlock(); if (file == 0){ result = layout_virt_indent_literal(app, arena, buffer, range, face, width, kind); } } else{ result = layout_basic(app, arena, buffer, range, face, width, kind); } return(result); } function Layout_Item_List layout_virt_indent_index_unwrapped(Application_Links *app, Arena *arena, Buffer_ID buffer, Range_i64 range, Face_ID face, f32 width){ return(layout_virt_indent_index(app, arena, buffer, range, face, width, Layout_Unwrapped)); } function Layout_Item_List layout_virt_indent_index_wrapped(Application_Links *app, Arena *arena, Buffer_ID buffer, Range_i64 range, Face_ID face, f32 width){ return(layout_virt_indent_index(app, arena, buffer, range, face, width, Layout_Wrapped)); } function Layout_Item_List layout_virt_indent_index_generic(Application_Links *app, Arena *arena, Buffer_ID buffer, Range_i64 range, Face_ID face, f32 width){ Managed_Scope scope = buffer_get_managed_scope(app, buffer); b32 *wrap_lines_ptr = scope_attachment(app, scope, buffer_wrap_lines, b32); b32 wrap_lines = (wrap_lines_ptr != 0 && *wrap_lines_ptr); return(layout_virt_indent_index(app, arena, buffer, range, face, width, wrap_lines?Layout_Wrapped:Layout_Unwrapped)); } CUSTOM_COMMAND_SIG(toggle_virtual_whitespace) CUSTOM_DOC("Toggles virtual whitespace for all files.") { String_ID key = vars_save_string_lit("enable_virtual_whitespace"); b32 enable_virtual_whitespace = def_get_config_b32(key); def_set_config_b32(key, !enable_virtual_whitespace); } // BOTTOM