diff --git a/4coder_base_commands.cpp b/4coder_base_commands.cpp index 03ed5caa..cf456813 100644 --- a/4coder_base_commands.cpp +++ b/4coder_base_commands.cpp @@ -1099,6 +1099,46 @@ CUSTOM_DOC("Deletes the file of the current buffer if 4coder has the appropriate } } +CUSTOM_COMMAND_SIG(save_to_query) +CUSTOM_DOC("Queries the user for a name and saves the contents of the current buffer, altering the buffer's name too.") +{ + View_Summary view = get_active_view(app, AccessAll); + Buffer_Summary buffer = get_buffer(app, view.buffer_id, AccessAll); + + // Query the user + Query_Bar bar; + + char prompt_space[4096]; + bar.prompt = make_fixed_width_string(prompt_space); + append(&bar.prompt, "Save '"); + append(&bar.prompt, make_string(buffer.buffer_name, buffer.buffer_name_len)); + append(&bar.prompt, "' to: "); + + char name_space[4096]; + bar.string = make_fixed_width_string(name_space); + if (!query_user_string(app, &bar)) return; + if (bar.string.size == 0) return; + + char new_file_name_space[4096]; + String new_file_name = make_fixed_width_string(new_file_name_space); + int32_t hot_dir_size = directory_get_hot(app, 0, 0); + if (new_file_name.size + hot_dir_size <= new_file_name.memory_size){ + new_file_name.size += directory_get_hot(app, new_file_name.str + new_file_name.size, new_file_name.memory_size - new_file_name.size); + //append(&new_file_name, "/"); + if (append(&new_file_name, bar.string)){ + if (save_buffer(app, &buffer, new_file_name.str, new_file_name.size, BufferSave_IgnoreDirtyFlag)){ + Buffer_Summary new_buffer = create_buffer(app, new_file_name.str, new_file_name.size, BufferCreate_NeverNew|BufferCreate_JustChangedFile); + if (new_buffer.exists){ + if (new_buffer.buffer_id != buffer.buffer_id){ + kill_buffer(app, buffer_identifier(buffer.buffer_id), 0, BufferKill_AlwaysKill); + view_set_buffer(app, &view, new_buffer.buffer_id, 0); + } + } + } + } + } +} + CUSTOM_COMMAND_SIG(rename_file_query) CUSTOM_DOC("Queries the user for a new name and renames the file of the current buffer, altering the buffer's name too.") { diff --git a/4coder_default_hooks.cpp b/4coder_default_hooks.cpp index 6e329127..2250fcfb 100644 --- a/4coder_default_hooks.cpp +++ b/4coder_default_hooks.cpp @@ -293,7 +293,7 @@ OPEN_FILE_HOOK_SIG(default_file_settings){ buffer_set_setting(app, &buffer, BufferSetting_LexWithoutStrings, true); buffer_set_setting(app, &buffer, BufferSetting_VirtualWhitespace, true); } - else if (treat_as_code && enable_code_wrapping && buffer.size < (512 << 10)){ + else if (treat_as_code && enable_code_wrapping && buffer.size < (128 << 10)){ // NOTE(allen|a4.0.12): There is a little bit of grossness going on here. // If we set BufferSetting_Lex to true, it will launch a lexing job. // If a lexing job is active when we set BufferSetting_VirtualWhitespace, the call can fail. diff --git a/4coder_default_include.cpp b/4coder_default_include.cpp index f9c4e285..dc2da6e3 100644 --- a/4coder_default_include.cpp +++ b/4coder_default_include.cpp @@ -354,15 +354,15 @@ CUSTOM_DOC("Create a copy of the line on which the cursor sits.") Temp_Memory temp = begin_temp_memory(part); String line_string = {0}; - read_line(app, part, &buffer, view.cursor.line, &line_string); - - push_array(part, char, 1); - ++line_string.memory_size; - append_s_char(&line_string, '\n'); - - int32_t pos = buffer_get_line_end(app, &buffer, view.cursor.line) + 1; - buffer_replace_range(app, &buffer, pos, pos, line_string.str, line_string.size); - + char *before_line = push_array(part, char, 1); + if (read_line(app, part, &buffer, view.cursor.line, &line_string)){ + *before_line = '\n'; + line_string.str = before_line; + line_string.size += 1; + + int32_t pos = buffer_get_line_end(app, &buffer, view.cursor.line); + buffer_replace_range(app, &buffer, pos, pos, line_string.str, line_string.size); + } end_temp_memory(temp); } diff --git a/4coder_generated/command_metadata.h b/4coder_generated/command_metadata.h index 44cd2d60..0ac65252 100644 --- a/4coder_generated/command_metadata.h +++ b/4coder_generated/command_metadata.h @@ -1,7 +1,7 @@ #define command_id(c) (fcoder_metacmd_ID_##c) #define command_metadata(c) (&fcoder_metacmd_table[command_id(c)]) #define command_metadata_by_id(id) (&fcoder_metacmd_table[id]) -#define command_one_past_last_id 194 +#define command_one_past_last_id 195 #if defined(CUSTOM_COMMAND_SIG) #define PROC_LINKS(x,y) x #else @@ -148,6 +148,7 @@ CUSTOM_COMMAND_SIG(reverse_search); CUSTOM_COMMAND_SIG(reverse_search_identifier); CUSTOM_COMMAND_SIG(save); CUSTOM_COMMAND_SIG(save_all_dirty_buffers); +CUSTOM_COMMAND_SIG(save_to_query); CUSTOM_COMMAND_SIG(scope_absorb_down); CUSTOM_COMMAND_SIG(search); CUSTOM_COMMAND_SIG(search_identifier); @@ -213,7 +214,7 @@ char *source_name; int32_t source_name_len; int32_t line_number; }; -static Command_Metadata fcoder_metacmd_table[194] = { +static Command_Metadata fcoder_metacmd_table[195] = { { PROC_LINKS(allow_mouse, 0), "allow_mouse", 11, "Shows the mouse and causes all mouse input to be processed normally.", 68, "C:\\work\\4ed\\code\\4coder_default_framework.h", 47, 232 }, { PROC_LINKS(auto_tab_line_at_cursor, 0), "auto_tab_line_at_cursor", 23, "Auto-indents the line on which the cursor sits.", 47, "C:\\work\\4ed\\code\\4coder_auto_indent.cpp", 43, 667 }, { PROC_LINKS(auto_tab_range, 0), "auto_tab_range", 14, "Auto-indents the range between the cursor and the mark.", 55, "C:\\work\\4ed\\code\\4coder_auto_indent.cpp", 43, 678 }, @@ -277,12 +278,12 @@ static Command_Metadata fcoder_metacmd_table[194] = { { PROC_LINKS(if0_off, 0), "if0_off", 7, "Surround the range between the cursor and mark with an '#if 0' and an '#endif'", 78, "C:\\work\\4ed\\code\\4coder_default_include.cpp", 47, 537 }, { PROC_LINKS(increase_face_size, 0), "increase_face_size", 18, "Increase the size of the face used by the current buffer.", 57, "C:\\work\\4ed\\code\\4coder_base_commands.cpp", 45, 617 }, { PROC_LINKS(increase_line_wrap, 0), "increase_line_wrap", 18, "Increases the current buffer's width for line wrapping.", 55, "C:\\work\\4ed\\code\\4coder_base_commands.cpp", 45, 595 }, -{ PROC_LINKS(interactive_kill_buffer, 0), "interactive_kill_buffer", 23, "Interactively kill an open buffer.", 34, "C:\\work\\4ed\\code\\4coder_base_commands.cpp", 45, 1215 }, -{ PROC_LINKS(interactive_new, 0), "interactive_new", 15, "Interactively creates a new file.", 33, "C:\\work\\4ed\\code\\4coder_base_commands.cpp", 45, 1191 }, -{ PROC_LINKS(interactive_open, 0), "interactive_open", 16, "Interactively opens a file.", 27, "C:\\work\\4ed\\code\\4coder_base_commands.cpp", 45, 1197 }, -{ PROC_LINKS(interactive_open_or_new, 0), "interactive_open_or_new", 23, "Interactively opens or creates a new file.", 42, "C:\\work\\4ed\\code\\4coder_base_commands.cpp", 45, 1203 }, -{ PROC_LINKS(interactive_switch_buffer, 0), "interactive_switch_buffer", 25, "Interactively switch to an open buffer.", 39, "C:\\work\\4ed\\code\\4coder_base_commands.cpp", 45, 1209 }, -{ PROC_LINKS(kill_buffer, 0), "kill_buffer", 11, "Kills the current buffer.", 25, "C:\\work\\4ed\\code\\4coder_base_commands.cpp", 45, 1233 }, +{ PROC_LINKS(interactive_kill_buffer, 0), "interactive_kill_buffer", 23, "Interactively kill an open buffer.", 34, "C:\\work\\4ed\\code\\4coder_base_commands.cpp", 45, 1255 }, +{ PROC_LINKS(interactive_new, 0), "interactive_new", 15, "Interactively creates a new file.", 33, "C:\\work\\4ed\\code\\4coder_base_commands.cpp", 45, 1231 }, +{ PROC_LINKS(interactive_open, 0), "interactive_open", 16, "Interactively opens a file.", 27, "C:\\work\\4ed\\code\\4coder_base_commands.cpp", 45, 1237 }, +{ PROC_LINKS(interactive_open_or_new, 0), "interactive_open_or_new", 23, "Interactively opens or creates a new file.", 42, "C:\\work\\4ed\\code\\4coder_base_commands.cpp", 45, 1243 }, +{ PROC_LINKS(interactive_switch_buffer, 0), "interactive_switch_buffer", 25, "Interactively switch to an open buffer.", 39, "C:\\work\\4ed\\code\\4coder_base_commands.cpp", 45, 1249 }, +{ PROC_LINKS(kill_buffer, 0), "kill_buffer", 11, "Kills the current buffer.", 25, "C:\\work\\4ed\\code\\4coder_base_commands.cpp", 45, 1273 }, { PROC_LINKS(kill_rect, 0), "kill_rect", 9, "Delete characters in a rectangular region. Range testing is done by unwrapped-xy coordinates.", 93, "C:\\work\\4ed\\code\\power\\4coder_experiments.cpp", 50, 31 }, { PROC_LINKS(left_adjust_view, 0), "left_adjust_view", 16, "Sets the left size of the view near the x position of the cursor.", 65, "C:\\work\\4ed\\code\\4coder_base_commands.cpp", 45, 151 }, { PROC_LINKS(list_all_functions_current_buffer, 0), "list_all_functions_current_buffer", 33, "Creates a jump list of lines of the current buffer that appear to define or declare functions.", 94, "C:\\work\\4ed\\code\\4coder_function_list.cpp", 45, 348 }, @@ -297,7 +298,7 @@ static Command_Metadata fcoder_metacmd_table[194] = { { PROC_LINKS(list_all_substring_locations, 0), "list_all_substring_locations", 28, "Queries the user for a string and lists all case-sensitive substring matches found in all open buffers.", 103, "C:\\work\\4ed\\code\\4coder_search.cpp", 38, 712 }, { PROC_LINKS(list_all_substring_locations_case_insensitive, 0), "list_all_substring_locations_case_insensitive", 45, "Queries the user for a string and lists all case-insensitive substring matches found in all open buffers.", 105, "C:\\work\\4ed\\code\\4coder_search.cpp", 38, 732 }, { PROC_LINKS(load_project, 0), "load_project", 12, "Looks for a project.4coder file in the current directory and tries to load it. Looks in parent directories until a project file is found or there are no more parents.", 167, "C:\\work\\4ed\\code\\4coder_project_commands.cpp", 48, 408 }, -{ PROC_LINKS(make_directory_query, 0), "make_directory_query", 20, "Queries the user for a name and creates a new directory with the given name.", 76, "C:\\work\\4ed\\code\\4coder_base_commands.cpp", 45, 1144 }, +{ PROC_LINKS(make_directory_query, 0), "make_directory_query", 20, "Queries the user for a name and creates a new directory with the given name.", 76, "C:\\work\\4ed\\code\\4coder_base_commands.cpp", 45, 1184 }, { PROC_LINKS(miblo_decrement_basic, 0), "miblo_decrement_basic", 21, "Decrement an integer under the cursor by one.", 45, "C:\\work\\4ed\\code\\power\\4coder_miblo_numbers.cpp", 52, 119 }, { PROC_LINKS(miblo_decrement_time_stamp, 0), "miblo_decrement_time_stamp", 26, "Decrement a time stamp under the cursor by one second. (format [m]m:ss or h:mm:ss", 81, "C:\\work\\4ed\\code\\power\\4coder_miblo_numbers.cpp", 52, 392 }, { PROC_LINKS(miblo_decrement_time_stamp_minute, 0), "miblo_decrement_time_stamp_minute", 33, "Decrement a time stamp under the cursor by one minute. (format [m]m:ss or h:mm:ss", 81, "C:\\work\\4ed\\code\\power\\4coder_miblo_numbers.cpp", 52, 404 }, @@ -320,8 +321,8 @@ static Command_Metadata fcoder_metacmd_table[194] = { { PROC_LINKS(newline_or_goto_position_sticky, 0), "newline_or_goto_position_sticky", 31, "If the buffer in the active view is writable, inserts a character, otherwise performs goto_jump_at_cursor.", 106, "C:\\work\\4ed\\code\\4coder_jump_sticky.cpp", 43, 585 }, { PROC_LINKS(open_all_code, 0), "open_all_code", 13, "Open all code in the current directory. File types are determined by extensions. An extension is considered code based on the extensions specified in 4coder.config.", 164, "C:\\work\\4ed\\code\\4coder_project_commands.cpp", 48, 165 }, { PROC_LINKS(open_all_code_recursive, 0), "open_all_code_recursive", 23, "Works as open_all_code but also runs in all subdirectories.", 59, "C:\\work\\4ed\\code\\4coder_project_commands.cpp", 48, 180 }, -{ PROC_LINKS(open_color_tweaker, 0), "open_color_tweaker", 18, "Opens the 4coder colors and fonts selector menu.", 48, "C:\\work\\4ed\\code\\4coder_base_commands.cpp", 45, 1239 }, -{ PROC_LINKS(open_debug, 0), "open_debug", 10, "Opens a debug view for internal use.", 36, "C:\\work\\4ed\\code\\4coder_base_commands.cpp", 45, 1245 }, +{ PROC_LINKS(open_color_tweaker, 0), "open_color_tweaker", 18, "Opens the 4coder colors and fonts selector menu.", 48, "C:\\work\\4ed\\code\\4coder_base_commands.cpp", 45, 1279 }, +{ PROC_LINKS(open_debug, 0), "open_debug", 10, "Opens a debug view for internal use.", 36, "C:\\work\\4ed\\code\\4coder_base_commands.cpp", 45, 1285 }, { PROC_LINKS(open_file_in_quotes, 0), "open_file_in_quotes", 19, "Reads a filename from surrounding '\"' characters and attempts to open the corresponding file.", 94, "C:\\work\\4ed\\code\\4coder_default_include.cpp", 47, 634 }, { PROC_LINKS(open_in_other, 0), "open_in_other", 13, "Reads a filename from surrounding '\"' characters and attempts to open the corresponding file, displaying it in the other view.", 127, "C:\\work\\4ed\\code\\4coder_default_include.cpp", 47, 651 }, { PROC_LINKS(open_long_braces, 0), "open_long_braces", 16, "At the cursor, insert a '{' and '}' separated by a blank line.", 62, "C:\\work\\4ed\\code\\4coder_default_include.cpp", 47, 513 }, @@ -342,18 +343,19 @@ static Command_Metadata fcoder_metacmd_table[194] = { { PROC_LINKS(query_replace, 0), "query_replace", 13, "Queries the user for two strings, and incrementally replaces every occurence of the first string with the second string.", 120, "C:\\work\\4ed\\code\\4coder_base_commands.cpp", 45, 997 }, { PROC_LINKS(query_replace_identifier, 0), "query_replace_identifier", 24, "Queries the user for a string, and incrementally replace every occurence of the word or token found at the cursor with the specified string.", 140, "C:\\work\\4ed\\code\\4coder_base_commands.cpp", 45, 1018 }, { PROC_LINKS(query_replace_selection, 0), "query_replace_selection", 23, "Queries the user for a string, and incrementally replace every occurence of the string found in the selected range with the specified string.", 141, "C:\\work\\4ed\\code\\4coder_default_include.cpp", 47, 240 }, -{ PROC_LINKS(redo, 0), "redo", 4, "Advances forewards through the undo history.", 44, "C:\\work\\4ed\\code\\4coder_base_commands.cpp", 45, 1185 }, +{ PROC_LINKS(redo, 0), "redo", 4, "Advances forewards through the undo history.", 44, "C:\\work\\4ed\\code\\4coder_base_commands.cpp", 45, 1225 }, { PROC_LINKS(reload_current_project, 0), "reload_current_project", 22, "If a project file has already been loaded, reloads the same file. Useful for when the project configuration is changed.", 120, "C:\\work\\4ed\\code\\4coder_project_commands.cpp", 48, 479 }, { PROC_LINKS(remap_interactive, 0), "remap_interactive", 17, "Switch to a named key binding map.", 34, "C:\\work\\4ed\\code\\4coder_default_framework.h", 47, 743 }, -{ PROC_LINKS(rename_file_query, 0), "rename_file_query", 17, "Queries the user for a new name and renames the file of the current buffer, altering the buffer's name too.", 107, "C:\\work\\4ed\\code\\4coder_base_commands.cpp", 45, 1102 }, +{ PROC_LINKS(rename_file_query, 0), "rename_file_query", 17, "Queries the user for a new name and renames the file of the current buffer, altering the buffer's name too.", 107, "C:\\work\\4ed\\code\\4coder_base_commands.cpp", 45, 1142 }, { PROC_LINKS(rename_parameter, 0), "rename_parameter", 16, "If the cursor is found to be on the name of a function parameter in the signature of a function definition, all occurences within the scope of the function will be replaced with a new provided string.", 200, "C:\\work\\4ed\\code\\power\\4coder_experiments.cpp", 50, 387 }, -{ PROC_LINKS(reopen, 0), "reopen", 6, "Reopen the current buffer from the hard drive.", 46, "C:\\work\\4ed\\code\\4coder_base_commands.cpp", 45, 1221 }, +{ PROC_LINKS(reopen, 0), "reopen", 6, "Reopen the current buffer from the hard drive.", 46, "C:\\work\\4ed\\code\\4coder_base_commands.cpp", 45, 1261 }, { PROC_LINKS(replace_all_occurrences, 0), "replace_all_occurrences", 23, "Queries the user for two strings, and replaces all occurrences of the first string with the second string in all open buffers.", 126, "C:\\work\\4ed\\code\\power\\4coder_experiments.cpp", 50, 773 }, { PROC_LINKS(replace_in_range, 0), "replace_in_range", 16, "Queries the user for two strings, and replaces all occurences of the first string in the range between the cursor and the mark with the second string.", 150, "C:\\work\\4ed\\code\\4coder_base_commands.cpp", 45, 894 }, { PROC_LINKS(reverse_search, 0), "reverse_search", 14, "Begins an incremental search up through the current buffer for a user specified string.", 87, "C:\\work\\4ed\\code\\4coder_base_commands.cpp", 45, 865 }, { PROC_LINKS(reverse_search_identifier, 0), "reverse_search_identifier", 25, "Begins an incremental search up through the current buffer for the word or token under the cursor.", 98, "C:\\work\\4ed\\code\\4coder_base_commands.cpp", 45, 883 }, -{ PROC_LINKS(save, 0), "save", 4, "Saves the current buffer.", 25, "C:\\work\\4ed\\code\\4coder_base_commands.cpp", 45, 1227 }, +{ PROC_LINKS(save, 0), "save", 4, "Saves the current buffer.", 25, "C:\\work\\4ed\\code\\4coder_base_commands.cpp", 45, 1267 }, { PROC_LINKS(save_all_dirty_buffers, 0), "save_all_dirty_buffers", 22, "Saves all buffers marked dirty (showing the '*' indicator).", 59, "C:\\work\\4ed\\code\\4coder_base_commands.cpp", 45, 1041 }, +{ PROC_LINKS(save_to_query, 0), "save_to_query", 13, "Queries the user for a name and saves the contents of the current buffer, altering the buffer's name too.", 105, "C:\\work\\4ed\\code\\4coder_base_commands.cpp", 45, 1102 }, { PROC_LINKS(scope_absorb_down, 0), "scope_absorb_down", 17, "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.", 143, "C:\\work\\4ed\\code\\4coder_scope_commands.cpp", 46, 751 }, { PROC_LINKS(search, 0), "search", 6, "Begins an incremental search down through the current buffer for a user specified string.", 89, "C:\\work\\4ed\\code\\4coder_base_commands.cpp", 45, 858 }, { PROC_LINKS(search_identifier, 0), "search_identifier", 17, "Begins an incremental search down through the current buffer for the word or token under the cursor.", 100, "C:\\work\\4ed\\code\\4coder_base_commands.cpp", 45, 872 }, @@ -395,7 +397,7 @@ static Command_Metadata fcoder_metacmd_table[194] = { { PROC_LINKS(toggle_mouse, 0), "toggle_mouse", 12, "Toggles the mouse suppression mode, see suppress_mouse and allow_mouse.", 71, "C:\\work\\4ed\\code\\4coder_default_framework.h", 47, 238 }, { PROC_LINKS(toggle_show_whitespace, 0), "toggle_show_whitespace", 22, "Toggles the current buffer's whitespace visibility status.", 58, "C:\\work\\4ed\\code\\4coder_base_commands.cpp", 45, 652 }, { PROC_LINKS(toggle_virtual_whitespace, 0), "toggle_virtual_whitespace", 25, "Toggles the current buffer's virtual whitespace status.", 55, "C:\\work\\4ed\\code\\4coder_base_commands.cpp", 45, 641 }, -{ PROC_LINKS(undo, 0), "undo", 4, "Advances backwards through the undo history.", 44, "C:\\work\\4ed\\code\\4coder_base_commands.cpp", 45, 1179 }, +{ PROC_LINKS(undo, 0), "undo", 4, "Advances backwards through the undo history.", 44, "C:\\work\\4ed\\code\\4coder_base_commands.cpp", 45, 1219 }, { PROC_LINKS(view_buffer_other_panel, 0), "view_buffer_other_panel", 23, "Set the other non-active panel to view the buffer that the active panel views, and switch to that panel.", 104, "C:\\work\\4ed\\code\\4coder_default_include.cpp", 47, 721 }, { PROC_LINKS(word_complete, 0), "word_complete", 13, "Iteratively tries completing the word to the left of the cursor with other words in open buffers that have the same prefix string.", 130, "C:\\work\\4ed\\code\\4coder_search.cpp", 38, 863 }, { PROC_LINKS(write_and_auto_tab, 0), "write_and_auto_tab", 18, "Inserts a character and auto-indents the line on which the cursor sits.", 71, "C:\\work\\4ed\\code\\4coder_auto_indent.cpp", 43, 690 }, @@ -549,57 +551,58 @@ static int32_t fcoder_metacmd_ID_reverse_search = 136; static int32_t fcoder_metacmd_ID_reverse_search_identifier = 137; static int32_t fcoder_metacmd_ID_save = 138; static int32_t fcoder_metacmd_ID_save_all_dirty_buffers = 139; -static int32_t fcoder_metacmd_ID_scope_absorb_down = 140; -static int32_t fcoder_metacmd_ID_search = 141; -static int32_t fcoder_metacmd_ID_search_identifier = 142; -static int32_t fcoder_metacmd_ID_seek_alphanumeric_left = 143; -static int32_t fcoder_metacmd_ID_seek_alphanumeric_or_camel_left = 144; -static int32_t fcoder_metacmd_ID_seek_alphanumeric_or_camel_right = 145; -static int32_t fcoder_metacmd_ID_seek_alphanumeric_right = 146; -static int32_t fcoder_metacmd_ID_seek_beginning_of_line = 147; -static int32_t fcoder_metacmd_ID_seek_beginning_of_textual_line = 148; -static int32_t fcoder_metacmd_ID_seek_end_of_line = 149; -static int32_t fcoder_metacmd_ID_seek_end_of_textual_line = 150; -static int32_t fcoder_metacmd_ID_seek_token_left = 151; -static int32_t fcoder_metacmd_ID_seek_token_right = 152; -static int32_t fcoder_metacmd_ID_seek_white_or_token_left = 153; -static int32_t fcoder_metacmd_ID_seek_white_or_token_right = 154; -static int32_t fcoder_metacmd_ID_seek_whitespace_down = 155; -static int32_t fcoder_metacmd_ID_seek_whitespace_down_end_line = 156; -static int32_t fcoder_metacmd_ID_seek_whitespace_left = 157; -static int32_t fcoder_metacmd_ID_seek_whitespace_right = 158; -static int32_t fcoder_metacmd_ID_seek_whitespace_up = 159; -static int32_t fcoder_metacmd_ID_seek_whitespace_up_end_line = 160; -static int32_t fcoder_metacmd_ID_select_all = 161; -static int32_t fcoder_metacmd_ID_set_bindings_choose = 162; -static int32_t fcoder_metacmd_ID_set_bindings_default = 163; -static int32_t fcoder_metacmd_ID_set_bindings_mac_default = 164; -static int32_t fcoder_metacmd_ID_set_mark = 165; -static int32_t fcoder_metacmd_ID_setup_new_project = 166; -static int32_t fcoder_metacmd_ID_show_filebar = 167; -static int32_t fcoder_metacmd_ID_show_scrollbar = 168; -static int32_t fcoder_metacmd_ID_snipe_token_or_word = 169; -static int32_t fcoder_metacmd_ID_snipe_token_or_word_right = 170; -static int32_t fcoder_metacmd_ID_suppress_mouse = 171; -static int32_t fcoder_metacmd_ID_swap_buffers_between_panels = 172; -static int32_t fcoder_metacmd_ID_to_lowercase = 173; -static int32_t fcoder_metacmd_ID_to_uppercase = 174; -static int32_t fcoder_metacmd_ID_toggle_filebar = 175; -static int32_t fcoder_metacmd_ID_toggle_fullscreen = 176; -static int32_t fcoder_metacmd_ID_toggle_line_wrap = 177; -static int32_t fcoder_metacmd_ID_toggle_mouse = 178; -static int32_t fcoder_metacmd_ID_toggle_show_whitespace = 179; -static int32_t fcoder_metacmd_ID_toggle_virtual_whitespace = 180; -static int32_t fcoder_metacmd_ID_undo = 181; -static int32_t fcoder_metacmd_ID_view_buffer_other_panel = 182; -static int32_t fcoder_metacmd_ID_word_complete = 183; -static int32_t fcoder_metacmd_ID_write_and_auto_tab = 184; -static int32_t fcoder_metacmd_ID_write_block = 185; -static int32_t fcoder_metacmd_ID_write_character = 186; -static int32_t fcoder_metacmd_ID_write_explicit_enum_flags = 187; -static int32_t fcoder_metacmd_ID_write_explicit_enum_values = 188; -static int32_t fcoder_metacmd_ID_write_hack = 189; -static int32_t fcoder_metacmd_ID_write_note = 190; -static int32_t fcoder_metacmd_ID_write_todo = 191; -static int32_t fcoder_metacmd_ID_write_underscore = 192; -static int32_t fcoder_metacmd_ID_write_zero_struct = 193; +static int32_t fcoder_metacmd_ID_save_to_query = 140; +static int32_t fcoder_metacmd_ID_scope_absorb_down = 141; +static int32_t fcoder_metacmd_ID_search = 142; +static int32_t fcoder_metacmd_ID_search_identifier = 143; +static int32_t fcoder_metacmd_ID_seek_alphanumeric_left = 144; +static int32_t fcoder_metacmd_ID_seek_alphanumeric_or_camel_left = 145; +static int32_t fcoder_metacmd_ID_seek_alphanumeric_or_camel_right = 146; +static int32_t fcoder_metacmd_ID_seek_alphanumeric_right = 147; +static int32_t fcoder_metacmd_ID_seek_beginning_of_line = 148; +static int32_t fcoder_metacmd_ID_seek_beginning_of_textual_line = 149; +static int32_t fcoder_metacmd_ID_seek_end_of_line = 150; +static int32_t fcoder_metacmd_ID_seek_end_of_textual_line = 151; +static int32_t fcoder_metacmd_ID_seek_token_left = 152; +static int32_t fcoder_metacmd_ID_seek_token_right = 153; +static int32_t fcoder_metacmd_ID_seek_white_or_token_left = 154; +static int32_t fcoder_metacmd_ID_seek_white_or_token_right = 155; +static int32_t fcoder_metacmd_ID_seek_whitespace_down = 156; +static int32_t fcoder_metacmd_ID_seek_whitespace_down_end_line = 157; +static int32_t fcoder_metacmd_ID_seek_whitespace_left = 158; +static int32_t fcoder_metacmd_ID_seek_whitespace_right = 159; +static int32_t fcoder_metacmd_ID_seek_whitespace_up = 160; +static int32_t fcoder_metacmd_ID_seek_whitespace_up_end_line = 161; +static int32_t fcoder_metacmd_ID_select_all = 162; +static int32_t fcoder_metacmd_ID_set_bindings_choose = 163; +static int32_t fcoder_metacmd_ID_set_bindings_default = 164; +static int32_t fcoder_metacmd_ID_set_bindings_mac_default = 165; +static int32_t fcoder_metacmd_ID_set_mark = 166; +static int32_t fcoder_metacmd_ID_setup_new_project = 167; +static int32_t fcoder_metacmd_ID_show_filebar = 168; +static int32_t fcoder_metacmd_ID_show_scrollbar = 169; +static int32_t fcoder_metacmd_ID_snipe_token_or_word = 170; +static int32_t fcoder_metacmd_ID_snipe_token_or_word_right = 171; +static int32_t fcoder_metacmd_ID_suppress_mouse = 172; +static int32_t fcoder_metacmd_ID_swap_buffers_between_panels = 173; +static int32_t fcoder_metacmd_ID_to_lowercase = 174; +static int32_t fcoder_metacmd_ID_to_uppercase = 175; +static int32_t fcoder_metacmd_ID_toggle_filebar = 176; +static int32_t fcoder_metacmd_ID_toggle_fullscreen = 177; +static int32_t fcoder_metacmd_ID_toggle_line_wrap = 178; +static int32_t fcoder_metacmd_ID_toggle_mouse = 179; +static int32_t fcoder_metacmd_ID_toggle_show_whitespace = 180; +static int32_t fcoder_metacmd_ID_toggle_virtual_whitespace = 181; +static int32_t fcoder_metacmd_ID_undo = 182; +static int32_t fcoder_metacmd_ID_view_buffer_other_panel = 183; +static int32_t fcoder_metacmd_ID_word_complete = 184; +static int32_t fcoder_metacmd_ID_write_and_auto_tab = 185; +static int32_t fcoder_metacmd_ID_write_block = 186; +static int32_t fcoder_metacmd_ID_write_character = 187; +static int32_t fcoder_metacmd_ID_write_explicit_enum_flags = 188; +static int32_t fcoder_metacmd_ID_write_explicit_enum_values = 189; +static int32_t fcoder_metacmd_ID_write_hack = 190; +static int32_t fcoder_metacmd_ID_write_note = 191; +static int32_t fcoder_metacmd_ID_write_todo = 192; +static int32_t fcoder_metacmd_ID_write_underscore = 193; +static int32_t fcoder_metacmd_ID_write_zero_struct = 194; diff --git a/4coder_generated/remapping.h b/4coder_generated/remapping.h index dc0274b9..c2ce2d77 100644 --- a/4coder_generated/remapping.h +++ b/4coder_generated/remapping.h @@ -25,7 +25,7 @@ bind(context, 'm', MDFR_ALT, build_in_build_panel); bind(context, 'z', MDFR_ALT, execute_any_cli); bind(context, 'Z', MDFR_ALT, execute_previous_cli); bind(context, 'x', MDFR_ALT, execute_arbitrary_command); -bind(context, 's', MDFR_ALT, show_scrollbar); +bind(context, 'W', MDFR_ALT, show_scrollbar); bind(context, 'w', MDFR_ALT, hide_scrollbar); bind(context, 'b', MDFR_ALT, toggle_filebar); bind(context, '@', MDFR_ALT, toggle_mouse); @@ -102,6 +102,7 @@ bind(context, 'Q', MDFR_CTRL, query_replace_identifier); bind(context, 'q', MDFR_ALT, query_replace_selection); bind(context, 'r', MDFR_CTRL, reverse_search); bind(context, 's', MDFR_CTRL, save); +bind(context, 's', MDFR_ALT, save_to_query); bind(context, 't', MDFR_CTRL, search_identifier); bind(context, 'T', MDFR_CTRL, list_all_locations_of_identifier); bind(context, 'u', MDFR_CTRL, to_uppercase); @@ -181,7 +182,7 @@ bind(context, 'm', MDFR_CTRL, build_in_build_panel); bind(context, 'z', MDFR_CTRL, execute_any_cli); bind(context, 'Z', MDFR_CTRL, execute_previous_cli); bind(context, 'x', MDFR_CTRL, execute_arbitrary_command); -bind(context, 's', MDFR_CTRL, show_scrollbar); +bind(context, 'W', MDFR_CTRL, show_scrollbar); bind(context, 'w', MDFR_CTRL, hide_scrollbar); bind(context, 'b', MDFR_CTRL, toggle_filebar); bind(context, '@', MDFR_CTRL, toggle_mouse); @@ -256,6 +257,7 @@ bind(context, 'q', MDFR_CMND, query_replace); bind(context, 'Q', MDFR_CMND, query_replace_identifier); bind(context, 'r', MDFR_CMND, reverse_search); bind(context, 's', MDFR_CMND, save); +bind(context, 's', MDFR_CTRL, save_to_query); bind(context, 't', MDFR_CMND, search_identifier); bind(context, 'T', MDFR_CMND, list_all_locations_of_identifier); bind(context, 'u', MDFR_CMND, to_uppercase); @@ -367,7 +369,7 @@ static Meta_Key_Bind fcoder_binds_for_default_mapid_global[48] = { {0, 122, 2, "execute_any_cli", 15, LINK_PROCS(execute_any_cli)}, {0, 90, 2, "execute_previous_cli", 20, LINK_PROCS(execute_previous_cli)}, {0, 120, 2, "execute_arbitrary_command", 25, LINK_PROCS(execute_arbitrary_command)}, -{0, 115, 2, "show_scrollbar", 14, LINK_PROCS(show_scrollbar)}, +{0, 87, 2, "show_scrollbar", 14, LINK_PROCS(show_scrollbar)}, {0, 119, 2, "hide_scrollbar", 14, LINK_PROCS(hide_scrollbar)}, {0, 98, 2, "toggle_filebar", 14, LINK_PROCS(toggle_filebar)}, {0, 64, 2, "toggle_mouse", 12, LINK_PROCS(toggle_mouse)}, @@ -392,7 +394,7 @@ static Meta_Key_Bind fcoder_binds_for_default_mapid_global[48] = { {0, 55326, 0, "project_fkey_command", 20, LINK_PROCS(project_fkey_command)}, {0, 55327, 0, "project_fkey_command", 20, LINK_PROCS(project_fkey_command)}, }; -static Meta_Key_Bind fcoder_binds_for_default_mapid_file[67] = { +static Meta_Key_Bind fcoder_binds_for_default_mapid_file[68] = { {1, 0, 0, "write_character", 15, LINK_PROCS(write_character)}, {0, 55308, 0, "click_set_cursor", 16, LINK_PROCS(click_set_cursor)}, {0, 55310, 0, "click_set_mark", 14, LINK_PROCS(click_set_mark)}, @@ -444,6 +446,7 @@ static Meta_Key_Bind fcoder_binds_for_default_mapid_file[67] = { {0, 113, 2, "query_replace_selection", 23, LINK_PROCS(query_replace_selection)}, {0, 114, 1, "reverse_search", 14, LINK_PROCS(reverse_search)}, {0, 115, 1, "save", 4, LINK_PROCS(save)}, +{0, 115, 2, "save_to_query", 13, LINK_PROCS(save_to_query)}, {0, 116, 1, "search_identifier", 17, LINK_PROCS(search_identifier)}, {0, 84, 1, "list_all_locations_of_identifier", 32, LINK_PROCS(list_all_locations_of_identifier)}, {0, 117, 1, "to_uppercase", 12, LINK_PROCS(to_uppercase)}, @@ -497,7 +500,7 @@ static Meta_Key_Bind fcoder_binds_for_default_default_code_map[32] = { }; static Meta_Sub_Map fcoder_submaps_for_default[3] = { {"mapid_global", 12, "The following bindings apply in all situations.", 47, 0, 0, fcoder_binds_for_default_mapid_global, 48}, -{"mapid_file", 10, "The following bindings apply in general text files and most apply in code files, but some are overriden by other commands specific to code files.", 145, 0, 0, fcoder_binds_for_default_mapid_file, 67}, +{"mapid_file", 10, "The following bindings apply in general text files and most apply in code files, but some are overriden by other commands specific to code files.", 145, 0, 0, fcoder_binds_for_default_mapid_file, 68}, {"default_code_map", 16, "The following commands only apply in files where the lexer (syntax highlighting) is turned on.", 94, "mapid_file", 10, fcoder_binds_for_default_default_code_map, 32}, }; static Meta_Key_Bind fcoder_binds_for_mac_default_mapid_global[48] = { @@ -525,7 +528,7 @@ static Meta_Key_Bind fcoder_binds_for_mac_default_mapid_global[48] = { {0, 122, 1, "execute_any_cli", 15, LINK_PROCS(execute_any_cli)}, {0, 90, 1, "execute_previous_cli", 20, LINK_PROCS(execute_previous_cli)}, {0, 120, 1, "execute_arbitrary_command", 25, LINK_PROCS(execute_arbitrary_command)}, -{0, 115, 1, "show_scrollbar", 14, LINK_PROCS(show_scrollbar)}, +{0, 87, 1, "show_scrollbar", 14, LINK_PROCS(show_scrollbar)}, {0, 119, 1, "hide_scrollbar", 14, LINK_PROCS(hide_scrollbar)}, {0, 98, 1, "toggle_filebar", 14, LINK_PROCS(toggle_filebar)}, {0, 64, 1, "toggle_mouse", 12, LINK_PROCS(toggle_mouse)}, @@ -550,7 +553,7 @@ static Meta_Key_Bind fcoder_binds_for_mac_default_mapid_global[48] = { {0, 55326, 0, "project_fkey_command", 20, LINK_PROCS(project_fkey_command)}, {0, 55327, 0, "project_fkey_command", 20, LINK_PROCS(project_fkey_command)}, }; -static Meta_Key_Bind fcoder_binds_for_mac_default_mapid_file[65] = { +static Meta_Key_Bind fcoder_binds_for_mac_default_mapid_file[66] = { {1, 0, 0, "write_character", 15, LINK_PROCS(write_character)}, {1, 0, 2, "write_character", 15, LINK_PROCS(write_character)}, {0, 55308, 0, "click_set_cursor", 16, LINK_PROCS(click_set_cursor)}, @@ -600,6 +603,7 @@ static Meta_Key_Bind fcoder_binds_for_mac_default_mapid_file[65] = { {0, 81, 4, "query_replace_identifier", 24, LINK_PROCS(query_replace_identifier)}, {0, 114, 4, "reverse_search", 14, LINK_PROCS(reverse_search)}, {0, 115, 4, "save", 4, LINK_PROCS(save)}, +{0, 115, 1, "save_to_query", 13, LINK_PROCS(save_to_query)}, {0, 116, 4, "search_identifier", 17, LINK_PROCS(search_identifier)}, {0, 84, 4, "list_all_locations_of_identifier", 32, LINK_PROCS(list_all_locations_of_identifier)}, {0, 117, 4, "to_uppercase", 12, LINK_PROCS(to_uppercase)}, @@ -653,7 +657,7 @@ static Meta_Key_Bind fcoder_binds_for_mac_default_default_code_map[32] = { }; static Meta_Sub_Map fcoder_submaps_for_mac_default[3] = { {"mapid_global", 12, "The following bindings apply in all situations.", 47, 0, 0, fcoder_binds_for_mac_default_mapid_global, 48}, -{"mapid_file", 10, "The following bindings apply in general text files and most apply in code files, but some are overriden by other commands specific to code files.", 145, 0, 0, fcoder_binds_for_mac_default_mapid_file, 65}, +{"mapid_file", 10, "The following bindings apply in general text files and most apply in code files, but some are overriden by other commands specific to code files.", 145, 0, 0, fcoder_binds_for_mac_default_mapid_file, 66}, {"default_code_map", 16, "The following commands only apply in files where the lexer (syntax highlighting) is turned on.", 94, "mapid_file", 10, fcoder_binds_for_mac_default_default_code_map, 32}, }; static Meta_Mapping fcoder_meta_maps[2] = { diff --git a/4ed.cpp b/4ed.cpp index 10e59cbe..4aa4ce85 100644 --- a/4ed.cpp +++ b/4ed.cpp @@ -394,10 +394,15 @@ do_feedback_message(System_Functions *system, Models *models, String value, b32 if (!set_to_start){ pos = buffer_size(&file->state.buffer); } - for (View_Iter iter = file_view_iter_init(&models->layout, file, 0); - file_view_iter_good(iter); - iter = file_view_iter_next(iter)){ - view_cursor_move(system, iter.view, pos); + + for (Panel *panel = models->layout.used_sentinel.next; + panel != &models->layout.used_sentinel; + panel = panel->next){ + View *view = panel->view; + if (view->transient.file_data.file != file){ + continue; + } + view_cursor_move(system, view, pos); } } } @@ -420,12 +425,10 @@ do_feedback_message(System_Functions *system, Models *models, String value, b32 n = __panel__->view; \ }while(false) -#define REQ_OPEN_VIEW(n) USE_VIEW(n); if (view_lock_level(n) > LockLevel_Open) return +#define REQ_OPEN_VIEW(n) USE_VIEW(n); if (view_lock_flags(n) != 0) return -#define REQ_READABLE_VIEW(n) USE_VIEW(n); if (view_lock_level(n) > LockLevel_Protected) return - -#define REQ_FILE(n,v) Editing_File *n = (v)->file_data.file; if (!n) return -#define REQ_FILE_HISTORY(n,v) Editing_File *n = (v)->file_data.file; if (!n || !n->state.undo.undo.edits) return +#define REQ_FILE(n,v) Editing_File *n = (v)->transient.file_data.file; if (n == 0) return +#define REQ_FILE_HISTORY(n,v) Editing_File *n = (v)->transient.file_data.file; if (n == 0 || n->state.undo.undo.edits == 0) return #define COMMAND_DECL(n) internal void command_##n(System_Functions *system, Command_Data *command, Command_Binding binding) @@ -434,7 +437,7 @@ panel_make_empty(System_Functions *system, Models *models, Panel *panel){ Assert(panel->view == 0); View_And_ID new_view = live_set_alloc_view(&models->live_set, panel, models); view_set_file(system, new_view.view, models->scratch_buffer, models); - new_view.view->map = models->scratch_buffer->settings.base_map_id; + new_view.view->transient.map = models->scratch_buffer->settings.base_map_id; return(new_view.view); } @@ -446,9 +449,7 @@ COMMAND_DECL(undo){ USE_MODELS(models); REQ_OPEN_VIEW(view); REQ_FILE_HISTORY(file, view); - view_undo_redo(system, models, view, &file->state.undo.undo, ED_UNDO); - Assert(file->state.undo.undo.size >= 0); } @@ -456,9 +457,7 @@ COMMAND_DECL(redo){ USE_MODELS(models); REQ_OPEN_VIEW(view); REQ_FILE_HISTORY(file, view); - view_undo_redo(system, models, view, &file->state.undo.redo, ED_REDO); - Assert(file->state.undo.undo.size >= 0); } @@ -500,7 +499,7 @@ COMMAND_DECL(reopen){ Temp_Memory temp = begin_temp_memory(part); char *buffer = push_array(part, char, size); - if (buffer){ + if (buffer != 0){ if (system->load_file(handle, buffer, size)){ system->load_close(handle); @@ -511,14 +510,19 @@ COMMAND_DECL(reopen){ int32_t column_number[16]; View *vptrs[16]; i32 vptr_count = 0; - for (View_Iter iter = file_view_iter_init(&models->layout, file, 0); - file_view_iter_good(iter); - iter = file_view_iter_next(iter)){ - vptrs[vptr_count] = iter.view; - edit_poss[vptr_count] = iter.view->edit_pos[0]; - line_number[vptr_count] = iter.view->edit_pos[0].cursor.line; - column_number[vptr_count] = iter.view->edit_pos[0].cursor.character; - iter.view->edit_pos = 0; + + for (Panel *panel = models->layout.used_sentinel.next; + panel != &models->layout.used_sentinel; + panel = panel->next){ + View *view = panel->view; + if (view->transient.file_data.file != file){ + continue; + } + vptrs[vptr_count] = view; + edit_poss[vptr_count] = view->transient.edit_pos[0]; + line_number[vptr_count] = view->transient.edit_pos[0].cursor.line; + column_number[vptr_count] = view->transient.edit_pos[0].cursor.character; + view->transient.edit_pos = 0; ++vptr_count; } @@ -531,8 +535,8 @@ COMMAND_DECL(reopen){ int32_t line = line_number[i]; int32_t character = column_number[i]; - *vptrs[i]->edit_pos = edit_poss[i]; - Full_Cursor cursor = view_compute_cursor(system, vptrs[i], seek_line_char(line, character), 0); + *vptrs[i]->transient.edit_pos = edit_poss[i]; + Full_Cursor cursor = file_compute_cursor(system, file, seek_line_char(line, character), 0); view_set_cursor(vptrs[i], cursor, true, file->settings.unwrapped_lines); } @@ -596,8 +600,8 @@ COMMAND_DECL(kill_buffer){ internal void case_change_range(System_Functions *system, Models *models, View *view, Editing_File *file, u8 a, u8 z, u8 char_delta){ Range range = {0}; - range.min = Min(view->edit_pos->cursor.pos, view->edit_pos->mark); - range.max = Max(view->edit_pos->cursor.pos, view->edit_pos->mark); + range.min = Min(view->transient.edit_pos->cursor.pos, view->transient.edit_pos->mark); + range.max = Max(view->transient.edit_pos->cursor.pos, view->transient.edit_pos->mark); if (range.start < range.end){ Edit_Step step = {}; step.type = ED_NORMAL; @@ -634,7 +638,7 @@ COMMAND_DECL(open_debug){ USE_VIEW(view); USE_MODELS(models); view_show_GUI(view, models, VUI_Debug); - view->debug_vars = null_debug_vars; + view->transient.debug_vars = null_debug_vars; } COMMAND_DECL(user_callback){ @@ -1412,12 +1416,18 @@ App_Init_Sig(app_init){ models->live_set.views = push_array(partition, View, models->live_set.max); - dll_init_sentinel(&models->live_set.free_sentinel); + //dll_init_sentinel + models->live_set.free_sentinel.transient.next = &models->live_set.free_sentinel; + models->live_set.free_sentinel.transient.prev = &models->live_set.free_sentinel; i32 max = models->live_set.max; View *view = models->live_set.views; for (i32 i = 0; i < max; ++i, ++view){ - dll_insert(&models->live_set.free_sentinel, view); + //dll_insert(&models->live_set.free_sentinel, view); + view->transient.next = models->live_set.free_sentinel.transient.next; + view->transient.prev = &models->live_set.free_sentinel; + models->live_set.free_sentinel.transient.next = view; + view->transient.next->transient.prev = view; View_Persistent *persistent = &view->persistent; persistent->id = i; @@ -1745,19 +1755,18 @@ App_Step_Sig(app_step){ b32 mouse_in_margin_area = false; Panel *mouse_panel = 0; - { - Panel *used_panels = &models->layout.used_sentinel, *panel = 0; - for (dll_items(panel, used_panels)){ - if (hit_check(mx, my, panel->inner)){ - mouse_panel = panel; - mouse_in_edit_area = true; - break; - } - else if (hit_check(mx, my, panel->full)){ - mouse_panel = panel; - mouse_in_margin_area = true; - break; - } + for (Panel *panel = models->layout.used_sentinel.next; + panel != &models->layout.used_sentinel; + panel = panel->next){ + if (hit_check(mx, my, panel->inner)){ + mouse_panel = panel; + mouse_in_edit_area = true; + break; + } + else if (hit_check(mx, my, panel->full)){ + mouse_panel = panel; + mouse_in_margin_area = true; + break; } } @@ -1906,24 +1915,19 @@ App_Step_Sig(app_step){ models->hook_start(&models->app_links, files, files_count, flags, flags_count); } - - Panel *panel = models->layout.used_sentinel.next; - for (i32 i = 0; i < models->settings.init_files_count; ++i, panel = panel->next){ - Assert(panel->view->file_data.file != 0); - } } // NOTE(allen): respond if the user is trying to kill the application if (input->trying_to_kill){ - b32 there_is_unsaved = 0; - app_result.animating = 1; + b32 there_is_unsaved = false; + app_result.animating = true; - File_Node *node, *sent; - sent = &models->working_set.used_sentinel; - for (dll_items(node, sent)){ + for (File_Node *node = models->working_set.used_sentinel.next; + node != &models->working_set.used_sentinel; + node = node->next){ Editing_File *file = (Editing_File*)node; if (buffer_needs_save(file)){ - there_is_unsaved = 1; + there_is_unsaved = true; break; } } @@ -1944,7 +1948,7 @@ App_Step_Sig(app_step){ command_coroutine = 0; } if (view != 0){ - init_query_set(&view->query_set); + init_query_set(&view->transient.query_set); } if (view == 0){ @@ -2044,7 +2048,7 @@ App_Step_Sig(app_step){ i32 map = mapid_global; if (view != 0){ - map = view->map; + map = view->transient.map; } Command_Binding cmd_bind = map_extract_recursive(&models->mapping, map, key); @@ -2124,7 +2128,7 @@ App_Step_Sig(app_step){ // TODO(allen): Should I somehow allow a view to clean up however it wants after a // command finishes, or after transfering to another view mid command? if (view != 0 && models->command_coroutine == 0){ - init_query_set(&view->query_set); + init_query_set(&view->transient.query_set); } if (models->command_coroutine == 0) break; } @@ -2149,22 +2153,18 @@ App_Step_Sig(app_step){ Mouse_State mouse_state = get_mouse_state(&vars->available_input); { - Panel *panel = 0, *used_panels = 0; - View *view = 0; - b32 active = 0; - Input_Summary summary = {0}; - Command_Data *command = cmd; USE_VIEW(active_view); USE_PANEL(active_panel); - used_panels = &models->layout.used_sentinel; - for (dll_items(panel, used_panels)){ - view = panel->view; - active = (panel == active_panel); - summary = (active)?(active_input):(dead_input); + for (Panel *panel = models->layout.used_sentinel.next; + panel != &models->layout.used_sentinel; + panel = panel->next){ + View *view = panel->view; + b32 active = (panel == active_panel); + Input_Summary summary = (active)?(active_input):(dead_input); - view->changed_context_in_step = 0; + view->transient.changed_context_in_step = 0; View_Step_Result result = step_file_view(system, view, models, active_view, summary); @@ -2178,7 +2178,7 @@ App_Step_Sig(app_step){ consume_input(&vars->available_input, Input_Esc, "file view step"); } - if (view->changed_context_in_step == 0){ + if (view->transient.changed_context_in_step == 0){ active = (panel == active_panel); summary = (active)?(active_input):(dead_input); if (panel == mouse_panel && !input->mouse.out_of_window){ @@ -2186,22 +2186,22 @@ App_Step_Sig(app_step){ } b32 file_scroll = false; - GUI_Scroll_Vars *scroll_vars = &view->gui_scroll; - if (view->showing_ui == VUI_None){ - Assert(view->file_data.file != 0); - scroll_vars = &view->edit_pos->scroll; + GUI_Scroll_Vars *scroll_vars = &view->transient.gui_scroll; + if (view->transient.showing_ui == VUI_None){ + Assert(view->transient.file_data.file != 0); + scroll_vars = &view->transient.edit_pos->scroll; file_scroll = true; } i32 max_y = 0; - if (view->showing_ui == VUI_None){ + if (view->transient.showing_ui == VUI_None){ max_y = view_compute_max_target_y(view); } else{ - max_y = view->gui_max_y; + max_y = view->transient.gui_max_y; } - Input_Process_Result ip_result = do_step_file_view(system, view, models, panel->inner, active, &summary, *scroll_vars, view->scroll_region, max_y); + Input_Process_Result ip_result = do_step_file_view(system, view, models, panel->inner, active, &summary, *scroll_vars, view->transient.scroll_region, max_y); if (ip_result.is_animating){ app_result.animating = 1; @@ -2214,7 +2214,7 @@ App_Step_Sig(app_step){ } if (ip_result.has_max_y_suggestion){ - view->gui_max_y = ip_result.max_y; + view->transient.gui_max_y = ip_result.max_y; } if (!gui_scroll_eq(scroll_vars, &ip_result.vars)){ @@ -2226,7 +2226,7 @@ App_Step_Sig(app_step){ } } - view->scroll_region = ip_result.region; + view->transient.scroll_region = ip_result.region; } } } @@ -2252,7 +2252,7 @@ App_Step_Sig(app_step){ USE_VIEW(view); Assert(view != 0); - Command_Binding cmd_bind = map_extract_recursive(&models->mapping, view->map, key); + Command_Binding cmd_bind = map_extract_recursive(&models->mapping, view->transient.map, key); if (cmd_bind.function){ if (key.keycode == key_esc){ @@ -2472,7 +2472,7 @@ App_Step_Sig(app_step){ Panel *active_panel = &models->layout.panels[models->layout.active_panel]; View *view = active_panel->view; - init_query_set(&view->query_set); + init_query_set(&view->transient.query_set); } models->layout.active_panel = new_panel_id; @@ -2482,12 +2482,13 @@ App_Step_Sig(app_step){ // NOTE(allen): on the first frame there should be no scrolling if (input->first_step){ - Panel *panel = 0, *used_panels = &models->layout.used_sentinel; - for (dll_items(panel, used_panels)){ + for (Panel *panel = models->layout.used_sentinel.next; + panel != &models->layout.used_sentinel; + panel = panel->next){ View *view = panel->view; - GUI_Scroll_Vars *scroll_vars = &view->gui_scroll; - if (view->edit_pos){ - scroll_vars = &view->edit_pos->scroll; + GUI_Scroll_Vars *scroll_vars = &view->transient.gui_scroll; + if (view->transient.edit_pos != 0){ + scroll_vars = &view->transient.edit_pos->scroll; } scroll_vars->scroll_x = (f32)scroll_vars->target_x; scroll_vars->scroll_y = (f32)scroll_vars->target_y; @@ -2495,9 +2496,9 @@ App_Step_Sig(app_step){ } // NOTE(allen): if this is the last frame, run the exit hook - if (!models->keep_playing && models->hooks[hook_exit]){ + if (!models->keep_playing && models->hooks[hook_exit] != 0){ if (!models->hooks[hook_exit](&models->app_links)){ - models->keep_playing = 1; + models->keep_playing = true; } } @@ -2510,9 +2511,9 @@ App_Step_Sig(app_step){ USE_VIEW(active_view); // NOTE(allen): render the panels - Panel *panel, *used_panels; - used_panels = &models->layout.used_sentinel; - for (dll_items(panel, used_panels)){ + for (Panel *panel = models->layout.used_sentinel.next; + panel != &models->layout.used_sentinel; + panel = panel->next){ i32_Rect full = panel->full; i32_Rect inner = panel->inner; @@ -2524,10 +2525,10 @@ App_Step_Sig(app_step){ draw_rectangle(target, full, back_color); b32 file_scroll = false; - GUI_Scroll_Vars *scroll_vars = &view->gui_scroll; - if (view->showing_ui == VUI_None){ - Assert(view->file_data.file != 0); - scroll_vars = &view->edit_pos->scroll; + GUI_Scroll_Vars *scroll_vars = &view->transient.gui_scroll; + if (view->transient.showing_ui == VUI_None){ + Assert(view->transient.file_data.file != 0); + scroll_vars = &view->transient.edit_pos->scroll; file_scroll = true; } diff --git a/4ed_api_implementation.cpp b/4ed_api_implementation.cpp index fc4e9ada..9395770f 100644 --- a/4ed_api_implementation.cpp +++ b/4ed_api_implementation.cpp @@ -13,12 +13,7 @@ inline b32 access_test(u32 lock_flags, u32 access_flags){ - b32 result = 0; - - if ((lock_flags & ~access_flags) == 0){ - result = 1; - } - + b32 result = ((lock_flags & ~access_flags) == 0); return(result); } @@ -70,32 +65,28 @@ fill_buffer_summary(Buffer_Summary *buffer, Editing_File *file, Command_Data *cm internal void fill_view_summary(System_Functions *system, View_Summary *view, View *vptr, Live_Views *live_set, Working_Set *working_set){ - Buffer_ID buffer_id = 0; - File_Viewing_Data *data = &vptr->file_data; + File_Viewing_Data *data = &vptr->transient.file_data; *view = null_view_summary; - if (vptr->in_use){ - view->exists = 1; + if (vptr->transient.in_use){ + view->exists = true; view->view_id = (int32_t)(vptr - live_set->views) + 1; - view->line_height = (f32)(vptr->line_height); + view->line_height = (f32)(vptr->transient.line_height); view->unwrapped_lines = data->file->settings.unwrapped_lines; view->show_whitespace = data->show_whitespace; view->lock_flags = view_lock_flags(vptr); - Assert(data->file); + view->buffer_id = vptr->transient.file_data.file->id.id; - buffer_id = vptr->file_data.file->id.id; + Assert(data->file != 0); + view->mark = file_compute_cursor(system, data->file, seek_pos(vptr->transient.edit_pos->mark), 0); + view->cursor = vptr->transient.edit_pos->cursor; + view->preferred_x = vptr->transient.edit_pos->preferred_x; - view->buffer_id = buffer_id; - - view->mark = view_compute_cursor(system, vptr, seek_pos(vptr->edit_pos->mark), 0); - view->cursor = vptr->edit_pos->cursor; - view->preferred_x = vptr->edit_pos->preferred_x; - - view->view_region = vptr->panel->inner; - view->file_region = vptr->file_region; - view->scroll_vars = vptr->edit_pos->scroll; + view->view_region = vptr->transient.panel->inner; + view->file_region = vptr->transient.file_region; + view->scroll_vars = vptr->transient.edit_pos->scroll; } } @@ -143,7 +134,7 @@ imp_get_view(Command_Data *cmd, View_ID view_id){ view_id = view_id - 1; if (view_id >= 0 && view_id < live_set->max){ vptr = live_set->views + view_id; - if (!vptr->in_use){ + if (!vptr->transient.in_use){ vptr = 0; } } @@ -337,8 +328,7 @@ DOC_SEE(Command_Line_Interface_Flag) if (file != 0){ b32 bind_to_new_view = true; if (!(flags & CLI_AlwaysBindToView)){ - View_Iter iter = file_view_iter_init(&models->layout, file, 0); - if (file_view_iter_good(iter)){ + if (file_is_viewed(&models->layout, file)){ bind_to_new_view = false; } } @@ -1062,10 +1052,14 @@ DOC_SEE(Buffer_Setting_ID) file->settings.base_map_id = value; } - for (View_Iter iter = file_view_iter_init(&models->layout, file, 0); - file_view_iter_good(iter); - iter = file_view_iter_next(iter)){ - iter.view->map = file->settings.base_map_id; + for (Panel *panel = models->layout.used_sentinel.next; + panel != &models->layout.used_sentinel; + panel = panel->next){ + View *view = panel->view; + if (view->transient.file_data.file != file){ + continue; + } + view->transient.map = file->settings.base_map_id; } }break; @@ -1453,7 +1447,7 @@ internal_get_view_next(Command_Data *cmd, View_Summary *view){ if (index >= 0 && index < live_set->max){ View *vptr = live_set->views + index; - Panel *panel = vptr->panel; + Panel *panel = vptr->transient.panel; if (panel != 0){ panel = panel->next; } @@ -1580,7 +1574,7 @@ DOC_SEE(View_Split_Position) System_Functions *system = cmd->system; Models *models = cmd->models; View *vptr = imp_get_view(cmd, view_location); - Panel *panel = vptr->panel; + Panel *panel = vptr->transient.panel; View_Summary result = {0}; if (models->layout.panel_count < models->layout.panel_max_count){ @@ -1644,86 +1638,79 @@ in the system, the call will fail.) bool32 result = 0; - if (vptr){ - if (models->layout.panel_count > 1){ - Panel *panel = vptr->panel; - - Divider_And_ID div, parent_div, child_div; - i32 child; - i32 parent; - i32 which_child; - i32 active; - - live_set_free_view(&models->live_set, vptr, models); - panel->view = 0; - - div = layout_get_divider(&models->layout, panel->parent); - - // This divider cannot have two child dividers. - Assert(div.divider->child1 == -1 || div.divider->child2 == -1); - - // Get the child who needs to fill in this node's spot - child = div.divider->child1; - if (child == -1) child = div.divider->child2; - - parent = div.divider->parent; - which_child = div.divider->which_child; - - // Fill the child in the slot this node use to hold - if (parent == -1){ - Assert(models->layout.root == div.id); - models->layout.root = child; - } - else{ - parent_div = layout_get_divider(&models->layout, parent); - if (which_child == -1){ - parent_div.divider->child1 = child; - } - else{ - parent_div.divider->child2 = child; - } - } - - // If there was a child divider, give it information about it's new parent. - if (child != -1){ - child_div = layout_get_divider(&models->layout, child); - child_div.divider->parent = parent; - child_div.divider->which_child = div.divider->which_child; - } - - // What is the new active panel? - active = -1; - if (child == -1){ - Panel *panel_ptr = 0; - Panel *used_panels = &models->layout.used_sentinel; - for (dll_items(panel_ptr, used_panels)){ - if (panel_ptr != panel && panel_ptr->parent == div.id){ - panel_ptr->parent = parent; - panel_ptr->which_child = which_child; - active = (i32)(panel_ptr - models->layout.panels); - break; - } - } - } - else{ - Panel *panel_ptr = panel->next; - if (panel_ptr == &models->layout.used_sentinel) panel_ptr = panel_ptr->next; - Assert(panel_ptr != panel); - active = (i32)(panel_ptr - models->layout.panels); - } - Assert(active != -1 && panel != models->layout.panels + active); - - // If the panel we're closing was previously active, we have to switch to it's sibling. - if (models->layout.active_panel == (i32)(panel - models->layout.panels)){ - models->layout.active_panel = active; - } - - layout_free_divider(&models->layout, div.divider); - layout_free_panel(&models->layout, panel); - layout_fix_all_panels(&models->layout); + if (vptr != 0 && models->layout.panel_count > 1){ + Panel *panel = vptr->transient.panel; + + live_set_free_view(&models->live_set, vptr, models); + panel->view = 0; + + Divider_And_ID div = layout_get_divider(&models->layout, panel->parent); + + // This divider cannot have two child dividers. + Assert(div.divider->child1 == -1 || div.divider->child2 == -1); + + // Get the child who needs to fill in this node's spot + i32 child = div.divider->child1; + if (child == -1) child = div.divider->child2; + + i32 parent = div.divider->parent; + i32 which_child = div.divider->which_child; + + // Fill the child in the slot this node use to hold + if (parent == -1){ + Assert(models->layout.root == div.id); + models->layout.root = child; } + else{ + Divider_And_ID parent_div = layout_get_divider(&models->layout, parent); + if (which_child == -1){ + parent_div.divider->child1 = child; + } + else{ + parent_div.divider->child2 = child; + } + } + + // If there was a child divider, give it information about it's new parent. + if (child != -1){ + Divider_And_ID child_div = layout_get_divider(&models->layout, child); + child_div.divider->parent = parent; + child_div.divider->which_child = div.divider->which_child; + } + + // What is the new active panel? + i32 active = -1; + if (child == -1){ + for (Panel *panel_ptr = models->layout.used_sentinel.next; + panel_ptr != &models->layout.used_sentinel; + panel_ptr = panel_ptr->next){ + if (panel_ptr != panel && panel_ptr->parent == div.id){ + panel_ptr->parent = parent; + panel_ptr->which_child = which_child; + active = (i32)(panel_ptr - models->layout.panels); + break; + } + } + } + else{ + Panel *panel_ptr = panel->next; + if (panel_ptr == &models->layout.used_sentinel) panel_ptr = panel_ptr->next; + Assert(panel_ptr != panel); + active = (i32)(panel_ptr - models->layout.panels); + } + Assert(active != -1 && panel != models->layout.panels + active); + + // If the panel we're closing was previously active, we have to switch to it's sibling. + if (models->layout.active_panel == (i32)(panel - models->layout.panels)){ + models->layout.active_panel = active; + } + + layout_free_divider(&models->layout, div.divider); + layout_free_panel(&models->layout, panel); + layout_fix_all_panels(&models->layout); } + return(result); } @@ -1744,10 +1731,9 @@ DOC_SEE(get_active_view) View *vptr = imp_get_view(cmd, view); bool32 result = false; - if (vptr){ + if (vptr != 0){ result = true; - - Panel *panel = vptr->panel; + Panel *panel = vptr->transient.panel; models->layout.active_panel = (i32)(panel - models->layout.panels); } @@ -1766,13 +1752,28 @@ DOC_RETURN(returns non-zero on success) View *vptr = imp_get_view(cmd, view); int32_t result = 0; - if (vptr){ + if (vptr != 0){ result = 1; switch (setting){ - case ViewSetting_ShowWhitespace: *value_out = vptr->file_data.show_whitespace; break; - case ViewSetting_ShowScrollbar: *value_out = !vptr->hide_scrollbar; break; - case ViewSetting_ShowFileBar: *value_out = !vptr->hide_file_bar; break; - default: result = 0; break; + case ViewSetting_ShowWhitespace: + { + *value_out = vptr->transient.file_data.show_whitespace; + }break; + + case ViewSetting_ShowScrollbar: + { + *value_out = !vptr->transient.hide_scrollbar; + }break; + + case ViewSetting_ShowFileBar: + { + *value_out = !vptr->transient.hide_file_bar; + }break; + + default: + { + result = 0; + }break; } } @@ -1793,22 +1794,22 @@ DOC_SEE(View_Setting_ID) View *vptr = imp_get_view(cmd, view); bool32 result = false; - if (vptr){ + if (vptr != 0){ result = true; switch (setting){ case ViewSetting_ShowWhitespace: { - vptr->file_data.show_whitespace = value; + vptr->transient.file_data.show_whitespace = value; }break; case ViewSetting_ShowScrollbar: { - vptr->hide_scrollbar = !value; + vptr->transient.hide_scrollbar = !value; }break; case ViewSetting_ShowFileBar: { - vptr->hide_file_bar = !value; + vptr->transient.hide_file_bar = !value; }break; default: @@ -1838,10 +1839,10 @@ DOC_RETURN(This call returns non-zero on success.) Editing_Layout *layout = &models->layout; View *vptr = imp_get_view(cmd, view); - if (vptr){ + if (vptr != 0){ result = true; - Panel *panel = vptr->panel; + Panel *panel = vptr->transient.panel; Panel_Divider *div = layout->dividers + panel->parent; if (panel->which_child == 1){ @@ -1872,11 +1873,12 @@ DOC_SEE(Full_Cursor) View *vptr = imp_get_view(cmd, view); bool32 result = false; - if (vptr){ - Editing_File *file = vptr->file_data.file; - if (file != 0 && !file->is_loading){ + if (vptr != 0){ + Editing_File *file = vptr->transient.file_data.file; + Assert(file != 0); + if (!file->is_loading){ result = true; - *cursor_out = view_compute_cursor(system, vptr, seek, 0); + *cursor_out = file_compute_cursor(system, file, seek, 0); fill_view_summary(system, view, vptr, cmd); } } @@ -1897,15 +1899,14 @@ DOC_SEE(Buffer_Seek) Command_Data *cmd = (Command_Data*)app->cmd_context; System_Functions *system = cmd->system; View *vptr = imp_get_view(cmd, view); - Editing_File *file = 0; bool32 result = false; if (vptr){ - file = vptr->file_data.file; - Assert(file); + Editing_File *file = vptr->transient.file_data.file; + Assert(file != 0); if (!file->is_loading){ result = true; - Full_Cursor cursor = view_compute_cursor(system, vptr, seek, 0); + Full_Cursor cursor = file_compute_cursor(system, file, seek, 0); view_set_cursor(vptr, cursor, set_preferred_x, file->settings.unwrapped_lines); fill_view_summary(system, view, vptr, cmd); } @@ -1925,12 +1926,12 @@ DOC_SEE(GUI_Scroll_Vars) Command_Data *cmd = (Command_Data*)app->cmd_context; System_Functions *system = cmd->system; View *vptr = imp_get_view(cmd, view); - Editing_File *file = 0; bool32 result = false; - if (vptr){ - file = vptr->file_data.file; - if (file && !file->is_loading){ + if (vptr != 0){ + Editing_File *file = vptr->transient.file_data.file; + Assert(file != 0); + if (!file->is_loading){ result = true; view_set_scroll(system, vptr, scroll); fill_view_summary(system, view, vptr, cmd); @@ -1952,21 +1953,20 @@ DOC_SEE(Buffer_Seek) Command_Data *cmd = (Command_Data*)app->cmd_context; System_Functions *system = cmd->system; View *vptr = imp_get_view(cmd, view); - Editing_File *file = 0; - Full_Cursor cursor = {0}; bool32 result = false; - if (vptr){ - file = vptr->file_data.file; - if (file && !file->is_loading){ + if (vptr != 0){ + Editing_File *file = vptr->transient.file_data.file; + Assert(file != 0); + if (!file->is_loading){ if (seek.type != buffer_seek_pos){ result = true; - cursor = view_compute_cursor(system, vptr, seek, 0); - vptr->edit_pos->mark = cursor.pos; + Full_Cursor cursor = file_compute_cursor(system, file, seek, 0); + vptr->transient.edit_pos->mark = cursor.pos; } else{ result = true; - vptr->edit_pos->mark = seek.pos; + vptr->transient.edit_pos->mark = seek.pos; } fill_view_summary(system, view, vptr, cmd); } @@ -1995,13 +1995,13 @@ and the turn_on set to false, will switch back to showing the cursor. View *vptr = imp_get_view(cmd, view); bool32 result = false; - if (vptr){ + if (vptr != 0){ result = true; if (turn_on){ view_set_temp_highlight(system, vptr, start, end); } else{ - vptr->file_data.show_temp_highlight = 0; + vptr->transient.file_data.show_temp_highlight = false; } fill_view_summary(system, view, vptr, cmd); } @@ -2025,12 +2025,11 @@ DOC_SEE(Set_Buffer_Flag) Models *models = cmd->models; bool32 result = false; - if (vptr){ + if (vptr != 0){ Editing_File *file = working_set_get_active_file(&models->working_set, buffer_id); - if (file != 0){ result = true; - if (file != vptr->file_data.file){ + if (file != vptr->transient.file_data.file){ view_set_file(system, vptr, file, models); if (!(flags & SetBuffer_KeepOriginalGUI)){ view_show_file(vptr, models); @@ -2168,12 +2167,11 @@ only use for this call is in an interactive command that makes calls to get_user */{ Command_Data *command = (Command_Data*)app->cmd_context; USE_VIEW(vptr); - Query_Slot *slot = 0; - - slot = alloc_query_slot(&vptr->query_set); - slot->query_bar = bar; - + Query_Slot *slot = alloc_query_slot(&vptr->transient.query_set); bool32 result = (slot != 0); + if (result){ + slot->query_bar = bar; + } return(result); } @@ -2186,7 +2184,7 @@ DOC(Stops showing the particular query bar specified by the bar parameter.) */{ Command_Data *command = (Command_Data*)app->cmd_context; USE_VIEW(vptr); - free_query_slot(&vptr->query_set, bar); + free_query_slot(&vptr->transient.query_set, bar); } API_EXPORT void @@ -2201,6 +2199,14 @@ DOC(This call posts a string to the *messages* buffer.) do_feedback_message(cmd->system, models, make_string(str, len)); } +internal void +style_set_colors(Style *style, Theme *theme){ + for (u32 i = 0; i < Stag_COUNT; ++i){ + u32 *color_ptr = style_index_by_tag(&style->main, i); + *color_ptr = theme->colors[i]; + } +} + API_EXPORT void Create_Theme(Application_Links *app, Theme *theme, char *name, int32_t len) /* @@ -2210,22 +2216,26 @@ DOC_PARAM(len, The length of the name string.) DOC(This call creates a new theme. If the given name is already the name of a string, the old string will be replaced with the new one. This call does not set the current theme.) */{ Command_Data *cmd = (Command_Data*)app->cmd_context; - Style_Library *styles = &cmd->models->styles; + Style_Library *library = &cmd->models->styles; String theme_name = make_string(name, len); b32 hit_existing_theme = false; - i32 count = styles->count; - Style *s = styles->styles; - for (i32 i = 0; i < count; ++i, ++s){ - if (match_ss(s->name, theme_name)){ - style_set_colors(s, theme); + i32 count = library->count; + Style *style = library->styles; + for (i32 i = 0; i < count; ++i, ++style){ + if (match(style->name, theme_name)){ + style_set_colors(style, theme); hit_existing_theme = true; break; } } if (!hit_existing_theme){ - style_add(styles, theme, make_string(name, len)); + if (library->count < library->max){ + Style *style = &library->styles[library->count++]; + style_set_colors(style, theme); + style_set_name(style, make_string(name, len)); + } } } @@ -2607,18 +2617,20 @@ Directory_Get_Hot(Application_Links *app, char *out, int32_t capacity) DOC_PARAM(out, On success this character buffer is filled with the 4coder 'hot directory'.) DOC_PARAM(capacity, Specifies the capacity in bytes of the of the out buffer.) DOC(4coder has a concept of a 'hot directory' which is the directory most recently accessed in the GUI. Whenever the GUI is opened it shows the hot directory. In the future this will be deprecated and eliminated in favor of more flexible hot directories created and controlled in the custom layer.) -DOC_RETURN(This call returns the length of the hot directory string whether or not it was successfully copied into the output buffer. The call is successful if and only if capacity is greater than or the return size.) +DOC_RETURN(This call returns the length of the hot directory string whether or not it was successfully copied into the output buffer. The call is successful if and only if capacity is greater than or equal to the return size.) DOC_SEE(directory_set_hot) */{ Command_Data *cmd = (Command_Data*)app->cmd_context; Hot_Directory *hot = &cmd->models->hot_directory; - i32 copy_max = capacity - 1; hot_directory_clean_end(hot); - if (copy_max > hot->string.size){ - copy_max = hot->string.size; + if (capacity > 0){ + i32 copy_max = capacity - 1; + if (copy_max > hot->string.size){ + copy_max = hot->string.size; + } + memcpy(out, hot->string.str, copy_max); + out[copy_max] = 0; } - memcpy(out, hot->string.str, copy_max); - out[copy_max] = 0; return(hot->string.size); } diff --git a/4ed_app_target.cpp b/4ed_app_target.cpp index 499aa50e..932f580c 100644 --- a/4ed_app_target.cpp +++ b/4ed_app_target.cpp @@ -44,34 +44,38 @@ #include "4ed_linked_node_macros.h" #include "4ed_log.h" +#include "4ed_gui.h" + +#include "4ed_buffer_model.h" +#include "4ed_translation.h" +#include "4ed_buffer.h" +#include "4ed_undo.h" +#include "4ed_file.h" +#include "4ed_style.h" +#include "4ed_hot_directory.h" +#include "4ed_view.h" #include "4ed_font.cpp" +//check #include "4ed_translation.cpp" - #include "4ed_render_target.cpp" #include "4ed_render_fill.cpp" - -#include "4ed_style.h" -#include "4ed_style.cpp" #include "4ed_command.cpp" - +//check #include "4ed_buffer.cpp" -#include "4ed_undo.cpp" +//check #include "4ed_file.cpp" #include "4ed_working_set.cpp" +//check #include "4ed_hot_directory.cpp" #include "4ed_parse_contexts.cpp" - #include "4ed_cli.cpp" - -#include "4ed_gui.h" +//check #include "4ed_gui.cpp" #include "4ed_layout.cpp" -#include "4ed_view.cpp" - #include "4ed_app_models.h" -#include "4ed_file_view.cpp" +#include "4ed_view.cpp" #include "4ed.cpp" // BOTTOM diff --git a/4ed_buffer.cpp b/4ed_buffer.cpp index c3a8cf67..e9a44032 100644 --- a/4ed_buffer.cpp +++ b/4ed_buffer.cpp @@ -15,11 +15,6 @@ #include "4coder_helper/4coder_seek_types.h" -struct Cursor_With_Index{ - i32 pos; - i32 index; -}; - inline void write_cursor_with_index(Cursor_With_Index *positions, i32 *count, i32 pos){ positions[(*count)].index = *count; @@ -278,18 +273,6 @@ eol_in_place_convert_out(char *data, i32 size, i32 max, i32 *size_out){ // Implementation of the gap buffer // -struct Gap_Buffer{ - char *data; - i32 size1; - i32 gap_size; - i32 size2; - i32 max; - - i32 *line_starts; - i32 line_count; - i32 line_max; -}; - inline i32 buffer_good(Gap_Buffer *buffer){ i32 good = (buffer->data != 0); @@ -302,12 +285,6 @@ buffer_size(Gap_Buffer *buffer){ return(size); } -struct Gap_Buffer_Init{ - Gap_Buffer *buffer; - char *data; - i32 size; -}; - internal Gap_Buffer_Init buffer_begin_init(Gap_Buffer *buffer, char *data, i32 size){ Gap_Buffer_Init init; @@ -366,18 +343,6 @@ buffer_end_init(Gap_Buffer_Init *init, void *scratch, i32 scratch_size){ return(result); } -struct Gap_Buffer_Stream{ - Gap_Buffer *buffer; - char *data; - i32 end; - i32 separated; - i32 absolute_end; - - b32 use_termination_character; - char terminator; -}; -global Gap_Buffer_Stream null_buffer_stream = {0}; - internal b32 buffer_stringify_loop(Gap_Buffer_Stream *stream, Gap_Buffer *buffer, i32 start, i32 end){ b32 result = 0; @@ -494,11 +459,6 @@ buffer_replace_range(Gap_Buffer *buffer, i32 start, i32 end, char *str, i32 len, return(result); } -struct Buffer_Batch_State{ - i32 i; - i32 shift_total; -}; - // TODO(allen): Now that we are just using Gap_Buffer we could afford to improve // this for the Gap_Buffer's behavior. internal i32 @@ -610,12 +570,6 @@ buffer_count_newlines(Gap_Buffer *buffer, i32 start, i32 end){ return(count); } -struct Buffer_Measure_Starts{ - i32 i; - i32 count; - i32 start; -}; - // TODO(allen): Rewrite this with a duff routine // Also make it so that the array goes one past the end // and stores the size in the extra spot. @@ -728,56 +682,6 @@ buffer_measure_character_starts(System_Functions *system, Font_Pointers font, Ga Assert(line_index-1 == buffer->line_count); } -enum{ - BLStatus_Finished, - BLStatus_NeedWrapLineShift, - BLStatus_NeedLineShift, - BLStatus_NeedWrapDetermination, -}; - -struct Buffer_Layout_Stop{ - u32 status; - i32 line_index; - i32 wrap_line_index; - i32 pos; - i32 next_line_pos; - f32 x; -}; - -struct Buffer_Measure_Wrap_Params{ - Gap_Buffer *buffer; - i32 *wrap_line_index; - System_Functions *system; - Font_Pointers font; - b32 virtual_white; -}; - -struct Buffer_Measure_Wrap_State{ - Gap_Buffer_Stream stream; - i32 i; - i32 size; - b32 still_looping; - - i32 line_index; - - i32 current_wrap_index; - f32 current_adv; - f32 x; - - b32 skipping_whitespace; - i32 wrap_unit_end; - b32 did_wrap; - b32 first_of_the_line; - - Translation_State tran; - Translation_Emits emits; - u32 J; - Buffer_Model_Step step; - Buffer_Model_Behavior behavior; - - i32 __pc__; -}; - // duff-routine defines #define DrCase(PC) case PC: goto resumespot_##PC #define DrYield(PC, n) { *S_ptr = S; S_ptr->__pc__ = PC; return(n); resumespot_##PC:; } @@ -1295,45 +1199,6 @@ buffer_partial_from_line_character(Gap_Buffer *buffer, i32 line, i32 character){ return(result); } -struct Buffer_Cursor_Seek_Params{ - Gap_Buffer *buffer; - Buffer_Seek seek; - System_Functions *system; - Font_Pointers font; - i32 *wrap_line_index; - i32 *character_starts; - b32 virtual_white; - b32 return_hint; - Full_Cursor *cursor_out; -}; - -struct Buffer_Cursor_Seek_State{ - Full_Cursor next_cursor; - Full_Cursor this_cursor; - Full_Cursor prev_cursor; - - Gap_Buffer_Stream stream; - b32 still_looping; - i32 i; - i32 size; - i32 wrap_unit_end; - - b32 first_of_the_line; - b32 xy_seek; - f32 ch_width; - - i32 font_height; - - Translation_State tran; - Translation_Emits emits; - u32 J; - Buffer_Model_Step step; - Buffer_Model_Behavior behavior; - - - i32 __pc__; -}; - // dialogical-routine defines #define DrCase(PC) case PC: goto resumespot_##PC #define DrYield(PC, n) { *S_ptr = S; S_ptr->__pc__ = PC; return(n); resumespot_##PC:; } @@ -1683,12 +1548,6 @@ buffer_invert_edit(Gap_Buffer *buffer, Buffer_Edit edit, Buffer_Edit *inverse, c buffer_invert_edit_shift(buffer, edit, inverse, strings, str_pos, max, 0); } -struct Buffer_Invert_Batch{ - i32 i; - i32 shift_amount; - i32 len; -}; - internal i32 buffer_invert_batch(Buffer_Invert_Batch *state, Gap_Buffer *buffer, Buffer_Edit *edits, i32 count, Buffer_Edit *inverse, char *strings, i32 *str_pos, i32 max){ @@ -1715,29 +1574,6 @@ buffer_invert_batch(Buffer_Invert_Batch *state, Gap_Buffer *buffer, Buffer_Edit return(result); } -enum Buffer_Render_Flag{ - BRFlag_Special_Character = (1 << 0), - BRFlag_Ghost_Character = (1 << 1) -}; - -struct Buffer_Render_Item{ - i32 index; - u32 codepoint; - u32 flags; - f32 x0, y0; - f32 x1, y1; -}; - -struct Render_Item_Write{ - Buffer_Render_Item *item; - f32 x, y; - System_Functions *system; - Font_Pointers font; - i32 font_height; - f32 x_min; - f32 x_max; -}; - inline Render_Item_Write write_render_item(Render_Item_Write write, i32 index, u32 codepoint, u32 flags){ @@ -1760,55 +1596,6 @@ write_render_item(Render_Item_Write write, i32 index, u32 codepoint, u32 flags){ return(write); } -struct Buffer_Render_Params{ - Gap_Buffer *buffer; - Buffer_Render_Item *items; - i32 max; - i32 *count; - f32 port_x; - f32 port_y; - f32 clip_w; - f32 scroll_x; - f32 scroll_y; - f32 width; - f32 height; - Full_Cursor start_cursor; - i32 wrapped; - System_Functions *system; - Font_Pointers font; - b32 virtual_white; - i32 wrap_slashes; -}; - -struct Buffer_Render_State{ - Gap_Buffer_Stream stream; - b32 still_looping; - i32 i; - i32 size; - - f32 shift_x; - f32 shift_y; - - f32 ch_width; - - Render_Item_Write write; - f32 byte_advance; - - i32 line; - i32 wrap_line; - b32 skipping_whitespace; - b32 first_of_the_line; - i32 wrap_unit_end; - - Translation_State tran; - Translation_Emits emits; - u32 J; - Buffer_Model_Step step; - Buffer_Model_Behavior behavior; - - i32 __pc__; -}; - // duff-routine defines #define DrCase(PC) case PC: goto resumespot_##PC #define DrYield(PC, n) { *S_ptr = S; S_ptr->__pc__ = PC; return(n); resumespot_##PC:; } diff --git a/4ed_buffer.h b/4ed_buffer.h new file mode 100644 index 00000000..131428c8 --- /dev/null +++ b/4ed_buffer.h @@ -0,0 +1,230 @@ +/* + * Mr. 4th Dimention - Allen Webster + * + * 24.01.2018 + * + * Buffer types + * + */ + +// TOP + +#if !defined(FRED_BUFFER_H) +#define FRED_BUFFER_H + +struct Cursor_With_Index{ + i32 pos; + i32 index; +}; + +struct Gap_Buffer{ + char *data; + i32 size1; + i32 gap_size; + i32 size2; + i32 max; + + i32 *line_starts; + i32 line_count; + i32 line_max; +}; + +struct Gap_Buffer_Init{ + Gap_Buffer *buffer; + char *data; + i32 size; +}; + +struct Gap_Buffer_Stream{ + Gap_Buffer *buffer; + char *data; + i32 end; + i32 separated; + i32 absolute_end; + + b32 use_termination_character; + char terminator; +}; +global Gap_Buffer_Stream null_buffer_stream = {0}; + +struct Buffer_Batch_State{ + i32 i; + i32 shift_total; +}; + +struct Buffer_Measure_Starts{ + i32 i; + i32 count; + i32 start; +}; + +enum{ + BLStatus_Finished, + BLStatus_NeedWrapLineShift, + BLStatus_NeedLineShift, + BLStatus_NeedWrapDetermination, +}; + +struct Buffer_Layout_Stop{ + u32 status; + i32 line_index; + i32 wrap_line_index; + i32 pos; + i32 next_line_pos; + f32 x; +}; + +struct Buffer_Measure_Wrap_Params{ + Gap_Buffer *buffer; + i32 *wrap_line_index; + System_Functions *system; + Font_Pointers font; + b32 virtual_white; +}; + +struct Buffer_Measure_Wrap_State{ + Gap_Buffer_Stream stream; + i32 i; + i32 size; + b32 still_looping; + + i32 line_index; + + i32 current_wrap_index; + f32 current_adv; + f32 x; + + b32 skipping_whitespace; + i32 wrap_unit_end; + b32 did_wrap; + b32 first_of_the_line; + + Translation_State tran; + Translation_Emits emits; + u32 J; + Buffer_Model_Step step; + Buffer_Model_Behavior behavior; + + i32 __pc__; +}; + +struct Buffer_Cursor_Seek_Params{ + Gap_Buffer *buffer; + Buffer_Seek seek; + System_Functions *system; + Font_Pointers font; + i32 *wrap_line_index; + i32 *character_starts; + b32 virtual_white; + b32 return_hint; + Full_Cursor *cursor_out; +}; + +struct Buffer_Cursor_Seek_State{ + Full_Cursor next_cursor; + Full_Cursor this_cursor; + Full_Cursor prev_cursor; + + Gap_Buffer_Stream stream; + b32 still_looping; + i32 i; + i32 size; + i32 wrap_unit_end; + + b32 first_of_the_line; + b32 xy_seek; + f32 ch_width; + + i32 font_height; + + Translation_State tran; + Translation_Emits emits; + u32 J; + Buffer_Model_Step step; + Buffer_Model_Behavior behavior; + + + i32 __pc__; +}; + +struct Buffer_Invert_Batch{ + i32 i; + i32 shift_amount; + i32 len; +}; + +enum Buffer_Render_Flag{ + BRFlag_Special_Character = (1 << 0), + BRFlag_Ghost_Character = (1 << 1) +}; + +struct Buffer_Render_Item{ + i32 index; + u32 codepoint; + u32 flags; + f32 x0, y0; + f32 x1, y1; +}; + +struct Render_Item_Write{ + Buffer_Render_Item *item; + f32 x, y; + System_Functions *system; + Font_Pointers font; + i32 font_height; + f32 x_min; + f32 x_max; +}; + +struct Buffer_Render_Params{ + Gap_Buffer *buffer; + Buffer_Render_Item *items; + i32 max; + i32 *count; + f32 port_x; + f32 port_y; + f32 clip_w; + f32 scroll_x; + f32 scroll_y; + f32 width; + f32 height; + Full_Cursor start_cursor; + i32 wrapped; + System_Functions *system; + Font_Pointers font; + b32 virtual_white; + i32 wrap_slashes; +}; + +struct Buffer_Render_State{ + Gap_Buffer_Stream stream; + b32 still_looping; + i32 i; + i32 size; + + f32 shift_x; + f32 shift_y; + + f32 ch_width; + + Render_Item_Write write; + f32 byte_advance; + + i32 line; + i32 wrap_line; + b32 skipping_whitespace; + b32 first_of_the_line; + i32 wrap_unit_end; + + Translation_State tran; + Translation_Emits emits; + u32 J; + Buffer_Model_Step step; + Buffer_Model_Behavior behavior; + + i32 __pc__; +}; + +#endif + +// BOTTOM \ No newline at end of file diff --git a/4ed_file.cpp b/4ed_file.cpp index fcc74936..c745dc6a 100644 --- a/4ed_file.cpp +++ b/4ed_file.cpp @@ -13,22 +13,6 @@ // Edit Position Basics // -enum Edit_Pos_Set_Type{ - EditPos_None, - EditPos_CursorSet, - EditPos_ScrollSet -}; -struct File_Edit_Positions{ - GUI_Scroll_Vars scroll; - Full_Cursor cursor; - i32 mark; - f32 preferred_x; - i32 scroll_i; - i32 last_set_type; - b32 in_view; -}; -static File_Edit_Positions null_edit_pos = {0}; - internal void edit_pos_set_cursor(File_Edit_Positions *edit_pos, Full_Cursor cursor, b32 set_preferred_x, b32 unwrapped_lines){ edit_pos->cursor = cursor; @@ -47,26 +31,10 @@ edit_pos_set_scroll(File_Edit_Positions *edit_pos, GUI_Scroll_Vars scroll){ edit_pos->last_set_type = EditPos_ScrollSet; } - -// TODO(NAME): Replace this with markers over time. -// -// Highlighting Information -// - -struct Text_Effect{ - i32 start, end; - u32 color; - f32 seconds_down, seconds_max; -}; - // // Editing_File // -union Buffer_Slot_ID{ - Buffer_ID id; - i16 part[2]; -}; inline Buffer_Slot_ID to_file_id(i32 id){ Buffer_Slot_ID result; @@ -74,110 +42,6 @@ to_file_id(i32 id){ return(result); } -struct Marker_Array{ - Marker_Array *next, *prev; - Buffer_Slot_ID buffer_id; - u32 count, sim_max, max; - Marker marker_0; -}; -global_const u32 sizeof_marker_array = sizeof(Marker_Array) - sizeof(Marker); - -struct Editing_File_Markers{ - Marker_Array sentinel; - u32 array_count; - u32 marker_count; -}; - -struct Editing_File_Settings{ - i32 base_map_id; - i32 display_width; - i32 minimum_base_display_width; - i32 wrap_indicator; - Parse_Context_ID parse_context_id; - b32 dos_write_mode; - b32 virtual_white; - Face_ID font_id; - b8 unwrapped_lines; - b8 tokens_exist; - b8 tokens_without_strings; - b8 is_initialized; - b8 unimportant; - b8 read_only; - b8 never_kill; - u8 pad[1]; -}; -global_const Editing_File_Settings null_editing_file_settings = {0}; - -struct Editing_Hacks{ - b32 suppression_mode; - b32 needs_wraps_and_fix_cursor; -}; - -struct Editing_File_State{ - Gap_Buffer buffer; - - i32 *wrap_line_index; - i32 wrap_max; - - i32 *character_starts; - i32 character_start_max; - - f32 *line_indents; - i32 line_indent_max; - - i32 wrap_line_count; - - i32 *wrap_positions; - i32 wrap_position_count; - i32 wrap_position_max; - - Undo_Data undo; - - Cpp_Token_Array token_array; - Cpp_Token_Array swap_array; - u32 lex_job; - b32 tokens_complete; - b32 still_lexing; - - Text_Effect paste_effect; - - Dirty_State dirty; - u32 ignore_behind_os; - - File_Edit_Positions edit_pos_space[16]; - File_Edit_Positions *edit_poss[16]; - i32 edit_poss_count; - - Editing_Hacks hacks; -}; -global_const Editing_File_State null_editing_file_state = {0}; - -struct Editing_File_Name{ - char name_[256]; - String name; -}; - -struct File_Node{ - File_Node *next; - File_Node *prev; -}; - -struct Editing_File{ - // NOTE(allen): node must be the first member of Editing_File! - File_Node node; - Editing_File_Settings settings; - b32 is_loading; - b32 is_dummy; - Editing_File_State state; - Editing_File_Markers markers; - Editing_File_Name base_name; - Editing_File_Name unique_name; - Editing_File_Name canon; - Buffer_Slot_ID id; -}; -static Editing_File null_editing_file = {0}; - - // // Handling a file's Marker Arrays // @@ -207,9 +71,9 @@ clear_file_markers_state(General_Memory *general, Editing_File_Markers *markers) internal void* allocate_markers_state(General_Memory *general, Editing_File *file, u32 new_array_max){ - u32 memory_size = sizeof_marker_array + sizeof(Marker)*new_array_max; + u32 memory_size = sizeof(Marker_Array) + sizeof(Marker)*new_array_max; memory_size = l_round_up_u32(memory_size, KB(4)); - u32 real_max = (memory_size - sizeof_marker_array)/sizeof(Marker); + u32 real_max = (memory_size - sizeof(Marker_Array))/sizeof(Marker); Marker_Array *array = (Marker_Array*)general_memory_allocate(general, memory_size); dll_insert_back(&file->markers.sentinel, array); @@ -243,7 +107,7 @@ markers_set(Editing_File *file, void *handle, u32 first_index, u32 count, Marker file->markers.marker_count += new_count - markers->count; markers->count = new_count; } - Marker *dst = &markers->marker_0; + Marker *dst = MarkerArrayBase(markers); memcpy(dst + first_index, source, sizeof(Marker)*count); result = true; } @@ -260,7 +124,7 @@ markers_get(Editing_File *file, void *handle, u32 first_index, u32 count, Marker Marker_Array *markers = (Marker_Array*)handle; if (markers->buffer_id.id == file->id.id){ if (first_index + count <= markers->count){ - Marker *src = &markers->marker_0; + Marker *src = MarkerArrayBase(markers); memcpy(output, src + first_index, sizeof(Marker)*count); result = true; } @@ -465,9 +329,9 @@ file_is_ready(Editing_File *file){ inline void file_set_to_loading(Editing_File *file){ - file->state = null_editing_file_state; - file->settings = null_editing_file_settings; - file->is_loading = 1; + memset(&file->state, 0, sizeof(file->state)); + memset(&file->settings, 0, sizeof(file->settings)); + file->is_loading = true; } inline void diff --git a/4ed_file.h b/4ed_file.h new file mode 100644 index 00000000..f53ba750 --- /dev/null +++ b/4ed_file.h @@ -0,0 +1,145 @@ +/* + * Mr. 4th Dimention - Allen Webster + * + * 24.01.2018 + * + * Buffer types + * + */ + +// TOP + +#if !defined(FRED_FILE_H) +#define FRED_FILE_H + +enum Edit_Pos_Set_Type{ + EditPos_None, + EditPos_CursorSet, + EditPos_ScrollSet +}; +struct File_Edit_Positions{ + GUI_Scroll_Vars scroll; + Full_Cursor cursor; + i32 mark; + f32 preferred_x; + i32 scroll_i; + i32 last_set_type; + b32 in_view; +}; + +// TODO(NAME): Replace this with markers over time. +struct Text_Effect{ + i32 start, end; + u32 color; + f32 seconds_down, seconds_max; +}; + +union Buffer_Slot_ID{ + Buffer_ID id; + i16 part[2]; +}; + +struct Marker_Array{ + Marker_Array *next, *prev; + Buffer_Slot_ID buffer_id; + u32 count, sim_max, max; +}; + +#define MarkerArrayBase(array) (Marker*)((u8*)(array) + sizeof(Marker_Array)) + +struct Editing_File_Markers{ + Marker_Array sentinel; + u32 array_count; + u32 marker_count; +}; + +struct Editing_File_Settings{ + i32 base_map_id; + i32 display_width; + i32 minimum_base_display_width; + i32 wrap_indicator; + Parse_Context_ID parse_context_id; + b32 dos_write_mode; + b32 virtual_white; + Face_ID font_id; + b8 unwrapped_lines; + b8 tokens_exist; + b8 tokens_without_strings; + b8 is_initialized; + b8 unimportant; + b8 read_only; + b8 never_kill; + u8 pad[1]; +}; + +struct Editing_Hacks{ + b32 suppression_mode; + b32 needs_wraps_and_fix_cursor; +}; + +struct Editing_File_State{ + Gap_Buffer buffer; + + i32 *wrap_line_index; + i32 wrap_max; + + i32 *character_starts; + i32 character_start_max; + + f32 *line_indents; + i32 line_indent_max; + + i32 wrap_line_count; + + i32 *wrap_positions; + i32 wrap_position_count; + i32 wrap_position_max; + + Undo_Data undo; + + Cpp_Token_Array token_array; + Cpp_Token_Array swap_array; + u32 lex_job; + b32 tokens_complete; + b32 still_lexing; + + Text_Effect paste_effect; + + Dirty_State dirty; + u32 ignore_behind_os; + + File_Edit_Positions edit_pos_space[16]; + File_Edit_Positions *edit_poss[16]; + i32 edit_poss_count; + + Editing_Hacks hacks; +}; + +struct Editing_File_Name{ + char name_[256]; + String name; +}; + +struct File_Node{ + File_Node *next; + File_Node *prev; +}; + +struct Editing_File{ + // NOTE(allen): node must be the first member of Editing_File! + File_Node node; + Editing_File_Settings settings; + b32 is_loading; + b32 is_dummy; + Editing_File_State state; + Editing_File_Markers markers; + Editing_File_Name base_name; + Editing_File_Name unique_name; + Editing_File_Name canon; + Buffer_Slot_ID id; +}; + +#endif + +// BOTTOM + diff --git a/4ed_file_view.cpp b/4ed_file_view.cpp deleted file mode 100644 index b62b2381..00000000 --- a/4ed_file_view.cpp +++ /dev/null @@ -1,6711 +0,0 @@ -/* - * Mr. 4th Dimention - Allen Webster - * - * 19.08.2015 - * - * File editing view for 4coder. - * - */ - -// TOP - -inline void* -get_view_body(View *view){ - char *result = (char*)view; - result += sizeof(View_Persistent); - return(result); -} -inline i32 -get_view_size(){ - return(sizeof(View) - sizeof(View_Persistent)); -} - -// TODO(allen): Switch over to using an i32 for these. -inline f32 -view_width(View *view){ - i32_Rect file_rect = view->file_region; - f32 result = (f32)(file_rect.x1 - file_rect.x0); - return (result); -} - -inline f32 -view_file_display_width(View *view){ - Editing_File *file = view->file_data.file; - f32 result = (f32)file->settings.display_width; - return(result); -} - -inline f32 -view_file_minimum_base__width(View *view){ - Editing_File *file = view->file_data.file; - f32 result = (f32)file->settings.display_width; - return(result); -} - -inline f32 -view_file_height(View *view){ - i32_Rect file_rect = view->file_region; - f32 result = (f32)(file_rect.y1 - file_rect.y0); - return (result); -} - -inline i32 -view_get_cursor_pos(View *view){ - i32 result = 0; - if (view->file_data.show_temp_highlight){ - result = view->file_data.temp_highlight.pos; - } - else if (view->edit_pos){ - result = view->edit_pos->cursor.pos; - } - return(result); -} - -inline f32 -view_get_cursor_x(View *view){ - f32 result = 0; - - Full_Cursor *cursor = 0; - if (view->file_data.show_temp_highlight){ - cursor = &view->file_data.temp_highlight; - } - else if (view->edit_pos){ - cursor = &view->edit_pos->cursor; - } - - if (cursor){ - result = cursor->wrapped_x; - if (view->file_data.file->settings.unwrapped_lines){ - result = cursor->unwrapped_x; - } - } - - return(result); -} - -inline f32 -view_get_cursor_y(View *view){ - f32 result = 0; - - Full_Cursor *cursor = 0; - if (view->file_data.show_temp_highlight){ - cursor = &view->file_data.temp_highlight; - } - else if (view->edit_pos){ - cursor = &view->edit_pos->cursor; - } - - if (cursor){ - result = cursor->wrapped_y; - if (view->file_data.file->settings.unwrapped_lines){ - result = cursor->unwrapped_y; - } - } - - return(result); -} - -struct Cursor_Limits{ - f32 min, max; - f32 delta; -}; - -inline Cursor_Limits -view_cursor_limits(View *view){ - Cursor_Limits limits = {0}; - - f32 line_height = (f32)view->line_height; - f32 visible_height = view_file_height(view); - - limits.max = visible_height - line_height*3.f; - limits.min = line_height * 2; - - if (limits.max - limits.min <= line_height){ - if (visible_height >= line_height){ - limits.max = visible_height - line_height; - limits.min = -line_height; - } - else{ - limits.max = visible_height; - limits.min = -line_height; - } - } - - limits.max = (limits.max > 0)?(limits.max):(0); - limits.min = (limits.min > 0)?(limits.min):(0); - - limits.delta = clamp_top(line_height*3.f, (limits.max - limits.min)*.5f); - - return(limits); -} - -internal Full_Cursor -view_compute_cursor(System_Functions *system, View *view, Buffer_Seek seek, b32 return_hint){ - Editing_File *file = view->file_data.file; - - Font_Pointers font = system->font.get_pointers_by_id(file->settings.font_id); - Assert(font.valid); - - Full_Cursor result = {0}; - - Buffer_Cursor_Seek_Params params; - params.buffer = &file->state.buffer; - params.seek = seek; - params.system = system; - params.font = font; - params.wrap_line_index = file->state.wrap_line_index; - params.character_starts = file->state.character_starts; - params.virtual_white = file->settings.virtual_white; - params.return_hint = return_hint; - params.cursor_out = &result; - - Buffer_Cursor_Seek_State state = {0}; - Buffer_Layout_Stop stop = {0}; - - i32 size = buffer_size(params.buffer); - - f32 line_shift = 0.f; - b32 do_wrap = 0; - i32 wrap_unit_end = 0; - - b32 first_wrap_determination = 1; - i32 wrap_array_index = 0; - - do{ - stop = buffer_cursor_seek(&state, params, line_shift, do_wrap, wrap_unit_end); - switch (stop.status){ - case BLStatus_NeedWrapDetermination: - { - if (stop.pos >= size){ - do_wrap = 0; - wrap_unit_end = max_i32; - } - else{ - if (first_wrap_determination){ - wrap_array_index = binary_search(file->state.wrap_positions, stop.pos, 0, file->state.wrap_position_count); - ++wrap_array_index; - if (file->state.wrap_positions[wrap_array_index] == stop.pos){ - do_wrap = 1; - wrap_unit_end = file->state.wrap_positions[wrap_array_index]; - } - else{ - do_wrap = 0; - wrap_unit_end = file->state.wrap_positions[wrap_array_index]; - } - first_wrap_determination = 0; - } - else{ - Assert(stop.pos == wrap_unit_end); - do_wrap = 1; - ++wrap_array_index; - wrap_unit_end = file->state.wrap_positions[wrap_array_index]; - } - } - }break; - - case BLStatus_NeedWrapLineShift: - case BLStatus_NeedLineShift: - { - line_shift = file->state.line_indents[stop.wrap_line_index]; - }break; - } - }while(stop.status != BLStatus_Finished); - - return(result); -} - -inline Full_Cursor -view_compute_cursor_from_xy(System_Functions *system, View *view, f32 seek_x, f32 seek_y){ - Buffer_Seek seek; - if (view->file_data.file->settings.unwrapped_lines){ - seek = seek_unwrapped_xy(seek_x, seek_y, 0); - } - else{ - seek = seek_wrapped_xy(seek_x, seek_y, 0); - } - - Full_Cursor result = view_compute_cursor(system, view, seek, 0); - return(result); -} - -inline i32 -view_compute_max_target_y(i32 lowest_line, i32 line_height, f32 view_height){ - f32 max_target_y = ((lowest_line+.5f)*line_height) - view_height*.5f; - max_target_y = clamp_bottom(0.f, max_target_y); - return(ceil32(max_target_y)); -} - -internal i32 -file_compute_lowest_line(Editing_File *file, f32 font_height){ - i32 lowest_line = 0; - - Gap_Buffer *buffer = &file->state.buffer; - if (file->settings.unwrapped_lines){ - lowest_line = buffer->line_count; - } - else{ - lowest_line = file->state.wrap_line_index[buffer->line_count]; - } - - return(lowest_line); -} - -inline i32 -view_compute_max_target_y(View *view){ - i32 line_height = view->line_height; - i32 lowest_line = file_compute_lowest_line(view->file_data.file, (f32)line_height); - f32 view_height = clamp_bottom((f32)line_height, view_file_height(view)); - i32 max_target_y = view_compute_max_target_y(lowest_line, line_height, view_height); - return(max_target_y); -} - -internal b32 -view_move_view_to_cursor(View *view, GUI_Scroll_Vars *scroll, b32 center_view){ - b32 result = 0; - f32 max_x = view_width(view); - i32 max_y = view_compute_max_target_y(view); - - f32 cursor_y = view_get_cursor_y(view); - f32 cursor_x = view_get_cursor_x(view); - - GUI_Scroll_Vars scroll_vars = *scroll; - i32 target_y = scroll_vars.target_y; - i32 target_x = scroll_vars.target_x; - - Cursor_Limits limits = view_cursor_limits(view); - - if (cursor_y > target_y + limits.max){ - if (center_view){ - target_y = round32(cursor_y - limits.max*.5f); - } - else{ - target_y = ceil32(cursor_y - limits.max + limits.delta); - } - } - if (cursor_y < target_y + limits.min){ - if (center_view){ - target_y = round32(cursor_y - limits.max*.5f); - } - else{ - target_y = floor32(cursor_y - limits.delta - limits.min); - } - } - - target_y = clamp(0, target_y, max_y); - - if (cursor_x >= target_x + max_x){ - target_x = ceil32(cursor_x - max_x/2); - } - else if (cursor_x < target_x){ - target_x = floor32(Max(0, cursor_x - max_x/2)); - } - - if (target_x != scroll_vars.target_x || target_y != scroll_vars.target_y){ - scroll->target_x = target_x; - scroll->target_y = target_y; - result = 1; - } - - return(result); -} - -internal b32 -view_move_cursor_to_view(System_Functions *system, View *view, GUI_Scroll_Vars scroll, Full_Cursor *cursor, f32 preferred_x){ - b32 result = 0; - - if (view->edit_pos){ - i32 line_height = view->line_height; - f32 old_cursor_y = cursor->wrapped_y; - if (view->file_data.file->settings.unwrapped_lines){ - old_cursor_y = cursor->unwrapped_y; - } - f32 cursor_y = old_cursor_y; - f32 target_y = scroll.target_y + view->widget_height; - - Cursor_Limits limits = view_cursor_limits(view); - - if (cursor_y > target_y + limits.max){ - cursor_y = target_y + limits.max; - } - if (target_y != 0 && cursor_y < target_y + limits.min){ - cursor_y = target_y + limits.min; - } - - if (cursor_y != old_cursor_y){ - if (cursor_y > old_cursor_y){ - cursor_y += line_height; - } - else{ - cursor_y -= line_height; - } - - *cursor = view_compute_cursor_from_xy(system, view, preferred_x, cursor_y); - - result = 1; - } - } - - return(result); -} - -internal void -view_set_cursor(View *view, Full_Cursor cursor, b32 set_preferred_x, b32 unwrapped_lines){ - if (edit_pos_move_to_front(view->file_data.file, view->edit_pos)){ - edit_pos_set_cursor(view->edit_pos, cursor, set_preferred_x, unwrapped_lines); - GUI_Scroll_Vars scroll = view->edit_pos->scroll; - if (view_move_view_to_cursor(view, &scroll, 0)){ - view->edit_pos->scroll = scroll; - } - } -} - -internal void -view_set_scroll(System_Functions *system, View *view, GUI_Scroll_Vars scroll){ - if (edit_pos_move_to_front(view->file_data.file, view->edit_pos)){ - edit_pos_set_scroll(view->edit_pos, scroll); - Full_Cursor cursor = view->edit_pos->cursor; - if (view_move_cursor_to_view(system, view, view->edit_pos->scroll, &cursor, view->edit_pos->preferred_x)){ - view->edit_pos->cursor = cursor; - } - } -} - -internal void -view_set_cursor_and_scroll(View *view, Full_Cursor cursor, b32 set_preferred_x, b32 unwrapped_lines, GUI_Scroll_Vars scroll){ - File_Edit_Positions *edit_pos = view->edit_pos; - if (edit_pos_move_to_front(view->file_data.file, edit_pos)){ - edit_pos_set_cursor(edit_pos, cursor, set_preferred_x, unwrapped_lines); - edit_pos_set_scroll(edit_pos, scroll); - edit_pos->last_set_type = EditPos_None; - } -} - -inline void -view_set_temp_highlight(System_Functions *system, View *view, i32 pos, i32 end_pos){ - view->file_data.temp_highlight = view_compute_cursor(system, view, seek_pos(pos), 0); - view->file_data.temp_highlight_end_pos = end_pos; - view->file_data.show_temp_highlight = 1; - - view_set_cursor(view, view->file_data.temp_highlight, 0, view->file_data.file->settings.unwrapped_lines); -} - -struct View_And_ID{ - View *view; - i32 id; -}; - -enum Lock_Level{ - LockLevel_Open = 0, - LockLevel_Protected = 1, - LockLevel_Hidden = 2 -}; - -inline u32 -view_lock_flags(View *view){ - u32 result = AccessOpen; - File_Viewing_Data *data = &view->file_data; - if (view->showing_ui != VUI_None){ - result |= AccessHidden; - } - if (data->file_locked || - (data->file && data->file->settings.read_only)){ - result |= AccessProtected; - } - return(result); -} - -inline i32 -view_lock_level(View *view){ - i32 result = LockLevel_Open; - u32 flags = view_lock_flags(view); - if (flags & AccessHidden){ - result = LockLevel_Hidden; - } - else if (flags & AccessProtected){ - result = LockLevel_Protected; - } - return(result); -} - -struct View_Iter{ - View *view; - - Editing_File *file; - View *skip; - Panel *used_panels; - Panel *panel; -}; - -internal View_Iter -file_view_iter_next(View_Iter iter){ - View *view; - - for (iter.panel = iter.panel->next; iter.panel != iter.used_panels; iter.panel = iter.panel->next){ - view = iter.panel->view; - if (view != iter.skip && (view->file_data.file == iter.file || iter.file == 0)){ - iter.view = view; - break; - } - } - - return(iter); -} - -internal View_Iter -file_view_iter_init(Editing_Layout *layout, Editing_File *file, View *skip){ - View_Iter result; - result.used_panels = &layout->used_sentinel; - result.panel = result.used_panels; - result.file = file; - result.skip = skip; - - result = file_view_iter_next(result); - - return(result); -} - -internal b32 -file_view_iter_good(View_Iter iter){ - b32 result = (iter.panel != iter.used_panels); - return(result); -} - -inline b32 -starts_new_line(u8 character){ - return (character == '\n'); -} - -inline void -file_synchronize_times(System_Functions *system, Editing_File *file){ - file->state.dirty = DirtyState_UpToDate; -} - -internal b32 -save_file_to_name(System_Functions *system, Models *models, Editing_File *file, char *filename){ - b32 result = 0; - b32 using_actual_filename = false; - - if (!filename){ - terminate_with_null(&file->canon.name); - filename = file->canon.name.str; - using_actual_filename = true; - } - - if (filename){ - Mem_Options *mem = &models->mem; - if (models->hook_save_file){ - models->hook_save_file(&models->app_links, file->id.id); - } - - i32 max = 0, size = 0; - b32 dos_write_mode = file->settings.dos_write_mode; - char *data = 0; - Gap_Buffer *buffer = &file->state.buffer; - - if (dos_write_mode){ - max = buffer_size(buffer) + buffer->line_count + 1; - } - else{ - max = buffer_size(buffer); - } - - b32 used_general = 0; - Temp_Memory temp = begin_temp_memory(&mem->part); - char empty = 0; - if (max == 0){ - data = ∅ - } - else{ - data = (char*)push_array(&mem->part, char, max); - if (!data){ - used_general = 1; - data = (char*)general_memory_allocate(&mem->general, max); - } - } - Assert(data != 0); - - if (dos_write_mode){ - size = buffer_convert_out(buffer, data, max); - } - else{ - size = max; - buffer_stringify(buffer, 0, size, data); - } - - if (!using_actual_filename && file->canon.name.str != 0){ - char space[512]; - u32 length = str_size(filename); - system->get_canonical(filename, length, space, sizeof(space)); - - char *source_path = file->canon.name.str; - if (match(space, source_path)){ - using_actual_filename = true; - } - } - - result = system->save_file(filename, data, size); - - if (result && using_actual_filename){ - file->state.ignore_behind_os = 1; - } - - file_mark_clean(file); - - if (used_general){ - general_memory_free(&mem->general, data); - } - end_temp_memory(temp); - - file_synchronize_times(system, file); - } - - return(result); -} - -inline b32 -save_file(System_Functions *system, Models *models, Editing_File *file){ - b32 result = save_file_to_name(system, models, file, 0); - return(result); -} - -enum{ - GROW_FAILED, - GROW_NOT_NEEDED, - GROW_SUCCESS, -}; - -internal i32 -file_grow_starts_as_needed(General_Memory *general, Gap_Buffer *buffer, i32 additional_lines){ - b32 result = GROW_NOT_NEEDED; - i32 max = buffer->line_max; - i32 count = buffer->line_count; - i32 target_lines = count + additional_lines; - - if (target_lines > max || max == 0){ - max = l_round_up_i32(target_lines + max, KB(1)); - - i32 *new_lines = (i32*)general_memory_reallocate(general, buffer->line_starts, sizeof(i32)*count, sizeof(f32)*max); - - if (new_lines){ - result = GROW_SUCCESS; - buffer->line_max = max; - buffer->line_starts = new_lines; - } - else{ - result = GROW_FAILED; - } - } - - return(result); -} - -internal void -file_update_cursor_positions(System_Functions *system, Models *models, Editing_File *file){ - Editing_Layout *layout = &models->layout; - for (View_Iter iter = file_view_iter_init(layout, file, 0); - file_view_iter_good(iter); - iter = file_view_iter_next(iter)){ - i32 pos = view_get_cursor_pos(iter.view); - - if (!iter.view->file_data.show_temp_highlight){ - Full_Cursor cursor = view_compute_cursor(system, iter.view, seek_pos(pos), 0); - view_set_cursor(iter.view, cursor, 1, iter.view->file_data.file->settings.unwrapped_lines); - } - else{ - view_set_temp_highlight(system, iter.view, pos, iter.view->file_data.temp_highlight_end_pos); - } - } -} - -// -// File Metadata Measuring -// - -internal void -file_measure_starts(General_Memory *general, Gap_Buffer *buffer){ - PRFL_FUNC_GROUP(); - - if (!buffer->line_starts){ - i32 max = buffer->line_max = KB(1); - buffer->line_starts = (i32*)general_memory_allocate(general, max*sizeof(i32)); - TentativeAssert(buffer->line_starts); - // TODO(allen): when unable to allocate? - } - - Buffer_Measure_Starts state = {0}; - while (buffer_measure_starts(&state, buffer)){ - i32 count = state.count; - i32 max = buffer->line_max; - max = ((max + 1) << 1); - - { - i32 *new_lines = (i32*)general_memory_reallocate(general, buffer->line_starts, sizeof(i32)*count, sizeof(i32)*max); - - // TODO(allen): when unable to grow? - TentativeAssert(new_lines); - buffer->line_starts = new_lines; - buffer->line_max = max; - } - } -} - -// NOTE(allen): These calls assumes that the buffer's line starts are already correct, -// and that the buffer's line_count is correct. -internal void -file_allocate_metadata_as_needed(General_Memory *general, Gap_Buffer *buffer, void **mem, i32 *mem_max_count, i32 count, i32 item_size){ - if (*mem == 0){ - i32 max = ((count+1)*2); - max = (max+(0x3FF))&(~(0x3FF)); - *mem = general_memory_allocate(general, max*item_size); - *mem_max_count = max; - } - else if (*mem_max_count < count){ - i32 old_max = *mem_max_count; - i32 max = ((count+1)*2); - max = (max+(0x3FF))&(~(0x3FF)); - - void *new_mem = general_memory_reallocate(general, *mem, item_size*old_max, item_size*max); - - Assert(new_mem); - *mem = new_mem; - *mem_max_count = max; - } -} - -inline void -file_allocate_character_starts_as_needed(General_Memory *general, Editing_File *file){ - file_allocate_metadata_as_needed(general, &file->state.buffer, (void**)&file->state.character_starts, &file->state. character_start_max, file->state.buffer.line_count, sizeof(i32)); -} - -internal void -file_measure_character_starts(System_Functions *system, Models *models, Editing_File *file){ - file_allocate_character_starts_as_needed(&models->mem.general, file); - Font_Pointers font = system->font.get_pointers_by_id(file->settings.font_id); - buffer_measure_character_starts(system, font, &file->state.buffer, file->state.character_starts, 0, file->settings.virtual_white); - file_update_cursor_positions(system, models, file); -} - -internal void -file_allocate_indents_as_needed(General_Memory *general, Editing_File *file, i32 min_last_index){ - i32 min_amount = min_last_index + 1; - file_allocate_metadata_as_needed(general, &file->state.buffer, (void**)&file->state.line_indents, &file->state.line_indent_max, min_amount, sizeof(f32)); -} - -inline void -file_allocate_wraps_as_needed(General_Memory *general, Editing_File *file){ - file_allocate_metadata_as_needed(general, &file->state.buffer, (void**)&file->state.wrap_line_index, &file->state.wrap_max, file->state.buffer.line_count, sizeof(f32)); -} - -inline void -file_allocate_wrap_positions_as_needed(General_Memory *general, Editing_File *file, i32 min_last_index){ - i32 min_amount = min_last_index + 1; - file_allocate_metadata_as_needed(general, &file->state.buffer, (void**)&file->state.wrap_positions, &file->state.wrap_position_max, min_amount, sizeof(f32)); -} - -struct Code_Wrap_X{ - f32 base_x; - f32 paren_nesting[32]; - i32 paren_safe_top; - i32 paren_top; -}; -global Code_Wrap_X null_wrap_x = {0}; - -struct Code_Wrap_State{ - Cpp_Token_Array token_array; - Cpp_Token *token_ptr; - Cpp_Token *end_token; - - Code_Wrap_X wrap_x; - - b32 in_pp_body; - Code_Wrap_X plane_wrap_x; - - i32 *line_starts; - i32 line_count; - i32 line_index; - i32 next_line_start; - - f32 x; - b32 consume_newline; - - Gap_Buffer_Stream stream; - i32 size; - i32 i; - - Font_Pointers font; - f32 tab_indent_amount; - f32 byte_advance; - - Translation_State tran; - Translation_Emits emits; - u32 J; - Buffer_Model_Step step; - Buffer_Model_Behavior behavior; -}; - -internal void -wrap_state_init(System_Functions *system, Code_Wrap_State *state, Editing_File *file, Font_Pointers font){ - state->token_array = file->state.token_array; - state->token_ptr = state->token_array.tokens; - state->end_token = state->token_ptr + state->token_array.count; - - state->line_starts = file->state.buffer.line_starts; - state->line_count = file->state.buffer.line_count; - state->next_line_start = state->line_starts[1]; - - Gap_Buffer *buffer = &file->state.buffer; - i32 size = buffer_size(buffer); - buffer_stringify_loop(&state->stream, buffer, 0, size); - state->size = size; - state->i = 0; - - state->font = font; - - state->tab_indent_amount = font_get_glyph_advance(system, font.settings, font.metrics, font.pages, '\t'); - state->byte_advance = font.metrics->byte_advance; - - state->tran = null_buffer_translating_state; -} - -internal void -wrap_state_set_x(Code_Wrap_State *state, f32 line_shift){ - state->x = line_shift; -} - -internal void -wrap_state_set_i(Code_Wrap_State *state, i32 i){ - state->i = i; -} - -internal void -wrap_state_set_top(Code_Wrap_State *state, f32 line_shift){ - if (state->wrap_x.paren_nesting[state->wrap_x.paren_safe_top] > line_shift){ - state->wrap_x.paren_nesting[state->wrap_x.paren_safe_top] = line_shift; - } -} - -struct Code_Wrap_Step{ - i32 position_start; - i32 position_end; - - f32 start_x; - f32 final_x; - - Cpp_Token *this_token; -}; - -internal Code_Wrap_Step -wrap_state_consume_token(System_Functions *system, Font_Pointers font, Code_Wrap_State *state, i32 fixed_end_point){ - Code_Wrap_Step result = {0}; - i32 i = state->i; - - result.position_start = i; - - Cpp_Token token = {0}; - - token.start = state->size; - if (state->token_ptr < state->end_token){ - token = *state->token_ptr; - } - - if (state->consume_newline){ - ++i; - state->x = 0; - state->consume_newline = 0; - } - - - if (state->in_pp_body){ - if (!(token.flags & CPP_TFLAG_PP_BODY)){ - state->in_pp_body = false; - state->wrap_x = state->plane_wrap_x; - } - } - - if (!state->in_pp_body){ - if (token.flags & CPP_TFLAG_PP_DIRECTIVE){ - state->in_pp_body = true; - state->plane_wrap_x = state->wrap_x; - state->wrap_x = null_wrap_x; - } - } - - b32 skipping_whitespace = false; - if (i >= state->next_line_start){ - state->x = state->wrap_x.paren_nesting[state->wrap_x.paren_safe_top]; - state->x = state->wrap_x.paren_nesting[state->wrap_x.paren_safe_top]; - skipping_whitespace = true; - } - - // TODO(allen): exponential search this shit! - for (;i >= state->next_line_start;){ - state->next_line_start = state->size; - if (state->line_index < state->line_count){ - ++state->line_index; - if (state->line_index + 1 < state->line_count){ - state->next_line_start = state->line_starts[state->line_index + 1]; - } - } - else{ - break; - } - } - - i32 line_start = state->size; - if (state->line_index < 0){ - line_start = 0; - } - else if (state->line_index < state->line_count){ - line_start = state->line_starts[state->line_index]; - } - b32 still_looping = 0; - i32 end = token.start + token.size; - if (fixed_end_point >= 0 && end > fixed_end_point){ - end = fixed_end_point; - } - - i = clamp_bottom(line_start, i); - if (i == line_start){ - skipping_whitespace = true; - } - - b32 recorded_start_x = false; - do{ - for (; i < state->stream.end; ++i){ - if (!(i < end)){ - goto doublebreak; - } - - u8 ch = (u8)state->stream.data[i]; - translating_fully_process_byte(system, font, &state->tran, ch, i, state->size, &state->emits); - - for (TRANSLATION_EMIT_LOOP(state->J, state->emits)){ - TRANSLATION_GET_STEP(state->step, state->behavior, state->J, state->emits); - - if (state->behavior.do_newline){ - state->consume_newline = 1; - goto doublebreak; - } - else if(state->behavior.do_number_advance || state->behavior.do_codepoint_advance){ - u32 n = state->step.value; - f32 adv = 0; - if (state->behavior.do_codepoint_advance){ - adv = font_get_glyph_advance(system, state->font.settings, state->font.metrics, state->font.pages, n); - - if (n != ' ' && n != '\t'){ - skipping_whitespace = false; - } - } - else{ - adv = state->byte_advance; - skipping_whitespace = false; - } - - if (!skipping_whitespace){ - if (!recorded_start_x){ - result.start_x = state->x; - recorded_start_x = true; - } - state->x += adv; - } - } - } - } - still_looping = buffer_stringify_next(&state->stream); - }while(still_looping); - doublebreak:; - - state->i = i; - - b32 consume_token = false; - if (state->token_ptr < state->end_token && i >= token.start + token.size){ - consume_token = true; - } - - result.this_token = state->token_ptr; - if (consume_token){ - Assert(state->token_ptr < state->end_token); - switch (state->token_ptr->type){ - case CPP_TOKEN_BRACE_OPEN: - { - state->wrap_x.paren_nesting[state->wrap_x.paren_safe_top] += state->tab_indent_amount; - }break; - - case CPP_TOKEN_BRACE_CLOSE: - { - state->wrap_x.paren_nesting[state->wrap_x.paren_safe_top] -= state->tab_indent_amount; - }break; - - case CPP_TOKEN_PARENTHESE_OPEN: - case CPP_TOKEN_BRACKET_OPEN: - { - ++state->wrap_x.paren_top; - - i32 top = state->wrap_x.paren_top; - if (top >= ArrayCount(state->wrap_x.paren_nesting)){ - top = ArrayCount(state->wrap_x.paren_nesting) - 1; - } - state->wrap_x.paren_safe_top = top; - - state->wrap_x.paren_nesting[top] = state->x; - }break; - - case CPP_TOKEN_PARENTHESE_CLOSE: - case CPP_TOKEN_BRACKET_CLOSE: - { - --state->wrap_x.paren_top; - - if (state->wrap_x.paren_top < 0){ - state->wrap_x.paren_top = 0; - } - - i32 top = state->wrap_x.paren_top; - if (top >= ArrayCount(state->wrap_x.paren_nesting)){ - top = ArrayCount(state->wrap_x.paren_nesting) - 1; - } - state->wrap_x.paren_safe_top = top; - }break; - } - - ++state->token_ptr; - if (state->token_ptr > state->end_token){ - state->token_ptr = state->end_token; - } - } - - result.position_end = state->i; - result.final_x = state->x; - - if (!recorded_start_x){ - result.start_x = state->x; - recorded_start_x = true; - } - - return(result); -} - -struct Wrap_Indent_Pair{ - i32 wrap_position; - f32 line_shift; -}; - -struct Potential_Wrap_Indent_Pair{ - i32 wrap_position; - f32 line_shift; - - f32 wrap_x; - i32 wrappable_score; - - b32 adjust_top_to_this; -}; - -internal i32 -stickieness_guess(Cpp_Token_Type type, Cpp_Token_Type other_type, u16 flags, u16 other_flags, b32 on_left){ - i32 guess = 0; - - b32 is_words = 0, other_is_words = 0; - if (type == CPP_TOKEN_IDENTIFIER || (type >= CPP_TOKEN_KEY_TYPE && type <= CPP_TOKEN_KEY_OTHER)){ - is_words = 1; - } - if (other_type == CPP_TOKEN_IDENTIFIER || (other_type >= CPP_TOKEN_KEY_TYPE && other_type <= CPP_TOKEN_KEY_OTHER)){ - other_is_words = 1; - } - - b32 is_operator = 0, other_is_operator = 0; - if (flags & CPP_TFLAG_IS_OPERATOR){ - is_operator = 1; - } - if (other_flags & CPP_TFLAG_IS_OPERATOR){ - other_is_operator = 1; - } - - i32 operator_side_bias = 70*(!on_left); - - if (is_words && other_is_words){ - guess = 200; - } - else if (type == CPP_TOKEN_PARENTHESE_OPEN){ - if (on_left){ - guess = 100; - } - else{ - if (other_is_words){ - guess = 100; - } - else{ - guess = 0; - } - } - } - else if (type == CPP_TOKEN_SEMICOLON){ - if (on_left){ - guess = 0; - } - else{ - guess = 1000; - } - } - else if (type == CPP_TOKEN_COMMA){ - guess = 20; - } - else if (type == CPP_TOKEN_COLON || - type == CPP_TOKEN_PARENTHESE_CLOSE || - type == CPP_TOKEN_BRACKET_OPEN || - type == CPP_TOKEN_BRACKET_CLOSE){ - if (on_left){ - guess = 20; - if (other_is_words){ - guess = 100; - } - } - else{ - guess = 100; - } - } - else if (type == CPP_PP_DEFINED){ - if (on_left){ - guess = 100; - } - else{ - guess = 0; - } - } - else if (type == CPP_TOKEN_SCOPE){ - guess = 90; - } - else if (type == CPP_TOKEN_MINUS){ - if (on_left){ - guess = 80; - } - else{ - guess = 60; - } - } - else if (type == CPP_TOKEN_DOT || - type == CPP_TOKEN_ARROW){ - guess = 200; - } - else if (type == CPP_TOKEN_NOT || - type == CPP_TOKEN_TILDE){ - if (on_left){ - guess = 80; - } - else{ - guess = 20; - } - } - else if (type == CPP_TOKEN_INCREMENT || - type == CPP_TOKEN_DECREMENT || - type == CPP_TOKEN_STAR || - type == CPP_TOKEN_AMPERSAND || - (type >= CPP_TOKEN_POSTINC && - type <= CPP_TOKEN_DELETE_ARRAY)){ - guess = 80; - if (!on_left && other_is_operator){ - guess = 20; - } - } - else if (type >= CPP_TOKEN_MUL && type <= CPP_TOKEN_MOD){ - guess = 70; - } - else if (type == CPP_TOKEN_PLUS){ - guess = 60 + operator_side_bias; - } - else if (type >= CPP_TOKEN_LSHIFT && type <= CPP_TOKEN_RSHIFT){ - guess = 50; - } - else if (type >= CPP_TOKEN_LESS && type <= CPP_TOKEN_NOTEQ){ - guess = 40 + operator_side_bias; - } - else if (type >= CPP_TOKEN_BIT_XOR && type <= CPP_TOKEN_BIT_OR){ - guess = 40; - } - else if (type >= CPP_TOKEN_AND && type <= CPP_TOKEN_OR){ - guess = 30 + operator_side_bias; - } - else if (type >= CPP_TOKEN_TERNARY_QMARK && type <= CPP_TOKEN_COLON){ - guess = 20 + operator_side_bias; - } - else if (type == CPP_TOKEN_THROW){ - if (on_left){ - guess = 100; - } - else{ - guess = 0; - } - } - else if (type >= CPP_TOKEN_EQ && type <= CPP_TOKEN_XOREQ){ - guess = 15 + operator_side_bias; - } - - return(guess); -} - -struct Wrap_Current_Shift{ - f32 shift; - b32 adjust_top_to_this; -}; - -internal Wrap_Current_Shift -get_current_shift(Code_Wrap_State *wrap_state, i32 next_line_start){ - Wrap_Current_Shift result = {0}; - - result.shift = wrap_state->wrap_x.paren_nesting[wrap_state->wrap_x.paren_safe_top]; - - Cpp_Token next_token = {0}; - if (wrap_state->token_ptr < wrap_state->end_token){ - next_token = *wrap_state->token_ptr; - } - - if (wrap_state->token_ptr > wrap_state->token_array.tokens){ - Cpp_Token prev_token = *(wrap_state->token_ptr-1); - - if (wrap_state->wrap_x.paren_safe_top != 0 && prev_token.type == CPP_TOKEN_PARENTHESE_OPEN){ - result.shift = wrap_state->wrap_x.paren_nesting[wrap_state->wrap_x.paren_safe_top-1] + wrap_state->tab_indent_amount; - result.adjust_top_to_this = 1; - } - - f32 statement_continuation_indent = 0.f; - if (result.shift != 0.f && wrap_state->wrap_x.paren_safe_top == 0){ - if (!(prev_token.flags & (CPP_TFLAG_PP_DIRECTIVE|CPP_TFLAG_PP_BODY))){ - switch (prev_token.type){ - case CPP_TOKEN_BRACKET_OPEN: - case CPP_TOKEN_BRACE_OPEN: - case CPP_TOKEN_BRACE_CLOSE: - case CPP_TOKEN_SEMICOLON: - case CPP_TOKEN_COLON: - case CPP_TOKEN_COMMA: - case CPP_TOKEN_COMMENT: break; - default: statement_continuation_indent += wrap_state->tab_indent_amount; break; - } - } - } - - switch (next_token.type){ - case CPP_TOKEN_BRACE_CLOSE: case CPP_TOKEN_BRACE_OPEN: break; - default: result.shift += statement_continuation_indent; break; - } - } - - if (next_token.start < next_line_start){ - if (next_token.flags & CPP_TFLAG_PP_DIRECTIVE){ - result.shift = 0; - } - else{ - switch (next_token.type){ - case CPP_TOKEN_BRACE_CLOSE: - { - if (wrap_state->wrap_x.paren_safe_top == 0){ - result.shift -= wrap_state->tab_indent_amount; - } - }break; - } - } - } - - result.shift = clamp_bottom(0.f, result.shift); - return(result); -} - -internal void -file_measure_wraps(System_Functions *system, Models *models, Editing_File *file, Font_Pointers font){ - PRFL_FUNC_GROUP(); - - General_Memory *general = &models->mem.general; - Partition *part = &models->mem.part; - - Temp_Memory temp = begin_temp_memory(part); - - file_allocate_wraps_as_needed(general, file); - file_allocate_indents_as_needed(general, file, file->state.buffer.line_count); - file_allocate_wrap_positions_as_needed(general, file, file->state.buffer.line_count); - - Buffer_Measure_Wrap_Params params; - params.buffer = &file->state.buffer; - params.wrap_line_index = file->state.wrap_line_index; - params.system = system; - params.font = font; - params.virtual_white = file->settings.virtual_white; - - f32 width = (f32)file->settings.display_width; - f32 minimum_base_width = (f32)file->settings.minimum_base_display_width; - - i32 size = buffer_size(params.buffer); - - Buffer_Measure_Wrap_State state = {0}; - Buffer_Layout_Stop stop = {0}; - - f32 edge_tolerance = 50.f; - edge_tolerance = clamp_top(edge_tolerance, 50.f); - - f32 current_line_shift = 0.f; - b32 do_wrap = 0; - i32 wrap_unit_end = 0; - - i32 wrap_position_index = 0; - file->state.wrap_positions[wrap_position_index++] = 0; - - Code_Wrap_State wrap_state = {0}; - - b32 use_tokens = false; - - Wrap_Indent_Pair *wrap_indent_marks = 0; - Potential_Wrap_Indent_Pair *potential_marks = 0; - i32 max_wrap_indent_mark = 0; - - if (params.virtual_white && file->state.tokens_complete && !file->state.still_lexing){ - wrap_state_init(system, &wrap_state, file, font); - use_tokens = true; - - potential_marks = push_array(part, Potential_Wrap_Indent_Pair, floor32(width)); - - max_wrap_indent_mark = partition_remaining(part)/sizeof(Wrap_Indent_Pair); - wrap_indent_marks = push_array(part, Wrap_Indent_Pair, max_wrap_indent_mark); - } - - i32 real_count = 0; - i32 potential_count = 0; - i32 stage = 0; - - do{ - stop = buffer_measure_wrap_y(&state, params, current_line_shift, do_wrap, wrap_unit_end); - - switch (stop.status){ - case BLStatus_NeedWrapDetermination: - { - if (use_tokens){ - if (stage == 0){ - do_wrap = 0; - wrap_unit_end = wrap_indent_marks[stage+1].wrap_position; - ++stage; - } - else{ - do_wrap = 1; - wrap_unit_end = wrap_indent_marks[stage+1].wrap_position; - file_allocate_wrap_positions_as_needed(general, file, wrap_position_index); - file->state.wrap_positions[wrap_position_index++] = stop.pos; - } - } - else{ - Translation_State tran = {0}; - Translation_Emits emits = {0}; - Gap_Buffer_Stream stream = {0}; - - i32 word_stage = 0; - i32 i = stop.pos; - f32 x = stop.x; - f32 self_x = 0; - i32 wrap_end_result = size; - if (buffer_stringify_loop(&stream, params.buffer, i, size)){ - b32 still_looping = false; - do{ - for (; i < stream.end; ++i){ - { - u8 ch = stream.data[i]; - translating_fully_process_byte(system, font, &tran, ch, i, size, &emits); - } - - for (TRANSLATION_DECL_EMIT_LOOP(J, emits)){ - TRANSLATION_DECL_GET_STEP(step, behavior, J, emits); - - u32 codepoint = step.value; - switch (word_stage){ - case 0: - { - if (codepoint_is_whitespace(codepoint)){ - word_stage = 1; - } - else{ - f32 adv = font_get_glyph_advance(params.system, params.font.settings, params.font.metrics, params.font.pages, codepoint); - - x += adv; - self_x += adv; - if (self_x > width){ - wrap_end_result = step.i; - goto doublebreak; - } - } - }break; - - case 1: - { - if (!codepoint_is_whitespace(codepoint)){ - wrap_end_result = step.i; - goto doublebreak; - } - }break; - } - } - } - still_looping = buffer_stringify_next(&stream); - }while(still_looping); - } - - doublebreak:; - wrap_unit_end = wrap_end_result; - if (x > width){ - do_wrap = 1; - file_allocate_wrap_positions_as_needed(general, file, wrap_position_index); - file->state.wrap_positions[wrap_position_index++] = stop.pos; - } - else{ - do_wrap = 0; - } - } - }break; - - case BLStatus_NeedWrapLineShift: - case BLStatus_NeedLineShift: - { - f32 current_width = width; - - if (use_tokens){ - Code_Wrap_State original_wrap_state = wrap_state; - i32 next_line_start = buffer_size(params.buffer); - if (stop.line_index+1 < params.buffer->line_count){ - next_line_start = params.buffer->line_starts[stop.line_index+1]; - } - - f32 base_adjusted_width = wrap_state.wrap_x.base_x + minimum_base_width; - - if (minimum_base_width != 0 && current_width < base_adjusted_width){ - current_width = base_adjusted_width; - } - - if (stop.status == BLStatus_NeedLineShift){ - real_count = 0; - potential_count = 0; - stage = 0; - - Wrap_Current_Shift current_shift = get_current_shift(&wrap_state, next_line_start); - - if (current_shift.adjust_top_to_this){ - wrap_state_set_top(&wrap_state, current_shift.shift); - } - - wrap_indent_marks[real_count].wrap_position = 0; - wrap_indent_marks[real_count].line_shift =current_shift.shift; - ++real_count; - - wrap_state.wrap_x.base_x = wrap_state.wrap_x.paren_nesting[0]; - - for (; wrap_state.token_ptr < wrap_state.end_token; ){ - Code_Wrap_Step step = {0}; - b32 emit_comment_position = false; - b32 first_word = true; - - if (wrap_state.token_ptr->type == CPP_TOKEN_COMMENT || wrap_state.token_ptr->type == CPP_TOKEN_STRING_CONSTANT){ - i32 i = wrap_state.token_ptr->start; - i32 end_i = i + wrap_state.token_ptr->size; - - if (i < wrap_state.i){ - i = wrap_state.i; - } - - if (end_i > wrap_state.next_line_start){ - end_i = wrap_state.next_line_start; - } - - f32 x = wrap_state.x; - - step.position_start = i; - step.start_x = x; - step.this_token = wrap_state.token_ptr; - - Gap_Buffer_Stream stream = {0}; - Translation_State tran = {0}; - Translation_Emits emits = {0}; - - Potential_Wrap_Indent_Pair potential_wrap = {0}; - potential_wrap.wrap_position = i; - potential_wrap.line_shift = x; - potential_wrap.wrappable_score = 5; - potential_wrap.wrap_x = x; - potential_wrap.adjust_top_to_this = 0; - - if (buffer_stringify_loop(&stream, params.buffer, i, end_i)){ - b32 still_looping = true; - - while(still_looping){ - for (; i < stream.end; ++i){ - { - u8 ch = stream.data[i]; - translating_fully_process_byte(system, font, &tran, ch, i, end_i, &emits); - } - - for (TRANSLATION_DECL_EMIT_LOOP(J, emits)){ - TRANSLATION_DECL_GET_STEP(buffer_step, behav, J, emits); - if (!codepoint_is_whitespace(buffer_step.value)){ - i = buffer_step.i; - goto doublebreak_stage_vspace; - } - } - } - still_looping = buffer_stringify_next(&stream); - } - doublebreak_stage_vspace:; - - do{ - i32 pos_end_i = end_i; - while (still_looping){ - for (; i < stream.end; ++i){ - { - u8 ch = stream.data[i]; - translating_fully_process_byte(system, font, &tran, ch, i, end_i, &emits); - } - - for (TRANSLATION_DECL_EMIT_LOOP(J, emits)){ - TRANSLATION_DECL_GET_STEP(buffer_step, behav, J, emits); - if (codepoint_is_whitespace(buffer_step.value)){ - pos_end_i = buffer_step.i; - goto doublebreak_stage1; - } - - f32 adv = font_get_glyph_advance(params.system, params.font.settings, params.font.metrics, params.font.pages, buffer_step.value); - x += adv; - - if (!first_word && x > current_width){ - pos_end_i = buffer_step.i; - emit_comment_position = true; - goto doublebreak_stage1; - } - } - } - still_looping = buffer_stringify_next(&stream); - } - doublebreak_stage1:; - first_word = 0; - - if (emit_comment_position){ - step.position_end = pos_end_i; - step.final_x = x; - goto finished_comment_split; - } - - while(still_looping){ - for (; i < stream.end; ++i){ - { - u8 ch = stream.data[i]; - translating_fully_process_byte(system, font, &tran, ch, i, end_i, &emits); - } - - for (TRANSLATION_DECL_EMIT_LOOP(J, emits)){ - TRANSLATION_DECL_GET_STEP(buffer_step, behav, J, emits); - - if (!codepoint_is_whitespace(buffer_step.value)){ - pos_end_i = buffer_step.i; - goto doublebreak_stage2; - } - - f32 adv = font_get_glyph_advance(params.system, params.font.settings, params.font.metrics, params.font.pages, buffer_step.value); - x += adv; - } - } - still_looping = buffer_stringify_next(&stream); - } - doublebreak_stage2:; - - potential_wrap.wrap_position = pos_end_i; - potential_wrap.wrap_x = x; - }while(still_looping); - } - - finished_comment_split:; - if (emit_comment_position){ - potential_marks[potential_count] = potential_wrap; - ++potential_count; - } - } - - if (!emit_comment_position){ - step = wrap_state_consume_token(system, font, &wrap_state, next_line_start); - } - - b32 need_to_choose_a_wrap = false; - if (step.final_x > current_width){ - need_to_choose_a_wrap = true; - } - - current_shift = get_current_shift(&wrap_state, next_line_start); - - b32 next_token_is_on_line = false; - if (wrap_state.token_ptr < wrap_state.end_token){ - if (wrap_state.token_ptr->start < next_line_start){ - next_token_is_on_line = true; - } - } - - i32 next_wrap_position = step.position_end; - f32 wrap_x = step.final_x; - if (next_token_is_on_line){ - if (wrap_state.token_ptr < wrap_state.end_token){ - i32 pos_i = wrap_state.token_ptr->start; - if (pos_i > step.position_start && next_wrap_position < pos_i){ - next_wrap_position = pos_i; - } - } - } - - if (!need_to_choose_a_wrap){ - i32 wrappable_score = 1; - - Cpp_Token *this_token = step.this_token; - Cpp_Token *next_token = 0; - if (wrap_state.token_ptr < wrap_state.end_token){ - next_token = wrap_state.token_ptr; - } - - Cpp_Token_Type this_type = this_token->type; - Cpp_Token_Type next_type = CPP_TOKEN_JUNK; - - u16 this_flags = this_token->flags; - u16 next_flags = 0; - - if (this_token == next_token || !next_token_is_on_line){ - next_token = 0; - } - - if (next_token){ - next_type = next_token->type; - next_flags = next_token->flags; - } - - i32 this_stickieness = stickieness_guess(this_type, next_type, this_flags, next_flags, 1); - - i32 next_stickieness = 0; - if (next_token){ - next_stickieness = stickieness_guess(next_type, this_type, next_flags, this_flags, 0); - } - - i32 general_stickieness = this_stickieness; - if (general_stickieness < next_stickieness){ - general_stickieness = next_stickieness; - } - - if (wrap_state.wrap_x.paren_top != 0 && this_type == CPP_TOKEN_COMMA){ - general_stickieness = 0; - } - - wrappable_score = 64*50; - wrappable_score += 101 - general_stickieness - wrap_state.wrap_x.paren_safe_top*80; - - potential_marks[potential_count].wrap_position = next_wrap_position; - potential_marks[potential_count].line_shift = current_shift.shift; - potential_marks[potential_count].wrappable_score = wrappable_score; - potential_marks[potential_count].wrap_x = wrap_x; - potential_marks[potential_count].adjust_top_to_this = current_shift.adjust_top_to_this; - ++potential_count; - } - - if (need_to_choose_a_wrap){ - if (potential_count == 0){ - wrap_indent_marks[real_count].wrap_position = next_wrap_position; - wrap_indent_marks[real_count].line_shift = current_shift.shift; - ++real_count; - } - else{ - i32 i = 0, best_i = 0; - i32 best_score = -1; - f32 best_x_shift = 0; - - f32 x_gain_threshold = 18.f; - - for (; i < potential_count; ++i){ - i32 this_score = potential_marks[i].wrappable_score; - f32 x_shift = potential_marks[i].wrap_x - potential_marks[i].line_shift; - - f32 x_shift_adjusted = x_shift - x_gain_threshold; - f32 x_left_over = step.final_x - x_shift; - - if (x_shift_adjusted < 0){ - this_score = 0; - } - else if (x_left_over <= x_gain_threshold){ - this_score = 1; - } - - if (this_score > best_score){ - best_score = this_score; - best_x_shift = x_shift; - best_i = i; - } - else if (this_score == best_score && x_shift > best_x_shift){ - best_x_shift = x_shift; - best_i = i; - } - } - - i32 wrap_position = potential_marks[best_i].wrap_position; - f32 line_shift = potential_marks[best_i].line_shift; - b32 adjust_top_to_this = potential_marks[best_i].adjust_top_to_this; - wrap_indent_marks[real_count].wrap_position = wrap_position; - wrap_indent_marks[real_count].line_shift = line_shift; - ++real_count; - potential_count = 0; - - wrap_state = original_wrap_state; - for (;;){ - step = wrap_state_consume_token(system, font, &wrap_state, wrap_position); - if (step.position_end >= wrap_position){ - break; - } - } - - wrap_state_set_x(&wrap_state, line_shift); - wrap_state_set_i(&wrap_state, wrap_position); - if (adjust_top_to_this){ - wrap_state_set_top(&wrap_state, line_shift); - } - - original_wrap_state = wrap_state; - } - } - - if (step.position_end >= next_line_start-1){ - break; - } - } - - wrap_indent_marks[real_count].wrap_position = next_line_start; - wrap_indent_marks[real_count].line_shift = 0; - ++real_count; - - for (i32 l = 0; wrap_state.i < next_line_start && l < 3; ++l){ - wrap_state_consume_token(system, font, &wrap_state, next_line_start); - } - } - - current_line_shift = wrap_indent_marks[stage].line_shift; - - if (stage > 0){ - ++stage; - } - - current_line_shift = clamp_bottom(0.f, current_line_shift); - } - else{ - current_line_shift = 0.f; - } - - current_line_shift = clamp_top(current_line_shift, current_width - edge_tolerance); - - if (stop.wrap_line_index >= file->state.line_indent_max){ - file_allocate_indents_as_needed(general, file, stop.wrap_line_index); - } - - file->state.line_indents[stop.wrap_line_index] = current_line_shift; - file->state.wrap_line_count = stop.wrap_line_index; - }break; - } - }while(stop.status != BLStatus_Finished); - - ++file->state.wrap_line_count; - - file_allocate_wrap_positions_as_needed(general, file, wrap_position_index); - file->state.wrap_positions[wrap_position_index++] = size; - file->state.wrap_position_count = wrap_position_index; - - end_temp_memory(temp); - - if (file->state.hacks.needs_wraps_and_fix_cursor){ - file->state.hacks.needs_wraps_and_fix_cursor = false; - file_update_cursor_positions(system, models, file); - } -} - -internal void -file_measure_wraps_and_fix_cursor(System_Functions *system, Models *models, Editing_File *file, Font_Pointers font){ - if (file->state.hacks.suppression_mode){ - file->state.hacks.needs_wraps_and_fix_cursor = true; - } - else{ - file->state.hacks.needs_wraps_and_fix_cursor = false; - file_measure_wraps(system, models, file, font); - file_update_cursor_positions(system, models, file); - } -} - -// -// -// - -enum{ - FileCreateFlag_ReadOnly = 1, -}; - -internal void -file_create_from_string(System_Functions *system, Models *models, Editing_File *file, String val, u32 flags){ - PRFL_FUNC_GROUP(); - - General_Memory *general = &models->mem.general; - Partition *part = &models->mem.part; - Open_File_Hook_Function *hook_open_file = models->hook_open_file; - Application_Links *app_links = &models->app_links; - - file->state = null_editing_file_state; - Gap_Buffer_Init init = buffer_begin_init(&file->state.buffer, val.str, val.size); - for (; buffer_init_need_more(&init); ){ - i32 page_size = buffer_init_page_size(&init); - page_size = l_round_up_i32(page_size, KB(4)); - if (page_size < KB(4)){ - page_size = KB(4); - } - void *data = general_memory_allocate(general, page_size); - buffer_init_provide_page(&init, data, page_size); - } - - i32 scratch_size = partition_remaining(part); - Assert(scratch_size > 0); - b32 init_success = buffer_end_init(&init, part->base + part->pos, scratch_size); - AllowLocal(init_success); Assert(init_success); - - if (buffer_size(&file->state.buffer) < val.size){ - file->settings.dos_write_mode = 1; - } - file_synchronize_times(system, file); - - Face_ID font_id = models->global_font_id; - file->settings.font_id = font_id; - Font_Pointers font = system->font.get_pointers_by_id(font_id); - Assert(font.valid); - - { - file_measure_starts(general, &file->state.buffer); - - file_allocate_character_starts_as_needed(general, file); - buffer_measure_character_starts(system, font, &file->state.buffer, file->state.character_starts, 0, file->settings.virtual_white); - - file_measure_wraps(system, models, file, font); - } - - file->settings.read_only = ((flags & FileCreateFlag_ReadOnly) != 0); - if (!file->settings.read_only){ - // TODO(allen): Redo undo system (if you don't mind the pun) - i32 request_size = KB(64); - file->state.undo.undo.max = request_size; - file->state.undo.undo.strings = (u8*)general_memory_allocate(general, request_size); - file->state.undo.undo.edit_max = request_size / sizeof(Edit_Step); - file->state.undo.undo.edits = (Edit_Step*)general_memory_allocate(general, request_size); - - file->state.undo.redo.max = request_size; - file->state.undo.redo.strings = (u8*)general_memory_allocate(general, request_size); - file->state.undo.redo.edit_max = request_size / sizeof(Edit_Step); - file->state.undo.redo.edits = (Edit_Step*)general_memory_allocate(general, request_size); - - file->state.undo.history.max = request_size; - file->state.undo.history.strings = (u8*)general_memory_allocate(general, request_size); - file->state.undo.history.edit_max = request_size / sizeof(Edit_Step); - file->state.undo.history.edits = (Edit_Step*)general_memory_allocate(general, request_size); - - file->state.undo.children.max = request_size; - file->state.undo.children.strings = (u8*)general_memory_allocate(general, request_size); - file->state.undo.children.edit_max = request_size / sizeof(Buffer_Edit); - file->state.undo.children.edits = (Buffer_Edit*)general_memory_allocate(general, request_size); - - file->state.undo.history_block_count = 1; - file->state.undo.history_head_block = 0; - file->state.undo.current_block_normal = 1; - } - - if (hook_open_file){ - file->state.hacks.suppression_mode = true; - hook_open_file(app_links, file->id.id); - file->state.hacks.suppression_mode = false; - if (file->state.hacks.needs_wraps_and_fix_cursor){ - file_measure_wraps_and_fix_cursor(system, models, file, font); - } - } - file->settings.is_initialized = true; -} - -internal void -file_close(System_Functions *system, General_Memory *general, Editing_File *file){ - if (file->state.still_lexing){ - system->cancel_job(BACKGROUND_THREADS, file->state.lex_job); - if (file->state.swap_array.tokens){ - general_memory_free(general, file->state.swap_array.tokens); - file->state.swap_array.tokens = 0; - } - } - if (file->state.token_array.tokens){ - general_memory_free(general, file->state.token_array.tokens); - } - - Gap_Buffer *buffer = &file->state.buffer; - if (buffer->data){ - general_memory_free(general, buffer->data); - general_memory_free(general, buffer->line_starts); - } - - general_memory_free(general, file->state.wrap_line_index); - general_memory_free(general, file->state.character_starts); - general_memory_free(general, file->state.line_indents); - - if (file->state.undo.undo.edits){ - general_memory_free(general, file->state.undo.undo.strings); - general_memory_free(general, file->state.undo.undo.edits); - - general_memory_free(general, file->state.undo.redo.strings); - general_memory_free(general, file->state.undo.redo.edits); - - general_memory_free(general, file->state.undo.history.strings); - general_memory_free(general, file->state.undo.history.edits); - - general_memory_free(general, file->state.undo.children.strings); - general_memory_free(general, file->state.undo.children.edits); - } -} - -struct Shift_Information{ - i32 start, end, amount; -}; - -internal -Job_Callback_Sig(job_full_lex){ - Editing_File *file = (Editing_File*)data[0]; - General_Memory *general = (General_Memory*)data[1]; - Models *models = (Models*)data[2]; - - Parse_Context parse_context = parse_context_get(&models->parse_context_memory, file->settings.parse_context_id, memory->data, memory->size); - if (!parse_context.valid){ - return; - } - - Gap_Buffer *buffer = &file->state.buffer; - i32 text_size = buffer_size(buffer); - - u32 aligned_buffer_size = (text_size + 3)&(~3); - - for (;memory->size < aligned_buffer_size + parse_context.memory_size;){ - void *old_base = memory->data; - system->grow_thread_memory(memory); - parse_context_rebase(&parse_context, old_base, memory->data); - } - - u8 *data_ptr = (u8*)memory->data; - umem data_size = memory->size; - data_ptr += parse_context.memory_size; - data_size -= parse_context.memory_size; - - Cpp_Token_Array tokens = {0}; - tokens.tokens = (Cpp_Token*)(data_ptr); - tokens.max_count = (u32)(data_size / sizeof(Cpp_Token)); - tokens.count = 0; - - b32 still_lexing = true; - - Cpp_Lex_Data lex = cpp_lex_data_init(file->settings.tokens_without_strings, parse_context.kw_table, parse_context.pp_table); - - // TODO(allen): deduplicate this against relex - char *chunks[3]; - i32 chunk_sizes[3]; - chunks[0] = buffer->data; - chunk_sizes[0] = buffer->size1; - chunks[1] = buffer->data + buffer->size1 + buffer->gap_size; - chunk_sizes[1] = buffer->size2; - chunks[2] = 0; - chunk_sizes[2] = 0; - - i32 chunk_index = 0; - - do{ - char *chunk = chunks[chunk_index]; - i32 chunk_size = chunk_sizes[chunk_index]; - - i32 result = - cpp_lex_step(&lex, chunk, chunk_size, text_size, &tokens, 2048); - - switch (result){ - case LexResult_NeedChunk: - { - ++chunk_index; - Assert(chunk_index < ArrayCount(chunks)); - }break; - - case LexResult_NeedTokenMemory: - { - if (system->check_cancel(thread)){ - return; - } - - void *old_base = memory->data; - system->grow_thread_memory(memory); - cpp_rebase_tables(&lex, old_base, memory->data); - - data_ptr = (u8*)memory->data; - data_size = memory->size; - data_ptr += parse_context.memory_size; - data_size -= parse_context.memory_size; - tokens.tokens = (Cpp_Token*)(data_ptr); - tokens.max_count = (u32)(data_size / sizeof(Cpp_Token)); - }break; - - case LexResult_HitTokenLimit: - { - if (system->check_cancel(thread)){ - return; - } - }break; - - case LexResult_Finished: - { - still_lexing = false; - }break; - } - }while(still_lexing); - - i32 new_max = l_round_up_i32(tokens.count+1, KB(1)); - - system->acquire_lock(FRAME_LOCK); - { - Assert(file->state.swap_array.tokens == 0); - file->state.swap_array.tokens = (Cpp_Token*)general_memory_allocate(general, new_max*sizeof(Cpp_Token)); - } - system->release_lock(FRAME_LOCK); - - u8 *dest = (u8*)file->state.swap_array.tokens; - u8 *src = (u8*)tokens.tokens; - - memcpy(dest, src, tokens.count*sizeof(Cpp_Token)); - - system->acquire_lock(FRAME_LOCK); - { - Cpp_Token_Array *file_token_array = &file->state.token_array; - file_token_array->count = tokens.count; - file_token_array->max_count = new_max; - if (file_token_array->tokens){ - general_memory_free(general, file_token_array->tokens); - } - file_token_array->tokens = file->state.swap_array.tokens; - file->state.swap_array.tokens = 0; - } - system->release_lock(FRAME_LOCK); - - // NOTE(allen): These are outside the locked section because I don't - // think getting these out of order will cause critical bugs, and I - // want to minimize what's done in locked sections. - file->state.tokens_complete = true; - file->state.still_lexing = false; -} - -internal void -file_kill_tokens(System_Functions *system, General_Memory *general, Editing_File *file){ - file->settings.tokens_exist = 0; - if (file->state.still_lexing){ - system->cancel_job(BACKGROUND_THREADS, file->state.lex_job); - if (file->state.swap_array.tokens){ - general_memory_free(general, file->state.swap_array.tokens); - file->state.swap_array.tokens = 0; - } - } - if (file->state.token_array.tokens){ - general_memory_free(general, file->state.token_array.tokens); - } - file->state.tokens_complete = 0; - file->state.token_array = null_cpp_token_array; -} - -internal void -file_first_lex_parallel(System_Functions *system, Models *models, Editing_File *file){ - General_Memory *general = &models->mem.general; - file->settings.tokens_exist = true; - - if (file->is_loading == 0 && file->state.still_lexing == 0){ - Assert(file->state.token_array.tokens == 0); - - file->state.tokens_complete = false; - file->state.still_lexing = true; - - Job_Data job; - job.callback = job_full_lex; - job.data[0] = file; - job.data[1] = general; - job.data[2] = models; - file->state.lex_job = system->post_job(BACKGROUND_THREADS, job); - } -} - -internal void -file_first_lex_serial(Models *models, Editing_File *file){ - Mem_Options *mem = &models->mem; - Partition *part = &mem->part; - General_Memory *general = &mem->general; - file->settings.tokens_exist = true; - - Assert(!file->state.still_lexing); - - if (file->is_loading == 0){ - Assert(file->state.token_array.tokens == 0); - - { - Temp_Memory temp = begin_temp_memory(part); - - Parse_Context parse_context = parse_context_get(&models->parse_context_memory, file->settings.parse_context_id, partition_current(part), partition_remaining(part)); - Assert(parse_context.valid); - push_block(part, (i32)parse_context.memory_size); - - Gap_Buffer *buffer = &file->state.buffer; - i32 text_size = buffer_size(buffer); - - i32 mem_size = partition_remaining(part); - - Cpp_Token_Array tokens; - tokens.max_count = mem_size / sizeof(Cpp_Token); - tokens.count = 0; - tokens.tokens = push_array(part, Cpp_Token, tokens.max_count); - - b32 still_lexing = true; - - Cpp_Lex_Data lex = cpp_lex_data_init(file->settings.tokens_without_strings, parse_context.kw_table, parse_context.pp_table); - - // TODO(allen): deduplicate this against relex - char *chunks[3]; - i32 chunk_sizes[3]; - chunks[0] = buffer->data; - chunk_sizes[0] = buffer->size1; - chunks[1] = buffer->data + buffer->size1 + buffer->gap_size; - chunk_sizes[1] = buffer->size2; - chunks[2] = 0; - chunk_sizes[2] = 0; - - i32 chunk_index = 0; - - Cpp_Token_Array *swap_array = &file->state.swap_array; - - do{ - char *chunk = chunks[chunk_index]; - i32 chunk_size = chunk_sizes[chunk_index]; - - i32 result = cpp_lex_step(&lex, chunk, chunk_size, text_size, &tokens, NO_OUT_LIMIT); - - switch (result){ - case LexResult_NeedChunk: - { - ++chunk_index; - Assert(chunk_index < ArrayCount(chunks)); - }break; - - case LexResult_Finished: - case LexResult_NeedTokenMemory: - { - u32 new_max = l_round_up_u32(tokens.count+1, KB(1)); - u32 new_mem_max = new_max*sizeof(Cpp_Token); - u32 old_mem_max = swap_array->count*sizeof(Cpp_Token); - if (swap_array->tokens == 0){ - swap_array->tokens = (Cpp_Token*)general_memory_allocate(general, new_mem_max); - } - else{ - swap_array->tokens = (Cpp_Token*) - general_memory_reallocate(general, swap_array->tokens, old_mem_max, new_mem_max); - } - swap_array->max_count = new_max; - - memcpy(swap_array->tokens + swap_array->count, tokens.tokens, tokens.count*sizeof(Cpp_Token)); - swap_array->count += tokens.count; - tokens.count = 0; - - if (result == LexResult_Finished){ - still_lexing = false; - } - }break; - - case LexResult_HitTokenLimit: InvalidCodePath; - } - } while (still_lexing); - - Cpp_Token_Array *token_array = &file->state.token_array; - token_array->count = swap_array->count; - token_array->max_count = swap_array->max_count; - if (token_array->tokens != 0){ - general_memory_free(general, token_array->tokens); - } - token_array->tokens = swap_array->tokens; - - swap_array->tokens = 0; - swap_array->count = 0; - swap_array->max_count = 0; - - file->state.tokens_complete = true; - file->state.still_lexing = false; - - end_temp_memory(temp); - } - - file->state.tokens_complete = true; - } -} - -internal b32 -file_relex_parallel(System_Functions *system, Models *models, Editing_File *file, i32 start_i, i32 end_i, i32 shift_amount){ - Mem_Options *mem = &models->mem; - General_Memory *general = &mem->general; - Partition *part = &mem->part; - - if (file->state.token_array.tokens == 0){ - file_first_lex_parallel(system, models, file); - return(false); - } - - b32 result = true; - b32 inline_lex = !file->state.still_lexing; - if (inline_lex){ - Gap_Buffer *buffer = &file->state.buffer; - i32 extra_tolerance = 100; - - Cpp_Token_Array *array = &file->state.token_array; - Cpp_Relex_Range relex_range = cpp_get_relex_range(array, start_i, end_i); - - i32 relex_space_size = - relex_range.end_token_index - relex_range.start_token_index + extra_tolerance; - - Temp_Memory temp = begin_temp_memory(part); - Parse_Context parse_context = parse_context_get(&models->parse_context_memory, file->settings.parse_context_id, partition_current(part), partition_remaining(part)); - Assert(parse_context.valid); - push_block(part, (i32)parse_context.memory_size); - - Cpp_Token_Array relex_array; - relex_array.count = 0; - relex_array.max_count = relex_space_size; - relex_array.tokens = push_array(part, Cpp_Token, relex_array.max_count); - - i32 size = buffer_size(buffer); - - Cpp_Relex_Data state = cpp_relex_init(array, start_i, end_i, shift_amount, file->settings.tokens_without_strings, parse_context.kw_table, parse_context.pp_table); - - char *chunks[3]; - i32 chunk_sizes[3]; - - chunks[0] = buffer->data; - chunk_sizes[0] = buffer->size1; - - chunks[1] = buffer->data + buffer->size1 + buffer->gap_size; - chunk_sizes[1] = buffer->size2; - - chunks[2] = 0; - chunk_sizes[2] = 0; - - i32 chunk_index = 0; - char *chunk = chunks[chunk_index]; - i32 chunk_size = chunk_sizes[chunk_index]; - - while (!cpp_relex_is_start_chunk(&state, chunk, chunk_size)){ - ++chunk_index; - Assert(chunk_index < ArrayCount(chunks)); - chunk = chunks[chunk_index]; - chunk_size = chunk_sizes[chunk_index]; - } - - for(;;){ - Cpp_Lex_Result lex_result = - cpp_relex_step(&state, chunk, chunk_size, size, array, &relex_array); - - switch (lex_result){ - case LexResult_NeedChunk: - { - ++chunk_index; - Assert(chunk_index < ArrayCount(chunks)); - chunk = chunks[chunk_index]; - chunk_size = chunk_sizes[chunk_index]; - }break; - - case LexResult_NeedTokenMemory: - { - inline_lex = false; - }goto doublebreak; - - case LexResult_Finished: goto doublebreak; - } - } - doublebreak:; - - if (inline_lex){ - i32 new_count = cpp_relex_get_new_count(&state, array->count, &relex_array); - if (new_count > array->max_count){ - i32 new_max = l_round_up_i32(new_count, KB(1)); - void *memory = general_memory_reallocate(general, array->tokens, array->count*sizeof(Cpp_Token), new_max*sizeof(Cpp_Token)); - array->tokens = (Cpp_Token*)memory; - array->max_count = new_max; - } - - cpp_relex_complete(&state, array, &relex_array); - } - else{ - cpp_relex_abort(&state, array); - } - - end_temp_memory(temp); - } - - if (!inline_lex){ - Cpp_Token_Array *array = &file->state.token_array; - Cpp_Get_Token_Result get_token_result = cpp_get_token(*array, end_i); - i32 end_token_i = get_token_result.token_index; - - if (end_token_i < 0){ - end_token_i = 0; - } - else if (end_i > array->tokens[end_token_i].start){ - ++end_token_i; - } - - cpp_shift_token_starts(array, end_token_i, shift_amount); - --end_token_i; - if (end_token_i >= 0){ - Cpp_Token *token = array->tokens + end_token_i; - if (token->start < end_i && token->start + token->size > end_i){ - token->size += shift_amount; - } - } - - file->state.still_lexing = true; - - Job_Data job; - job.callback = job_full_lex; - job.data[0] = file; - job.data[1] = general; - job.data[2] = models; - file->state.lex_job = system->post_job(BACKGROUND_THREADS, job); - result = false; - } - - return(result); -} - -internal b32 -file_relex_serial(Models *models, Editing_File *file, i32 start_i, i32 end_i, i32 shift_amount){ - Mem_Options *mem = &models->mem; - General_Memory *general = &mem->general; - Partition *part = &mem->part; - - if (file->state.token_array.tokens == 0){ - file_first_lex_serial(models, file); - return(1); - } - - Assert(!file->state.still_lexing); - - Gap_Buffer *buffer = &file->state.buffer; - Cpp_Token_Array *array = &file->state.token_array; - - Temp_Memory temp = begin_temp_memory(part); - Parse_Context parse_context = parse_context_get(&models->parse_context_memory, file->settings.parse_context_id, partition_current(part), partition_remaining(part)); - Assert(parse_context.valid); - push_block(part, (i32)parse_context.memory_size); - - Cpp_Token_Array relex_array; - relex_array.count = 0; - relex_array.max_count = partition_remaining(part) / sizeof(Cpp_Token); - relex_array.tokens = push_array(part, Cpp_Token, relex_array.max_count); - - i32 size = buffer_size(buffer); - - Cpp_Relex_Data state = cpp_relex_init(array, start_i, end_i, shift_amount, file->settings.tokens_without_strings, parse_context.kw_table, parse_context.pp_table); - - char *chunks[3]; - i32 chunk_sizes[3]; - - chunks[0] = buffer->data; - chunk_sizes[0] = buffer->size1; - - chunks[1] = buffer->data + buffer->size1 + buffer->gap_size; - chunk_sizes[1] = buffer->size2; - - chunks[2] = 0; - chunk_sizes[2] = 0; - - i32 chunk_index = 0; - char *chunk = chunks[chunk_index]; - i32 chunk_size = chunk_sizes[chunk_index]; - - while (!cpp_relex_is_start_chunk(&state, chunk, chunk_size)){ - ++chunk_index; - Assert(chunk_index < ArrayCount(chunks)); - chunk = chunks[chunk_index]; - chunk_size = chunk_sizes[chunk_index]; - } - - for(;;){ - Cpp_Lex_Result lex_result = cpp_relex_step(&state, chunk, chunk_size, size, array, &relex_array); - - switch (lex_result){ - case LexResult_NeedChunk: - { - ++chunk_index; - Assert(chunk_index < ArrayCount(chunks)); - chunk = chunks[chunk_index]; - chunk_size = chunk_sizes[chunk_index]; - }break; - - case LexResult_NeedTokenMemory: InvalidCodePath; - - case LexResult_Finished: goto doublebreak; - } - } - doublebreak:; - - i32 new_count = cpp_relex_get_new_count(&state, array->count, &relex_array); - if (new_count > array->max_count){ - i32 new_max = l_round_up_i32(new_count, KB(1)); - array->tokens = (Cpp_Token*)general_memory_reallocate(general, array->tokens, array->count*sizeof(Cpp_Token), new_max*sizeof(Cpp_Token)); - array->max_count = new_max; - } - - cpp_relex_complete(&state, array, &relex_array); - - end_temp_memory(temp); - - return(1); -} - -internal void -undo_stack_grow_string(General_Memory *general, Edit_Stack *stack, i32 extra_size){ - i32 old_max = stack->max; - u8 *old_str = stack->strings; - i32 new_max = old_max*2 + extra_size; - u8 *new_str = (u8*)general_memory_reallocate(general, old_str, old_max, new_max); - stack->strings = new_str; - stack->max = new_max; -} - -internal void -undo_stack_grow_edits(General_Memory *general, Edit_Stack *stack){ - i32 old_max = stack->edit_max; - Edit_Step *old_eds = stack->edits; - i32 new_max = old_max*2 + 2; - Edit_Step *new_eds = (Edit_Step*)general_memory_reallocate(general, old_eds, old_max*sizeof(Edit_Step), new_max*sizeof(Edit_Step)); - stack->edits = new_eds; - stack->edit_max = new_max; -} - -internal void -child_stack_grow_string(General_Memory *general, Small_Edit_Stack *stack, i32 extra_size){ - i32 old_max = stack->max; - u8 *old_str = stack->strings; - i32 new_max = old_max*2 + extra_size; - u8 *new_str = (u8*)general_memory_reallocate(general, old_str, old_max, new_max); - stack->strings = new_str; - stack->max = new_max; -} - -internal void -child_stack_grow_edits(General_Memory *general, Small_Edit_Stack *stack, i32 amount){ - i32 old_max = stack->edit_max; - Buffer_Edit *old_eds = stack->edits; - i32 new_max = old_max*2 + amount; - Buffer_Edit *new_eds = (Buffer_Edit*)general_memory_reallocate(general, old_eds, old_max*sizeof(Buffer_Edit), new_max*sizeof(Buffer_Edit)); - stack->edits = new_eds; - stack->edit_max = new_max; -} - -internal i32 -undo_children_push(General_Memory *general, Small_Edit_Stack *children, Buffer_Edit *edits, i32 edit_count, u8 *strings, i32 string_size){ - i32 result = children->edit_count; - if (children->edit_count + edit_count > children->edit_max){ - child_stack_grow_edits(general, children, edit_count); - } - - if (children->size + string_size > children->max){ - child_stack_grow_string(general, children, string_size); - } - - memcpy(children->edits + children->edit_count, edits, edit_count*sizeof(Buffer_Edit)); - memcpy(children->strings + children->size, strings, string_size); - - Buffer_Edit *edit = children->edits + children->edit_count; - i32 start_pos = children->size; - for (i32 i = 0; i < edit_count; ++i, ++edit){ - edit->str_start += start_pos; - } - - children->edit_count += edit_count; - children->size += string_size; - - return result; -} - -struct Edit_Spec{ - u8 *str; - Edit_Step step; -}; - -internal Edit_Step* -file_post_undo(General_Memory *general, Editing_File *file, Edit_Step step, b32 do_merge, b32 can_merge){ - if (step.type == ED_NORMAL){ - file->state.undo.redo.size = 0; - file->state.undo.redo.edit_count = 0; - } - - Edit_Stack *undo = &file->state.undo.undo; - Edit_Step *result = 0; - - if (step.child_count == 0){ - if (step.edit.end - step.edit.start + undo->size > undo->max){ - undo_stack_grow_string(general, undo, step.edit.end - step.edit.start); - } - - Buffer_Edit inv; - buffer_invert_edit(&file->state.buffer, step.edit, &inv, (char*)undo->strings, &undo->size, undo->max); - - Edit_Step inv_step = {}; - inv_step.edit = inv; - inv_step.can_merge = (b8)can_merge; - inv_step.type = ED_UNDO; - - b32 did_merge = 0; - if (do_merge && undo->edit_count > 0){ - Edit_Step prev = undo->edits[undo->edit_count-1]; - if (prev.can_merge && inv_step.edit.len == 0 && prev.edit.len == 0){ - if (prev.edit.end == inv_step.edit.start){ - did_merge = 1; - inv_step.edit.start = prev.edit.start; - } - } - } - - if (did_merge){ - result = undo->edits + (undo->edit_count-1); - *result = inv_step; - } - else{ - if (undo->edit_count == undo->edit_max){ - undo_stack_grow_edits(general, undo); - } - - result = undo->edits + (undo->edit_count++); - *result = inv_step; - } - } - else{ - Edit_Step inv_step = {}; - inv_step.type = ED_UNDO; - inv_step.first_child = step.inverse_first_child; - inv_step.inverse_first_child = step.first_child; - inv_step.special_type = step.special_type; - inv_step.child_count = step.inverse_child_count; - inv_step.inverse_child_count = step.child_count; - - if (undo->edit_count == undo->edit_max){ - undo_stack_grow_edits(general, undo); - } - result = undo->edits + (undo->edit_count++); - *result = inv_step; - } - return result; -} - -inline void -undo_stack_pop(Edit_Stack *stack){ - if (stack->edit_count > 0){ - Edit_Step *edit = stack->edits + (--stack->edit_count); - if (edit->child_count == 0){ - stack->size -= edit->edit.len; - } - } -} - -internal void -file_post_redo(General_Memory *general, Editing_File *file, Edit_Step step){ - Edit_Stack *redo = &file->state.undo.redo; - - if (step.child_count == 0){ - if (step.edit.end - step.edit.start + redo->size > redo->max){ - undo_stack_grow_string(general, redo, step.edit.end - step.edit.start); - } - - Buffer_Edit inv; - buffer_invert_edit(&file->state.buffer, step.edit, &inv, (char*)redo->strings, &redo->size, redo->max); - - Edit_Step inv_step = {}; - inv_step.edit = inv; - inv_step.type = ED_REDO; - - if (redo->edit_count == redo->edit_max){ - undo_stack_grow_edits(general, redo); - } - redo->edits[redo->edit_count++] = inv_step; - } - else{ - Edit_Step inv_step = {}; - inv_step.type = ED_REDO; - inv_step.first_child = step.inverse_first_child; - inv_step.inverse_first_child = step.first_child; - inv_step.special_type = step.special_type; - inv_step.child_count = step.inverse_child_count; - inv_step.inverse_child_count = step.child_count; - - if (redo->edit_count == redo->edit_max){ - undo_stack_grow_edits(general, redo); - } - redo->edits[redo->edit_count++] = inv_step; - } -} - -inline void -file_post_history_block(Editing_File *file, i32 pos){ - Assert(file->state.undo.history_head_block < pos); - Assert(pos < file->state.undo.history.edit_count); - - Edit_Step *history = file->state.undo.history.edits; - Edit_Step *step = history + file->state.undo.history_head_block; - step->next_block = pos; - step = history + pos; - step->prev_block = file->state.undo.history_head_block; - file->state.undo.history_head_block = pos; - ++file->state.undo.history_block_count; -} - -inline void -file_unpost_history_block(Editing_File *file){ - Assert(file->state.undo.history_block_count > 1); - --file->state.undo.history_block_count; - Edit_Step *old_head = file->state.undo.history.edits + file->state.undo.history_head_block; - file->state.undo.history_head_block = old_head->prev_block; -} - -internal Edit_Step* -file_post_history(General_Memory *general, Editing_File *file, Edit_Step step, b32 do_merge, b32 can_merge){ - Edit_Stack *history = &file->state.undo.history; - Edit_Step *result = 0; - - local_persist Edit_Type reverse_types[4]; - if (reverse_types[ED_UNDO] == 0){ - reverse_types[ED_NORMAL] = ED_REVERSE_NORMAL; - reverse_types[ED_REVERSE_NORMAL] = ED_NORMAL; - reverse_types[ED_UNDO] = ED_REDO; - reverse_types[ED_REDO] = ED_UNDO; - } - - if (step.child_count == 0){ - if (step.edit.end - step.edit.start + history->size > history->max){ - undo_stack_grow_string(general, history, step.edit.end - step.edit.start); - } - - Buffer_Edit inv; - buffer_invert_edit(&file->state.buffer, step.edit, &inv, - (char*)history->strings, &history->size, history->max); - - Edit_Step inv_step = {}; - inv_step.edit = inv; - inv_step.can_merge = (b8)can_merge; - inv_step.type = reverse_types[step.type]; - - b32 did_merge = 0; - if (do_merge && history->edit_count > 0){ - Edit_Step prev = history->edits[history->edit_count-1]; - if (prev.can_merge && inv_step.edit.len == 0 && prev.edit.len == 0){ - if (prev.edit.end == inv_step.edit.start){ - did_merge = 1; - inv_step.edit.start = prev.edit.start; - } - } - } - - if (did_merge){ - result = history->edits + (history->edit_count-1); - } - else{ - if (history->edit_count == history->edit_max){ - undo_stack_grow_edits(general, history); - } - result = history->edits + (history->edit_count++); - } - - *result = inv_step; - } - else{ - Edit_Step inv_step = {}; - inv_step.type = reverse_types[step.type]; - inv_step.first_child = step.inverse_first_child; - inv_step.inverse_first_child = step.first_child; - inv_step.special_type = step.special_type; - inv_step.inverse_child_count = step.child_count; - inv_step.child_count = step.inverse_child_count; - - if (history->edit_count == history->edit_max){ - undo_stack_grow_edits(general, history); - } - result = history->edits + (history->edit_count++); - *result = inv_step; - } - - return(result); -} - -// TODO(allen): burn this shit to the ground yo! -inline void -file_view_nullify_file(View *view){ - view->file_data = null_file_viewing_data; -} - -internal void -update_view_line_height(System_Functions *system, Models *models, View *view, Face_ID font_id){ - Font_Pointers font = system->font.get_pointers_by_id(font_id); - Assert(font.valid); - view->line_height = font.metrics->height; -} - -inline void -view_cursor_move(View *view, Full_Cursor cursor){ - view_set_cursor(view, cursor, 1, view->file_data.file->settings.unwrapped_lines); - view->file_data.show_temp_highlight = 0; -} - -inline void -view_cursor_move(System_Functions *system, View *view, i32 pos){ - Full_Cursor cursor = view_compute_cursor(system, view, seek_pos(pos), 0); - view_cursor_move(view, cursor); -} - -inline void -view_cursor_move(System_Functions *system, View *view, f32 x, f32 y, b32 round_down = 0){ - Buffer_Seek seek; - if (view->file_data.file->settings.unwrapped_lines){ - seek = seek_unwrapped_xy(x, y, round_down); - } - else{ - seek = seek_wrapped_xy(x, y, round_down); - } - - Full_Cursor cursor = view_compute_cursor(system, view, seek, 0); - view_cursor_move(view, cursor); -} - -inline void -view_cursor_move(System_Functions *system, View *view, i32 line, i32 character){ - Full_Cursor cursor = view_compute_cursor(system, view, seek_line_char(line, character), 0); - view_cursor_move(view, cursor); -} - -// TODO(allen): Eliminate models. -inline void -view_show_file(View *view, Models *models){ - Editing_File *file = view->file_data.file; - if (file != 0){ - //view->map = get_map(&models->mapping, file->settings.base_map_id); - view->map = file->settings.base_map_id; - } - else{ - //view->map = get_map(&models->mapping, mapid_global); - view->map = mapid_global; - } - - if (view->showing_ui != VUI_None){ - view->showing_ui = VUI_None; - view->changed_context_in_step = 1; - } -} - -internal void -view_set_file(System_Functions *system, View *view, Editing_File *file, Models *models){ - Assert(file); - - if (view->file_data.file != 0){ - touch_file(&models->working_set, view->file_data.file); - } - - File_Edit_Positions *edit_pos = view->edit_pos; - - if (edit_pos){ - edit_pos_unset(view->file_data.file, edit_pos); - edit_pos = 0; - } - - file_view_nullify_file(view); - view->file_data.file = file; - - edit_pos = edit_pos_get_new(file, view->persistent.id); - view->edit_pos = edit_pos; - - update_view_line_height(system, models, view, file->settings.font_id); - - if (edit_pos->cursor.line == 0){ - view_cursor_move(system, view, 0); - } - - if (view->showing_ui == VUI_None){ - view_show_file(view, models); - } -} - -struct Relative_Scrolling{ - f32 scroll_x, scroll_y; - f32 target_x, target_y; -}; - -internal Relative_Scrolling -view_get_relative_scrolling(View *view){ - Relative_Scrolling result = {0}; - if (view->edit_pos){ - f32 cursor_y = view_get_cursor_y(view); - result.scroll_y = cursor_y - view->edit_pos->scroll.scroll_y; - result.target_y = cursor_y - view->edit_pos->scroll.target_y; - } - return(result); -} - -internal void -view_set_relative_scrolling(View *view, Relative_Scrolling scrolling){ - f32 cursor_y = view_get_cursor_y(view); - - if (view->edit_pos){ - view->edit_pos->scroll.scroll_y = cursor_y - scrolling.scroll_y; - view->edit_pos->scroll.target_y = round32(clamp_bottom(0.f, cursor_y - scrolling.target_y)); - } -} - -inline i32_Rect -view_widget_rect(View *view, i32 line_height){ - Assert(view->file_data.file); - Panel *panel = view->panel; - i32_Rect result = panel->inner; - result.y0 = result.y0 + line_height + 2; - return(result); -} - -enum History_Mode{ - hist_normal, - hist_backward, - hist_forward -}; - -internal void -file_update_history_before_edit(Mem_Options *mem, Editing_File *file, Edit_Step step, u8 *str, History_Mode history_mode){ - if (!file->state.undo.undo.edits) return; - General_Memory *general = &mem->general; - - b32 can_merge = 0, do_merge = 0; - switch (step.type){ - case ED_NORMAL: - { - if (step.edit.len == 1 && str && char_is_alpha_numeric(*str)){ - can_merge = 1; - } - if (step.edit.len == 1 && str && (can_merge || char_is_whitespace(*str))){ - do_merge = 1; - } - - if (history_mode != hist_forward){ - file_post_history(general, file, step, do_merge, can_merge); - } - - file_post_undo(general, file, step, do_merge, can_merge); - }break; - - case ED_REVERSE_NORMAL: - { - if (history_mode != hist_forward){ - file_post_history(general, file, step, do_merge, can_merge); - } - - undo_stack_pop(&file->state.undo.undo); - - b32 restore_redos = 0; - Edit_Step *redo_end = 0; - - if (history_mode == hist_backward && file->state.undo.edit_history_cursor > 0){ - restore_redos = 1; - redo_end = file->state.undo.history.edits + (file->state.undo.edit_history_cursor - 1); - } - else if (history_mode == hist_forward && file->state.undo.history.edit_count > 0){ - restore_redos = 1; - redo_end = file->state.undo.history.edits + (file->state.undo.history.edit_count - 1); - } - - if (restore_redos){ - Edit_Step *redo_start = redo_end; - i32 steps_of_redo = 0; - i32 strings_of_redo = 0; - { - i32 undo_count = 0; - while (redo_start->type == ED_REDO || redo_start->type == ED_UNDO){ - if (redo_start->type == ED_REDO){ - if (undo_count > 0){ - --undo_count; - } - else{ - ++steps_of_redo; - strings_of_redo += redo_start->edit.len; - } - } - else{ - ++undo_count; - } - --redo_start; - } - } - - if (redo_start < redo_end){ - ++redo_start; - ++redo_end; - - if (file->state.undo.redo.edit_count + steps_of_redo > file->state.undo.redo.edit_max) - undo_stack_grow_edits(general, &file->state.undo.redo); - - if (file->state.undo.redo.size + strings_of_redo > file->state.undo.redo.max) - undo_stack_grow_string(general, &file->state.undo.redo, strings_of_redo); - - u8 *str_src = file->state.undo.history.strings + redo_end->edit.str_start; - u8 *str_dest_base = file->state.undo.redo.strings; - i32 str_redo_pos = file->state.undo.redo.size + strings_of_redo; - - Edit_Step *edit_src = redo_end; - Edit_Step *edit_dest = file->state.undo.redo.edits + file->state.undo.redo.edit_count + steps_of_redo; - - { - i32 undo_count = 0; - for (i32 i = 0; i < steps_of_redo;){ - --edit_src; - str_src -= edit_src->edit.len; - if (edit_src->type == ED_REDO){ - if (undo_count > 0){ - --undo_count; - } - else{ - ++i; - - --edit_dest; - *edit_dest = *edit_src; - - str_redo_pos -= edit_dest->edit.len; - edit_dest->edit.str_start = str_redo_pos; - - memcpy(str_dest_base + str_redo_pos, str_src, edit_dest->edit.len); - } - } - else{ - ++undo_count; - } - } - Assert(undo_count == 0); - } - - file->state.undo.redo.size += strings_of_redo; - file->state.undo.redo.edit_count += steps_of_redo; - } - } - }break; - - case ED_UNDO: - { - if (history_mode != hist_forward){ - file_post_history(general, file, step, do_merge, can_merge); - } - file_post_redo(general, file, step); - undo_stack_pop(&file->state.undo.undo); - }break; - - case ED_REDO: - { - if (step.edit.len == 1 && str && char_is_alpha_numeric(*str)) can_merge = 1; - if (step.edit.len == 1 && str && (can_merge || char_is_whitespace(*str))) do_merge = 1; - - if (history_mode != hist_forward){ - file_post_history(general, file, step, do_merge, can_merge); - } - - file_post_undo(general, file, step, do_merge, can_merge); - undo_stack_pop(&file->state.undo.redo); - }break; - } - - if (history_mode != hist_forward){ - if (step.type == ED_UNDO || step.type == ED_REDO){ - if (file->state.undo.current_block_normal){ - file_post_history_block(file, file->state.undo.history.edit_count - 1); - file->state.undo.current_block_normal = 0; - } - } - else{ - if (!file->state.undo.current_block_normal){ - file_post_history_block(file, file->state.undo.history.edit_count - 1); - file->state.undo.current_block_normal = 1; - } - } - } - else{ - if (file->state.undo.history_head_block == file->state.undo.history.edit_count){ - file_unpost_history_block(file); - file->state.undo.current_block_normal = !file->state.undo.current_block_normal; - } - } - - if (history_mode == hist_normal){ - file->state.undo.edit_history_cursor = file->state.undo.history.edit_count; - } -} - -inline void -file_pre_edit_maintenance(System_Functions *system, General_Memory *general, Editing_File *file){ - if (file->state.still_lexing){ - system->cancel_job(BACKGROUND_THREADS, file->state.lex_job); - if (file->state.swap_array.tokens){ - general_memory_free(general, file->state.swap_array.tokens); - file->state.swap_array.tokens = 0; - } - file->state.still_lexing = 0; - } - file_mark_dirty(file); -} - -struct Cursor_Fix_Descriptor{ - b32 is_batch; - union{ - struct{ - Buffer_Edit *batch; - i32 batch_size; - }; - struct{ - i32 start, end; - i32 shift_amount; - }; - }; -}; - -internal void -file_edit_cursor_fix(System_Functions *system, Models *models, Editing_File *file, Editing_Layout *layout, Cursor_Fix_Descriptor desc){ - - Partition *part = &models->mem.part; - - Temp_Memory cursor_temp = begin_temp_memory(part); - i32 cursor_max = layout->panel_max_count * 3; - cursor_max += file->markers.marker_count; - Cursor_With_Index *cursors = push_array(part, Cursor_With_Index, cursor_max); - Cursor_With_Index *r_cursors = push_array(part, Cursor_With_Index, cursor_max); - Assert(cursors != 0); - - i32 cursor_count = 0; - i32 r_cursor_count = 0; - - View *view = 0; - Panel *panel = 0, *used_panels = &layout->used_sentinel; - for (dll_items(panel, used_panels)){ - view = panel->view; - if (view->file_data.file == file){ - Assert(view->edit_pos); - write_cursor_with_index(cursors, &cursor_count, view->edit_pos->cursor.pos); - write_cursor_with_index(cursors, &cursor_count, view->edit_pos->mark); - write_cursor_with_index(cursors, &cursor_count, view->edit_pos->scroll_i); - } - } - - Marker_Array *marker_it = 0; - Marker_Array *marker_sent = &file->markers.sentinel; - for (dll_items(marker_it, marker_sent)){ - u32 count = marker_it->count; - Marker *markers = &marker_it->marker_0; - for (u32 i = 0; i < count; ++i){ - if (markers[i].lean_right){ - write_cursor_with_index(r_cursors, &r_cursor_count, markers[i].pos); - } - else{ - write_cursor_with_index(cursors, &cursor_count, markers[i].pos); - } - } - } - - if (cursor_count > 0 || r_cursor_count > 0){ - buffer_sort_cursors(cursors, cursor_count); - if (desc.is_batch){ - buffer_batch_edit_update_cursors(cursors, cursor_count, desc.batch, desc.batch_size, false); - buffer_batch_edit_update_cursors(r_cursors, r_cursor_count, desc.batch, desc.batch_size, true); - } - else{ - buffer_update_cursors(cursors, cursor_count, desc.start, desc.end, desc.shift_amount + (desc.end - desc.start), false); - buffer_update_cursors(r_cursors, r_cursor_count, desc.start, desc.end, desc.shift_amount + (desc.end - desc.start), true); - } - buffer_unsort_cursors(cursors, cursor_count); - - cursor_count = 0; - r_cursor_count = 0; - for (dll_items(panel, used_panels)){ - view = panel->view; - if (view->file_data.file == file){ - Assert(view->edit_pos); - - i32 cursor_pos = cursors[cursor_count++].pos; - Full_Cursor new_cursor = view_compute_cursor(system, view, seek_pos(cursor_pos), 0); - - GUI_Scroll_Vars scroll = view->edit_pos->scroll; - - view->edit_pos->mark = cursors[cursor_count++].pos; - i32 new_scroll_i = cursors[cursor_count++].pos; - if (view->edit_pos->scroll_i != new_scroll_i){ - view->edit_pos->scroll_i = new_scroll_i; - - Full_Cursor temp_cursor = view_compute_cursor(system, view, seek_pos(view->edit_pos->scroll_i), 0); - - f32 y_offset = MOD(view->edit_pos->scroll.scroll_y, view->line_height); - f32 y_position = temp_cursor.wrapped_y; - if (view->file_data.file->settings.unwrapped_lines){ - y_position = temp_cursor.unwrapped_y; - } - y_position += y_offset; - - scroll.target_y += round32(y_position - scroll.scroll_y); - scroll.scroll_y = y_position; - } - - view_set_cursor_and_scroll(view, new_cursor, 1, view->file_data.file->settings.unwrapped_lines, scroll); - } - } - - for (dll_items(marker_it, marker_sent)){ - u32 count = marker_it->count; - Marker *markers = &marker_it->marker_0; - for (u32 i = 0; i < count; ++i){ - if (markers[i].lean_right){ - markers[i].pos = r_cursors[r_cursor_count++].pos; - } - else{ - markers[i].pos = cursors[cursor_count++].pos; - } - } - } - } - - end_temp_memory(cursor_temp); -} - -internal void -file_do_single_edit(System_Functions *system, Models *models, Editing_File *file, Edit_Spec spec, History_Mode history_mode){ - - Mem_Options *mem = &models->mem; - Editing_Layout *layout = &models->layout; - - // NOTE(allen): fixing stuff beforewards???? - file_update_history_before_edit(mem, file, spec.step, spec.str, history_mode); - file_pre_edit_maintenance(system, &mem->general, file); - - // NOTE(allen): actual text replacement - i32 shift_amount = 0; - General_Memory *general = &mem->general; - Partition *part = &mem->part; - - char *str = (char*)spec.str; - i32 start = spec.step.edit.start; - i32 end = spec.step.edit.end; - i32 str_len = spec.step.edit.len; - - i32 scratch_size = partition_remaining(part); - - Assert(scratch_size > 0); - i32 request_amount = 0; - Assert(end <= buffer_size(&file->state.buffer)); - while (buffer_replace_range(&file->state.buffer, start, end, str, str_len, &shift_amount, part->base + part->pos, scratch_size, &request_amount)){ - void *new_data = 0; - if (request_amount > 0){ - new_data = general_memory_allocate(general, request_amount); - } - void *old_data = buffer_edit_provide_memory(&file->state.buffer, new_data, request_amount); - if (old_data){ - general_memory_free(general, old_data); - } - } - - // NOTE(allen): token fixing - if (file->settings.tokens_exist){ - if (!file->settings.virtual_white){ - file_relex_parallel(system, models, file, start, end, shift_amount); - } - else{ - file_relex_serial(models, file, start, end, shift_amount); - } - } - - // NOTE(allen): meta data - Gap_Buffer *buffer = &file->state.buffer; - i32 line_start = buffer_get_line_number(&file->state.buffer, start); - i32 line_end = buffer_get_line_number(&file->state.buffer, end); - i32 replaced_line_count = line_end - line_start; - i32 new_line_count = buffer_count_newlines(&file->state.buffer, start, start+str_len); - i32 line_shift = new_line_count - replaced_line_count; - - Font_Pointers font = system->font.get_pointers_by_id(file->settings.font_id); - Assert(font.valid); - - file_grow_starts_as_needed(general, buffer, line_shift); - buffer_remeasure_starts(buffer, line_start, line_end, line_shift, shift_amount); - - file_allocate_character_starts_as_needed(general, file); - buffer_remeasure_character_starts(system, font, buffer, line_start, line_end, line_shift, file->state.character_starts, 0, file->settings.virtual_white); - - // TODO(allen): Redo this as some sort of dialogical API -#if 0 - file_allocate_wraps_as_needed(general, file); - buffer_remeasure_wrap_y(buffer, line_start, line_end, line_shift, file->state.wraps, (f32)font->height, font->advance_data, (f32)file->settings.display_width); -#endif - - file_measure_wraps(system, models, file, font); - - // NOTE(allen): cursor fixing - Cursor_Fix_Descriptor desc = {0}; - desc.start = start; - desc.end = end; - desc.shift_amount = shift_amount; - file_edit_cursor_fix(system, models, file, layout, desc); -} - -internal void -file_do_batch_edit(System_Functions *system, Models *models, Editing_File *file, Edit_Spec spec, History_Mode history_mode, i32 batch_type){ - - Mem_Options *mem = &models->mem; - General_Memory *general = &mem->general; - Partition *part = &mem->part; - Editing_Layout *layout = &models->layout; - - // NOTE(allen): fixing stuff "beforewards"??? - Assert(spec.str == 0); - file_update_history_before_edit(mem, file, spec.step, 0, history_mode); - file_pre_edit_maintenance(system, &mem->general, file); - - // NOTE(allen): actual text replacement - u8 *str_base = file->state.undo.children.strings; - i32 batch_size = spec.step.child_count; - Buffer_Edit *batch = file->state.undo.children.edits + spec.step.first_child; - - Assert(spec.step.first_child < file->state.undo.children.edit_count); - Assert(batch_size >= 0); - - i32 scratch_size = partition_remaining(part); - Buffer_Batch_State state = {}; - i32 request_amount = 0; - while (buffer_batch_edit_step(&state, &file->state.buffer, batch, - (char*)str_base, batch_size, part->base + part->pos, - scratch_size, &request_amount)){ - void *new_data = 0; - if (request_amount > 0){ - new_data = general_memory_allocate(general, request_amount); - } - void *old_data = buffer_edit_provide_memory(&file->state.buffer, new_data, request_amount); - if (old_data){ - general_memory_free(general, old_data); - } - } - - i32 shift_total = state.shift_total; - - // NOTE(allen): token fixing - switch (batch_type){ - case BatchEdit_Normal: - { - if (file->settings.tokens_exist){ - // TODO(allen): Write a smart fast one here someday. - Buffer_Edit *first_edit = batch; - Buffer_Edit *last_edit = batch + batch_size - 1; - - if (!file->settings.virtual_white){ - file_relex_parallel(system, models, file, first_edit->start, last_edit->end, shift_total); - } - else{ - file_relex_serial(models, file, first_edit->start, last_edit->end, shift_total); - } - } - }break; - - case BatchEdit_PreserveTokens: - { - if (file->state.tokens_complete){ - Cpp_Token_Array tokens = file->state.token_array; - Cpp_Token *token = tokens.tokens; - Cpp_Token *end_token = tokens.tokens + tokens.count; - Cpp_Token original = {(Cpp_Token_Type)0}; - - Buffer_Edit *edit = batch; - Buffer_Edit *end_edit = batch + batch_size; - - i32 shift_amount = 0; - i32 local_shift = 0; - - for (; token < end_token; ++token){ - original = *token; - for (; edit < end_edit && edit->start <= original.start; ++edit){ - local_shift = (edit->len - (edit->end - edit->start)); - shift_amount += local_shift; - } - token->start += shift_amount; - local_shift = 0; - for (; edit < end_edit && edit->start < original.start + original.size; ++edit){ - local_shift += (edit->len - (edit->end - edit->start)); - } - token->size += local_shift; - shift_amount += local_shift; - } - } - }break; - } - - // TODO(allen): Let's try to switch to remeasuring here moron! - // We'll need to get the total shift from the actual batch edit state - // instead of from the cursor fixing. The only reason we're getting - // it from cursor fixing is because you're a lazy asshole. - - // NOTE(allen): meta data - file_measure_starts(general, &file->state.buffer); - - Font_Pointers font = system->font.get_pointers_by_id(file->settings.font_id); - Assert(font.valid); - - // TODO(allen): write the remeasurement version - file_allocate_character_starts_as_needed(general, file); - buffer_measure_character_starts(system, font, &file->state.buffer, file->state.character_starts, 0, file->settings.virtual_white); - - file_measure_wraps(system, models, file, font); - - // NOTE(allen): cursor fixing - Cursor_Fix_Descriptor desc = {0}; - desc.is_batch = 1; - desc.batch = batch; - desc.batch_size = batch_size; - file_edit_cursor_fix(system, models, file, layout, desc); -} - -inline void -file_replace_range(System_Functions *system, Models *models, Editing_File *file, i32 start, i32 end, char *str, i32 len){ - Edit_Spec spec = {}; - spec.step.type = ED_NORMAL; - spec.step.edit.start = start; - spec.step.edit.end = end; - spec.step.edit.len = len; - spec.str = (u8*)str; - file_do_single_edit(system, models, file, spec, hist_normal); -} - -inline void -file_clear(System_Functions *system, Models *models, Editing_File *file){ - if (models->hook_end_file != 0){ - models->hook_end_file(&models->app_links, file->id.id); - } - file_replace_range(system, models, file, 0, buffer_size(&file->state.buffer), 0, 0); -} - -inline void -view_post_paste_effect(View *view, f32 seconds, i32 start, i32 size, u32 color){ - Editing_File *file = view->file_data.file; - - file->state.paste_effect.start = start; - file->state.paste_effect.end = start + size; - file->state.paste_effect.color = color; - file->state.paste_effect.seconds_down = seconds; - file->state.paste_effect.seconds_max = seconds; -} - -internal Style* -get_style(Models *models, i32 i){ - return (&models->styles.styles[i]); -} - -internal Style* -main_style(Models *models){ - return (get_style(models, 0)); -} - -internal void -apply_history_edit(System_Functions *system, Models *models, Editing_File *file, View *view, Edit_Stack *stack, Edit_Step step, History_Mode history_mode){ - Edit_Spec spec = {}; - spec.step = step; - - if (step.child_count == 0){ - spec.step.edit.str_start = 0; - spec.str = stack->strings + step.edit.str_start; - - file_do_single_edit(system, models, file, spec, history_mode); - - if (view){ - view_cursor_move(system, view, step.edit.start + step.edit.len); - view->edit_pos->mark = view->edit_pos->cursor.pos; - - Style *style = main_style(models); - view_post_paste_effect(view, 0.333f, step.edit.start, step.edit.len, style->main.undo_color); - } - } - else{ - file_do_batch_edit(system, models, view->file_data.file, spec, hist_normal, spec.step.special_type); - } -} - -internal void -view_undo_redo(System_Functions *system, Models *models, View *view, Edit_Stack *stack, Edit_Type expected_type){ - Editing_File *file = view->file_data.file; - - Assert(file); - Assert(view->edit_pos); - - if (stack->edit_count > 0){ - Edit_Step step = stack->edits[stack->edit_count-1]; - Assert(step.type == expected_type); - apply_history_edit(system, models, file, view, stack, step, hist_normal); - } -} - -internal void -view_history_step(System_Functions *system, Models *models, View *view, History_Mode history_mode){ - Assert(history_mode != hist_normal); - - Editing_File *file = view->file_data.file; - - Assert(file); - Assert(view->edit_pos); - - b32 do_history_step = 0; - Edit_Step step = {}; - if (history_mode == hist_backward){ - if (file->state.undo.edit_history_cursor > 0){ - do_history_step = 1; - step = file->state.undo.history.edits[--file->state.undo.edit_history_cursor]; - } - } - else{ - if (file->state.undo.edit_history_cursor < file->state.undo.history.edit_count){ - Assert(((file->state.undo.history.edit_count - file->state.undo.edit_history_cursor) & 1) == 0); - step = file->state.undo.history.edits[--file->state.undo.history.edit_count]; - file->state.undo.history.size -= step.edit.len; - ++file->state.undo.edit_history_cursor; - do_history_step = 1; - } - } - - if (do_history_step){ - apply_history_edit(system, models, - file, view, - &file->state.undo.history, step, history_mode); - } -} - -internal String* -working_set_next_clipboard_string(General_Memory *general, Working_Set *working, i32 str_size){ - String *result = 0; - i32 clipboard_current = working->clipboard_current; - if (working->clipboard_size == 0){ - clipboard_current = 0; - working->clipboard_size = 1; - } - else{ - ++clipboard_current; - if (clipboard_current >= working->clipboard_max_size){ - clipboard_current = 0; - } - else if (working->clipboard_size <= clipboard_current){ - working->clipboard_size = clipboard_current+1; - } - } - result = &working->clipboards[clipboard_current]; - working->clipboard_current = clipboard_current; - working->clipboard_rolling = clipboard_current; - char *new_str; - if (result->str){ - new_str = (char*)general_memory_reallocate(general, result->str, result->size, str_size); - } - else{ - new_str = (char*)general_memory_allocate(general, str_size+1); - } - // TODO(allen): What if new_str == 0? - *result = make_string_cap(new_str, 0, str_size); - return result; -} - -internal String* -working_set_clipboard_index(Working_Set *working, i32 index){ - String *result = 0; - i32 size = working->clipboard_size; - i32 current = working->clipboard_current; - if (index >= 0 && size > 0){ - index = index % size; - index = current + size - index; - index = index % size; - result = &working->clipboards[index]; - } - return(result); -} - -internal String* -working_set_clipboard_head(Working_Set *working){ - String *result = 0; - if (working->clipboard_size > 0){ - working->clipboard_rolling = 0; - result = working_set_clipboard_index(working, working->clipboard_rolling); - } - return(result); -} - -internal String* -working_set_clipboard_roll_down(Working_Set *working){ - String *result = 0; - if (working->clipboard_size > 0){ - i32 clipboard_index = working->clipboard_rolling; - ++clipboard_index; - working->clipboard_rolling = clipboard_index; - result = working_set_clipboard_index(working, working->clipboard_rolling); - } - return(result); -} - -internal Edit_Spec -file_compute_edit(Mem_Options *mem, Editing_File *file, Buffer_Edit *edits, char *str_base, i32 str_size, Buffer_Edit *inverse_array, char *inv_str, i32 inv_max, i32 edit_count, i32 batch_type){ - General_Memory *general = &mem->general; - - i32 inv_str_pos = 0; - Buffer_Invert_Batch state = {}; - if (buffer_invert_batch(&state, &file->state.buffer, edits, edit_count, - inverse_array, inv_str, &inv_str_pos, inv_max)){ - Assert(0); - } - - i32 first_child = undo_children_push(general, &file->state.undo.children, edits, edit_count, (u8*)(str_base), str_size); - i32 inverse_first_child = undo_children_push(general, &file->state.undo.children, inverse_array, edit_count, (u8*)(inv_str), inv_str_pos); - - Edit_Spec spec = {}; - spec.step.type = ED_NORMAL; - spec.step.first_child = first_child; - spec.step.inverse_first_child = inverse_first_child; - spec.step.special_type = batch_type; - spec.step.child_count = edit_count; - spec.step.inverse_child_count = edit_count; - - return(spec); -} - -internal u32* -style_get_color(Style *style, Cpp_Token token){ - u32 *result; - if (token.flags & CPP_TFLAG_IS_KEYWORD){ - if (token.type == CPP_TOKEN_BOOLEAN_CONSTANT){ - result = &style->main.bool_constant_color; - } - else{ - result = &style->main.keyword_color; - } - } - else if(token.flags & CPP_TFLAG_PP_DIRECTIVE){ - result = &style->main.preproc_color; - } - else{ - switch (token.type){ - case CPP_TOKEN_COMMENT: - result = &style->main.comment_color; - break; - - case CPP_TOKEN_STRING_CONSTANT: - result = &style->main.str_constant_color; - break; - - case CPP_TOKEN_CHARACTER_CONSTANT: - result = &style->main.char_constant_color; - break; - - case CPP_TOKEN_INTEGER_CONSTANT: - result = &style->main.int_constant_color; - break; - - case CPP_TOKEN_FLOATING_CONSTANT: - result = &style->main.float_constant_color; - break; - - case CPP_PP_INCLUDE_FILE: - result = &style->main.include_color; - break; - - default: - result = &style->main.default_color; - break; - } - } - return result; -} - -internal void -file_full_remeasure(System_Functions *system, Models *models, Editing_File *file){ - Face_ID font_id = file->settings.font_id; - Font_Pointers font = system->font.get_pointers_by_id(font_id); - file_measure_wraps_and_fix_cursor(system, models, file, font); - - Editing_Layout *layout = &models->layout; - for (View_Iter iter = file_view_iter_init(layout, file, 0); - file_view_iter_good(iter); - iter = file_view_iter_next(iter)){ - update_view_line_height(system, models, iter.view, font_id); - } -} - -internal void -file_set_font(System_Functions *system, Models *models, Editing_File *file, Face_ID font_id){ - file->settings.font_id = font_id; - file_full_remeasure(system, models, file); -} - -internal void -global_set_font(System_Functions *system, Models *models, Face_ID font_id){ - File_Node *node = 0; - File_Node *sentinel = &models->working_set.used_sentinel; - for (dll_items(node, sentinel)){ - Editing_File *file = (Editing_File*)node; - file_set_font(system, models, file, font_id); - } - models->global_font_id = font_id; -} - -internal b32 -alter_font(System_Functions *system, Models *models, Face_ID font_id, Font_Settings *new_settings){ - b32 success = false; - - if (system->font.face_change_settings(font_id, new_settings)){ - success = true; - - File_Node *node = 0; - File_Node *sentinel = &models->working_set.used_sentinel; - for (dll_items(node, sentinel)){ - Editing_File *file = (Editing_File*)node; - if (file->settings.font_id == font_id){ - file_full_remeasure(system, models, file); - } - } - } - - return(success); -} - -internal b32 -release_font(System_Functions *system, Models *models, Face_ID font_id, Face_ID replacement_id){ - b32 success = false; - - if (system->font.face_release(font_id)){ - Font_Pointers font = system->font.get_pointers_by_id(replacement_id); - if (!font.valid){ - Face_ID largest_id = system->font.get_largest_id(); - for (replacement_id = 1; replacement_id <= largest_id && replacement_id > 0; ++replacement_id){ - font = system->font.get_pointers_by_id(replacement_id); - if (font.valid){ - break; - } - } - Assert(replacement_id <= largest_id && replacement_id > 0); - } - - success = true; - File_Node *node = 0; - File_Node *sentinel = &models->working_set.used_sentinel; - for (dll_items(node, sentinel)){ - Editing_File *file = (Editing_File*)node; - if (file->settings.font_id == font_id){ - file_set_font(system, models, file, replacement_id); - } - } - } - - return(success); -} - -inline void -view_show_GUI(View *view, Models *models, View_UI ui){ - view->map = mapid_ui; - view->showing_ui = ui; - view->changed_context_in_step = true; -} - -inline void -view_show_interactive(System_Functions *system, View *view, Models *models, Interactive_Action action, Interactive_Interaction interaction, String query){ - view->showing_ui = VUI_Interactive; - view->action = action; - view->interaction = interaction; - view->dest = make_fixed_width_string(view->dest_); - view->list_i = 0; - - view->map = mapid_ui; - - hot_directory_clean_end(&models->hot_directory); - hot_directory_reload(system, &models->hot_directory); - view->changed_context_in_step = 1; -} - -inline void -view_show_theme(View *view, Models *models){ - view->map = mapid_ui; - view->showing_ui = VUI_Theme; - view->color_mode = CV_Mode_Library; - view->color = super_color_create(0xFF000000); - view->current_color_editing = 0; - view->changed_context_in_step = 1; -} - -internal String -make_string_terminated(Partition *part, char *str, i32 len){ - char *space = (char*)push_array(part, char, len + 1); - String string = make_string_cap(str, len, len+1); - copy_fast_unsafe_cs(space, string); - string.str = space; - terminate_with_null(&string); - return(string); -} - -internal void -init_normal_file(System_Functions *system, Models *models, Editing_File *file, char *buffer, i32 size){ - PRFL_FUNC_GROUP(); - - String val = make_string(buffer, size); - file_create_from_string(system, models, file, val, 0); - - if (file->settings.tokens_exist && file->state.token_array.tokens == 0){ - if (!file->settings.virtual_white){ - file_first_lex_parallel(system, models, file); - } - else{ - file_first_lex_serial(models, file); - } - } -} - -internal void -init_read_only_file(System_Functions *system, Models *models, Editing_File *file){ - String val = null_string; - file_create_from_string(system, models, file, val, FileCreateFlag_ReadOnly); - - if (file->settings.tokens_exist && file->state.token_array.tokens == 0){ - if (!file->settings.virtual_white){ - file_first_lex_parallel(system, models, file); - } - else{ - file_first_lex_serial(models, file); - } - } -} - -internal char* -make_string_part(Partition *scratch, String src, int32_t *size_out){ - char *r = {0}; - if (src.size > 0){ - r = push_array(scratch, char, src.size); - *size_out = src.size; - memcpy(r, src.str, *size_out); - } - return(r); -} - -internal char* -make_string_part(Partition *scratch, String src, int32_t cap, int32_t *size_out, int32_t *cap_out){ - cap = clamp_bottom(src.size, cap); - char *r = 0; - if (src.size > 0){ - r = push_array(scratch, char, cap); - *size_out = src.size; - *cap_out = cap; - memcpy(r, src.str, *size_out); - } - return(r); -} - -internal void -buffer_bind_name(Models *models, General_Memory *general, Partition *scratch, Working_Set *working_set, Editing_File *file, String file_name){ - String base_name = front_of_directory(file_name); - - Temp_Memory temp = begin_temp_memory(scratch); - - // List of conflict files. - Editing_File **conflict_file_ptrs = push_array(scratch, Editing_File*, 0); - int32_t conflict_count = 0; - - { - ++conflict_count; - Editing_File **new_file_ptr = push_array(scratch, Editing_File*, 1); - *new_file_ptr = file; - } - - File_Node *used_nodes = &working_set->used_sentinel; - for (File_Node *node = used_nodes->next; node != used_nodes; node = node->next){ - Editing_File *file_ptr = (Editing_File*)node; - if (file_is_ready(file_ptr) && match(base_name, file_ptr->base_name.name)){ - ++conflict_count; - Editing_File **new_file_ptr = push_array(scratch, Editing_File*, 1); - *new_file_ptr = file_ptr; - } - } - - // Fill conflict array. - Buffer_Name_Conflict_Entry *conflicts = push_array(scratch, Buffer_Name_Conflict_Entry, conflict_count); - - for (int32_t i = 0; i < conflict_count; ++i){ - Editing_File *file_ptr = conflict_file_ptrs[i]; - Buffer_Name_Conflict_Entry *entry = &conflicts[i]; - entry->buffer_id = file_ptr->id.id; - entry->file_name = make_string_part(scratch, file_ptr->canon.name, &entry->file_name_len); - entry->base_name = make_string_part(scratch, base_name, &entry->base_name_len); - - String b = base_name; - if (i > 0){ - b = file_ptr->unique_name.name; - } - entry->unique_name_in_out = make_string_part(scratch, b, 256, &entry->unique_name_len_in_out, &entry->unique_name_capacity); - } - - // Get user's resolution data. - if (models->buffer_name_resolver != 0){ - models->buffer_name_resolver(&models->app_links, conflicts, conflict_count); - } - - // Re-bind all of the files - for (int32_t i = 0; i < conflict_count; ++i){ - Editing_File *file_ptr = conflict_file_ptrs[i]; - if (file_ptr->unique_name.name.str != 0){ - buffer_unbind_name_low_level(working_set, file_ptr); - } - } - for (int32_t i = 0; i < conflict_count; ++i){ - Editing_File *file_ptr = conflict_file_ptrs[i]; - Buffer_Name_Conflict_Entry *entry = &conflicts[i]; - - String unique_name = make_string(entry->unique_name_in_out, entry->unique_name_len_in_out); - - buffer_bind_name_low_level(general, working_set, file_ptr, base_name, unique_name); - } - - end_temp_memory(temp); -} - -internal Editing_File* -open_file(System_Functions *system, Models *models, String filename){ - Working_Set *working_set = &models->working_set; - Editing_File *file = 0; - - if (terminate_with_null(&filename)){ - Editing_File_Name canon_name = {0}; - if (get_canon_name(system, &canon_name, filename)){ - file = working_set_contains_canon(working_set, canon_name.name); - - if (file == 0){ - Plat_Handle handle; - if (system->load_handle(canon_name.name.str, &handle)){ - Mem_Options *mem = &models->mem; - General_Memory *general = &mem->general; - Partition *part = &mem->part; - - file = working_set_alloc_always(working_set, general); - - buffer_bind_file(system, general, working_set, file, canon_name.name); - buffer_bind_name(models, general, part, working_set, file, filename); - - i32 size = system->load_size(handle); - char *buffer = 0; - b32 gen_buffer = 0; - - Temp_Memory temp = begin_temp_memory(part); - - buffer = push_array(part, char, size); - if (buffer == 0){ - buffer = (char*)general_memory_allocate(general, size); - Assert(buffer); - gen_buffer = 1; - } - - if (system->load_file(handle, buffer, size)){ - system->load_close(handle); - init_normal_file(system, models, file, buffer, size); - } - else{ - system->load_close(handle); - } - - if (gen_buffer){ - general_memory_free(general, buffer); - } - - end_temp_memory(temp); - } - } - } - } - - return(file); -} - -internal void -view_open_file(System_Functions *system, Models *models, View *view, String filename){ - Editing_File *file = open_file(system, models, filename); - if (file){ - view_set_file(system, view, file, models); - } -} - -internal void -view_interactive_new_file(System_Functions *system, Models *models, View *view, String filename){ - Working_Set *working_set = &models->working_set; - Editing_File *file = 0; - - if (terminate_with_null(&filename)){ - Editing_File_Name canon_name = {0}; - if (get_canon_name(system, &canon_name, filename)){ - file = working_set_contains_canon(working_set, canon_name.name); - - if (file != 0){ - file_clear(system, models, file); - } - else{ - Mem_Options *mem = &models->mem; - General_Memory *general = &mem->general; - Partition *part = &mem->part; - - file = working_set_alloc_always(working_set, general); - - buffer_bind_file(system, general, working_set, file, canon_name.name); - buffer_bind_name(models, general, part, working_set, file, filename); - - init_normal_file(system, models, file, 0, 0); - } - } - } - - if (file){ - view_set_file(system, view, file, models); - } -} - -internal void -kill_file(System_Functions *system, Models *models, Editing_File *file){ - Working_Set *working_set = &models->working_set; - - if (file != 0 && !file->settings.never_kill){ - if (models->hook_end_file != 0){ - models->hook_end_file(&models->app_links, file->id.id); - } - - buffer_unbind_name_low_level(working_set, file); - if (file->canon.name.size != 0){ - buffer_unbind_file(system, working_set, file); - } - file_close(system, &models->mem.general, file); - working_set_free_file(&models->mem.general, working_set, file); - - File_Node *used = &models->working_set.used_sentinel; - File_Node *node = used->next; - for (View_Iter iter = file_view_iter_init(&models->layout, file, 0); - file_view_iter_good(iter); - iter = file_view_iter_next(iter)){ - if (node != used){ - iter.view->file_data.file = 0; - view_set_file(system, iter.view, (Editing_File*)node, models); - node = node->next; - } - else{ - iter.view->file_data.file = 0; - view_set_file(system, iter.view, 0, models); - } - } - } -} - -internal void -kill_file_by_name(System_Functions *system, Models *models, String name){ - Editing_File *file = working_set_contains_name(&models->working_set, name); - kill_file(system, models, file); -} - -internal void -save_file_by_name(System_Functions *system, Models *models, String name){ - Editing_File *file = working_set_contains_name(&models->working_set, name); - if (file != 0){ - save_file(system, models, file); - } -} - -internal void -interactive_begin_sure_to_kill(System_Functions *system, View *view, Models *models, Editing_File *file){ - view_show_interactive(system, view, models, IAct_Sure_To_Kill, IInt_Sure_To_Kill, make_lit_string("Are you sure?")); - copy_ss(&view->dest, file->unique_name.name); -} - -enum Try_Kill_Result{ - TryKill_CannotKill, - TryKill_NeedDialogue, - TryKill_Success -}; - -internal Try_Kill_Result -interactive_try_kill_file(System_Functions *system, Models *models, Editing_File *file){ - Try_Kill_Result result = TryKill_CannotKill; - - if (!file->settings.never_kill){ - if (buffer_needs_save(file)){ - result = TryKill_NeedDialogue; - } - else{ - kill_file(system, models, file); - result = TryKill_Success; - } - } - - return(result); -} - -internal b32 -interactive_try_kill_file(System_Functions *system, Models *models, View *view, Editing_File *file){ - Try_Kill_Result kill_result = interactive_try_kill_file(system, models, file); - b32 result = (kill_result == TryKill_NeedDialogue); - if (result){ - interactive_begin_sure_to_kill(system, view, models, file); - } - return(result); -} - -internal b32 -interactive_try_kill_file_by_name(System_Functions *system, Models *models, View *view, String name){ - b32 kill_dialogue = 0; - - Editing_File *file = working_set_contains_name(&models->working_set, name); - if (file){ - kill_dialogue = interactive_try_kill_file(system, models, view, file); - } - - return(kill_dialogue); -} - -internal void -interactive_view_complete(System_Functions *system, View *view, Models *models, String dest, i32 user_action){ - switch (view->action){ - case IAct_Open: - { - view_open_file(system, models, view, dest); - view_show_file(view, models); - }break; - - case IAct_New: - if (dest.size > 0 && !char_is_slash(dest.str[dest.size-1])){ - view_interactive_new_file(system, models, view, dest); - view_show_file(view, models); - if (models->hook_new_file != 0){ - Editing_File *file = view->file_data.file; - models->hook_new_file(&models->app_links, file->id.id); - } - }break; - - case IAct_OpenOrNew: - { - InvalidCodePath; - }break; - - case IAct_Switch: - { - Editing_File *file = working_set_contains_name(&models->working_set, dest); - if (file){ - view_set_file(system, view, file, models); - } - view_show_file(view, models); - }break; - - case IAct_Kill: - if (!interactive_try_kill_file_by_name(system, models, view, dest)){ - view_show_file(view, models); - }break; - - case IAct_Sure_To_Close: - switch (user_action){ - case 0: - { - models->keep_playing = 0; - }break; - - case 1: - { - view_show_file(view, models); - }break; - - case 2: // TODO(allen): Save all and close. - break; - }break; - - case IAct_Sure_To_Kill: - switch (user_action){ - case 0: - { - kill_file_by_name(system, models, dest); - view_show_file(view, models); - }break; - - case 1: - { - view_show_file(view, models); - }break; - - case 2: - { - save_file_by_name(system, models, dest); - kill_file_by_name(system, models, dest); - view_show_file(view, models); - }break; - }break; - } -} - -struct File_Bar{ - f32 pos_x, pos_y; - f32 text_shift_x, text_shift_y; - i32_Rect rect; - Face_ID font_id; -}; - -internal void -intbar_draw_string(System_Functions *system, Render_Target *target, File_Bar *bar, String str, u32 char_color){ - draw_string(system, target, bar->font_id, str, (i32)(bar->pos_x + bar->text_shift_x), (i32)(bar->pos_y + bar->text_shift_y), char_color); - bar->pos_x += font_string_width(system, target, bar->font_id, str); -} - -internal GUI_Scroll_Vars -view_reinit_scrolling(View *view){ - Editing_File *file = view->file_data.file; - f32 w = 0, h = 0; - f32 cursor_x = 0, cursor_y = 0; - i32 target_x = 0, target_y = 0; - - view->reinit_scrolling = 0; - - if (file && file_is_ready(file)){ - cursor_x = view_get_cursor_x(view); - cursor_y = view_get_cursor_y(view); - - w = view_width(view); - h = view_file_height(view); - - if (cursor_x >= target_x + w){ - target_x = round32(cursor_x - w*.35f); - } - - target_y = clamp_bottom(0, floor32(cursor_y - h*.5f)); - } - - GUI_Scroll_Vars scroll = {0}; - - scroll.target_y = target_y; - scroll.scroll_y = (f32)target_y; - scroll.prev_target_y = -1000; - - scroll.target_x = target_x; - scroll.scroll_x = (f32)target_x; - scroll.prev_target_x = -1000; - - return(scroll); -} - -internal b32 -file_step(View *view, i32_Rect region, Input_Summary *user_input, b32 is_active, b32 *consumed_l){ - b32 is_animating = false; - Editing_File *file = view->file_data.file; - Assert(file != 0); - if (!file->is_loading){ - if (file->state.paste_effect.seconds_down > 0.f){ - file->state.paste_effect.seconds_down -= user_input->dt; - is_animating = true; - } - } - return(is_animating); -} - -internal void -do_widget(View *view, GUI_Target *target){ - Query_Slot *slot; - Query_Bar *bar; - - // NOTE(allen): A temporary measure... although in - // general we maybe want the user to be able to ask - // how large a particular section of the GUI turns - // out to be after layout? - f32 height = 0.f; - - for (slot = view->query_set.used_slot; slot != 0; slot = slot->next){ - bar = slot->query_bar; - gui_do_text_field(target, bar->prompt, bar->string); - - height += view->line_height + 2; - } - - view->widget_height = height; -} - -struct Exhaustive_File_Loop{ - char front_name_[256]; - char full_path_[256]; - String front_name, full_path; - - Absolutes absolutes; - - File_Info *infos; - i32 count, r; -}; - -struct Exhaustive_File_Info{ - File_Info *info; - String message; - b8 is_folder; - b8 name_match; - b8 is_loaded; -}; - -internal void -begin_exhaustive_loop(Exhaustive_File_Loop *loop, Hot_Directory *hdir){ - loop->front_name = make_fixed_width_string(loop->front_name_); - loop->full_path = make_fixed_width_string(loop->full_path_); - - loop->infos = hdir->file_list.infos; - loop->count = hdir->file_list.count; - - copy_ss(&loop->front_name, front_of_directory(hdir->string)); - get_absolutes(loop->front_name, &loop->absolutes, 1, 1); - copy_ss(&loop->full_path, hdir->canon_dir); - loop->r = loop->full_path.size; -} - -internal Exhaustive_File_Info -get_exhaustive_info(System_Functions *system, Working_Set *working_set, Exhaustive_File_Loop *loop, i32 i){ - local_persist String message_loaded = make_lit_string(" LOADED"); - local_persist String message_unsaved = make_lit_string(" LOADED *"); - local_persist String message_unsynced = make_lit_string(" LOADED !"); - - Exhaustive_File_Info result = {0}; - - result.info = loop->infos + i; - loop->full_path.size = loop->r; - append_sc(&loop->full_path, result.info->filename); - terminate_with_null(&loop->full_path); - - Editing_File *file = working_set_contains_canon(working_set, loop->full_path); - - String filename = make_string_cap(result.info->filename, result.info->filename_len, result.info->filename_len+1); - - result.is_folder = (result.info->folder != 0); - result.name_match = (wildcard_match_s(&loop->absolutes, filename, 0) != 0); - result.is_loaded = (file != 0 && file_is_ready(file)); - - result.message = null_string; - if (result.is_loaded){ - switch (file_get_sync(file)){ - case DirtyState_UpToDate: result.message = message_loaded; break; - case DirtyState_UnsavedChanges: result.message = message_unsaved; break; - case DirtyState_UnloadedChanges: result.message = message_unsynced; break; - } - } - - return(result); -} - -struct Style_Color_Edit{ - Style_Tag target; - Style_Tag fore; - Style_Tag back; - String text; -}; - -static Style_Color_Edit colors_to_edit[] = { - {Stag_Back, Stag_Default, Stag_Back, make_lit_string("Background")}, - {Stag_Margin, Stag_Default, Stag_Margin, make_lit_string("Margin")}, - {Stag_Margin_Hover, Stag_Default, Stag_Margin_Hover, make_lit_string("Margin Hover")}, - {Stag_Margin_Active, Stag_Default, Stag_Margin_Active, make_lit_string("Margin Active")}, - - {Stag_Cursor, Stag_At_Cursor, Stag_Cursor, make_lit_string("Cursor")}, - {Stag_At_Cursor, Stag_At_Cursor, Stag_Cursor, make_lit_string("Text At Cursor")}, - {Stag_Mark, Stag_Mark, Stag_Back, make_lit_string("Mark")}, - - {Stag_Highlight, Stag_At_Highlight, Stag_Highlight, make_lit_string("Highlight")}, - {Stag_At_Highlight, Stag_At_Highlight, Stag_Highlight, make_lit_string("Text At Highlight")}, - - {Stag_Default, Stag_Default, Stag_Back, make_lit_string("Text Default")}, - {Stag_Comment, Stag_Comment, Stag_Back, make_lit_string("Comment")}, - {Stag_Keyword, Stag_Keyword, Stag_Back, make_lit_string("Keyword")}, - {Stag_Str_Constant, Stag_Str_Constant, Stag_Back, make_lit_string("String Constant")}, - {Stag_Char_Constant, Stag_Char_Constant, Stag_Back, make_lit_string("Character Constant")}, - {Stag_Int_Constant, Stag_Int_Constant, Stag_Back, make_lit_string("Integer Constant")}, - {Stag_Float_Constant, Stag_Float_Constant, Stag_Back, make_lit_string("Float Constant")}, - {Stag_Bool_Constant, Stag_Bool_Constant, Stag_Back, make_lit_string("Boolean Constant")}, - {Stag_Preproc, Stag_Preproc, Stag_Back, make_lit_string("Preprocessor")}, - {Stag_Special_Character, Stag_Special_Character, Stag_Back, make_lit_string("Special Character")}, - - {Stag_Highlight_Junk, Stag_Default, Stag_Highlight_Junk, make_lit_string("Junk Highlight")}, - {Stag_Highlight_White, Stag_Default, Stag_Highlight_White, make_lit_string("Whitespace Highlight")}, - - {Stag_Paste, Stag_Paste, Stag_Back, make_lit_string("Paste Color")}, - - {Stag_Bar, Stag_Base, Stag_Bar, make_lit_string("Bar")}, - {Stag_Base, Stag_Base, Stag_Bar, make_lit_string("Bar Text")}, - {Stag_Pop1, Stag_Pop1, Stag_Bar, make_lit_string("Bar Pop 1")}, - {Stag_Pop2, Stag_Pop2, Stag_Bar, make_lit_string("Bar Pop 2")}, -}; - -struct Single_Line_Input_Step{ - b8 hit_newline; - b8 hit_ctrl_newline; - b8 hit_a_character; - b8 hit_backspace; - b8 hit_esc; - b8 made_a_change; - b8 did_command; - b8 no_file_match; -}; - -enum Single_Line_Input_Type{ - SINGLE_LINE_STRING, - SINGLE_LINE_FILE -}; - -struct Single_Line_Mode{ - Single_Line_Input_Type type; - String *string; - Hot_Directory *hot_directory; - b32 fast_folder_select; - b32 try_to_match; - b32 case_sensitive; -}; - -internal Single_Line_Input_Step -app_single_line_input_core(System_Functions *system, Working_Set *working_set, Key_Event_Data key, Single_Line_Mode mode){ - Single_Line_Input_Step result = {0}; - - b8 ctrl = key.modifiers[MDFR_CONTROL_INDEX]; - b8 cmnd = key.modifiers[MDFR_COMMAND_INDEX]; - b8 alt = key.modifiers[MDFR_ALT_INDEX]; - - if (key.keycode == key_back){ - result.hit_backspace = true; - if (mode.string->size > 0){ - result.made_a_change = true; - --mode.string->size; - switch (mode.type){ - case SINGLE_LINE_STRING: - { - mode.string->str[mode.string->size] = 0; - }break; - - case SINGLE_LINE_FILE: - { - if (!ctrl && !cmnd && !alt){ - char end_character = mode.string->str[mode.string->size]; - if (char_is_slash(end_character)){ - mode.string->size = reverse_seek_slash(*mode.string) + 1; - mode.string->str[mode.string->size] = 0; - hot_directory_set(system, mode.hot_directory, *mode.string); - } - else{ - mode.string->str[mode.string->size] = 0; - } - } - else{ - mode.string->str[mode.string->size] = 0; - } - }break; - } - } - } - - else if (key.character == '\n' || key.character == '\t'){ - // NOTE(allen): do nothing! - } - - else if (key.keycode == key_esc){ - result.hit_esc = true; - result.made_a_change = true; - } - - else if (key.character){ - result.hit_a_character = true; - if (!ctrl && !cmnd && !alt){ - if (mode.string->size + 1 < mode.string->memory_size){ - u8 new_character = (u8)key.character; - mode.string->str[mode.string->size] = new_character; - mode.string->size++; - mode.string->str[mode.string->size] = 0; - if (mode.type == SINGLE_LINE_FILE && char_is_slash(new_character)){ - hot_directory_set(system, mode.hot_directory, *mode.string); - } - result.made_a_change = true; - } - } - else{ - result.did_command = true; - } - } - - return result; -} - -inline Single_Line_Input_Step -app_single_line_input_step(System_Functions *system, Key_Event_Data key, String *string){ - Single_Line_Mode mode = {}; - mode.type = SINGLE_LINE_STRING; - mode.string = string; - return app_single_line_input_core(system, 0, key, mode); -} - -inline Single_Line_Input_Step -app_single_file_input_step(System_Functions *system, - Working_Set *working_set, Key_Event_Data key, - String *string, Hot_Directory *hot_directory, - b32 try_to_match, b32 case_sensitive){ - Single_Line_Mode mode = {}; - mode.type = SINGLE_LINE_FILE; - mode.string = string; - mode.hot_directory = hot_directory; - mode.try_to_match = try_to_match; - mode.case_sensitive = case_sensitive; - return app_single_line_input_core(system, working_set, key, mode); -} - -inline Single_Line_Input_Step -app_single_number_input_step(System_Functions *system, Key_Event_Data key, String *string){ - Single_Line_Input_Step result = {}; - Single_Line_Mode mode = {}; - mode.type = SINGLE_LINE_STRING; - mode.string = string; - - char c = (char)key.character; - if (c == 0 || c == '\n' || char_is_numeric(c)) - result = app_single_line_input_core(system, 0, key, mode); - return result; -} - -internal void -append_label(String *string, i32 indent_level, char *message){ - i32 r = 0; - for (r = 0; r < indent_level; ++r){ - append_s_char(string, '>'); - } - append_sc(string, message); -} - -internal void -show_gui_line(GUI_Target *target, String *string, i32 indent_level, i32 h_align, char *message, char *follow_up){ - string->size = 0; - append_label(string, indent_level, message); - if (follow_up){ - append_padding(string, '-', h_align); - append_s_char(string, ' '); - append_sc(string, follow_up); - } - gui_do_text_field(target, *string, null_string); -} - -internal void -show_gui_int(GUI_Target *target, String *string, - i32 indent_level, i32 h_align, char *message, i32 x){ - string->size = 0; - append_label(string, indent_level, message); - append_padding(string, '-', h_align); - append_s_char(string, ' '); - append_int_to_str(string, x); - gui_do_text_field(target, *string, null_string); -} - -internal void -show_gui_u64(GUI_Target *target, String *string, - i32 indent_level, i32 h_align, char *message, u64 x){ - string->size = 0; - append_label(string, indent_level, message); - append_padding(string, '-', h_align); - append_s_char(string, ' '); - append_u64_to_str(string, x); - gui_do_text_field(target, *string, null_string); -} - -internal void -show_gui_int_int(GUI_Target *target, String *string, - i32 indent_level, i32 h_align, char *message, i32 x, i32 m){ - string->size = 0; - append_label(string, indent_level, message); - append_padding(string, '-', h_align); - append_s_char(string, ' '); - append_int_to_str(string, x); - append_s_char(string, '/'); - append_int_to_str(string, m); - gui_do_text_field(target, *string, null_string); -} - -internal void -show_gui_id(GUI_Target *target, String *string, - i32 indent_level, i32 h_align, char *message, GUI_id id){ - string->size = 0; - append_label(string, indent_level, message); - append_padding(string, '-', h_align); - append_ss(string, make_lit_string(" [0]: ")); - append_u64_to_str(string, id.id[0]); - append_padding(string, ' ', h_align + 26); - append_ss(string, make_lit_string(" [1]: ")); - append_u64_to_str(string, id.id[1]); - gui_do_text_field(target, *string, null_string); -} - -internal void -show_gui_float(GUI_Target *target, String *string, - i32 indent_level, i32 h_align, char *message, float x){ - string->size = 0; - append_label(string, indent_level, message); - append_padding(string, '-', h_align); - append_s_char(string, ' '); - append_float_to_str(string, x); - gui_do_text_field(target, *string, null_string); -} - -internal void -show_gui_scroll(GUI_Target *target, String *string, - i32 indent_level, i32 h_align, char *message, - GUI_Scroll_Vars scroll){ - show_gui_line (target, string, indent_level, 0, message, 0); - show_gui_float(target, string, indent_level+1, h_align, " scroll_y ", scroll.scroll_y); - show_gui_int (target, string, indent_level+1, h_align, " target_y ", scroll.target_y); - show_gui_int (target, string, indent_level+1, h_align, " prev_target_y ", scroll.prev_target_y); - show_gui_float(target, string, indent_level+1, h_align, " scroll_x ", scroll.scroll_x); - show_gui_int (target, string, indent_level+1, h_align, " target_x ", scroll.target_x); - show_gui_int (target, string, indent_level+1, h_align, " prev_target_x ", scroll.prev_target_x); -} - -internal void -show_gui_cursor(GUI_Target *target, String *string, - i32 indent_level, i32 h_align, char *message, - Full_Cursor cursor){ - show_gui_line (target, string, indent_level, 0, message, 0); - show_gui_int (target, string, indent_level+1, h_align, " pos ", cursor.pos); - show_gui_int (target, string, indent_level+1, h_align, " line ", cursor.line); - show_gui_int (target, string, indent_level+1, h_align, " column ", cursor.character); - show_gui_float(target, string, indent_level+1, h_align, " unwrapped_x ", cursor.unwrapped_x); - show_gui_float(target, string, indent_level+1, h_align, " unwrapped_y ", cursor.unwrapped_y); - show_gui_float(target, string, indent_level+1, h_align, " wrapped_x ", cursor.wrapped_x); - show_gui_float(target, string, indent_level+1, h_align, " wrapped_y ", cursor.wrapped_y); -} - -internal void -show_gui_region(GUI_Target *target, String *string, - i32 indent_level, i32 h_align, char *message, - i32_Rect region){ - show_gui_line(target, string, indent_level, 0, message, 0); - show_gui_int (target, string, indent_level+1, h_align, " x0 ", region.x0); - show_gui_int (target, string, indent_level+1, h_align, " y0 ", region.y0); - show_gui_int (target, string, indent_level+1, h_align, " x1 ", region.x1); - show_gui_int (target, string, indent_level+1, h_align, " y1 ", region.y1); -} - -struct View_Step_Result{ - b32 animating; - b32 consume_keys; - b32 consume_esc; -}; - -inline void -gui_show_mouse(GUI_Target *target, String *string, i32 mx, i32 my){ - string->size = 0; - append_ss(string, make_lit_string("mouse: (")); - append_int_to_str(string, mx); - append_s_char(string, ','); - append_int_to_str(string, my); - append_s_char(string, ')'); - - gui_do_text_field(target, *string, null_string); -} - -internal View_Step_Result -step_file_view(System_Functions *system, View *view, Models *models, View *active_view, Input_Summary input){ - View_Step_Result result = {0}; - GUI_Target *target = &view->gui_target; - Key_Input_Data keys = input.keys; - - b32 show_scrollbar = !view->hide_scrollbar; - - if (view->showing_ui != VUI_None){ - b32 did_esc = false; - for (i32 i = 0; i < keys.count; ++i){ - Key_Event_Data key = keys.keys[i]; - if (key.keycode == key_esc){ - did_esc = 1; - break; - } - } - - if (did_esc){ - view_show_file(view, models); - result.consume_esc = true; - } - } - - gui_begin_top_level(target, input); - { - if (view->showing_ui != VUI_Debug && !view->hide_file_bar){ - gui_do_top_bar(target); - } - do_widget(view, target); - - if (view->showing_ui == VUI_None){ - - gui_begin_serial_section(target); - { - i32 delta = 9 * view->line_height; - GUI_id scroll_context = {0}; - scroll_context.id[1] = view->showing_ui; - scroll_context.id[0] = (u64)(view->file_data.file); - - GUI_Scroll_Vars scroll_zero = {0}; - GUI_Scroll_Vars *scroll = &scroll_zero; - - Assert(view->file_data.file != 0); - Assert(view->edit_pos != 0); - scroll = &view->edit_pos->scroll; - - gui_begin_scrollable(target, scroll_context, *scroll, - delta, show_scrollbar); - - gui_do_file(target); - gui_end_scrollable(target); - } - gui_end_serial_section(target); - } - else{ - switch (view->showing_ui){ - case VUI_Theme: - { - if (view != active_view){ - view->hot_file_view = active_view; - } - - String message = {0}; - String empty_string = {0}; - - GUI_id id = {0}; - id.id[1] = VUI_Theme + ((u64)view->color_mode << 32); - - GUI_id scroll_context = {0}; - scroll_context.id[0] = 0; - scroll_context.id[1] = VUI_Theme + ((u64)view->color_mode << 32); - - switch (view->color_mode){ - case CV_Mode_Library: - { - message = make_lit_string("Current Theme - Click to Edit"); - gui_do_text_field(target, message, empty_string); - - id.id[0] = (u64)(main_style(models)); - if (gui_do_style_preview(target, id, 0)){ - view->color_mode = CV_Mode_Adjusting; - } - - Assert(view->file_data.file != 0); - - message = make_lit_string("Set Font"); - id.id[0] = (u64)(&view->file_data.file->settings.font_id); - if (gui_do_button(target, id, message)){ - view->color_mode = CV_Mode_Font; - } - - - message = make_lit_string("Set Global Font"); - id.id[0] = (u64)(&models->global_font_id); - - if (gui_do_button(target, id, message)){ - view->color_mode = CV_Mode_Global_Font; - } - - message = make_lit_string("Theme Library - Click to Select"); - gui_do_text_field(target, message, empty_string); - - gui_begin_scrollable(target, scroll_context, view->gui_scroll, - 9 * view->line_height, show_scrollbar); - - { - i32 count = models->styles.count; - for (i32 i = 1; i < count; ++i){ - Style *style = get_style(models, i); - id.id[0] = (u64)(style); - if (gui_do_style_preview(target, id, i)){ - style_copy(main_style(models), style); - } - } - } - - gui_end_scrollable(target); - } - break; - - case CV_Mode_Font: - case CV_Mode_Global_Font: - { - Editing_File *file = view->file_data.file; - Assert(file != 0); - - id.id[0] = 0; - if (gui_do_button(target, id, make_lit_string("Back"))){ - view->color_mode = CV_Mode_Library; - } - - gui_begin_scrollable(target, scroll_context, view->gui_scroll, 9*view->line_height, show_scrollbar); - - Face_ID font_id = file->settings.font_id; - Face_ID new_font_id = 0; - - Face_ID largest_id = system->font.get_largest_id(); - for (Face_ID i = 1; i <= largest_id; ++i){ - Font_Pointers font = system->font.get_pointers_by_id(i); - if (font.valid){ - Font_Settings *settings = font.settings; - Font_Metrics *metrics = font.metrics; - - char space[512]; - String m = make_fixed_width_string(space); - if (i == font_id){ - append(&m, "*"); - } - - append(&m, " \""); - append(&m, make_string(metrics->name, metrics->name_len)); - append(&m, "\" "); - append_int_to_str(&m, settings->parameters.pt_size); - append(&m, " "); - append(&m, (char*)(settings->parameters.italics?"italics ":"")); - append(&m, (char*)(settings->parameters.bold?"bold ":"")); - append(&m, (char*)(settings->parameters.underline?"underline ":"")); - append(&m, (char*)(settings->parameters.use_hinting?"hinting ":"")); - - if (i == font_id){ - append(&m, "*"); - } - - id.id[0] = i*2 + 0; - if (gui_do_button(target, id, m)){ - if (new_font_id == 0){ - new_font_id = i; - } - } - - id.id[0] = i*2 + 1; - if (gui_do_button(target, id, make_lit_string("edit"))){ - view->font_edit_id = i; - if (view->color_mode == CV_Mode_Font){ - view->color_mode = CV_Mode_Font_Editing; - } - else{ - view->color_mode = CV_Mode_Global_Font_Editing; - } - } - } - } - - id.id[0] = largest_id*2 + 2; - if (gui_do_button(target, id, make_lit_string("new face"))){ - if (new_font_id == 0){ - Font_Pointers font = system->font.get_pointers_by_id(font_id); - view->font_edit_id = system->font.face_allocate_and_init(font.settings); - if (view->color_mode == CV_Mode_Font){ - view->color_mode = CV_Mode_Font_Editing; - } - else{ - view->color_mode = CV_Mode_Global_Font_Editing; - } - } - } - - if (new_font_id != 0){ - if (view->color_mode == CV_Mode_Font && new_font_id != font_id){ - file_set_font(system, models, file, new_font_id); - } - else if (new_font_id != font_id || new_font_id != models->global_font_id){ - global_set_font(system, models, new_font_id); - } - } - - gui_end_scrollable(target); - }break; - - case CV_Mode_Font_Editing: - case CV_Mode_Global_Font_Editing: - { - id.id[0] = 0; - if (gui_do_button(target, id, make_lit_string("Back"))){ - if (view->color_mode == CV_Mode_Font_Editing){ - view->color_mode = CV_Mode_Font; - } - else{ - view->color_mode = CV_Mode_Global_Font; - } - } - - gui_begin_scrollable(target, scroll_context, view->gui_scroll, 9*view->line_height, show_scrollbar); - - Face_ID font_edit_id = view->font_edit_id; - Font_Pointers font = system->font.get_pointers_by_id(font_edit_id); - Font_Settings *settings = font.settings; - Font_Metrics *metrics = font.metrics; - Font_Settings new_settings = *settings; - b32 has_new_settings = false; - - char space[128]; - String m = make_fixed_width_string(space); - copy(&m, "Size Up ("); - append_int_to_str(&m, settings->parameters.pt_size); - append(&m, ")"); - ++id.id[0]; - if (gui_do_button(target, id, m)){ - if (!has_new_settings){ - has_new_settings = true; - ++new_settings.parameters.pt_size; - } - } - - copy(&m, "Size Down ("); - append_int_to_str(&m, settings->parameters.pt_size); - append(&m, ")"); - ++id.id[0]; - if (gui_do_button(target, id, m)){ - if (!has_new_settings){ - has_new_settings = true; - --new_settings.parameters.pt_size; - } - } - - copy(&m, "Italics ["); - append(&m, (char*)(settings->parameters.italics?"+":" ")); - append(&m, "]"); - ++id.id[0]; - if (gui_do_button(target, id, m)){ - if (!has_new_settings){ - has_new_settings = true; - new_settings.parameters.italics = !new_settings.parameters.italics; - } - } - - copy(&m, "Bold ["); - append(&m, (char*)(settings->parameters.bold?"+":" ")); - append(&m, "]"); - ++id.id[0]; - if (gui_do_button(target, id, m)){ - if (!has_new_settings){ - has_new_settings = true; - new_settings.parameters.bold = !new_settings.parameters.bold; - } - } - - copy(&m, "Underline ["); - append(&m, (char*)(settings->parameters.underline?"+":" ")); - append(&m, "]"); - ++id.id[0]; - if (gui_do_button(target, id, m)){ - if (!has_new_settings){ - has_new_settings = true; - new_settings.parameters.underline = !new_settings.parameters.underline; - } - } - - copy(&m, "Hinting ["); - append(&m, (char*)(settings->parameters.use_hinting?"+":" ")); - append(&m, "]"); - ++id.id[0]; - if (gui_do_button(target, id, m)){ - if (!has_new_settings){ - has_new_settings = true; - new_settings.parameters.use_hinting = !new_settings.parameters.use_hinting; - } - } - - copy(&m, "Current Family: "); - append(&m, make_string(metrics->name, metrics->name_len)); - ++id.id[0]; - gui_do_button(target, id, m); - - i32 total_count = system->font.get_loadable_count(); - for (i32 i = 0; i < total_count; ++i){ - Font_Loadable_Description loadable = {0}; - system->font.get_loadable(i, &loadable); - - if (loadable.valid){ - String name = make_string(loadable.display_name, loadable.display_len); - ++id.id[0]; - if (gui_do_button(target, id, name)){ - if (!has_new_settings){ - has_new_settings = true; - memcpy(&new_settings.stub, &loadable.stub, sizeof(loadable.stub)); - } - } - } - } - - gui_end_scrollable(target); - - if (has_new_settings){ - alter_font(system, models, font_edit_id, &new_settings); - } - }break; - - case CV_Mode_Adjusting: - { - Style *style = main_style(models); - u32 *edit_color = 0; - u32 *fore = 0, *back = 0; - i32 i = 0; - - message = make_lit_string("Back"); - - id.id[0] = 0; - if (gui_do_button(target, id, message)){ - view->color_mode = CV_Mode_Library; - } - - gui_begin_scrollable(target, scroll_context, view->gui_scroll, 9*view->line_height, show_scrollbar); - - i32 next_color_editing = view->current_color_editing; - - for (i = 0; i < ArrayCount(colors_to_edit); ++i){ - edit_color = style_index_by_tag(&style->main, colors_to_edit[i].target); - id.id[0] = (u64)(edit_color); - - fore = style_index_by_tag(&style->main, colors_to_edit[i].fore); - back = style_index_by_tag(&style->main, colors_to_edit[i].back); - - if (gui_do_color_button(target, id, *fore, *back, colors_to_edit[i].text)){ - next_color_editing = i; - view->color_cursor = 0; - } - - if (view->current_color_editing == i){ - GUI_Item_Update update = {0}; - char text_space[7]; - String text = make_fixed_width_string(text_space); - - color_to_hexstr(&text, *edit_color); - if (gui_do_text_with_cursor(target, view->color_cursor, text, &update)){ - b32 r = 0; - i32 j = 0; - - for (j = 0; j < keys.count; ++j){ - Key_Code key = keys.keys[j].keycode; - switch (key){ - case key_left: --view->color_cursor; r = 1; result.consume_keys = 1; break; - case key_right: ++view->color_cursor; r = 1; result.consume_keys = 1; break; - - case key_up: - if (next_color_editing > 0){ - --next_color_editing; - } - result.consume_keys = 1; - break; - - case key_down: - if (next_color_editing <= ArrayCount(colors_to_edit)-1){ - ++next_color_editing; - } - result.consume_keys = 1; - break; - - default: - if ((key >= '0' && key <= '9') || (key >= 'a' && key <= 'f') || (key >= 'A' && key <= 'F')){ - text.str[view->color_cursor] = (char)key; - r = 1; - result.consume_keys = 1; - } - break; - } - - if (view->color_cursor < 0) view->color_cursor = 0; - if (view->color_cursor >= 6) view->color_cursor = 5; - } - - if (r){ - hexstr_to_color(text, edit_color); - gui_rollback(target, &update); - gui_do_text_with_cursor(target, view->color_cursor, text, 0); - } - } - } - } - - if (view->current_color_editing != next_color_editing){ - view->current_color_editing = next_color_editing; - view->color_cursor = 0; - } - - gui_end_scrollable(target); - }break; - } - }break; - - case VUI_Interactive: - { - b32 complete = 0; - char comp_dest_space[1024]; - String comp_dest = make_fixed_width_string(comp_dest_space); - i32 comp_action = 0; - - GUI_id id = {0}; - id.id[1] = VUI_Interactive + ((u64)view->interaction << 32); - - GUI_id scroll_context = {0}; - scroll_context.id[1] = VUI_Interactive + ((u64)view->interaction << 32); - - Key_Code user_up_key = models->user_up_key; - Key_Code user_down_key = models->user_down_key; - Key_Modifier user_up_key_modifier = models->user_up_key_modifier; - Key_Modifier user_down_key_modifier = models->user_down_key_modifier; - - switch (view->interaction){ - case IInt_Sys_File_List: - { - b32 autocomplete_with_enter = true; - b32 activate_directly = false; - - if (view->action == IAct_New){ - autocomplete_with_enter = false; - } - - String message = null_string; - switch (view->action){ - case IAct_OpenOrNew: - case IAct_Open: message = make_lit_string("Open: "); break; - case IAct_New: message = make_lit_string("New: "); break; - } - - GUI_Item_Update update = {0}; - Hot_Directory *hdir = &models->hot_directory; - - b32 do_open_or_new = false; - for (i32 i = 0; i < keys.count; ++i){ - Key_Event_Data key = keys.keys[i]; - Single_Line_Input_Step step = app_single_file_input_step(system, &models->working_set, key, - &hdir->string, hdir, 1, 0); - - if (step.made_a_change){ - view->list_i = 0; - result.consume_keys = true; - } - - if (key.keycode == '\n'){ - if (!autocomplete_with_enter){ - activate_directly = true; - result.consume_keys = true; - } - else if (view->action == IAct_OpenOrNew){ - do_open_or_new = true; - result.consume_keys = true; - } - } - } - - gui_do_text_field(target, message, hdir->string); - - b32 snap_into_view = false; - scroll_context.id[0] = (u64)(hdir); - if (gui_scroll_was_activated(target, scroll_context)){ - snap_into_view = true; - } - gui_begin_scrollable(target, scroll_context, view->gui_scroll, 9 * view->line_height, show_scrollbar); - - id.id[0] = (u64)(hdir) + 1; - - if (gui_begin_list(target, id, view->list_i, 0, snap_into_view, &update)){ - // TODO(allen): Allow me to handle key consumption correctly here! - gui_standard_list(target, id, &view->gui_scroll, view->scroll_region, &keys, &view->list_i, &update, user_up_key, user_up_key_modifier, user_down_key, user_down_key_modifier); - } - - b32 do_new_directory = false; - Exhaustive_File_Loop loop; - begin_exhaustive_loop(&loop, hdir); - for (i32 i = 0; i < loop.count; ++i){ - Exhaustive_File_Info file_info = get_exhaustive_info(system, &models->working_set, &loop, i); - - if (file_info.name_match){ - id.id[0] = (u64)(file_info.info); - - char *str = file_info.info->filename; - i32 len = file_info.info->filename_len; - String filename = make_string_cap(str, len, len + 1); - - if (gui_do_file_option(target, id, filename, file_info.is_folder, file_info.message)){ - if (file_info.is_folder){ - set_last_folder_sc(&hdir->string, file_info.info->filename, '/'); - do_new_directory = true; - } - - else if (autocomplete_with_enter){ - complete = true; - copy_ss(&comp_dest, loop.full_path); - if (view->action == IAct_OpenOrNew){ - view->action = IAct_Open; - } - } - } - - if (do_open_or_new){ - do_open_or_new = false; - } - } - } - - gui_end_list(target); - - if (activate_directly || do_open_or_new){ - complete = true; - copy_ss(&comp_dest, hdir->string); - if (do_open_or_new){ - view->action = IAct_New; - } - } - - if (do_new_directory){ - hot_directory_reload(system, hdir); - } - - gui_end_scrollable(target); - }break; - - case IInt_Live_File_List: - { - local_persist String message_unsaved = make_lit_string(" *"); - local_persist String message_unsynced = make_lit_string(" !"); - - String message = null_string; - switch (view->action){ - case IAct_Switch: message = make_lit_string("Switch: "); break; - case IAct_Kill: message = make_lit_string("Kill: "); break; - } - - Working_Set *working_set = &models->working_set; - Editing_Layout *layout = &models->layout; - GUI_Item_Update update = {0}; - - for (i32 i = 0; i < keys.count; ++i){ - Key_Event_Data key = keys.keys[i]; - Single_Line_Input_Step step = app_single_line_input_step(system, key, &view->dest); - if (step.made_a_change){ - view->list_i = 0; - result.consume_keys = 1; - } - } - - Absolutes absolutes = {0}; - get_absolutes(view->dest, &absolutes, 1, 1); - - gui_do_text_field(target, message, view->dest); - - b32 snap_into_view = 0; - scroll_context.id[0] = (u64)(working_set); - if (gui_scroll_was_activated(target, scroll_context)){ - snap_into_view = 1; - } - gui_begin_scrollable(target, scroll_context, view->gui_scroll, 9 * view->line_height, show_scrollbar); - - id.id[0] = (u64)(working_set) + 1; - if (gui_begin_list(target, id, view->list_i, 0, snap_into_view, &update)){ - gui_standard_list(target, id, &view->gui_scroll, view->scroll_region, &keys, &view->list_i, &update, user_up_key, user_up_key_modifier, user_down_key, user_down_key_modifier); - } - - { - Partition *part = &models->mem.part; - Temp_Memory temp = begin_temp_memory(part); - File_Node *node = 0, *used_nodes = 0; - Editing_File **reserved_files = 0; - i32 reserved_top = 0, i = 0; - - partition_align(part, sizeof(i32)); - reserved_files = (Editing_File**)partition_current(part); - - used_nodes = &working_set->used_sentinel; - for (dll_items(node, used_nodes)){ - Editing_File *file = (Editing_File*)node; - Assert(!file->is_dummy); - - if (wildcard_match_s(&absolutes, file->unique_name.name, 0) != 0){ - View_Iter iter = file_view_iter_init(layout, file, 0); - if (file_view_iter_good(iter)){ - reserved_files[reserved_top++] = file; - } - else{ - if (file->unique_name.name.str[0] == '*'){ - reserved_files[reserved_top++] = file; - } - else{ - message = null_string; - if (!file->settings.unimportant){ - switch (file_get_sync(file)){ - case DirtyState_UnloadedChanges: message = message_unsynced; break; - case DirtyState_UnsavedChanges: message = message_unsaved; break; - } - } - - id.id[0] = (u64)(file); - if (gui_do_file_option(target, id, file->unique_name.name, 0, message)){ - complete = 1; - copy_ss(&comp_dest, file->unique_name.name); - } - } - } - } - } - - for (i = 0; i < reserved_top; ++i){ - Editing_File *file = reserved_files[i]; - - message = null_string; - if (!file->settings.unimportant){ - switch (file_get_sync(file)){ - case DirtyState_UnloadedChanges: message = message_unsynced; break; - case DirtyState_UnsavedChanges: message = message_unsaved; break; - } - } - - id.id[0] = (u64)(file); - if (gui_do_file_option(target, id, file->unique_name.name, 0, message)){ - complete = 1; - copy_ss(&comp_dest, file->unique_name.name); - } - } - - end_temp_memory(temp); - } - - gui_end_list(target); - - gui_end_scrollable(target); - }break; - - case IInt_Sure_To_Close: - { - i32 action = -1; - - String empty_str = {0}; - String message = make_lit_string("There is one or more files unsaved changes, close anyway?"); - - gui_do_text_field(target, message, empty_str); - - id.id[0] = (u64)('y'); - message = make_lit_string("(Y)es"); - if (gui_do_fixed_option(target, id, message, 'y')){ - action = 0; - } - - id.id[0] = (u64)('n'); - message = make_lit_string("(N)o"); - if (gui_do_fixed_option(target, id, message, 'n')){ - action = 1; - } - - if (action != -1){ - complete = 1; - copy_ss(&comp_dest, view->dest); - comp_action = action; - } - }break; - - case IInt_Sure_To_Kill: - { - i32 action = -1; - - String empty_str = {0}; - String message = make_lit_string("There are unsaved changes, close anyway?"); - - gui_do_text_field(target, message, empty_str); - - id.id[0] = (u64)('y'); - message = make_lit_string("(Y)es"); - if (gui_do_fixed_option(target, id, message, 'y')){ - action = 0; - } - - id.id[0] = (u64)('n'); - message = make_lit_string("(N)o"); - if (gui_do_fixed_option(target, id, message, 'n')){ - action = 1; - } - - id.id[0] = (u64)('s'); - message = make_lit_string("(S)ave and kill"); - if (gui_do_fixed_option(target, id, message, 's')){ - action = 2; - } - - if (action != -1){ - complete = 1; - copy_ss(&comp_dest, view->dest); - comp_action = action; - } - }break; - } - - if (complete){ - terminate_with_null(&comp_dest); - interactive_view_complete(system, view, models, comp_dest, comp_action); - } - }break; - - case VUI_Debug: - { - GUI_id scroll_context = {0}; - scroll_context.id[1] = VUI_Debug + ((u64)view->debug_vars.mode << 32); - - GUI_id id = {0}; - id.id[1] = VUI_Debug + ((u64)view->debug_vars.mode << 32); - - // TODO(allen): - // + Incoming input - // + Memory info - // + Thread info - // + View inspection - // - auto generate? - // - expand/collapse sections - // - Buffer inspection - // - Command maps inspection - // - Clipboard inspection - - String empty_str = null_string; - - char space1[512]; - String string = make_fixed_width_string(space1); - - // Time Watcher - { - string.size = 0; - u64 time = system->now_time(); - - append_ss(&string, make_lit_string("last redraw: ")); - append_u64_to_str(&string, time); - - gui_do_text_field(target, string, empty_str); - } - - { - i32 prev_mode = view->debug_vars.mode; - for (i32 i = 0; i < keys.count; ++i){ - Key_Event_Data key = keys.keys[i]; - if (key.modifiers[MDFR_CONTROL_INDEX] == 0 && - key.modifiers[MDFR_ALT_INDEX] == 0){ - if (key.keycode == 'i'){ - view->debug_vars.mode = DBG_Input; - } - if (key.keycode == 'm'){ - view->debug_vars.mode = DBG_Threads_And_Memory; - } - if (key.keycode == 'v'){ - view->debug_vars.mode = DBG_View_Inspection; - } - } - } - if (prev_mode != view->debug_vars.mode){ - result.consume_keys = 1; - } - } - - gui_begin_scrollable(target, scroll_context, view->gui_scroll, - 9 * view->line_height, show_scrollbar); - - switch (view->debug_vars.mode) - { - case DBG_Input: - { - Debug_Data *debug = &models->debug; - - gui_show_mouse(target, &string, input.mouse.x, input.mouse.y); - - Debug_Input_Event *input_event = debug->input_events; - for (i32 i = 0; - i < ArrayCount(debug->input_events); - ++i, ++input_event){ - string.size = 0; - - if (input_event->is_hold){ - append_ss(&string, make_lit_string("hold: ")); - } - else{ - append_ss(&string, make_lit_string("press: ")); - } - - if (input_event->is_ctrl){ - append_ss(&string, make_lit_string("ctrl-")); - } - else{ - append_ss(&string, make_lit_string(" -")); - } - - if (input_event->is_alt){ - append_ss(&string, make_lit_string("alt-")); - } - else{ - append_ss(&string, make_lit_string(" -")); - } - - if (input_event->is_shift){ - append_ss(&string, make_lit_string("shift ")); - } - else{ - append_ss(&string, make_lit_string(" ")); - } - - if (input_event->key > ' ' && input_event->key <= '~'){ - append_ss(&string, make_string(&input_event->key, 1)); - } - else if (input_event->key == ' '){ - append_ss(&string, make_lit_string("space")); - } - else if (input_event->key == '\n'){ - append_ss(&string, make_lit_string("\\n")); - } - else if (input_event->key == '\t'){ - append_ss(&string, make_lit_string("\\t")); - } - else{ - String str; - str.str = global_key_name(input_event->key, &str.size); - if (str.str){ - str.memory_size = str.size + 1; - append_ss(&string, str); - } - else{ - append_ss(&string, make_lit_string("unrecognized!")); - } - } - - if (input_event->consumer[0] != 0){ - append_padding(&string, ' ', 40); - append_sc(&string, input_event->consumer); - } - - gui_do_text_field(target, string, empty_str); - } - }break; - - case DBG_Threads_And_Memory: - { - b8 threads[4]; - i32 pending = 0; - - if (system->internal_get_thread_states){ - system->internal_get_thread_states(BACKGROUND_THREADS, - threads, &pending); - - string.size = 0; - append_ss(&string, make_lit_string("pending jobs: ")); - append_int_to_str(&string, pending); - gui_do_text_field(target, string, empty_str); - - for (i32 i = 0; i < 4; ++i){ - string.size = 0; - append_ss(&string, make_lit_string("thread ")); - append_int_to_str(&string, i); - append_ss(&string, make_lit_string(": ")); - - if (threads[i]){ - append_ss(&string, make_lit_string("running")); - } - else{ - append_ss(&string, make_lit_string("waiting")); - } - - gui_do_text_field(target, string, empty_str); - } - } - - Partition *part = &models->mem.part; - - string.size = 0; - append_ss(&string, make_lit_string("part memory: ")); - append_int_to_str(&string, part->pos); - append_s_char(&string, '/'); - append_int_to_str(&string, part->max); - gui_do_text_field(target, string, empty_str); - -#if !defined(FED_DEBUG_MEM_H) - General_Memory *general = &models->mem.general; - Bubble *bubble = 0, *sentinel = &general->sentinel; - for (dll_items(bubble, sentinel)){ - string.size = 0; - if (bubble->flags & MEM_BUBBLE_USED){ - append_ss(&string, make_lit_string(" used: ")); - } - else{ - append_ss(&string, make_lit_string(" free: ")); - } - - append_int_to_str(&string, bubble->size); - append_padding(&string, ' ', 40); - gui_do_text_field(target, string, empty_str); - } -#endif - }break; - - case DBG_View_Inspection: - { - i32 inspecting_id = view->debug_vars.inspecting_view_id; - View *views_to_inspect[16]; - i32 view_count = 0; - b32 low_detail = 1; - - if (inspecting_id == 0){ - Editing_Layout *layout = &models->layout; - - Panel *panel, *sentinel; - sentinel = &layout->used_sentinel; - for (dll_items(panel, sentinel)){ - View *view_ptr = panel->view; - views_to_inspect[view_count++] = view_ptr; - } - } - else if (inspecting_id >= 1 && inspecting_id <= 16){ - Live_Views *live_set = &models->live_set; - View *view_ptr = live_set->views + inspecting_id - 1; - views_to_inspect[view_count++] = view_ptr; - low_detail = 0; - } - - for (i32 i = 0; i < view_count; ++i){ - View *view_ptr = views_to_inspect[i]; - - string.size = 0; - append_ss(&string, make_lit_string("view: ")); - append_int_to_str(&string, view_ptr->persistent.id + 1); - gui_do_text_field(target, string, empty_str); - - string.size = 0; - Editing_File *file = view_ptr->file_data.file; - append_ss(&string, make_lit_string(" > buffer: ")); - if (file){ - append_ss(&string, file->unique_name.name); - gui_do_text_field(target, string, empty_str); - string.size = 0; - append_ss(&string, make_lit_string(" >> buffer-slot-id: ")); - append_int_to_str(&string, file->id.id); - } - else{ - append_ss(&string, make_lit_string("*NULL*")); - gui_do_text_field(target, string, empty_str); - } - - if (low_detail){ - string.size = 0; - append_ss(&string, make_lit_string("inspect this")); - - id.id[0] = (u64)(view_ptr->persistent.id); - if (gui_do_button(target, id, string)){ - view->debug_vars.inspecting_view_id = view_ptr->persistent.id + 1; - } - } - else{ - - gui_show_mouse(target, &string, input.mouse.x, input.mouse.y); - -#define SHOW_GUI_BLANK(n) show_gui_line(target, &string, n, 0, "", 0) -#define SHOW_GUI_LINE(n, str) show_gui_line(target, &string, n, 0, " " str, 0) -#define SHOW_GUI_STRING(n, h, str, mes) show_gui_line(target, &string, n, h, " " str " ", mes) -#define SHOW_GUI_INT(n, h, str, v) show_gui_int(target, &string, n, h, " " str " ", v) -#define SHOW_GUI_INT_INT(n, h, str, v, m) show_gui_int_int(target, &string, n, h, " " str " ", v, m) -#define SHOW_GUI_U64(n, h, str, v) show_gui_u64(target, &string, n, h, " " str " ", v) -#define SHOW_GUI_ID(n, h, str, v) show_gui_id(target, &string, n, h, " " str, v) -#define SHOW_GUI_FLOAT(n, h, str, v) show_gui_float(target, &string, n, h, " " str " ", v) -#define SHOW_GUI_BOOL(n, h, str, v) do { if (v) { show_gui_line(target, &string, n, h, " " str " ", "true"); }\ - else { show_gui_line(target, &string, n, h, " " str " ", "false"); } } while(0) - -#define SHOW_GUI_SCROLL(n, h, str, v) show_gui_scroll(target, &string, n, h, " " str, v) -#define SHOW_GUI_CURSOR(n, h, str, v) show_gui_cursor(target, &string, n, h, " " str, v) -#define SHOW_GUI_REGION(n, h, str, v) show_gui_region(target, &string, n, h, " " str, v) - - i32 h_align = 31; - - SHOW_GUI_BLANK(0); - { - i32 map = view_ptr->map; -#define MAP_LABEL "command map" - if (map == mapid_global){ - SHOW_GUI_STRING(1, h_align, MAP_LABEL, "global"); - } - else if (map == mapid_file){ - SHOW_GUI_STRING(1, h_align, MAP_LABEL, "file"); - } - else if (map == mapid_ui){ - SHOW_GUI_STRING(1, h_align, MAP_LABEL, "gui"); - } - else if (map == mapid_nomap){ - SHOW_GUI_STRING(1, h_align, MAP_LABEL, "nomap"); - } - else{ - SHOW_GUI_STRING(1, h_align, MAP_LABEL, "user"); - SHOW_GUI_INT(2, h_align, "custom map id", map); - } - } - - SHOW_GUI_BLANK(0); - SHOW_GUI_LINE(1, "file data:"); - SHOW_GUI_BOOL(2, h_align, "has file", view_ptr->file_data.file); - SHOW_GUI_BOOL(2, h_align, "show temp highlight", view_ptr->file_data.show_temp_highlight); - SHOW_GUI_INT (2, h_align, "start temp highlight", view_ptr->file_data.temp_highlight.pos); - SHOW_GUI_INT (2, h_align, "end temp highlight", view_ptr->file_data.temp_highlight_end_pos); - - SHOW_GUI_BOOL(2, h_align, "show whitespace", view_ptr->file_data.show_whitespace); - SHOW_GUI_BOOL(2, h_align, "locked", view_ptr->file_data.file_locked); - - SHOW_GUI_BLANK(0); - SHOW_GUI_REGION(1, h_align, "scroll region", view_ptr->scroll_region); - - SHOW_GUI_BLANK(0); - SHOW_GUI_LINE(1, "file editing positions"); - { - File_Edit_Positions *edit_pos = view_ptr->edit_pos; - - if (edit_pos){ - SHOW_GUI_SCROLL(2, h_align, "scroll:", edit_pos->scroll); - SHOW_GUI_BLANK (2); - SHOW_GUI_CURSOR(2, h_align, "cursor:", edit_pos->cursor); - SHOW_GUI_BLANK (2); - SHOW_GUI_INT (2, h_align, "mark", edit_pos->mark); - SHOW_GUI_FLOAT (2, h_align, "preferred_x", edit_pos->preferred_x); - SHOW_GUI_INT (2, h_align, "scroll_i", edit_pos->scroll_i); - } - else{ - SHOW_GUI_LINE(2, "NULL"); - } - } - - SHOW_GUI_BLANK (0); - SHOW_GUI_SCROLL(1, h_align, "gui scroll:", view_ptr->gui_scroll); - - SHOW_GUI_BLANK (0); - SHOW_GUI_LINE (1, "gui target"); - SHOW_GUI_INT_INT(2, h_align, "gui partition", - view_ptr->gui_target.push.pos, - view_ptr->gui_target.push.max); - - SHOW_GUI_BLANK (2); - SHOW_GUI_ID (2, h_align, "active", view_ptr->gui_target.active); - SHOW_GUI_ID (2, h_align, "mouse_hot", view_ptr->gui_target.mouse_hot); - SHOW_GUI_ID (2, h_align, "auto_hot", view_ptr->gui_target.auto_hot); - SHOW_GUI_ID (2, h_align, "hover", view_ptr->gui_target.hover); - SHOW_GUI_ID (2, h_align, "scroll_id", view_ptr->gui_target.scroll_id); - - SHOW_GUI_BLANK (2); - SHOW_GUI_SCROLL (2, h_align, "scroll_original", view_ptr->gui_target.scroll_original); - SHOW_GUI_REGION (2, h_align, "region_original", view_ptr->gui_target.region_original); - - SHOW_GUI_BLANK (2); - SHOW_GUI_REGION (2, h_align, "region_updated", view_ptr->gui_target.region_updated); - - - SHOW_GUI_BLANK (1); - SHOW_GUI_SCROLL (1, h_align, "gui scroll", view_ptr->gui_scroll); - } - } - }break; - } - - gui_end_scrollable(target); - }break; - } - } - } - gui_end_top_level(target); - - result.animating = target->animating; - return(result); -} - -internal f32 -view_get_scroll_y(View *view){ - f32 v = 0; - if (view->showing_ui == VUI_None){ - File_Edit_Positions *edit_pos = view->edit_pos; - if (edit_pos){ - v = edit_pos->scroll.scroll_y; - } - } - else{ - v = view->gui_scroll.scroll_y; - } - return(v); -} - -internal b32 -click_button_input(GUI_Target *target, GUI_Session *session, b32 in_scroll, i32_Rect scroll_rect, Input_Summary *user_input, GUI_Interactive *b, b32 *is_animating){ - b32 result = 0; - i32 mx = user_input->mouse.x; - i32 my = user_input->mouse.y; - - b32 in_sub_region = true; - if (in_scroll && !hit_check(mx, my, scroll_rect)){ - in_sub_region = false; - } - - if (in_sub_region && hit_check(mx, my, session->rect)){ - target->hover = b->id; - if (user_input->mouse.press_l){ - target->mouse_hot = b->id; - *is_animating = 1; - result = 1; - } - if (user_input->mouse.release_l && gui_id_eq(target->mouse_hot, b->id)){ - target->active = b->id; - target->mouse_hot = gui_id_zero(); - *is_animating = 1; - } - } - else if (gui_id_eq(target->hover, b->id)){ - target->hover = gui_id_zero(); - } - - return(result); -} - -internal b32 -scroll_button_input(GUI_Target *target, GUI_Session *session, Input_Summary *user_input, GUI_id id, b32 *is_animating){ - b32 result = 0; - i32 mx = user_input->mouse.x; - i32 my = user_input->mouse.y; - - if (hit_check(mx, my, session->rect)){ - target->hover = id; - if (user_input->mouse.l){ - target->mouse_hot = id; - gui_activate_scrolling(target); - *is_animating = 1; - result = 1; - } - } - else if (gui_id_eq(target->hover, id)){ - target->hover = gui_id_zero(); - } - return(result); -} - -struct Input_Process_Result{ - GUI_Scroll_Vars vars; - i32_Rect region; - b32 is_animating; - b32 consumed_l; - b32 consumed_r; - - b32 has_max_y_suggestion; - i32 max_y; -}; - -static u32 -to_writable_character(Key_Code long_character, u8 *character){ - u32 result = 0; - if (long_character != 0){ - u32_to_utf8_unchecked(long_character, character, &result); - } - return(result); -} - -internal Input_Process_Result -do_step_file_view(System_Functions *system, View *view, Models *models, i32_Rect rect, b32 is_active, Input_Summary *user_input, GUI_Scroll_Vars vars, i32_Rect region, i32 max_y){ - Input_Process_Result result = {0}; - b32 is_file_scroll = 0; - - GUI_Session gui_session = {0}; - GUI_Header *h = 0; - GUI_Target *target = &view->gui_target; - GUI_Interpret_Result interpret_result = {0}; - - vars.target_y = clamp(0, vars.target_y, max_y); - - result.vars = vars; - result.region = region; - - target->active = gui_id_zero(); - - // HACK(allen): UI sucks! Now just forcing it to - // not have the bug where it clicks buttons behind the - // header buttons before the scrollable section. - b32 in_scroll = false; - i32_Rect scroll_rect = {0}; - i32 prev_bottom = 0; - - if (target->push.pos > 0){ - gui_session_init(&gui_session, target, rect, view->line_height); - - for (h = (GUI_Header*)target->push.base; - h->type; - h = NextHeader(h)){ - interpret_result = gui_interpret(target, &gui_session, h, - result.vars, result.region, max_y); - - if (interpret_result.has_region){ - result.region = interpret_result.region; - } - - switch (h->type){ - case guicom_file_option: - case guicom_fixed_option: - case guicom_fixed_option_checkbox: - { - GUI_Interactive *b = (GUI_Interactive*)h; - - if (interpret_result.auto_activate){ - target->auto_hot = gui_id_zero(); - target->active = b->id; - result.is_animating = 1; - } - else if (interpret_result.auto_hot){ - if (!gui_id_eq(target->auto_hot, b->id)){ - target->auto_hot = b->id; - result.is_animating = 1; - } - } - }break; - } - - if (interpret_result.has_info){ - switch (h->type){ - case guicom_top_bar: break; - - case guicom_file: - { - // NOTE(allen): Set the file region first because the - // computation of view_compute_max_target_y depends on it. - view->file_region = gui_session.rect; - - if (view->reinit_scrolling){ - result.vars = view_reinit_scrolling(view); - result.is_animating = 1; - } - - if (file_step(view, gui_session.rect, user_input, is_active, &result.consumed_l)){ - result.is_animating = 1; - } - is_file_scroll = 1; - }break; - - case guicom_color_button: - case guicom_font_button: - case guicom_button: - case guicom_file_option: - case guicom_style_preview: - { - GUI_Interactive *b = (GUI_Interactive*)h; - - if (click_button_input(target, &gui_session, in_scroll, scroll_rect, user_input, b, &result.is_animating)){ - result.consumed_l = true; - } - - prev_bottom = gui_session.rect.y1; - }break; - - case guicom_fixed_option: - case guicom_fixed_option_checkbox: - { - GUI_Interactive *b = (GUI_Interactive*)h; - - if (click_button_input(target, &gui_session, in_scroll, scroll_rect, user_input, b, &result.is_animating)){ - result.consumed_l = true; - } - - { - Key_Input_Data *keys = &user_input->keys; - - void *ptr = (b + 1); - String string = gui_read_string(&ptr); - AllowLocal(string); - - char activation_key = *(char*)ptr; - activation_key = char_to_upper(activation_key); - - if (activation_key != 0){ - i32 count = keys->count; - for (i32 i = 0; i < count; ++i){ - Key_Event_Data key = keys->keys[i]; - - u8 character[4]; - u32 length = to_writable_character(key.character, character); - if (length == 1){ - if (char_to_upper(character[0]) == activation_key){ - target->active = b->id; - result.is_animating = 1; - break; - } - } - } - } - } - }break; - - case guicom_scrollable_slider: - { - GUI_id id = gui_id_scrollbar_slider(); - i32 mx = user_input->mouse.x; - i32 my = user_input->mouse.y; - f32 v = 0; - - if (hit_check(mx, my, gui_session.rect)){ - target->hover = id; - if (user_input->mouse.press_l){ - target->mouse_hot = id; - result.is_animating = 1; - result.consumed_l = 1; - } - } - else if (gui_id_eq(target->hover, id)){ - target->hover = gui_id_zero(); - } - - if (gui_id_eq(target->mouse_hot, id)){ - v = unlerp(gui_session.scroll_top, (f32)my, - gui_session.scroll_bottom); - v = clamp(0.f, v, 1.f); - result.vars.target_y = round32(lerp(0.f, v, (f32)max_y)); - - gui_activate_scrolling(target); - result.is_animating = 1; - } - } - // NOTE(allen): NO BREAK HERE!! - - case guicom_scrollable_invisible: - { - if (user_input->mouse.wheel != 0){ - result.vars.target_y += user_input->mouse.wheel; - - result.vars.target_y = - clamp(0, result.vars.target_y, max_y); - gui_activate_scrolling(target); - result.is_animating = 1; - } - }break; - - case guicom_scrollable_top: - { - GUI_id id = gui_id_scrollbar_top(); - - if (scroll_button_input(target, &gui_session, user_input, id, &result.is_animating)){ - result.vars.target_y -= clamp_bottom(1, target->delta >> 2); - result.vars.target_y = clamp_bottom(0, result.vars.target_y); - result.consumed_l = 1; - } - }break; - - case guicom_scrollable_bottom: - { - GUI_id id = gui_id_scrollbar_bottom(); - - if (scroll_button_input(target, &gui_session, user_input, id, &result.is_animating)){ - result.vars.target_y += clamp_bottom(1, target->delta >> 2); - result.vars.target_y = clamp_top(result.vars.target_y, max_y); - result.consumed_l = 1; - } - }break; - - case guicom_begin_scrollable_section: - { - in_scroll = true; - scroll_rect.x0 = region.x0; - scroll_rect.y0 = prev_bottom; - scroll_rect.x1 = region.x1; - scroll_rect.y1 = region.y1; - }break; - - case guicom_end_scrollable_section: - { - in_scroll = false; - if (!is_file_scroll){ - result.has_max_y_suggestion = 1; - result.max_y = gui_session.suggested_max_y; - } - }break; - } - } - } - - if (!user_input->mouse.l){ - if (!gui_id_is_null(target->mouse_hot)){ - target->mouse_hot = gui_id_zero(); - result.is_animating = 1; - } - } - - { - GUI_Scroll_Vars scroll_vars = result.vars; - b32 is_new_target = 0; - if (scroll_vars.target_x != scroll_vars.prev_target_x || - scroll_vars.target_y != scroll_vars.prev_target_y){ - is_new_target = 1; - } - - f32 target_x = (f32)scroll_vars.target_x; - f32 target_y = (f32)scroll_vars.target_y; - - if (models->scroll_rule(target_x, target_y, &scroll_vars.scroll_x, &scroll_vars.scroll_y, (view->persistent.id) + 1, is_new_target, user_input->dt)){ - result.is_animating = true; - } - - scroll_vars.prev_target_x = scroll_vars.target_x; - scroll_vars.prev_target_y = scroll_vars.target_y; - - result.vars = scroll_vars; - } - } - - return(result); -} - -internal i32 -draw_file_loaded(System_Functions *system, View *view, Models *models, i32_Rect rect, b32 is_active, Render_Target *target){ - Editing_File *file = view->file_data.file; - Style *style = main_style(models); - i32 line_height = view->line_height; - - f32 max_x = view_file_display_width(view); - i32 max_y = rect.y1 - rect.y0 + line_height; - - Assert(file && !file->is_dummy && buffer_good(&file->state.buffer)); - Assert(view->edit_pos); - - b32 tokens_use = 0; - Cpp_Token_Array token_array = {}; - if (file){ - tokens_use = file->state.tokens_complete && (file->state.token_array.count > 0); - token_array = file->state.token_array; - } - - Partition *part = &models->mem.part; - Temp_Memory temp = begin_temp_memory(part); - - partition_align(part, 4); - - f32 left_side_space = 0; - - i32 max = partition_remaining(part) / sizeof(Buffer_Render_Item); - Buffer_Render_Item *items = push_array(part, Buffer_Render_Item, max); - - Face_ID font_id = file->settings.font_id; - Font_Pointers font = system->font.get_pointers_by_id(font_id); - - f32 scroll_x = view->edit_pos->scroll.scroll_x; - f32 scroll_y = view->edit_pos->scroll.scroll_y; - - // NOTE(allen): For now we will temporarily adjust scroll_y to try - // to prevent the view moving around until floating sections are added - // to the gui system. - scroll_y += view->widget_height; - - Full_Cursor render_cursor; - if (!file->settings.unwrapped_lines){ - render_cursor = view_compute_cursor(system, view, seek_wrapped_xy(0, scroll_y, 0), 1); - } - else{ - render_cursor = view_compute_cursor(system, view, seek_unwrapped_xy(0, scroll_y, 0), 1); - } - - view->edit_pos->scroll_i = render_cursor.pos; - - i32 count = 0; - { - b32 wrapped = !file->settings.unwrapped_lines; - - Buffer_Render_Params params; - params.buffer = &file->state.buffer; - params.items = items; - params.max = max; - params.count = &count; - params.port_x = (f32)rect.x0 + left_side_space; - params.port_y = (f32)rect.y0; - params.clip_w = view_width(view) - left_side_space; - params.scroll_x = scroll_x; - params.scroll_y = scroll_y; - params.width = max_x; - params.height = (f32)max_y; - params.start_cursor = render_cursor; - params.wrapped = wrapped; - params.system = system; - params.font = font; - params.virtual_white = file->settings.virtual_white; - params.wrap_slashes = file->settings.wrap_indicator; - - Buffer_Render_State state = {0}; - Buffer_Layout_Stop stop = {0}; - - f32 line_shift = 0.f; - b32 do_wrap = 0; - i32 wrap_unit_end = 0; - - b32 first_wrap_determination = 1; - i32 wrap_array_index = 0; - - do{ - stop = buffer_render_data(&state, params, line_shift, do_wrap, wrap_unit_end); - switch (stop.status){ - case BLStatus_NeedWrapDetermination: - { - if (first_wrap_determination){ - wrap_array_index = binary_search(file->state.wrap_positions, stop.pos, 0, file->state.wrap_position_count); - ++wrap_array_index; - if (file->state.wrap_positions[wrap_array_index] == stop.pos){ - do_wrap = 1; - wrap_unit_end = file->state.wrap_positions[wrap_array_index]; - } - else{ - do_wrap = 0; - wrap_unit_end = file->state.wrap_positions[wrap_array_index]; - } - first_wrap_determination = 0; - } - else{ - Assert(stop.pos == wrap_unit_end); - do_wrap = 1; - ++wrap_array_index; - wrap_unit_end = file->state.wrap_positions[wrap_array_index]; - } - }break; - - case BLStatus_NeedWrapLineShift: - case BLStatus_NeedLineShift: - { - line_shift = file->state.line_indents[stop.wrap_line_index]; - }break; - } - }while(stop.status != BLStatus_Finished); - } - - i32 cursor_begin = 0, cursor_end = 0; - u32 cursor_color = 0, at_cursor_color = 0; - if (view->file_data.show_temp_highlight){ - cursor_begin = view->file_data.temp_highlight.pos; - cursor_end = view->file_data.temp_highlight_end_pos; - cursor_color = style->main.highlight_color; - at_cursor_color = style->main.at_highlight_color; - } - else{ - cursor_begin = view->edit_pos->cursor.pos; - cursor_end = cursor_begin + 1; - cursor_color = style->main.cursor_color; - at_cursor_color = style->main.at_cursor_color; - } - - i32 token_i = 0; - u32 main_color = style->main.default_color; - u32 special_color = style->main.special_character_color; - u32 ghost_color = style->main.ghost_character_color; - if (tokens_use){ - Cpp_Get_Token_Result result = cpp_get_token(token_array, items->index); - main_color = *style_get_color(style, token_array.tokens[result.token_index]); - token_i = result.token_index + 1; - } - - u32 mark_color = style->main.mark_color; - Buffer_Render_Item *item = items; - Buffer_Render_Item *item_end = item + count; - i32 prev_ind = -1; - u32 highlight_color = 0; - u32 highlight_this_color = 0; - - for (; item < item_end; ++item){ - i32 ind = item->index; - highlight_this_color = 0; - if (tokens_use && ind != prev_ind){ - Cpp_Token current_token = token_array.tokens[token_i-1]; - - if (token_i < token_array.count){ - if (ind >= token_array.tokens[token_i].start){ - for (;token_i < token_array.count && ind >= token_array.tokens[token_i].start; ++token_i){ - main_color = *style_get_color(style, token_array.tokens[token_i]); - current_token = token_array.tokens[token_i]; - } - } - else if (ind >= current_token.start + current_token.size){ - main_color = style->main.default_color; - } - } - - if (current_token.type == CPP_TOKEN_JUNK && ind >= current_token.start && ind < current_token.start + current_token.size){ - highlight_color = style->main.highlight_junk_color; - } - else{ - highlight_color = 0; - } - } - - u32 char_color = main_color; - if (item->flags & BRFlag_Special_Character){ - char_color = special_color; - } - else if (item->flags & BRFlag_Ghost_Character){ - char_color = ghost_color; - } - - f32_Rect char_rect = f32R(item->x0, item->y0, item->x1, item->y1); - - if (view->file_data.show_whitespace && highlight_color == 0 && codepoint_is_whitespace(item->codepoint)){ - highlight_this_color = style->main.highlight_white_color; - } - else{ - highlight_this_color = highlight_color; - } - - if (cursor_begin <= ind && ind < cursor_end && (ind != prev_ind || cursor_begin < ind)){ - if (is_active){ - draw_rectangle(target, char_rect, cursor_color); - char_color = at_cursor_color; - } - else{ - if (!view->file_data.show_temp_highlight){ - draw_rectangle_outline(target, char_rect, cursor_color); - } - } - } - else if (highlight_this_color){ - draw_rectangle(target, char_rect, highlight_this_color); - } - - u32 fade_color = 0xFFFF00FF; - f32 fade_amount = 0.f; - - if (file->state.paste_effect.seconds_down > 0.f && - file->state.paste_effect.start <= ind && - ind < file->state.paste_effect.end){ - fade_color = file->state.paste_effect.color; - fade_amount = file->state.paste_effect.seconds_down; - fade_amount /= file->state.paste_effect.seconds_max; - } - - char_color = color_blend(char_color, fade_amount, fade_color); - - if (ind == view->edit_pos->mark && prev_ind != ind){ - draw_rectangle_outline(target, char_rect, mark_color); - } - if (item->codepoint != 0){ - draw_font_glyph(target, font_id, item->codepoint, item->x0, item->y0, char_color); - } - prev_ind = ind; - } - - end_temp_memory(temp); - - return(0); -} - -internal void -draw_text_field(System_Functions *system, Render_Target *target, View *view, Models *models, Face_ID font_id, i32_Rect rect, String p, String t){ - Style *style = main_style(models); - - u32 back_color = style->main.margin_color; - u32 text1_color = style->main.default_color; - u32 text2_color = style->main.file_info_style.pop1_color; - - i32 x = rect.x0; - i32 y = rect.y0 + 2; - - if (target){ - draw_rectangle(target, rect, back_color); - x = ceil32(draw_string(system, target, font_id, p, x, y, text2_color)); - draw_string(system, target, font_id, t, x, y, text1_color); - } -} - -internal void -draw_text_with_cursor(System_Functions *system, Render_Target *target, View *view, Models *models, Face_ID font_id, i32_Rect rect, String s, i32 pos){ - Style *style = main_style(models); - - u32 back_color = style->main.margin_color; - u32 text_color = style->main.default_color; - u32 cursor_color = style->main.cursor_color; - u32 at_cursor_color = style->main.at_cursor_color; - - f32 x = (f32)rect.x0; - i32 y = rect.y0 + 2; - - if (target){ - draw_rectangle(target, rect, back_color); - - if (pos >= 0 && pos < s.size){ - Font_Pointers font = system->font.get_pointers_by_id(font_id); - - String part1 = substr(s, 0, pos); - String part2 = substr(s, pos, 1); - String part3 = substr(s, pos+1, s.size-pos-1); - - x = draw_string(system, target, font_id, part1, floor32(x), y, text_color); - - f32 adv = font_get_glyph_advance(system, font.settings, font.metrics, font.pages, s.str[pos]); - i32_Rect cursor_rect; - cursor_rect.x0 = floor32(x); - cursor_rect.x1 = floor32(x) + ceil32(adv); - cursor_rect.y0 = y; - cursor_rect.y1 = y + view->line_height; - draw_rectangle(target, cursor_rect, cursor_color); - x = draw_string(system, target, font_id, part2, floor32(x), y, at_cursor_color); - - draw_string(system, target, font_id, part3, floor32(x), y, text_color); - } - else{ - draw_string(system, target, font_id, s, floor32(x), y, text_color); - } - } -} - -internal void -draw_file_bar(System_Functions *system, Render_Target *target, View *view, Models *models, Editing_File *file, i32_Rect rect){ - File_Bar bar; - Style *style = main_style(models); - Interactive_Style bar_style = style->main.file_info_style; - - u32 back_color = bar_style.bar_color; - u32 base_color = bar_style.base_color; - u32 pop1_color = bar_style.pop1_color; - u32 pop2_color = bar_style.pop2_color; - - bar.rect = rect; - - if (target){ - bar.font_id = file->settings.font_id; - bar.pos_x = (f32)bar.rect.x0; - bar.pos_y = (f32)bar.rect.y0; - bar.text_shift_y = 2; - bar.text_shift_x = 0; - - draw_rectangle(target, bar.rect, back_color); - - Assert(file); - - intbar_draw_string(system, target, &bar, file->unique_name.name, base_color); - intbar_draw_string(system, target, &bar, make_lit_string(" -"), base_color); - - if (file->is_loading){ - intbar_draw_string(system, target, &bar, make_lit_string(" loading"), base_color); - } - else{ - char bar_space[526]; - String bar_text = make_fixed_width_string(bar_space); - append_ss (&bar_text, make_lit_string(" L#")); - append_int_to_str(&bar_text, view->edit_pos->cursor.line); - append_ss (&bar_text, make_lit_string(" C#")); - append_int_to_str(&bar_text, view->edit_pos->cursor.character); - - append_ss(&bar_text, make_lit_string(" -")); - - if (file->settings.dos_write_mode){ - append_ss(&bar_text, make_lit_string(" dos")); - } - else{ - append_ss(&bar_text, make_lit_string(" nix")); - } - - intbar_draw_string(system, target, &bar, bar_text, base_color); - - - if (file->state.still_lexing){ - intbar_draw_string(system, target, &bar, make_lit_string(" parsing"), pop1_color); - } - - if (!file->settings.unimportant){ - switch (file_get_sync(file)){ - case DirtyState_UnloadedChanges: - { - local_persist String out_of_sync = make_lit_string(" !"); - intbar_draw_string(system, target, &bar, out_of_sync, pop2_color); - }break; - - case DirtyState_UnsavedChanges: - { - local_persist String out_of_sync = make_lit_string(" *"); - intbar_draw_string(system, target, &bar, out_of_sync, pop2_color); - }break; - } - } - } - } -} - -u32 -get_margin_color(i32 active_level, Style *style){ - u32 margin = 0xFFFFFFFF; - - switch (active_level){ - default: - margin = style->main.list_item_color; - break; - - case 1: case 2: - margin = style->main.list_item_hover_color; - break; - - case 3: case 4: - margin = style->main.list_item_active_color; - break; - } - - return(margin); -} - -internal void -draw_color_button(System_Functions *system, GUI_Target *gui_target, Render_Target *target, View *view, Face_ID font_id, i32_Rect rect, GUI_id id, u32 fore, u32 back, String text){ - i32 active_level = gui_active_level(gui_target, id); - - if (active_level > 0){ - Swap(u32, back, fore); - } - - draw_rectangle(target, rect, back); - draw_string(system, target, font_id, text, rect.x0, rect.y0 + 1, fore); -} - -internal void -draw_font_button(System_Functions *system, GUI_Target *gui_target, Render_Target *target, View *view, Models *models, i32_Rect rect, GUI_id id, Face_ID font_id, String text){ - Style *style = main_style(models); - - i32 active_level = gui_active_level(gui_target, id); - - u32 margin_color = get_margin_color(active_level, style); - u32 back_color = style->main.back_color; - u32 text_color = style->main.default_color; - - draw_rectangle(target, rect, back_color); - draw_rectangle_outline(target, rect, margin_color); - draw_string(system, target, font_id, text, rect.x0, rect.y0 + 1, text_color); -} - -internal void -draw_fat_option_block(System_Functions *system, GUI_Target *gui_target, Render_Target *target, View *view, Models *models, Face_ID font_id, i32_Rect rect, GUI_id id, String text, String pop, i8 checkbox = -1){ - Style *style = main_style(models); - - i32 active_level = gui_active_level(gui_target, id); - - i32_Rect inner = get_inner_rect(rect, 3); - - u32 margin = get_margin_color(active_level, style); - u32 back = style->main.back_color; - u32 text_color = style->main.default_color; - u32 pop_color = style->main.file_info_style.pop2_color; - - i32 h = view->line_height; - i32 x = inner.x0 + 3; - i32 y = inner.y0 + h/2 - 1; - - draw_rectangle(target, inner, back); - draw_margin(target, rect, inner, margin); - - if (checkbox != -1){ - u32 checkbox_color = style->main.margin_active_color; - i32_Rect checkbox_rect = get_inner_rect(inner, (inner.y1 - inner.y0 - h)/2); - checkbox_rect.x1 = checkbox_rect.x0 + (checkbox_rect.y1 - checkbox_rect.y0); - - if (checkbox == 0){ - draw_rectangle_outline(target, checkbox_rect, checkbox_color); - } - else{ - draw_rectangle(target, checkbox_rect, checkbox_color); - } - - x = checkbox_rect.x1 + 3; - } - - x = ceil32(draw_string(system, target, font_id, text, x, y, text_color)); - draw_string(system, target, font_id, pop, x, y, pop_color); -} - -internal void -draw_button(System_Functions *system, GUI_Target *gui_target, Render_Target *target, View *view, Models *models, Face_ID font_id, i32_Rect rect, GUI_id id, String text){ - Style *style = main_style(models); - - i32 active_level = gui_active_level(gui_target, id); - - i32_Rect inner = get_inner_rect(rect, 3); - - u32 margin = style->main.default_color; - u32 back = get_margin_color(active_level, style); - u32 text_color = style->main.default_color; - - i32 h = view->line_height; - i32 y = inner.y0 + h/2 - 1; - - i32 w = (i32)font_string_width(system, target, font_id, text); - i32 x = (inner.x1 + inner.x0 - w)/2; - - draw_rectangle(target, inner, back); - draw_rectangle_outline(target, inner, margin); - - draw_string(system, target, font_id, text, x, y, text_color); -} - -internal void -draw_style_preview(System_Functions *system, GUI_Target *gui_target, Render_Target *target, View *view, Models *models, Face_ID font_id, i32_Rect rect, GUI_id id, Style *style){ - i32 active_level = gui_active_level(gui_target, id); - char font_name_space[256]; - String font_name = make_fixed_width_string(font_name_space); - font_name.size = system->font.get_name_by_id(font_id, font_name.str, font_name.memory_size); - Font_Pointers font = system->font.get_pointers_by_id(font_id); - - i32_Rect inner = get_inner_rect(rect, 3); - - u32 margin_color = get_margin_color(active_level, style); - u32 back = style->main.back_color; - u32 text_color = style->main.default_color; - u32 keyword_color = style->main.keyword_color; - u32 int_constant_color = style->main.int_constant_color; - u32 comment_color = style->main.comment_color; - - draw_margin(target, rect, inner, margin_color); - draw_rectangle(target, inner, back); - - i32 y = inner.y0; - i32 x = inner.x0; - x = ceil32(draw_string(system, target, font_id, style->name.str, x, y, text_color)); - i32 font_x = (i32)(inner.x1 - font_string_width(system, target, font_id, font_name)); - if (font_x > x + 10){ - draw_string(system, target, font_id, font_name, font_x, y, text_color); - } - - i32 height = font.metrics->height; - x = inner.x0; - y += height; - x = ceil32(draw_string(system, target, font_id, "if", x, y, keyword_color)); - x = ceil32(draw_string(system, target, font_id, "(x < ", x, y, text_color)); - x = ceil32(draw_string(system, target, font_id, "0", x, y, int_constant_color)); - x = ceil32(draw_string(system, target, font_id, ") { x = ", x, y, text_color)); - x = ceil32(draw_string(system, target, font_id, "0", x, y, int_constant_color)); - x = ceil32(draw_string(system, target, font_id, "; } ", x, y, text_color)); - x = ceil32(draw_string(system, target, font_id, "// comment", x, y, comment_color)); - - x = inner.x0; - y += height; - draw_string(system, target, font_id, "[] () {}; * -> +-/ <>= ! && || % ^", x, y, text_color); -} - -internal i32 -do_render_file_view(System_Functions *system, View *view, Models *models, GUI_Scroll_Vars *scroll, View *active, i32_Rect rect, b32 is_active, Render_Target *target, Input_Summary *user_input){ - - Editing_File *file = view->file_data.file; - Assert(file != 0); - - i32 result = 0; - - GUI_Session gui_session = {0}; - GUI_Header *h = 0; - GUI_Target *gui_target = &view->gui_target; - GUI_Interpret_Result interpret_result = {0}; - - f32 v = {0}; - i32 max_y = view_compute_max_target_y(view); - - Face_ID font_id = file->settings.font_id; - if (gui_target->push.pos > 0){ - gui_session_init(&gui_session, gui_target, rect, view->line_height); - - v = view_get_scroll_y(view); - - i32_Rect clip_rect = rect; - draw_push_clip(target, clip_rect); - - for (h = (GUI_Header*)gui_target->push.base; - h->type; - h = NextHeader(h)){ - interpret_result = gui_interpret(gui_target, &gui_session, h, - *scroll, view->scroll_region, max_y); - - if (interpret_result.has_info){ - if (gui_session.clip_y > clip_rect.y0){ - clip_rect.y0 = gui_session.clip_y; - draw_change_clip(target, clip_rect); - } - - switch (h->type){ - case guicom_top_bar: - { - draw_file_bar(system, target, view, models, file, gui_session.rect); - }break; - - case guicom_file: - { - if (file_is_ready(file)){ - result = draw_file_loaded(system, view, models, gui_session.rect, is_active, target); - } - }break; - - case guicom_text_field: - { - void *ptr = (h+1); - String p = gui_read_string(&ptr); - String t = gui_read_string(&ptr); - draw_text_field(system, target, view, models, font_id, gui_session.rect, p, t); - }break; - - case guicom_text_with_cursor: - { - void *ptr = (h+1); - String s = gui_read_string(&ptr); - i32 pos = gui_read_integer(&ptr); - - draw_text_with_cursor(system, target, view, models, font_id, gui_session.rect, s, pos); - }break; - - case guicom_color_button: - { - GUI_Interactive *b = (GUI_Interactive*)h; - void *ptr = (b + 1); - u32 fore = (u32)gui_read_integer(&ptr); - u32 back = (u32)gui_read_integer(&ptr); - String t = gui_read_string(&ptr); - - draw_color_button(system, gui_target, target, view, font_id, gui_session.rect, b->id, fore, back, t); - }break; - - case guicom_font_button: - { - GUI_Interactive *b = (GUI_Interactive*)h; - void *ptr = (b + 1); - Face_ID this_font_id = (Face_ID)gui_read_integer(&ptr); - String t = gui_read_string(&ptr); - - draw_font_button(system, gui_target, target, view, models, gui_session.rect, b->id, this_font_id, t); - }break; - - case guicom_file_option: - { - GUI_Interactive *b = (GUI_Interactive*)h; - void *ptr = (b + 1); - b32 folder = gui_read_integer(&ptr); - String f = gui_read_string(&ptr); - String m = gui_read_string(&ptr); - - if (folder){ - append_s_char(&f, '/'); - } - - draw_fat_option_block(system, gui_target, target, view, models, font_id, gui_session.rect, b->id, f, m); - }break; - - case guicom_style_preview: - { - GUI_Interactive *b = (GUI_Interactive*)h; - i32 style_index = *(i32*)(b + 1); - Style *style = get_style(models, style_index); - - draw_style_preview(system, gui_target, target, view, models, font_id, gui_session.rect, b->id, style); - }break; - - case guicom_fixed_option: - case guicom_fixed_option_checkbox: - { - GUI_Interactive *b = (GUI_Interactive*)h; - void *ptr = (b + 1); - String f = gui_read_string(&ptr); - String m = {0}; - i8 status = -1; - if (h->type == guicom_fixed_option_checkbox){ - gui_read_byte(&ptr); - status = (i8)gui_read_byte(&ptr); - } - - draw_fat_option_block(system, gui_target, target, view, models, font_id, gui_session.rect, b->id, f, m, status); - }break; - - case guicom_button: - { - GUI_Interactive *b = (GUI_Interactive*)h; - void *ptr = (b + 1); - String t = gui_read_string(&ptr); - - draw_button(system, gui_target, target, view, models, font_id, gui_session.rect, b->id, t); - }break; - - case guicom_scrollable_bar: - { - Style *style = main_style(models); - - u32 back; - u32 outline; - - i32_Rect bar = gui_session.rect; - - back = style->main.back_color; - if (is_active){ - outline = style->main.margin_active_color; - } - else{ - outline = style->main.margin_color; - } - - draw_rectangle(target, bar, back); - draw_rectangle_outline(target, bar, outline); - }break; - - case guicom_scrollable_top: - case guicom_scrollable_slider: - case guicom_scrollable_bottom: - { - GUI_id id; - Style *style = main_style(models); - i32_Rect box = gui_session.rect; - - i32 active_level; - - u32 back; - u32 outline; - - switch (h->type){ - case guicom_scrollable_top: id = gui_id_scrollbar_top(); break; - case guicom_scrollable_bottom: id = gui_id_scrollbar_bottom(); break; - default: id = gui_id_scrollbar_slider(); break; - } - - active_level = gui_active_level(gui_target, id); - - switch (active_level){ - case 0: back = style->main.back_color; break; - case 1: back = style->main.margin_hover_color; break; - default: back = style->main.margin_active_color; break; - } - - if (is_active){ - outline = style->main.margin_active_color; - } - else{ - outline = style->main.margin_color; - } - - draw_rectangle(target, box, back); - draw_margin(target, box, get_inner_rect(box, 2), outline); - }break; - - case guicom_begin_scrollable_section: - clip_rect.x1 = Min(gui_session.scroll_region.x1, clip_rect.x1); - draw_push_clip(target, clip_rect); - break; - - case guicom_end_scrollable_section: - clip_rect = draw_pop_clip(target); - break; - } - } - } - - draw_pop_clip(target); - } - - return(result); -} - -inline void -file_view_free_buffers(View *view, Models *models){ - General_Memory *general = &models->mem.general; - general_memory_free(general, view->gui_mem); - view->gui_mem = 0; -} - -internal View_And_ID -live_set_alloc_view(Live_Views *live_set, Panel *panel, Models *models){ - View_And_ID result = {}; - - Assert(live_set->count < live_set->max); - ++live_set->count; - - result.view = live_set->free_sentinel.next; - result.id = (i32)(result.view - live_set->views); - Assert(result.id == result.view->persistent.id); - - dll_remove(result.view); - memset(get_view_body(result.view), 0, get_view_size()); - - result.view->in_use = 1; - panel->view = result.view; - result.view->panel = panel; - - init_query_set(&result.view->query_set); - - { - i32 gui_mem_size = KB(512); - void *gui_mem = general_memory_allocate(&models->mem.general, gui_mem_size + 8); - result.view->gui_mem = gui_mem; - gui_mem = advance_to_alignment(gui_mem); - result.view->gui_target.push = make_part(gui_mem, gui_mem_size); - } - - return(result); -} - -inline void -live_set_free_view(Live_Views *live_set, View *view, Models *models){ - Assert(live_set->count > 0); - --live_set->count; - file_view_free_buffers(view, models); - dll_insert(&live_set->free_sentinel, view); - view->in_use = 0; -} - -// BOTTOM - diff --git a/4ed_generated_style.h b/4ed_generated_style.h new file mode 100644 index 00000000..61a4fd5d --- /dev/null +++ b/4ed_generated_style.h @@ -0,0 +1,83 @@ +struct Interactive_Style{ +u32 bar_color; +u32 bar_active_color; +u32 base_color; +u32 pop1_color; +u32 pop2_color; +}; + +struct Style_Main_Data{ +u32 back_color; +u32 margin_color; +u32 margin_hover_color; +u32 margin_active_color; +u32 list_item_color; +u32 list_item_hover_color; +u32 list_item_active_color; +u32 cursor_color; +u32 at_cursor_color; +u32 highlight_color; +u32 at_highlight_color; +u32 mark_color; +u32 default_color; +u32 comment_color; +u32 keyword_color; +u32 str_constant_color; +u32 char_constant_color; +u32 int_constant_color; +u32 float_constant_color; +u32 bool_constant_color; +u32 preproc_color; +u32 include_color; +u32 special_character_color; +u32 ghost_character_color; +u32 highlight_junk_color; +u32 highlight_white_color; +u32 paste_color; +u32 undo_color; +u32 next_undo_color; +Interactive_Style file_info_style; +}; + +inline u32* +style_index_by_tag(Style_Main_Data *s, u32 tag){ +u32 *result = 0; +switch (tag){ +case Stag_Bar: result = &s->file_info_style.bar_color; break; +case Stag_Bar_Active: result = &s->file_info_style.bar_active_color; break; +case Stag_Base: result = &s->file_info_style.base_color; break; +case Stag_Pop1: result = &s->file_info_style.pop1_color; break; +case Stag_Pop2: result = &s->file_info_style.pop2_color; break; +case Stag_Back: result = &s->back_color; break; +case Stag_Margin: result = &s->margin_color; break; +case Stag_Margin_Hover: result = &s->margin_hover_color; break; +case Stag_Margin_Active: result = &s->margin_active_color; break; +case Stag_List_Item: result = &s->list_item_color; break; +case Stag_List_Item_Hover: result = &s->list_item_hover_color; break; +case Stag_List_Item_Active: result = &s->list_item_active_color; break; +case Stag_Cursor: result = &s->cursor_color; break; +case Stag_At_Cursor: result = &s->at_cursor_color; break; +case Stag_Highlight: result = &s->highlight_color; break; +case Stag_At_Highlight: result = &s->at_highlight_color; break; +case Stag_Mark: result = &s->mark_color; break; +case Stag_Default: result = &s->default_color; break; +case Stag_Comment: result = &s->comment_color; break; +case Stag_Keyword: result = &s->keyword_color; break; +case Stag_Str_Constant: result = &s->str_constant_color; break; +case Stag_Char_Constant: result = &s->char_constant_color; break; +case Stag_Int_Constant: result = &s->int_constant_color; break; +case Stag_Float_Constant: result = &s->float_constant_color; break; +case Stag_Bool_Constant: result = &s->bool_constant_color; break; +case Stag_Preproc: result = &s->preproc_color; break; +case Stag_Include: result = &s->include_color; break; +case Stag_Special_Character: result = &s->special_character_color; break; +case Stag_Ghost_Character: result = &s->ghost_character_color; break; +case Stag_Highlight_Junk: result = &s->highlight_junk_color; break; +case Stag_Highlight_White: result = &s->highlight_white_color; break; +case Stag_Paste: result = &s->paste_color; break; +case Stag_Undo: result = &s->undo_color; break; +case Stag_Next_Undo: result = &s->next_undo_color; break; +} +return(result); +} + diff --git a/4ed_gui.cpp b/4ed_gui.cpp index 1e77096c..5369a81d 100644 --- a/4ed_gui.cpp +++ b/4ed_gui.cpp @@ -9,17 +9,6 @@ // TOP -struct Query_Slot{ - Query_Slot *next; - Query_Bar *query_bar; -}; - -struct Query_Set{ - Query_Slot slots[8]; - Query_Slot *free_slot; - Query_Slot *used_slot; -}; - internal void init_query_set(Query_Set *set){ Query_Slot *slot = set->slots; @@ -63,12 +52,6 @@ free_query_slot(Query_Set *set, Query_Bar *match_bar){ } } -struct Super_Color{ - Vec4 hsla; - Vec4 rgba; - u32 *out; -}; - internal Super_Color super_color_create(u32 packed){ Super_Color result = {}; @@ -112,91 +95,6 @@ super_color_post_byte(Super_Color *color, i32 channel, u8 byte){ return packed; } -struct GUI_Target{ - Partition push; - - GUI_id active; - GUI_id mouse_hot; - GUI_id auto_hot; - GUI_id hover; - - // TODO(allen): Can we remove original yet? - GUI_Scroll_Vars scroll_original; - i32_Rect region_original; - - //GUI_Scroll_Vars scroll_updated; - i32_Rect region_updated; - - // TODO(allen): Would rather have a way of tracking this - // for more than one list. Perhaps just throw in a hash table? - // Or maybe this only needs to be tracked for the active list. - i32 list_max; - b32 has_list_index_position; - i32_Rect list_index_position; - i32 list_view_min; - i32 list_view_max; - - GUI_id scroll_id; - // TODO(allen): is currently ignored in the wheel code, reevaluate? - i32 delta; - b32 has_keys; - b32 animating; - b32 did_file; -}; - -struct GUI_Item_Update{ - i32 partition_point; - - b32 has_adjustment; - i32 adjustment_value; - - b32 has_index_position; - i32_Rect index_position; -}; - -struct GUI_Header{ - i32 type; - i32 size; -}; - -struct GUI_Interactive{ - GUI_Header h; - GUI_id id; -}; - -struct GUI_Edit{ - GUI_Header h; - GUI_id id; - void *out; -}; - -enum GUI_Command_Type{ - guicom_null, - guicom_begin_serial, - guicom_end_serial, - guicom_top_bar, - guicom_file, - guicom_text_field, - guicom_color_button, - guicom_font_button, - guicom_text_with_cursor, - guicom_begin_list, - guicom_end_list, - guicom_file_option, - guicom_fixed_option, - guicom_button, - guicom_fixed_option_checkbox, - guicom_style_preview, - guicom_scrollable, - guicom_scrollable_bar, - guicom_scrollable_top, - guicom_scrollable_slider, - guicom_scrollable_bottom, - guicom_scrollable_invisible, - guicom_begin_scrollable_section, - guicom_end_scrollable_section, -}; - inline b32 gui_id_eq(GUI_id id1, GUI_id id2){ b32 result = (id1.id[0] == id2.id[0] && id1.id[1] == id2.id[1]); @@ -720,38 +618,6 @@ gui_activate_scrolling(GUI_Target *target){ target->active = gui_id_scrollbar(); } -struct GUI_Section{ - i32 max_v, v, top_v; -}; - -struct GUI_List_Vars{ - b32 in_list; - i32 index; - i32 auto_hot; - i32 auto_activate; -}; - -struct GUI_Session{ - i32_Rect full_rect; - i32_Rect rect; - - i32 suggested_max_y; - i32 clip_y; - - i32 line_height; - b32 is_scrollable; - i32 scrollable_items_bottom; - - i32_Rect scroll_region; - i32_Rect scroll_rect; - f32 scroll_top, scroll_bottom; - - GUI_List_Vars list; - - GUI_Section sections[64]; - i32 t; -}; - #define GUIScrollbarWidth 16 // TODO(allen): We can probably totally get rid of this now. @@ -924,16 +790,6 @@ gui_read_out(void **ptr){ return(result); } -struct GUI_Interpret_Result{ - b32 has_info; - b32 auto_hot; - b32 auto_activate; - i32 screen_orientation; - - b32 has_region; - i32_Rect region; -}; - internal GUI_Interpret_Result gui_interpret(GUI_Target *target, GUI_Session *session, GUI_Header *h, GUI_Scroll_Vars vars, i32_Rect region, i32 max_y){ @@ -1212,11 +1068,6 @@ gui_interpret(GUI_Target *target, GUI_Session *session, GUI_Header *h, return(result); } -struct GUI_View_Jump{ - i32 view_min; - i32 view_max; -}; - internal GUI_View_Jump gui_compute_view_jump(i32_Rect scroll_region, i32_Rect position){ GUI_View_Jump jump = {0}; diff --git a/4ed_gui.h b/4ed_gui.h index 5b91baa2..f5045d8f 100644 --- a/4ed_gui.h +++ b/4ed_gui.h @@ -1,3 +1,13 @@ +/* + * Mr. 4th Dimention - Allen Webster + * + * 20.02.2016 + * + * GUI system for 4coder + * + */ + +// TOP #ifndef FCODER_GUI_H #define FCODER_GUI_H @@ -55,4 +65,156 @@ struct GUI_Functions{ GUI_File_Function *file; }; +struct Query_Slot{ + Query_Slot *next; + Query_Bar *query_bar; +}; + +struct Query_Set{ + Query_Slot slots[8]; + Query_Slot *free_slot; + Query_Slot *used_slot; +}; + +struct Super_Color{ + Vec4 hsla; + Vec4 rgba; + u32 *out; +}; + +struct GUI_Target{ + Partition push; + + GUI_id active; + GUI_id mouse_hot; + GUI_id auto_hot; + GUI_id hover; + + // TODO(allen): Can we remove original yet? + GUI_Scroll_Vars scroll_original; + i32_Rect region_original; + + //GUI_Scroll_Vars scroll_updated; + i32_Rect region_updated; + + // TODO(allen): Would rather have a way of tracking this + // for more than one list. Perhaps just throw in a hash table? + // Or maybe this only needs to be tracked for the active list. + i32 list_max; + b32 has_list_index_position; + i32_Rect list_index_position; + i32 list_view_min; + i32 list_view_max; + + GUI_id scroll_id; + // TODO(allen): is currently ignored in the wheel code, reevaluate? + i32 delta; + b32 has_keys; + b32 animating; + b32 did_file; +}; + +struct GUI_Item_Update{ + i32 partition_point; + + b32 has_adjustment; + i32 adjustment_value; + + b32 has_index_position; + i32_Rect index_position; +}; + +struct GUI_Header{ + i32 type; + i32 size; +}; + +struct GUI_Interactive{ + GUI_Header h; + GUI_id id; +}; + +struct GUI_Edit{ + GUI_Header h; + GUI_id id; + void *out; +}; + +enum GUI_Command_Type{ + guicom_null, + guicom_begin_serial, + guicom_end_serial, + guicom_top_bar, + guicom_file, + guicom_text_field, + guicom_color_button, + guicom_font_button, + guicom_text_with_cursor, + guicom_begin_list, + guicom_end_list, + guicom_file_option, + guicom_fixed_option, + guicom_button, + guicom_fixed_option_checkbox, + guicom_style_preview, + guicom_scrollable, + guicom_scrollable_bar, + guicom_scrollable_top, + guicom_scrollable_slider, + guicom_scrollable_bottom, + guicom_scrollable_invisible, + guicom_begin_scrollable_section, + guicom_end_scrollable_section, +}; + +struct GUI_Section{ + i32 max_v, v, top_v; +}; + +struct GUI_List_Vars{ + b32 in_list; + i32 index; + i32 auto_hot; + i32 auto_activate; +}; + +struct GUI_Session{ + i32_Rect full_rect; + i32_Rect rect; + + i32 suggested_max_y; + i32 clip_y; + + i32 line_height; + b32 is_scrollable; + i32 scrollable_items_bottom; + + i32_Rect scroll_region; + i32_Rect scroll_rect; + f32 scroll_top, scroll_bottom; + + GUI_List_Vars list; + + GUI_Section sections[64]; + i32 t; +}; + +struct GUI_Interpret_Result{ + b32 has_info; + b32 auto_hot; + b32 auto_activate; + i32 screen_orientation; + + b32 has_region; + i32_Rect region; +}; + +struct GUI_View_Jump{ + i32 view_min; + i32 view_max; +}; + #endif + +// BOTTOM + diff --git a/4ed_hot_directory.cpp b/4ed_hot_directory.cpp index 889bb6f4..602fd30f 100644 --- a/4ed_hot_directory.cpp +++ b/4ed_hot_directory.cpp @@ -9,14 +9,6 @@ // TOP -struct Hot_Directory{ - char string_space[256]; - char canon_dir_space[256]; - String string; - String canon_dir; - File_List file_list; -}; - internal void hot_directory_clean_end(Hot_Directory *hot_directory){ String *str = &hot_directory->string; diff --git a/4ed_hot_directory.h b/4ed_hot_directory.h new file mode 100644 index 00000000..7fa3a52f --- /dev/null +++ b/4ed_hot_directory.h @@ -0,0 +1,26 @@ +/* + * Mr. 4th Dimention - Allen Webster + * + * 24.01.2018 + * + * Buffer types + * + */ + +// TOP + +#if !defined(FRED_HOT_DIRECTORY_H) +#define FRED_HOT_DIRECTORY_H + +struct Hot_Directory{ + char string_space[256]; + char canon_dir_space[256]; + String string; + String canon_dir; + File_List file_list; +}; + +#endif + +// BOTTOM + diff --git a/4ed_linked_node_macros.h b/4ed_linked_node_macros.h index 6fef18cc..74afc7b2 100644 --- a/4ed_linked_node_macros.h +++ b/4ed_linked_node_macros.h @@ -9,22 +9,14 @@ // TOP -// NOTE(allen): These macros are setup to work on structs -// with a next and prev pointer where the type of the struct -// is the same as the type of the next/prev pointers. +#if !defined(FRED_LINKED_NODE_MACROS_H) +#define FRED_LINKED_NODE_MACROS_H #define dll_init_sentinel(s) (s)->next=(s),(s)->prev=(s) #define dll_insert(p,n) (n)->next=(p)->next,(n)->prev=(p),(p)->next=(n),(n)->next->prev=(n) #define dll_insert_back(p,n) (n)->prev=(p)->prev,(n)->next=(p),(p)->prev=(n),(n)->prev->next=(n) #define dll_remove(n) (n)->next->prev=(n)->prev,(n)->prev->next=(n)->next -// HACK(allen): I don't like this anymore, get rid of it. -// for(dll_items(iterator, sentinel_ptr)){...} -#define dll_items(it, st) ((it) = (st)->next); ((it) != (st)); ((it) = (it)->next) - -// NOTE(allen): These macros work on structs with a next -// pointer to the saem type as the containing struct. - #define sll_clear(f,l) (f)=(l)=0 #define sll_push(f,l,n) if((f)==0&&(l)==0){(f)=(l)=(n);}else{(l)->next=(n);(l)=(n);}(l)->next=0 #define sll_pop(f,l) if((f)!=(l)){(f)=(f)->next;}else{(f)=(l)=0;} @@ -33,9 +25,7 @@ #define sll_insert(p,v) do{ (v)->next=(p)->next; (p)->next = (v); }while(0) #define sll_remove(p,v) do{ Assert((p)->next == (v)); (p)->next = (v)->next; }while(0) -// HACK(allen): I don't like this anymore, get rid of it. -// for(sll_items(iterator, sentinel_ptr)){...} -#define sll_items(it, st) ((it) = (st)->next); ((it) != (st)); ((it) = (it)->next) +#endif // BOTTOM diff --git a/4ed_os_custom_api.h b/4ed_os_custom_api.h deleted file mode 100644 index ddcd7561..00000000 --- a/4ed_os_custom_api.h +++ /dev/null @@ -1 +0,0 @@ -struct Application_Links; diff --git a/4ed_style.cpp b/4ed_style.cpp deleted file mode 100644 index 4b798eaa..00000000 --- a/4ed_style.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Mr. 4th Dimention - Allen Webster - * - * 28.08.2015 - * - * Styles for 4coder - * - */ - -// TOP - -struct Style_Font{ - Face_ID font_id; -}; - -struct Style{ - char name_[24]; - String name; - Style_Main_Data main; -}; - -internal void -style_copy(Style *dst, Style *src){ - *dst = *src; - dst->name.str = dst->name_; -} - -internal void -style_set_name(Style *style, String name){ - i32 count = ArrayCount(style->name_); - style->name = make_string_cap(style->name_, 0, count - 1); - copy_ss(&style->name, name); - terminate_with_null(&style->name); -} - -struct Style_Library{ - Style styles[64]; - i32 count, max; -}; - -internal void -style_set_colors(Style *style, Theme *theme){ - for (u32 i = 0; i < Stag_COUNT; ++i){ - u32 *color_ptr = style_index_by_tag(&style->main, i); - *color_ptr = theme->colors[i]; - } -} - -internal void -style_add(Style_Library *library, Theme *theme, String name){ - if (library->count < library->max){ - Style *style = &library->styles[library->count++]; - style_set_colors(style, theme); - style_set_name(style, name); - } -} - -// BOTTOM - diff --git a/4ed_style.h b/4ed_style.h index 61a4fd5d..b3d19250 100644 --- a/4ed_style.h +++ b/4ed_style.h @@ -1,83 +1,49 @@ -struct Interactive_Style{ -u32 bar_color; -u32 bar_active_color; -u32 base_color; -u32 pop1_color; -u32 pop2_color; +/* + * Mr. 4th Dimention - Allen Webster + * + * 28.08.2015 + * + * Styles for 4coder + * + */ + +// TOP + +#if !defined(FRED_STYLE_H) +#define FRED_STYLE_H + +#include "4ed_generated_style.h" + +struct Style_Font{ + Face_ID font_id; }; -struct Style_Main_Data{ -u32 back_color; -u32 margin_color; -u32 margin_hover_color; -u32 margin_active_color; -u32 list_item_color; -u32 list_item_hover_color; -u32 list_item_active_color; -u32 cursor_color; -u32 at_cursor_color; -u32 highlight_color; -u32 at_highlight_color; -u32 mark_color; -u32 default_color; -u32 comment_color; -u32 keyword_color; -u32 str_constant_color; -u32 char_constant_color; -u32 int_constant_color; -u32 float_constant_color; -u32 bool_constant_color; -u32 preproc_color; -u32 include_color; -u32 special_character_color; -u32 ghost_character_color; -u32 highlight_junk_color; -u32 highlight_white_color; -u32 paste_color; -u32 undo_color; -u32 next_undo_color; -Interactive_Style file_info_style; +struct Style{ + char name_[24]; + String name; + Style_Main_Data main; }; -inline u32* -style_index_by_tag(Style_Main_Data *s, u32 tag){ -u32 *result = 0; -switch (tag){ -case Stag_Bar: result = &s->file_info_style.bar_color; break; -case Stag_Bar_Active: result = &s->file_info_style.bar_active_color; break; -case Stag_Base: result = &s->file_info_style.base_color; break; -case Stag_Pop1: result = &s->file_info_style.pop1_color; break; -case Stag_Pop2: result = &s->file_info_style.pop2_color; break; -case Stag_Back: result = &s->back_color; break; -case Stag_Margin: result = &s->margin_color; break; -case Stag_Margin_Hover: result = &s->margin_hover_color; break; -case Stag_Margin_Active: result = &s->margin_active_color; break; -case Stag_List_Item: result = &s->list_item_color; break; -case Stag_List_Item_Hover: result = &s->list_item_hover_color; break; -case Stag_List_Item_Active: result = &s->list_item_active_color; break; -case Stag_Cursor: result = &s->cursor_color; break; -case Stag_At_Cursor: result = &s->at_cursor_color; break; -case Stag_Highlight: result = &s->highlight_color; break; -case Stag_At_Highlight: result = &s->at_highlight_color; break; -case Stag_Mark: result = &s->mark_color; break; -case Stag_Default: result = &s->default_color; break; -case Stag_Comment: result = &s->comment_color; break; -case Stag_Keyword: result = &s->keyword_color; break; -case Stag_Str_Constant: result = &s->str_constant_color; break; -case Stag_Char_Constant: result = &s->char_constant_color; break; -case Stag_Int_Constant: result = &s->int_constant_color; break; -case Stag_Float_Constant: result = &s->float_constant_color; break; -case Stag_Bool_Constant: result = &s->bool_constant_color; break; -case Stag_Preproc: result = &s->preproc_color; break; -case Stag_Include: result = &s->include_color; break; -case Stag_Special_Character: result = &s->special_character_color; break; -case Stag_Ghost_Character: result = &s->ghost_character_color; break; -case Stag_Highlight_Junk: result = &s->highlight_junk_color; break; -case Stag_Highlight_White: result = &s->highlight_white_color; break; -case Stag_Paste: result = &s->paste_color; break; -case Stag_Undo: result = &s->undo_color; break; -case Stag_Next_Undo: result = &s->next_undo_color; break; -} -return(result); +internal void +style_copy(Style *dst, Style *src){ + *dst = *src; + dst->name.str = dst->name_; } +internal void +style_set_name(Style *style, String name){ + i32 count = ArrayCount(style->name_); + style->name = make_string_cap(style->name_, 0, count - 1); + copy_ss(&style->name, name); + terminate_with_null(&style->name); +} + +struct Style_Library{ + Style styles[64]; + i32 count, max; +}; + +#endif + +// BOTTOM + diff --git a/4ed_translation.cpp b/4ed_translation.cpp index 5523917c..4a2d102a 100644 --- a/4ed_translation.cpp +++ b/4ed_translation.cpp @@ -9,41 +9,6 @@ // TOP -#include "4ed_buffer_model.h" - -struct Translation_State{ - u8 fill_buffer[4]; - u32 fill_start_i; - u8 fill_i; - u8 fill_expected; -}; -global_const Translation_State null_buffer_translating_state = {0}; - -enum{ - TranLBH_None, - TranLBH_Rebuffer, - TranLBH_EmitAsCP, -}; -struct Translation_Byte_Description{ - u8 byte_class; - u8 last_byte_handler; - u8 prelim_emit_type; -}; - -struct Translation_Emit_Rule{ - u8 byte_class; - u8 last_byte_handler; - u8 emit_type; - - u32 codepoint; - u32 codepoint_length; -}; - -struct Translation_Emits{ - Buffer_Model_Step steps[5]; - u32 step_count; -}; - #define ERROR_BYTE (max_u8-1) #define CONTINUATION_BYTE max_u8 diff --git a/4ed_translation.h b/4ed_translation.h new file mode 100644 index 00000000..28b806a5 --- /dev/null +++ b/4ed_translation.h @@ -0,0 +1,51 @@ +/* + * Mr. 4th Dimention - Allen Webster + * + * 24.01.2018 + * + * Buffer types + * + */ + +// TOP + +#if !defined(FRED_TRANSLATION_H) +#define FRED_TRANSLATION_H + +struct Translation_State{ + u8 fill_buffer[4]; + u32 fill_start_i; + u8 fill_i; + u8 fill_expected; +}; +global_const Translation_State null_buffer_translating_state = {0}; + +enum{ + TranLBH_None, + TranLBH_Rebuffer, + TranLBH_EmitAsCP, +}; +struct Translation_Byte_Description{ + u8 byte_class; + u8 last_byte_handler; + u8 prelim_emit_type; +}; + +struct Translation_Emit_Rule{ + u8 byte_class; + u8 last_byte_handler; + u8 emit_type; + + u32 codepoint; + u32 codepoint_length; +}; + +struct Translation_Emits{ + Buffer_Model_Step steps[5]; + u32 step_count; +}; + +#endif + +// BOTTOM + diff --git a/4ed_undo.cpp b/4ed_undo.h similarity index 91% rename from 4ed_undo.cpp rename to 4ed_undo.h index f0e416b1..eb7a6fc6 100644 --- a/4ed_undo.cpp +++ b/4ed_undo.h @@ -1,17 +1,16 @@ /* * Mr. 4th Dimention - Allen Webster * - * 06.01.2017 + * 24.01.2018 * - * Undo subsystem for 4coder + * Buffer types * */ // TOP -// -// Undo Basics -// +#if !defined(FRED_UNDO_H) +#define FRED_UNDO_H enum Edit_Type{ ED_NORMAL, @@ -66,5 +65,6 @@ struct Undo_Data{ b32 current_block_normal; }; -// BOTTOM +#endif +// BOTTOM \ No newline at end of file diff --git a/4ed_view.cpp b/4ed_view.cpp index f90d284a..97deafa7 100644 --- a/4ed_view.cpp +++ b/4ed_view.cpp @@ -1,7 +1,7 @@ /* * Mr. 4th Dimention - Allen Webster * - * 17.07.2017 + * 19.08.2015 * * File editing view for 4coder. * @@ -9,161 +9,6388 @@ // TOP -#if !defined(FRED_VIEW_CPP) -#define FRED_VIEW_CPP +// TODO(allen): Switch over to using an i32 for these. +inline f32 +view_width(View *view){ + i32_Rect file_rect = view->transient.file_region; + f32 result = (f32)(file_rect.x1 - file_rect.x0); + return (result); +} -struct View_Persistent{ - i32 id; - Coroutine_Head *coroutine; - Event_Message message_passing_slot; -}; +inline f32 +view_height(View *view){ + i32_Rect file_rect = view->transient.file_region; + f32 result = (f32)(file_rect.y1 - file_rect.y0); + return (result); +} -struct File_Viewing_Data{ - Editing_File *file; - - Full_Cursor temp_highlight; - i32 temp_highlight_end_pos; - b32 show_temp_highlight; - - b32 show_whitespace; - b32 file_locked; -}; -global File_Viewing_Data null_file_viewing_data = {0}; - -enum Interactive_Action{ - IAct_Open, - IAct_New, - IAct_OpenOrNew, - IAct_Switch, - IAct_Kill, - IAct_Sure_To_Kill, - IAct_Sure_To_Close -}; - -enum Interactive_Interaction{ - IInt_Sys_File_List, - IInt_Live_File_List, - IInt_Sure_To_Kill, - IInt_Sure_To_Close -}; - -enum View_UI{ - VUI_None, - VUI_Theme, - VUI_Interactive, - VUI_Debug -}; - -enum Debug_Mode{ - DBG_Input, - DBG_Threads_And_Memory, - DBG_View_Inspection -}; - -enum Color_View_Mode{ - CV_Mode_Library, - CV_Mode_Font, - CV_Mode_Global_Font, - CV_Mode_Font_Editing, - CV_Mode_Global_Font_Editing, - CV_Mode_Adjusting, -}; - -struct Scroll_Context{ - Editing_File *file; - GUI_id scroll; - View_UI mode; -}; -inline b32 -context_eq(Scroll_Context a, Scroll_Context b){ - b32 result = false; - if (gui_id_eq(a.scroll, b.scroll)){ - if (a.file == b.file){ - if (a.mode == b.mode){ - result = true; - } - } +inline Vec2 +view_get_cursor_xy(View *view){ + Full_Cursor *cursor = 0; + if (view->transient.file_data.show_temp_highlight){ + cursor = &view->transient.file_data.temp_highlight; + } + else if (view->transient.edit_pos){ + cursor = &view->transient.edit_pos->cursor; + } + Assert(cursor != 0); + Vec2 result; + result.x = cursor->wrapped_x; + result.y = cursor->wrapped_y; + if (view->transient.file_data.file->settings.unwrapped_lines){ + result.x = cursor->unwrapped_x; + result.y = cursor->unwrapped_y; } return(result); } -struct Debug_Vars{ - i32 mode; - i32 inspecting_view_id; -}; -global_const Debug_Vars null_debug_vars = {0}; +inline Cursor_Limits +view_cursor_limits(View *view){ + Cursor_Limits limits = {0}; + + f32 line_height = (f32)view->transient.line_height; + f32 visible_height = view_height(view); + + limits.max = visible_height - line_height*3.f; + limits.min = line_height * 2; + + if (limits.max - limits.min <= line_height){ + if (visible_height >= line_height){ + limits.max = visible_height - line_height; + limits.min = -line_height; + } + else{ + limits.max = visible_height; + limits.min = -line_height; + } + } + + limits.max = (limits.max > 0)?(limits.max):(0); + limits.min = (limits.min > 0)?(limits.min):(0); + + limits.delta = clamp_top(line_height*3.f, (limits.max - limits.min)*.5f); + + return(limits); +} -struct View{ - View_Persistent persistent; +internal Full_Cursor +file_compute_cursor(System_Functions *system, Editing_File *file, Buffer_Seek seek, b32 return_hint){ + Font_Pointers font = system->font.get_pointers_by_id(file->settings.font_id); + Assert(font.valid); - View *next, *prev; - Panel *panel; - b32 in_use; - i32 map; - //Command_Map *map; + Full_Cursor result = {0}; - File_Viewing_Data file_data; + Buffer_Cursor_Seek_Params params; + params.buffer = &file->state.buffer; + params.seek = seek; + params.system = system; + params.font = font; + params.wrap_line_index = file->state.wrap_line_index; + params.character_starts = file->state.character_starts; + params.virtual_white = file->settings.virtual_white; + params.return_hint = return_hint; + params.cursor_out = &result; - i32_Rect file_region_prev; - i32_Rect file_region; + Buffer_Cursor_Seek_State state = {0}; + Buffer_Layout_Stop stop = {0}; - i32_Rect scroll_region; - File_Edit_Positions *edit_pos; + i32 size = buffer_size(params.buffer); - View_UI showing_ui; - GUI_Target gui_target; - void *gui_mem; - GUI_Scroll_Vars gui_scroll; - i32 gui_max_y; - i32 list_i; + f32 line_shift = 0.f; + b32 do_wrap = 0; + i32 wrap_unit_end = 0; - b32 hide_scrollbar; - b32 hide_file_bar; + b32 first_wrap_determination = 1; + i32 wrap_array_index = 0; - // interactive stuff - Interactive_Interaction interaction; - Interactive_Action action; + do{ + stop = buffer_cursor_seek(&state, params, line_shift, do_wrap, wrap_unit_end); + switch (stop.status){ + case BLStatus_NeedWrapDetermination: + { + if (stop.pos >= size){ + do_wrap = 0; + wrap_unit_end = max_i32; + } + else{ + if (first_wrap_determination){ + wrap_array_index = binary_search(file->state.wrap_positions, stop.pos, 0, file->state.wrap_position_count); + ++wrap_array_index; + if (file->state.wrap_positions[wrap_array_index] == stop.pos){ + do_wrap = 1; + wrap_unit_end = file->state.wrap_positions[wrap_array_index]; + } + else{ + do_wrap = 0; + wrap_unit_end = file->state.wrap_positions[wrap_array_index]; + } + first_wrap_determination = 0; + } + else{ + Assert(stop.pos == wrap_unit_end); + do_wrap = 1; + ++wrap_array_index; + wrap_unit_end = file->state.wrap_positions[wrap_array_index]; + } + } + }break; + + case BLStatus_NeedWrapLineShift: + case BLStatus_NeedLineShift: + { + line_shift = file->state.line_indents[stop.wrap_line_index]; + }break; + } + }while(stop.status != BLStatus_Finished); - char dest_[256]; - String dest; + return(result); +} + +inline i32 +view_compute_max_target_y(View *view){ + i32 line_height = view->transient.line_height; + Editing_File *file = view->transient.file_data.file; + Gap_Buffer *buffer = &file->state.buffer; + i32 lowest_line = buffer->line_count; + if (!file->settings.unwrapped_lines){ + lowest_line = file->state.wrap_line_index[buffer->line_count]; + } + f32 height = clamp_bottom((f32)line_height, view_height(view)); + f32 max_target_y = clamp_bottom(0.f, ((lowest_line + 0.5f)*line_height) - height*0.5f); + return(ceil32(max_target_y)); +} + +internal b32 +view_move_view_to_cursor(View *view, GUI_Scroll_Vars *scroll, b32 center_view){ + b32 result = 0; + f32 max_x = view_width(view); + i32 max_y = view_compute_max_target_y(view); - b32 changed_context_in_step; + Vec2 cursor = view_get_cursor_xy(view); - // theme stuff - View *hot_file_view; - u32 *palette; - Color_View_Mode color_mode; - Face_ID font_edit_id; - Super_Color color; - b32 p4c_only; - Style_Library inspecting_styles; - b8 import_export_check[64]; - i32 import_file_id; - i32 current_color_editing; - i32 color_cursor; + GUI_Scroll_Vars scroll_vars = *scroll; + i32 target_x = scroll_vars.target_x; + i32 target_y = scroll_vars.target_y; - // misc + Cursor_Limits limits = view_cursor_limits(view); - // TODO(allen): Can we burn line_height to the ground now? - // It's what I've always wanted!!!! :D - i32 line_height; + if (cursor.y > target_y + limits.max){ + if (center_view){ + target_y = round32(cursor.y - limits.max*.5f); + } + else{ + target_y = ceil32(cursor.y - limits.max + limits.delta); + } + } + if (cursor.y < target_y + limits.min){ + if (center_view){ + target_y = round32(cursor.y - limits.max*.5f); + } + else{ + target_y = floor32(cursor.y - limits.delta - limits.min); + } + } - // TODO(allen): Do I still use mode? - Query_Set query_set; - f32 widget_height; + target_y = clamp(0, target_y, max_y); - b32 reinit_scrolling; + if (cursor.x >= target_x + max_x){ + target_x = ceil32(cursor.x - max_x/2); + } + else if (cursor.x < target_x){ + target_x = floor32(Max(0, cursor.x - max_x/2)); + } - Debug_Vars debug_vars; + if (target_x != scroll_vars.target_x || target_y != scroll_vars.target_y){ + scroll->target_x = target_x; + scroll->target_y = target_y; + result = 1; + } + + return(result); +} + +internal b32 +view_move_cursor_to_view(System_Functions *system, View *view, GUI_Scroll_Vars scroll, Full_Cursor *cursor, f32 preferred_x){ + b32 result = false; + + if (view->transient.edit_pos){ + i32 line_height = view->transient.line_height; + f32 old_cursor_y = cursor->wrapped_y; + Editing_File *file = view->transient.file_data.file; + if (file->settings.unwrapped_lines){ + old_cursor_y = cursor->unwrapped_y; + } + f32 cursor_y = old_cursor_y; + f32 target_y = scroll.target_y + view->transient.widget_height; + + Cursor_Limits limits = view_cursor_limits(view); + + if (cursor_y > target_y + limits.max){ + cursor_y = target_y + limits.max; + } + if (target_y != 0 && cursor_y < target_y + limits.min){ + cursor_y = target_y + limits.min; + } + + if (cursor_y != old_cursor_y){ + if (cursor_y > old_cursor_y){ + cursor_y += line_height; + } + else{ + cursor_y -= line_height; + } + + Buffer_Seek seek = seek_xy(preferred_x, cursor_y, false, file->settings.unwrapped_lines); + *cursor = file_compute_cursor(system, file, seek, false); + + result = true; + } + } + + return(result); +} + +internal void +view_set_cursor(View *view, Full_Cursor cursor, b32 set_preferred_x, b32 unwrapped_lines){ + if (edit_pos_move_to_front(view->transient.file_data.file, view->transient.edit_pos)){ + edit_pos_set_cursor(view->transient.edit_pos, cursor, set_preferred_x, unwrapped_lines); + GUI_Scroll_Vars scroll = view->transient.edit_pos->scroll; + if (view_move_view_to_cursor(view, &scroll, 0)){ + view->transient.edit_pos->scroll = scroll; + } + } +} + +internal void +view_set_scroll(System_Functions *system, View *view, GUI_Scroll_Vars scroll){ + if (edit_pos_move_to_front(view->transient.file_data.file, view->transient.edit_pos)){ + edit_pos_set_scroll(view->transient.edit_pos, scroll); + Full_Cursor cursor = view->transient.edit_pos->cursor; + if (view_move_cursor_to_view(system, view, view->transient.edit_pos->scroll, &cursor, view->transient.edit_pos->preferred_x)){ + view->transient.edit_pos->cursor = cursor; + } + } +} + +internal void +view_set_cursor_and_scroll(View *view, Full_Cursor cursor, b32 set_preferred_x, b32 unwrapped_lines, GUI_Scroll_Vars scroll){ + File_Edit_Positions *edit_pos = view->transient.edit_pos; + if (edit_pos_move_to_front(view->transient.file_data.file, edit_pos)){ + edit_pos_set_cursor(edit_pos, cursor, set_preferred_x, unwrapped_lines); + edit_pos_set_scroll(edit_pos, scroll); + edit_pos->last_set_type = EditPos_None; + } +} + +inline void +view_set_temp_highlight(System_Functions *system, View *view, i32 pos, i32 end_pos){ + Editing_File *file = view->transient.file_data.file; + Assert(file != 0); + view->transient.file_data.temp_highlight = file_compute_cursor(system, file, seek_pos(pos), 0); + view->transient.file_data.temp_highlight_end_pos = end_pos; + view->transient.file_data.show_temp_highlight = 1; + + view_set_cursor(view, view->transient.file_data.temp_highlight, 0, file->settings.unwrapped_lines); +} + +inline u32 +view_lock_flags(View *view){ + u32 result = AccessOpen; + File_Viewing_Data *data = &view->transient.file_data; + if (view->transient.showing_ui != VUI_None){ + result |= AccessHidden; + } + if (data->file_locked || + (data->file && data->file->settings.read_only)){ + result |= AccessProtected; + } + return(result); +} + +internal b32 +file_is_viewed(Editing_Layout *layout, Editing_File *file){ + b32 is_viewed = false; + for (Panel *panel = layout->used_sentinel.next; + panel != &layout->used_sentinel; + panel = panel->next){ + View *view = panel->view; + if (view->transient.file_data.file == file){ + is_viewed = true; + break; + } + } + return(is_viewed); +} + +internal b32 +save_file_to_name(System_Functions *system, Models *models, Editing_File *file, char *filename){ + b32 result = false; + b32 using_actual_filename = false; + + if (filename == 0){ + terminate_with_null(&file->canon.name); + filename = file->canon.name.str; + using_actual_filename = true; + } + + if (filename != 0){ + Mem_Options *mem = &models->mem; + if (models->hook_save_file){ + models->hook_save_file(&models->app_links, file->id.id); + } + + i32 max = 0, size = 0; + b32 dos_write_mode = file->settings.dos_write_mode; + char *data = 0; + Gap_Buffer *buffer = &file->state.buffer; + + if (dos_write_mode){ + max = buffer_size(buffer) + buffer->line_count + 1; + } + else{ + max = buffer_size(buffer); + } + + b32 used_general = 0; + Temp_Memory temp = begin_temp_memory(&mem->part); + char empty = 0; + if (max == 0){ + data = ∅ + } + else{ + data = (char*)push_array(&mem->part, char, max); + if (!data){ + used_general = 1; + data = (char*)general_memory_allocate(&mem->general, max); + } + } + Assert(data != 0); + + if (dos_write_mode){ + size = buffer_convert_out(buffer, data, max); + } + else{ + size = max; + buffer_stringify(buffer, 0, size, data); + } + + if (!using_actual_filename && file->canon.name.str != 0){ + char space[512]; + u32 length = str_size(filename); + system->get_canonical(filename, length, space, sizeof(space)); + + char *source_path = file->canon.name.str; + if (match(space, source_path)){ + using_actual_filename = true; + } + } + + result = system->save_file(filename, data, size); + + if (result && using_actual_filename){ + file->state.ignore_behind_os = 1; + } + + file_mark_clean(file); + + if (used_general){ + general_memory_free(&mem->general, data); + } + end_temp_memory(temp); + + file->state.dirty = DirtyState_UpToDate; + } + + return(result); +} + +inline b32 +save_file(System_Functions *system, Models *models, Editing_File *file){ + b32 result = save_file_to_name(system, models, file, 0); + return(result); +} + +internal i32 +file_grow_starts_as_needed(General_Memory *general, Gap_Buffer *buffer, i32 additional_lines){ + b32 result = GROW_NOT_NEEDED; + i32 max = buffer->line_max; + i32 count = buffer->line_count; + i32 target_lines = count + additional_lines; + + if (target_lines > max || max == 0){ + max = l_round_up_i32(target_lines + max, KB(1)); + + i32 *new_lines = (i32*)general_memory_reallocate(general, buffer->line_starts, sizeof(i32)*count, sizeof(f32)*max); + + if (new_lines){ + result = GROW_SUCCESS; + buffer->line_max = max; + buffer->line_starts = new_lines; + } + else{ + result = GROW_FAILED; + } + } + + return(result); +} + +internal void +file_update_cursor_positions(System_Functions *system, Models *models, Editing_File *file){ + Editing_Layout *layout = &models->layout; + + for (Panel *panel = layout->used_sentinel.next; + panel != &layout->used_sentinel; + panel = panel->next){ + View *view = panel->view; + if (view->transient.file_data.file != file){ + continue; + } + + i32 pos = 0; + if (view->transient.file_data.show_temp_highlight){ + pos = view->transient.file_data.temp_highlight.pos; + } + else if (view->transient.edit_pos != 0){ + pos = view->transient.edit_pos->cursor.pos; + } + + if (!view->transient.file_data.show_temp_highlight){ + Editing_File *file = view->transient.file_data.file; + Full_Cursor cursor = file_compute_cursor(system, file, seek_pos(pos), 0); + view_set_cursor(view, cursor, 1, view->transient.file_data.file->settings.unwrapped_lines); + } + else{ + view_set_temp_highlight(system, view, pos, view->transient.file_data.temp_highlight_end_pos); + } + } +} + +// +// File Metadata Measuring +// + +internal void +file_measure_starts(General_Memory *general, Gap_Buffer *buffer){ + PRFL_FUNC_GROUP(); + + if (!buffer->line_starts){ + i32 max = buffer->line_max = KB(1); + buffer->line_starts = (i32*)general_memory_allocate(general, max*sizeof(i32)); + TentativeAssert(buffer->line_starts); + // TODO(allen): when unable to allocate? + } + + Buffer_Measure_Starts state = {0}; + while (buffer_measure_starts(&state, buffer)){ + i32 count = state.count; + i32 max = buffer->line_max; + max = ((max + 1) << 1); + + { + i32 *new_lines = (i32*)general_memory_reallocate(general, buffer->line_starts, sizeof(i32)*count, sizeof(i32)*max); + + // TODO(allen): when unable to grow? + TentativeAssert(new_lines); + buffer->line_starts = new_lines; + buffer->line_max = max; + } + } +} + +// NOTE(allen): These calls assumes that the buffer's line starts are already correct, +// and that the buffer's line_count is correct. +internal void +file_allocate_metadata_as_needed(General_Memory *general, Gap_Buffer *buffer, void **mem, i32 *mem_max_count, i32 count, i32 item_size){ + if (*mem == 0){ + i32 max = ((count+1)*2); + max = (max+(0x3FF))&(~(0x3FF)); + *mem = general_memory_allocate(general, max*item_size); + *mem_max_count = max; + } + else if (*mem_max_count < count){ + i32 old_max = *mem_max_count; + i32 max = ((count+1)*2); + max = (max+(0x3FF))&(~(0x3FF)); + + void *new_mem = general_memory_reallocate(general, *mem, item_size*old_max, item_size*max); + + Assert(new_mem); + *mem = new_mem; + *mem_max_count = max; + } +} + +inline void +file_allocate_character_starts_as_needed(General_Memory *general, Editing_File *file){ + file_allocate_metadata_as_needed(general, &file->state.buffer, (void**)&file->state.character_starts, &file->state. character_start_max, file->state.buffer.line_count, sizeof(i32)); +} + +internal void +file_measure_character_starts(System_Functions *system, Models *models, Editing_File *file){ + file_allocate_character_starts_as_needed(&models->mem.general, file); + Font_Pointers font = system->font.get_pointers_by_id(file->settings.font_id); + buffer_measure_character_starts(system, font, &file->state.buffer, file->state.character_starts, 0, file->settings.virtual_white); + file_update_cursor_positions(system, models, file); +} + +internal void +file_allocate_indents_as_needed(General_Memory *general, Editing_File *file, i32 min_last_index){ + i32 min_amount = min_last_index + 1; + file_allocate_metadata_as_needed(general, &file->state.buffer, (void**)&file->state.line_indents, &file->state.line_indent_max, min_amount, sizeof(f32)); +} + +inline void +file_allocate_wraps_as_needed(General_Memory *general, Editing_File *file){ + file_allocate_metadata_as_needed(general, &file->state.buffer, (void**)&file->state.wrap_line_index, &file->state.wrap_max, file->state.buffer.line_count, sizeof(f32)); +} + +inline void +file_allocate_wrap_positions_as_needed(General_Memory *general, Editing_File *file, i32 min_last_index){ + i32 min_amount = min_last_index + 1; + file_allocate_metadata_as_needed(general, &file->state.buffer, (void**)&file->state.wrap_positions, &file->state.wrap_position_max, min_amount, sizeof(f32)); +} + +internal void +wrap_state_init(System_Functions *system, Code_Wrap_State *state, Editing_File *file, Font_Pointers font){ + state->token_array = file->state.token_array; + state->token_ptr = state->token_array.tokens; + state->end_token = state->token_ptr + state->token_array.count; + + state->line_starts = file->state.buffer.line_starts; + state->line_count = file->state.buffer.line_count; + state->next_line_start = state->line_starts[1]; + + Gap_Buffer *buffer = &file->state.buffer; + i32 size = buffer_size(buffer); + buffer_stringify_loop(&state->stream, buffer, 0, size); + state->size = size; + state->i = 0; + + state->font = font; + + state->tab_indent_amount = font_get_glyph_advance(system, font.settings, font.metrics, font.pages, '\t'); + state->byte_advance = font.metrics->byte_advance; + + state->tran = null_buffer_translating_state; +} + +internal void +wrap_state_set_x(Code_Wrap_State *state, f32 line_shift){ + state->x = line_shift; +} + +internal void +wrap_state_set_i(Code_Wrap_State *state, i32 i){ + state->i = i; +} + +internal void +wrap_state_set_top(Code_Wrap_State *state, f32 line_shift){ + if (state->wrap_x.paren_nesting[state->wrap_x.paren_safe_top] > line_shift){ + state->wrap_x.paren_nesting[state->wrap_x.paren_safe_top] = line_shift; + } +} + +internal Code_Wrap_Step +wrap_state_consume_token(System_Functions *system, Font_Pointers font, Code_Wrap_State *state, i32 fixed_end_point){ + Code_Wrap_Step result = {0}; + i32 i = state->i; + + result.position_start = i; + + Cpp_Token token = {0}; + + token.start = state->size; + if (state->token_ptr < state->end_token){ + token = *state->token_ptr; + } + + if (state->consume_newline){ + ++i; + state->x = 0; + state->consume_newline = 0; + } + + + if (state->in_pp_body){ + if (!(token.flags & CPP_TFLAG_PP_BODY)){ + state->in_pp_body = false; + state->wrap_x = state->plane_wrap_x; + } + } + + if (!state->in_pp_body){ + if (token.flags & CPP_TFLAG_PP_DIRECTIVE){ + state->in_pp_body = true; + state->plane_wrap_x = state->wrap_x; + state->wrap_x = null_wrap_x; + } + } + + b32 skipping_whitespace = false; + if (i >= state->next_line_start){ + state->x = state->wrap_x.paren_nesting[state->wrap_x.paren_safe_top]; + state->x = state->wrap_x.paren_nesting[state->wrap_x.paren_safe_top]; + skipping_whitespace = true; + } + + // TODO(allen): exponential search this shit! + for (;i >= state->next_line_start;){ + state->next_line_start = state->size; + if (state->line_index < state->line_count){ + ++state->line_index; + if (state->line_index + 1 < state->line_count){ + state->next_line_start = state->line_starts[state->line_index + 1]; + } + } + else{ + break; + } + } + + i32 line_start = state->size; + if (state->line_index < 0){ + line_start = 0; + } + else if (state->line_index < state->line_count){ + line_start = state->line_starts[state->line_index]; + } + b32 still_looping = 0; + i32 end = token.start + token.size; + if (fixed_end_point >= 0 && end > fixed_end_point){ + end = fixed_end_point; + } + + i = clamp_bottom(line_start, i); + if (i == line_start){ + skipping_whitespace = true; + } + + b32 recorded_start_x = false; + do{ + for (; i < state->stream.end; ++i){ + if (!(i < end)){ + goto doublebreak; + } + + u8 ch = (u8)state->stream.data[i]; + translating_fully_process_byte(system, font, &state->tran, ch, i, state->size, &state->emits); + + for (TRANSLATION_EMIT_LOOP(state->J, state->emits)){ + TRANSLATION_GET_STEP(state->step, state->behavior, state->J, state->emits); + + if (state->behavior.do_newline){ + state->consume_newline = 1; + goto doublebreak; + } + else if(state->behavior.do_number_advance || state->behavior.do_codepoint_advance){ + u32 n = state->step.value; + f32 adv = 0; + if (state->behavior.do_codepoint_advance){ + adv = font_get_glyph_advance(system, state->font.settings, state->font.metrics, state->font.pages, n); + + if (n != ' ' && n != '\t'){ + skipping_whitespace = false; + } + } + else{ + adv = state->byte_advance; + skipping_whitespace = false; + } + + if (!skipping_whitespace){ + if (!recorded_start_x){ + result.start_x = state->x; + recorded_start_x = true; + } + state->x += adv; + } + } + } + } + still_looping = buffer_stringify_next(&state->stream); + }while(still_looping); + doublebreak:; + + state->i = i; + + b32 consume_token = false; + if (state->token_ptr < state->end_token && i >= token.start + token.size){ + consume_token = true; + } + + result.this_token = state->token_ptr; + if (consume_token){ + Assert(state->token_ptr < state->end_token); + switch (state->token_ptr->type){ + case CPP_TOKEN_BRACE_OPEN: + { + state->wrap_x.paren_nesting[state->wrap_x.paren_safe_top] += state->tab_indent_amount; + }break; + + case CPP_TOKEN_BRACE_CLOSE: + { + state->wrap_x.paren_nesting[state->wrap_x.paren_safe_top] -= state->tab_indent_amount; + }break; + + case CPP_TOKEN_PARENTHESE_OPEN: + case CPP_TOKEN_BRACKET_OPEN: + { + ++state->wrap_x.paren_top; + + i32 top = state->wrap_x.paren_top; + if (top >= ArrayCount(state->wrap_x.paren_nesting)){ + top = ArrayCount(state->wrap_x.paren_nesting) - 1; + } + state->wrap_x.paren_safe_top = top; + + state->wrap_x.paren_nesting[top] = state->x; + }break; + + case CPP_TOKEN_PARENTHESE_CLOSE: + case CPP_TOKEN_BRACKET_CLOSE: + { + --state->wrap_x.paren_top; + + if (state->wrap_x.paren_top < 0){ + state->wrap_x.paren_top = 0; + } + + i32 top = state->wrap_x.paren_top; + if (top >= ArrayCount(state->wrap_x.paren_nesting)){ + top = ArrayCount(state->wrap_x.paren_nesting) - 1; + } + state->wrap_x.paren_safe_top = top; + }break; + } + + ++state->token_ptr; + if (state->token_ptr > state->end_token){ + state->token_ptr = state->end_token; + } + } + + result.position_end = state->i; + result.final_x = state->x; + + if (!recorded_start_x){ + result.start_x = state->x; + recorded_start_x = true; + } + + return(result); +} + +internal i32 +stickieness_guess(Cpp_Token_Type type, Cpp_Token_Type other_type, u16 flags, u16 other_flags, b32 on_left){ + i32 guess = 0; + + b32 is_words = 0, other_is_words = 0; + if (type == CPP_TOKEN_IDENTIFIER || (type >= CPP_TOKEN_KEY_TYPE && type <= CPP_TOKEN_KEY_OTHER)){ + is_words = 1; + } + if (other_type == CPP_TOKEN_IDENTIFIER || (other_type >= CPP_TOKEN_KEY_TYPE && other_type <= CPP_TOKEN_KEY_OTHER)){ + other_is_words = 1; + } + + b32 is_operator = 0, other_is_operator = 0; + if (flags & CPP_TFLAG_IS_OPERATOR){ + is_operator = 1; + } + if (other_flags & CPP_TFLAG_IS_OPERATOR){ + other_is_operator = 1; + } + + i32 operator_side_bias = 70*(!on_left); + + if (is_words && other_is_words){ + guess = 200; + } + else if (type == CPP_TOKEN_PARENTHESE_OPEN){ + if (on_left){ + guess = 100; + } + else{ + if (other_is_words){ + guess = 100; + } + else{ + guess = 0; + } + } + } + else if (type == CPP_TOKEN_SEMICOLON){ + if (on_left){ + guess = 0; + } + else{ + guess = 1000; + } + } + else if (type == CPP_TOKEN_COMMA){ + guess = 20; + } + else if (type == CPP_TOKEN_COLON || + type == CPP_TOKEN_PARENTHESE_CLOSE || + type == CPP_TOKEN_BRACKET_OPEN || + type == CPP_TOKEN_BRACKET_CLOSE){ + if (on_left){ + guess = 20; + if (other_is_words){ + guess = 100; + } + } + else{ + guess = 100; + } + } + else if (type == CPP_PP_DEFINED){ + if (on_left){ + guess = 100; + } + else{ + guess = 0; + } + } + else if (type == CPP_TOKEN_SCOPE){ + guess = 90; + } + else if (type == CPP_TOKEN_MINUS){ + if (on_left){ + guess = 80; + } + else{ + guess = 60; + } + } + else if (type == CPP_TOKEN_DOT || + type == CPP_TOKEN_ARROW){ + guess = 200; + } + else if (type == CPP_TOKEN_NOT || + type == CPP_TOKEN_TILDE){ + if (on_left){ + guess = 80; + } + else{ + guess = 20; + } + } + else if (type == CPP_TOKEN_INCREMENT || + type == CPP_TOKEN_DECREMENT || + type == CPP_TOKEN_STAR || + type == CPP_TOKEN_AMPERSAND || + (type >= CPP_TOKEN_POSTINC && + type <= CPP_TOKEN_DELETE_ARRAY)){ + guess = 80; + if (!on_left && other_is_operator){ + guess = 20; + } + } + else if (type >= CPP_TOKEN_MUL && type <= CPP_TOKEN_MOD){ + guess = 70; + } + else if (type == CPP_TOKEN_PLUS){ + guess = 60 + operator_side_bias; + } + else if (type >= CPP_TOKEN_LSHIFT && type <= CPP_TOKEN_RSHIFT){ + guess = 50; + } + else if (type >= CPP_TOKEN_LESS && type <= CPP_TOKEN_NOTEQ){ + guess = 40 + operator_side_bias; + } + else if (type >= CPP_TOKEN_BIT_XOR && type <= CPP_TOKEN_BIT_OR){ + guess = 40; + } + else if (type >= CPP_TOKEN_AND && type <= CPP_TOKEN_OR){ + guess = 30 + operator_side_bias; + } + else if (type >= CPP_TOKEN_TERNARY_QMARK && type <= CPP_TOKEN_COLON){ + guess = 20 + operator_side_bias; + } + else if (type == CPP_TOKEN_THROW){ + if (on_left){ + guess = 100; + } + else{ + guess = 0; + } + } + else if (type >= CPP_TOKEN_EQ && type <= CPP_TOKEN_XOREQ){ + guess = 15 + operator_side_bias; + } + + return(guess); +} + +internal Wrap_Current_Shift +get_current_shift(Code_Wrap_State *wrap_state, i32 next_line_start){ + Wrap_Current_Shift result = {0}; + + result.shift = wrap_state->wrap_x.paren_nesting[wrap_state->wrap_x.paren_safe_top]; + + Cpp_Token next_token = {0}; + if (wrap_state->token_ptr < wrap_state->end_token){ + next_token = *wrap_state->token_ptr; + } + + if (wrap_state->token_ptr > wrap_state->token_array.tokens){ + Cpp_Token prev_token = *(wrap_state->token_ptr-1); + + if (wrap_state->wrap_x.paren_safe_top != 0 && prev_token.type == CPP_TOKEN_PARENTHESE_OPEN){ + result.shift = wrap_state->wrap_x.paren_nesting[wrap_state->wrap_x.paren_safe_top-1] + wrap_state->tab_indent_amount; + result.adjust_top_to_this = 1; + } + + f32 statement_continuation_indent = 0.f; + if (result.shift != 0.f && wrap_state->wrap_x.paren_safe_top == 0){ + if (!(prev_token.flags & (CPP_TFLAG_PP_DIRECTIVE|CPP_TFLAG_PP_BODY))){ + switch (prev_token.type){ + case CPP_TOKEN_BRACKET_OPEN: + case CPP_TOKEN_BRACE_OPEN: + case CPP_TOKEN_BRACE_CLOSE: + case CPP_TOKEN_SEMICOLON: + case CPP_TOKEN_COLON: + case CPP_TOKEN_COMMA: + case CPP_TOKEN_COMMENT: break; + default: statement_continuation_indent += wrap_state->tab_indent_amount; break; + } + } + } + + switch (next_token.type){ + case CPP_TOKEN_BRACE_CLOSE: case CPP_TOKEN_BRACE_OPEN: break; + default: result.shift += statement_continuation_indent; break; + } + } + + if (next_token.start < next_line_start){ + if (next_token.flags & CPP_TFLAG_PP_DIRECTIVE){ + result.shift = 0; + } + else{ + switch (next_token.type){ + case CPP_TOKEN_BRACE_CLOSE: + { + if (wrap_state->wrap_x.paren_safe_top == 0){ + result.shift -= wrap_state->tab_indent_amount; + } + }break; + } + } + } + + result.shift = clamp_bottom(0.f, result.shift); + return(result); +} + +internal void +file_measure_wraps(System_Functions *system, Models *models, Editing_File *file, Font_Pointers font){ + PRFL_FUNC_GROUP(); + + General_Memory *general = &models->mem.general; + Partition *part = &models->mem.part; + + Temp_Memory temp = begin_temp_memory(part); + + file_allocate_wraps_as_needed(general, file); + file_allocate_indents_as_needed(general, file, file->state.buffer.line_count); + file_allocate_wrap_positions_as_needed(general, file, file->state.buffer.line_count); + + Buffer_Measure_Wrap_Params params; + params.buffer = &file->state.buffer; + params.wrap_line_index = file->state.wrap_line_index; + params.system = system; + params.font = font; + params.virtual_white = file->settings.virtual_white; + + f32 width = (f32)file->settings.display_width; + f32 minimum_base_width = (f32)file->settings.minimum_base_display_width; + + i32 size = buffer_size(params.buffer); + + Buffer_Measure_Wrap_State state = {0}; + Buffer_Layout_Stop stop = {0}; + + f32 edge_tolerance = 50.f; + edge_tolerance = clamp_top(edge_tolerance, 50.f); + + f32 current_line_shift = 0.f; + b32 do_wrap = 0; + i32 wrap_unit_end = 0; + + i32 wrap_position_index = 0; + file->state.wrap_positions[wrap_position_index++] = 0; + + Code_Wrap_State wrap_state = {0}; + + b32 use_tokens = false; + + Wrap_Indent_Pair *wrap_indent_marks = 0; + Potential_Wrap_Indent_Pair *potential_marks = 0; + i32 max_wrap_indent_mark = 0; + + if (params.virtual_white && file->state.tokens_complete && !file->state.still_lexing){ + wrap_state_init(system, &wrap_state, file, font); + use_tokens = true; + + potential_marks = push_array(part, Potential_Wrap_Indent_Pair, floor32(width)); + + max_wrap_indent_mark = partition_remaining(part)/sizeof(Wrap_Indent_Pair); + wrap_indent_marks = push_array(part, Wrap_Indent_Pair, max_wrap_indent_mark); + } + + i32 real_count = 0; + i32 potential_count = 0; + i32 stage = 0; + + do{ + stop = buffer_measure_wrap_y(&state, params, current_line_shift, do_wrap, wrap_unit_end); + + switch (stop.status){ + case BLStatus_NeedWrapDetermination: + { + if (use_tokens){ + if (stage == 0){ + do_wrap = 0; + wrap_unit_end = wrap_indent_marks[stage+1].wrap_position; + ++stage; + } + else{ + do_wrap = 1; + wrap_unit_end = wrap_indent_marks[stage+1].wrap_position; + file_allocate_wrap_positions_as_needed(general, file, wrap_position_index); + file->state.wrap_positions[wrap_position_index++] = stop.pos; + } + } + else{ + Translation_State tran = {0}; + Translation_Emits emits = {0}; + Gap_Buffer_Stream stream = {0}; + + i32 word_stage = 0; + i32 i = stop.pos; + f32 x = stop.x; + f32 self_x = 0; + i32 wrap_end_result = size; + if (buffer_stringify_loop(&stream, params.buffer, i, size)){ + b32 still_looping = false; + do{ + for (; i < stream.end; ++i){ + { + u8 ch = stream.data[i]; + translating_fully_process_byte(system, font, &tran, ch, i, size, &emits); + } + + for (TRANSLATION_DECL_EMIT_LOOP(J, emits)){ + TRANSLATION_DECL_GET_STEP(step, behavior, J, emits); + + u32 codepoint = step.value; + switch (word_stage){ + case 0: + { + if (codepoint_is_whitespace(codepoint)){ + word_stage = 1; + } + else{ + f32 adv = font_get_glyph_advance(params.system, params.font.settings, params.font.metrics, params.font.pages, codepoint); + + x += adv; + self_x += adv; + if (self_x > width){ + wrap_end_result = step.i; + goto doublebreak; + } + } + }break; + + case 1: + { + if (!codepoint_is_whitespace(codepoint)){ + wrap_end_result = step.i; + goto doublebreak; + } + }break; + } + } + } + still_looping = buffer_stringify_next(&stream); + }while(still_looping); + } + + doublebreak:; + wrap_unit_end = wrap_end_result; + if (x > width){ + do_wrap = 1; + file_allocate_wrap_positions_as_needed(general, file, wrap_position_index); + file->state.wrap_positions[wrap_position_index++] = stop.pos; + } + else{ + do_wrap = 0; + } + } + }break; + + case BLStatus_NeedWrapLineShift: + case BLStatus_NeedLineShift: + { + f32 current_width = width; + + if (use_tokens){ + Code_Wrap_State original_wrap_state = wrap_state; + i32 next_line_start = buffer_size(params.buffer); + if (stop.line_index+1 < params.buffer->line_count){ + next_line_start = params.buffer->line_starts[stop.line_index+1]; + } + + f32 base_adjusted_width = wrap_state.wrap_x.base_x + minimum_base_width; + + if (minimum_base_width != 0 && current_width < base_adjusted_width){ + current_width = base_adjusted_width; + } + + if (stop.status == BLStatus_NeedLineShift){ + real_count = 0; + potential_count = 0; + stage = 0; + + Wrap_Current_Shift current_shift = get_current_shift(&wrap_state, next_line_start); + + if (current_shift.adjust_top_to_this){ + wrap_state_set_top(&wrap_state, current_shift.shift); + } + + wrap_indent_marks[real_count].wrap_position = 0; + wrap_indent_marks[real_count].line_shift =current_shift.shift; + ++real_count; + + wrap_state.wrap_x.base_x = wrap_state.wrap_x.paren_nesting[0]; + + for (; wrap_state.token_ptr < wrap_state.end_token; ){ + Code_Wrap_Step step = {0}; + b32 emit_comment_position = false; + b32 first_word = true; + + if (wrap_state.token_ptr->type == CPP_TOKEN_COMMENT || wrap_state.token_ptr->type == CPP_TOKEN_STRING_CONSTANT){ + i32 i = wrap_state.token_ptr->start; + i32 end_i = i + wrap_state.token_ptr->size; + + if (i < wrap_state.i){ + i = wrap_state.i; + } + + if (end_i > wrap_state.next_line_start){ + end_i = wrap_state.next_line_start; + } + + f32 x = wrap_state.x; + + step.position_start = i; + step.start_x = x; + step.this_token = wrap_state.token_ptr; + + Gap_Buffer_Stream stream = {0}; + Translation_State tran = {0}; + Translation_Emits emits = {0}; + + Potential_Wrap_Indent_Pair potential_wrap = {0}; + potential_wrap.wrap_position = i; + potential_wrap.line_shift = x; + potential_wrap.wrappable_score = 5; + potential_wrap.wrap_x = x; + potential_wrap.adjust_top_to_this = 0; + + if (buffer_stringify_loop(&stream, params.buffer, i, end_i)){ + b32 still_looping = true; + + while(still_looping){ + for (; i < stream.end; ++i){ + { + u8 ch = stream.data[i]; + translating_fully_process_byte(system, font, &tran, ch, i, end_i, &emits); + } + + for (TRANSLATION_DECL_EMIT_LOOP(J, emits)){ + TRANSLATION_DECL_GET_STEP(buffer_step, behav, J, emits); + if (!codepoint_is_whitespace(buffer_step.value)){ + i = buffer_step.i; + goto doublebreak_stage_vspace; + } + } + } + still_looping = buffer_stringify_next(&stream); + } + doublebreak_stage_vspace:; + + do{ + i32 pos_end_i = end_i; + while (still_looping){ + for (; i < stream.end; ++i){ + { + u8 ch = stream.data[i]; + translating_fully_process_byte(system, font, &tran, ch, i, end_i, &emits); + } + + for (TRANSLATION_DECL_EMIT_LOOP(J, emits)){ + TRANSLATION_DECL_GET_STEP(buffer_step, behav, J, emits); + if (codepoint_is_whitespace(buffer_step.value)){ + pos_end_i = buffer_step.i; + goto doublebreak_stage1; + } + + f32 adv = font_get_glyph_advance(params.system, params.font.settings, params.font.metrics, params.font.pages, buffer_step.value); + x += adv; + + if (!first_word && x > current_width){ + pos_end_i = buffer_step.i; + emit_comment_position = true; + goto doublebreak_stage1; + } + } + } + still_looping = buffer_stringify_next(&stream); + } + doublebreak_stage1:; + first_word = 0; + + if (emit_comment_position){ + step.position_end = pos_end_i; + step.final_x = x; + goto finished_comment_split; + } + + while(still_looping){ + for (; i < stream.end; ++i){ + { + u8 ch = stream.data[i]; + translating_fully_process_byte(system, font, &tran, ch, i, end_i, &emits); + } + + for (TRANSLATION_DECL_EMIT_LOOP(J, emits)){ + TRANSLATION_DECL_GET_STEP(buffer_step, behav, J, emits); + + if (!codepoint_is_whitespace(buffer_step.value)){ + pos_end_i = buffer_step.i; + goto doublebreak_stage2; + } + + f32 adv = font_get_glyph_advance(params.system, params.font.settings, params.font.metrics, params.font.pages, buffer_step.value); + x += adv; + } + } + still_looping = buffer_stringify_next(&stream); + } + doublebreak_stage2:; + + potential_wrap.wrap_position = pos_end_i; + potential_wrap.wrap_x = x; + }while(still_looping); + } + + finished_comment_split:; + if (emit_comment_position){ + potential_marks[potential_count] = potential_wrap; + ++potential_count; + } + } + + if (!emit_comment_position){ + step = wrap_state_consume_token(system, font, &wrap_state, next_line_start); + } + + b32 need_to_choose_a_wrap = false; + if (step.final_x > current_width){ + need_to_choose_a_wrap = true; + } + + current_shift = get_current_shift(&wrap_state, next_line_start); + + b32 next_token_is_on_line = false; + if (wrap_state.token_ptr < wrap_state.end_token){ + if (wrap_state.token_ptr->start < next_line_start){ + next_token_is_on_line = true; + } + } + + i32 next_wrap_position = step.position_end; + f32 wrap_x = step.final_x; + if (next_token_is_on_line){ + if (wrap_state.token_ptr < wrap_state.end_token){ + i32 pos_i = wrap_state.token_ptr->start; + if (pos_i > step.position_start && next_wrap_position < pos_i){ + next_wrap_position = pos_i; + } + } + } + + if (!need_to_choose_a_wrap){ + i32 wrappable_score = 1; + + Cpp_Token *this_token = step.this_token; + Cpp_Token *next_token = 0; + if (wrap_state.token_ptr < wrap_state.end_token){ + next_token = wrap_state.token_ptr; + } + + Cpp_Token_Type this_type = this_token->type; + Cpp_Token_Type next_type = CPP_TOKEN_JUNK; + + u16 this_flags = this_token->flags; + u16 next_flags = 0; + + if (this_token == next_token || !next_token_is_on_line){ + next_token = 0; + } + + if (next_token){ + next_type = next_token->type; + next_flags = next_token->flags; + } + + i32 this_stickieness = stickieness_guess(this_type, next_type, this_flags, next_flags, 1); + + i32 next_stickieness = 0; + if (next_token){ + next_stickieness = stickieness_guess(next_type, this_type, next_flags, this_flags, 0); + } + + i32 general_stickieness = this_stickieness; + if (general_stickieness < next_stickieness){ + general_stickieness = next_stickieness; + } + + if (wrap_state.wrap_x.paren_top != 0 && this_type == CPP_TOKEN_COMMA){ + general_stickieness = 0; + } + + wrappable_score = 64*50; + wrappable_score += 101 - general_stickieness - wrap_state.wrap_x.paren_safe_top*80; + + potential_marks[potential_count].wrap_position = next_wrap_position; + potential_marks[potential_count].line_shift = current_shift.shift; + potential_marks[potential_count].wrappable_score = wrappable_score; + potential_marks[potential_count].wrap_x = wrap_x; + potential_marks[potential_count].adjust_top_to_this = current_shift.adjust_top_to_this; + ++potential_count; + } + + if (need_to_choose_a_wrap){ + if (potential_count == 0){ + wrap_indent_marks[real_count].wrap_position = next_wrap_position; + wrap_indent_marks[real_count].line_shift = current_shift.shift; + ++real_count; + } + else{ + i32 i = 0, best_i = 0; + i32 best_score = -1; + f32 best_x_shift = 0; + + f32 x_gain_threshold = 18.f; + + for (; i < potential_count; ++i){ + i32 this_score = potential_marks[i].wrappable_score; + f32 x_shift = potential_marks[i].wrap_x - potential_marks[i].line_shift; + + f32 x_shift_adjusted = x_shift - x_gain_threshold; + f32 x_left_over = step.final_x - x_shift; + + if (x_shift_adjusted < 0){ + this_score = 0; + } + else if (x_left_over <= x_gain_threshold){ + this_score = 1; + } + + if (this_score > best_score){ + best_score = this_score; + best_x_shift = x_shift; + best_i = i; + } + else if (this_score == best_score && x_shift > best_x_shift){ + best_x_shift = x_shift; + best_i = i; + } + } + + i32 wrap_position = potential_marks[best_i].wrap_position; + f32 line_shift = potential_marks[best_i].line_shift; + b32 adjust_top_to_this = potential_marks[best_i].adjust_top_to_this; + wrap_indent_marks[real_count].wrap_position = wrap_position; + wrap_indent_marks[real_count].line_shift = line_shift; + ++real_count; + potential_count = 0; + + wrap_state = original_wrap_state; + for (;;){ + step = wrap_state_consume_token(system, font, &wrap_state, wrap_position); + if (step.position_end >= wrap_position){ + break; + } + } + + wrap_state_set_x(&wrap_state, line_shift); + wrap_state_set_i(&wrap_state, wrap_position); + if (adjust_top_to_this){ + wrap_state_set_top(&wrap_state, line_shift); + } + + original_wrap_state = wrap_state; + } + } + + if (step.position_end >= next_line_start-1){ + break; + } + } + + wrap_indent_marks[real_count].wrap_position = next_line_start; + wrap_indent_marks[real_count].line_shift = 0; + ++real_count; + + for (i32 l = 0; wrap_state.i < next_line_start && l < 3; ++l){ + wrap_state_consume_token(system, font, &wrap_state, next_line_start); + } + } + + current_line_shift = wrap_indent_marks[stage].line_shift; + + if (stage > 0){ + ++stage; + } + + current_line_shift = clamp_bottom(0.f, current_line_shift); + } + else{ + current_line_shift = 0.f; + } + + current_line_shift = clamp_top(current_line_shift, current_width - edge_tolerance); + + if (stop.wrap_line_index >= file->state.line_indent_max){ + file_allocate_indents_as_needed(general, file, stop.wrap_line_index); + } + + file->state.line_indents[stop.wrap_line_index] = current_line_shift; + file->state.wrap_line_count = stop.wrap_line_index; + }break; + } + }while(stop.status != BLStatus_Finished); + + ++file->state.wrap_line_count; + + file_allocate_wrap_positions_as_needed(general, file, wrap_position_index); + file->state.wrap_positions[wrap_position_index++] = size; + file->state.wrap_position_count = wrap_position_index; + + end_temp_memory(temp); + + if (file->state.hacks.needs_wraps_and_fix_cursor){ + file->state.hacks.needs_wraps_and_fix_cursor = false; + file_update_cursor_positions(system, models, file); + } +} + +internal void +file_measure_wraps_and_fix_cursor(System_Functions *system, Models *models, Editing_File *file, Font_Pointers font){ + if (file->state.hacks.suppression_mode){ + file->state.hacks.needs_wraps_and_fix_cursor = true; + } + else{ + file->state.hacks.needs_wraps_and_fix_cursor = false; + file_measure_wraps(system, models, file, font); + file_update_cursor_positions(system, models, file); + } +} + +// +// +// + +internal void +file_create_from_string(System_Functions *system, Models *models, Editing_File *file, String val, u32 flags){ + PRFL_FUNC_GROUP(); + + General_Memory *general = &models->mem.general; + Partition *part = &models->mem.part; + Open_File_Hook_Function *hook_open_file = models->hook_open_file; + Application_Links *app_links = &models->app_links; + + memset(&file->state, 0, sizeof(file->state)); + Gap_Buffer_Init init = buffer_begin_init(&file->state.buffer, val.str, val.size); + for (; buffer_init_need_more(&init); ){ + i32 page_size = buffer_init_page_size(&init); + page_size = l_round_up_i32(page_size, KB(4)); + if (page_size < KB(4)){ + page_size = KB(4); + } + void *data = general_memory_allocate(general, page_size); + buffer_init_provide_page(&init, data, page_size); + } + + i32 scratch_size = partition_remaining(part); + Assert(scratch_size > 0); + b32 init_success = buffer_end_init(&init, part->base + part->pos, scratch_size); + AllowLocal(init_success); Assert(init_success); + + if (buffer_size(&file->state.buffer) < val.size){ + file->settings.dos_write_mode = 1; + } + file->state.dirty = DirtyState_UpToDate; + + Face_ID font_id = models->global_font_id; + file->settings.font_id = font_id; + Font_Pointers font = system->font.get_pointers_by_id(font_id); + Assert(font.valid); + + { + file_measure_starts(general, &file->state.buffer); + + file_allocate_character_starts_as_needed(general, file); + buffer_measure_character_starts(system, font, &file->state.buffer, file->state.character_starts, 0, file->settings.virtual_white); + + file_measure_wraps(system, models, file, font); + } + + file->settings.read_only = ((flags & FileCreateFlag_ReadOnly) != 0); + if (!file->settings.read_only){ + // TODO(allen): Redo undo system (if you don't mind the pun) + i32 request_size = KB(64); + file->state.undo.undo.max = request_size; + file->state.undo.undo.strings = (u8*)general_memory_allocate(general, request_size); + file->state.undo.undo.edit_max = request_size / sizeof(Edit_Step); + file->state.undo.undo.edits = (Edit_Step*)general_memory_allocate(general, request_size); + + file->state.undo.redo.max = request_size; + file->state.undo.redo.strings = (u8*)general_memory_allocate(general, request_size); + file->state.undo.redo.edit_max = request_size / sizeof(Edit_Step); + file->state.undo.redo.edits = (Edit_Step*)general_memory_allocate(general, request_size); + + file->state.undo.history.max = request_size; + file->state.undo.history.strings = (u8*)general_memory_allocate(general, request_size); + file->state.undo.history.edit_max = request_size / sizeof(Edit_Step); + file->state.undo.history.edits = (Edit_Step*)general_memory_allocate(general, request_size); + + file->state.undo.children.max = request_size; + file->state.undo.children.strings = (u8*)general_memory_allocate(general, request_size); + file->state.undo.children.edit_max = request_size / sizeof(Buffer_Edit); + file->state.undo.children.edits = (Buffer_Edit*)general_memory_allocate(general, request_size); + + file->state.undo.history_block_count = 1; + file->state.undo.history_head_block = 0; + file->state.undo.current_block_normal = 1; + } + + if (hook_open_file){ + file->state.hacks.suppression_mode = true; + hook_open_file(app_links, file->id.id); + file->state.hacks.suppression_mode = false; + if (file->state.hacks.needs_wraps_and_fix_cursor){ + file_measure_wraps_and_fix_cursor(system, models, file, font); + } + } + file->settings.is_initialized = true; +} + +internal void +file_close(System_Functions *system, General_Memory *general, Editing_File *file){ + if (file->state.still_lexing){ + system->cancel_job(BACKGROUND_THREADS, file->state.lex_job); + if (file->state.swap_array.tokens){ + general_memory_free(general, file->state.swap_array.tokens); + file->state.swap_array.tokens = 0; + } + } + if (file->state.token_array.tokens){ + general_memory_free(general, file->state.token_array.tokens); + } + + Gap_Buffer *buffer = &file->state.buffer; + if (buffer->data){ + general_memory_free(general, buffer->data); + general_memory_free(general, buffer->line_starts); + } + + general_memory_free(general, file->state.wrap_line_index); + general_memory_free(general, file->state.character_starts); + general_memory_free(general, file->state.line_indents); + + if (file->state.undo.undo.edits){ + general_memory_free(general, file->state.undo.undo.strings); + general_memory_free(general, file->state.undo.undo.edits); + + general_memory_free(general, file->state.undo.redo.strings); + general_memory_free(general, file->state.undo.redo.edits); + + general_memory_free(general, file->state.undo.history.strings); + general_memory_free(general, file->state.undo.history.edits); + + general_memory_free(general, file->state.undo.children.strings); + general_memory_free(general, file->state.undo.children.edits); + } +} + +internal +Job_Callback_Sig(job_full_lex){ + Editing_File *file = (Editing_File*)data[0]; + General_Memory *general = (General_Memory*)data[1]; + Models *models = (Models*)data[2]; + + Parse_Context parse_context = parse_context_get(&models->parse_context_memory, file->settings.parse_context_id, memory->data, memory->size); + if (!parse_context.valid){ + return; + } + + Gap_Buffer *buffer = &file->state.buffer; + i32 text_size = buffer_size(buffer); + + u32 aligned_buffer_size = (text_size + 3)&(~3); + + for (;memory->size < aligned_buffer_size + parse_context.memory_size;){ + void *old_base = memory->data; + system->grow_thread_memory(memory); + parse_context_rebase(&parse_context, old_base, memory->data); + } + + u8 *data_ptr = (u8*)memory->data; + umem data_size = memory->size; + data_ptr += parse_context.memory_size; + data_size -= parse_context.memory_size; + + Cpp_Token_Array tokens = {0}; + tokens.tokens = (Cpp_Token*)(data_ptr); + tokens.max_count = (u32)(data_size / sizeof(Cpp_Token)); + tokens.count = 0; + + b32 still_lexing = true; + + Cpp_Lex_Data lex = cpp_lex_data_init(file->settings.tokens_without_strings, parse_context.kw_table, parse_context.pp_table); + + // TODO(allen): deduplicate this against relex + char *chunks[3]; + i32 chunk_sizes[3]; + chunks[0] = buffer->data; + chunk_sizes[0] = buffer->size1; + chunks[1] = buffer->data + buffer->size1 + buffer->gap_size; + chunk_sizes[1] = buffer->size2; + chunks[2] = 0; + chunk_sizes[2] = 0; + + i32 chunk_index = 0; + + do{ + char *chunk = chunks[chunk_index]; + i32 chunk_size = chunk_sizes[chunk_index]; + + i32 result = + cpp_lex_step(&lex, chunk, chunk_size, text_size, &tokens, 2048); + + switch (result){ + case LexResult_NeedChunk: + { + ++chunk_index; + Assert(chunk_index < ArrayCount(chunks)); + }break; + + case LexResult_NeedTokenMemory: + { + if (system->check_cancel(thread)){ + return; + } + + void *old_base = memory->data; + system->grow_thread_memory(memory); + cpp_rebase_tables(&lex, old_base, memory->data); + + data_ptr = (u8*)memory->data; + data_size = memory->size; + data_ptr += parse_context.memory_size; + data_size -= parse_context.memory_size; + tokens.tokens = (Cpp_Token*)(data_ptr); + tokens.max_count = (u32)(data_size / sizeof(Cpp_Token)); + }break; + + case LexResult_HitTokenLimit: + { + if (system->check_cancel(thread)){ + return; + } + }break; + + case LexResult_Finished: + { + still_lexing = false; + }break; + } + }while(still_lexing); + + i32 new_max = l_round_up_i32(tokens.count+1, KB(1)); + + system->acquire_lock(FRAME_LOCK); + { + Assert(file->state.swap_array.tokens == 0); + file->state.swap_array.tokens = (Cpp_Token*)general_memory_allocate(general, new_max*sizeof(Cpp_Token)); + } + system->release_lock(FRAME_LOCK); + + u8 *dest = (u8*)file->state.swap_array.tokens; + u8 *src = (u8*)tokens.tokens; + + memcpy(dest, src, tokens.count*sizeof(Cpp_Token)); + + system->acquire_lock(FRAME_LOCK); + { + Cpp_Token_Array *file_token_array = &file->state.token_array; + file_token_array->count = tokens.count; + file_token_array->max_count = new_max; + if (file_token_array->tokens){ + general_memory_free(general, file_token_array->tokens); + } + file_token_array->tokens = file->state.swap_array.tokens; + file->state.swap_array.tokens = 0; + } + system->release_lock(FRAME_LOCK); + + // NOTE(allen): These are outside the locked section because I don't + // think getting these out of order will cause critical bugs, and I + // want to minimize what's done in locked sections. + file->state.tokens_complete = true; + file->state.still_lexing = false; +} + +internal void +file_kill_tokens(System_Functions *system, General_Memory *general, Editing_File *file){ + file->settings.tokens_exist = 0; + if (file->state.still_lexing){ + system->cancel_job(BACKGROUND_THREADS, file->state.lex_job); + if (file->state.swap_array.tokens){ + general_memory_free(general, file->state.swap_array.tokens); + file->state.swap_array.tokens = 0; + } + } + if (file->state.token_array.tokens){ + general_memory_free(general, file->state.token_array.tokens); + } + file->state.tokens_complete = 0; + file->state.token_array = null_cpp_token_array; +} + +internal void +file_first_lex_parallel(System_Functions *system, Models *models, Editing_File *file){ + General_Memory *general = &models->mem.general; + file->settings.tokens_exist = true; + + if (file->is_loading == 0 && file->state.still_lexing == 0){ + Assert(file->state.token_array.tokens == 0); + + file->state.tokens_complete = false; + file->state.still_lexing = true; + + Job_Data job; + job.callback = job_full_lex; + job.data[0] = file; + job.data[1] = general; + job.data[2] = models; + file->state.lex_job = system->post_job(BACKGROUND_THREADS, job); + } +} + +internal void +file_first_lex_serial(Models *models, Editing_File *file){ + Mem_Options *mem = &models->mem; + Partition *part = &mem->part; + General_Memory *general = &mem->general; + file->settings.tokens_exist = true; + + Assert(!file->state.still_lexing); + + if (file->is_loading == 0){ + Assert(file->state.token_array.tokens == 0); + + { + Temp_Memory temp = begin_temp_memory(part); + + Parse_Context parse_context = parse_context_get(&models->parse_context_memory, file->settings.parse_context_id, partition_current(part), partition_remaining(part)); + Assert(parse_context.valid); + push_block(part, (i32)parse_context.memory_size); + + Gap_Buffer *buffer = &file->state.buffer; + i32 text_size = buffer_size(buffer); + + i32 mem_size = partition_remaining(part); + + Cpp_Token_Array new_tokens; + new_tokens.max_count = mem_size/sizeof(Cpp_Token); + new_tokens.count = 0; + new_tokens.tokens = push_array(part, Cpp_Token, new_tokens.max_count); + + b32 still_lexing = true; + + Cpp_Lex_Data lex = cpp_lex_data_init(file->settings.tokens_without_strings, parse_context.kw_table, parse_context.pp_table); + + // TODO(allen): deduplicate this against relex + char *chunks[3]; + i32 chunk_sizes[3]; + chunks[0] = buffer->data; + chunk_sizes[0] = buffer->size1; + chunks[1] = buffer->data + buffer->size1 + buffer->gap_size; + chunk_sizes[1] = buffer->size2; + chunks[2] = 0; + chunk_sizes[2] = 0; + + i32 chunk_index = 0; + + Cpp_Token_Array *swap_array = &file->state.swap_array; + + do{ + char *chunk = chunks[chunk_index]; + i32 chunk_size = chunk_sizes[chunk_index]; + + i32 result = cpp_lex_step(&lex, chunk, chunk_size, text_size, &new_tokens, NO_OUT_LIMIT); + + switch (result){ + case LexResult_NeedChunk: + { + ++chunk_index; + Assert(chunk_index < ArrayCount(chunks)); + }break; + + case LexResult_Finished: + case LexResult_NeedTokenMemory: + { + u32 new_max = l_round_up_u32(swap_array->count + new_tokens.count + 1, KB(1)); + u32 new_mem_max = new_max*sizeof(Cpp_Token); + u32 old_mem_count = swap_array->count*sizeof(Cpp_Token); + if (swap_array->tokens == 0){ + swap_array->tokens = (Cpp_Token*)general_memory_allocate(general, new_mem_max); + } + else{ + swap_array->tokens = (Cpp_Token*) + general_memory_reallocate(general, swap_array->tokens, old_mem_count, new_mem_max); + } + swap_array->max_count = new_max; + + Assert(swap_array->count + new_tokens.count <= swap_array->max_count); + memcpy(swap_array->tokens + swap_array->count, new_tokens.tokens, new_tokens.count*sizeof(Cpp_Token)); + swap_array->count += new_tokens.count; + new_tokens.count = 0; + + if (result == LexResult_Finished){ + still_lexing = false; + } + }break; + + case LexResult_HitTokenLimit: InvalidCodePath; + } + } while (still_lexing); + + Cpp_Token_Array *token_array = &file->state.token_array; + token_array->count = swap_array->count; + token_array->max_count = swap_array->max_count; + if (token_array->tokens != 0){ + general_memory_free(general, token_array->tokens); + } + token_array->tokens = swap_array->tokens; + + swap_array->tokens = 0; + swap_array->count = 0; + swap_array->max_count = 0; + + file->state.tokens_complete = true; + file->state.still_lexing = false; + + end_temp_memory(temp); + } + + file->state.tokens_complete = true; + } +} + +internal b32 +file_relex_parallel(System_Functions *system, Models *models, Editing_File *file, i32 start_i, i32 end_i, i32 shift_amount){ + Mem_Options *mem = &models->mem; + General_Memory *general = &mem->general; + Partition *part = &mem->part; + + if (file->state.token_array.tokens == 0){ + file_first_lex_parallel(system, models, file); + return(false); + } + + b32 result = true; + b32 inline_lex = !file->state.still_lexing; + if (inline_lex){ + Gap_Buffer *buffer = &file->state.buffer; + i32 extra_tolerance = 100; + + Cpp_Token_Array *array = &file->state.token_array; + Cpp_Relex_Range relex_range = cpp_get_relex_range(array, start_i, end_i); + + i32 relex_space_size = + relex_range.end_token_index - relex_range.start_token_index + extra_tolerance; + + Temp_Memory temp = begin_temp_memory(part); + Parse_Context parse_context = parse_context_get(&models->parse_context_memory, file->settings.parse_context_id, partition_current(part), partition_remaining(part)); + Assert(parse_context.valid); + push_block(part, (i32)parse_context.memory_size); + + Cpp_Token_Array relex_array; + relex_array.count = 0; + relex_array.max_count = relex_space_size; + relex_array.tokens = push_array(part, Cpp_Token, relex_array.max_count); + + i32 size = buffer_size(buffer); + + Cpp_Relex_Data state = cpp_relex_init(array, start_i, end_i, shift_amount, file->settings.tokens_without_strings, parse_context.kw_table, parse_context.pp_table); + + char *chunks[3]; + i32 chunk_sizes[3]; + + chunks[0] = buffer->data; + chunk_sizes[0] = buffer->size1; + + chunks[1] = buffer->data + buffer->size1 + buffer->gap_size; + chunk_sizes[1] = buffer->size2; + + chunks[2] = 0; + chunk_sizes[2] = 0; + + i32 chunk_index = 0; + char *chunk = chunks[chunk_index]; + i32 chunk_size = chunk_sizes[chunk_index]; + + while (!cpp_relex_is_start_chunk(&state, chunk, chunk_size)){ + ++chunk_index; + Assert(chunk_index < ArrayCount(chunks)); + chunk = chunks[chunk_index]; + chunk_size = chunk_sizes[chunk_index]; + } + + for(;;){ + Cpp_Lex_Result lex_result = + cpp_relex_step(&state, chunk, chunk_size, size, array, &relex_array); + + switch (lex_result){ + case LexResult_NeedChunk: + { + ++chunk_index; + Assert(chunk_index < ArrayCount(chunks)); + chunk = chunks[chunk_index]; + chunk_size = chunk_sizes[chunk_index]; + }break; + + case LexResult_NeedTokenMemory: + { + inline_lex = false; + }goto doublebreak; + + case LexResult_Finished: goto doublebreak; + } + } + doublebreak:; + + if (inline_lex){ + i32 new_count = cpp_relex_get_new_count(&state, array->count, &relex_array); + if (new_count > array->max_count){ + i32 new_max = l_round_up_i32(new_count, KB(1)); + void *memory = general_memory_reallocate(general, array->tokens, array->count*sizeof(Cpp_Token), new_max*sizeof(Cpp_Token)); + array->tokens = (Cpp_Token*)memory; + array->max_count = new_max; + } + + cpp_relex_complete(&state, array, &relex_array); + } + else{ + cpp_relex_abort(&state, array); + } + + end_temp_memory(temp); + } + + if (!inline_lex){ + Cpp_Token_Array *array = &file->state.token_array; + Cpp_Get_Token_Result get_token_result = cpp_get_token(*array, end_i); + i32 end_token_i = get_token_result.token_index; + + if (end_token_i < 0){ + end_token_i = 0; + } + else if (end_i > array->tokens[end_token_i].start){ + ++end_token_i; + } + + cpp_shift_token_starts(array, end_token_i, shift_amount); + --end_token_i; + if (end_token_i >= 0){ + Cpp_Token *token = array->tokens + end_token_i; + if (token->start < end_i && token->start + token->size > end_i){ + token->size += shift_amount; + } + } + + file->state.still_lexing = true; + + Job_Data job; + job.callback = job_full_lex; + job.data[0] = file; + job.data[1] = general; + job.data[2] = models; + file->state.lex_job = system->post_job(BACKGROUND_THREADS, job); + result = false; + } + + return(result); +} + +internal b32 +file_relex_serial(Models *models, Editing_File *file, i32 start_i, i32 end_i, i32 shift_amount){ + Mem_Options *mem = &models->mem; + General_Memory *general = &mem->general; + Partition *part = &mem->part; + + if (file->state.token_array.tokens == 0){ + file_first_lex_serial(models, file); + return(1); + } + + Assert(!file->state.still_lexing); + + Gap_Buffer *buffer = &file->state.buffer; + Cpp_Token_Array *array = &file->state.token_array; + + Temp_Memory temp = begin_temp_memory(part); + Parse_Context parse_context = parse_context_get(&models->parse_context_memory, file->settings.parse_context_id, partition_current(part), partition_remaining(part)); + Assert(parse_context.valid); + push_block(part, (i32)parse_context.memory_size); + + Cpp_Token_Array relex_array; + relex_array.count = 0; + relex_array.max_count = partition_remaining(part) / sizeof(Cpp_Token); + relex_array.tokens = push_array(part, Cpp_Token, relex_array.max_count); + + i32 size = buffer_size(buffer); + + Cpp_Relex_Data state = cpp_relex_init(array, start_i, end_i, shift_amount, file->settings.tokens_without_strings, parse_context.kw_table, parse_context.pp_table); + + char *chunks[3]; + i32 chunk_sizes[3]; + + chunks[0] = buffer->data; + chunk_sizes[0] = buffer->size1; + + chunks[1] = buffer->data + buffer->size1 + buffer->gap_size; + chunk_sizes[1] = buffer->size2; + + chunks[2] = 0; + chunk_sizes[2] = 0; + + i32 chunk_index = 0; + char *chunk = chunks[chunk_index]; + i32 chunk_size = chunk_sizes[chunk_index]; + + while (!cpp_relex_is_start_chunk(&state, chunk, chunk_size)){ + ++chunk_index; + Assert(chunk_index < ArrayCount(chunks)); + chunk = chunks[chunk_index]; + chunk_size = chunk_sizes[chunk_index]; + } + + for(;;){ + Cpp_Lex_Result lex_result = cpp_relex_step(&state, chunk, chunk_size, size, array, &relex_array); + + switch (lex_result){ + case LexResult_NeedChunk: + { + ++chunk_index; + Assert(chunk_index < ArrayCount(chunks)); + chunk = chunks[chunk_index]; + chunk_size = chunk_sizes[chunk_index]; + }break; + + case LexResult_NeedTokenMemory: InvalidCodePath; + + case LexResult_Finished: goto doublebreak; + } + } + doublebreak:; + + i32 new_count = cpp_relex_get_new_count(&state, array->count, &relex_array); + if (new_count > array->max_count){ + i32 new_max = l_round_up_i32(new_count, KB(1)); + array->tokens = (Cpp_Token*)general_memory_reallocate(general, array->tokens, array->count*sizeof(Cpp_Token), new_max*sizeof(Cpp_Token)); + array->max_count = new_max; + } + + cpp_relex_complete(&state, array, &relex_array); + + end_temp_memory(temp); + + return(1); +} + +internal void +undo_stack_grow_string(General_Memory *general, Edit_Stack *stack, i32 extra_size){ + i32 old_max = stack->max; + u8 *old_str = stack->strings; + i32 new_max = old_max*2 + extra_size; + u8 *new_str = (u8*)general_memory_reallocate(general, old_str, old_max, new_max); + stack->strings = new_str; + stack->max = new_max; +} + +internal void +undo_stack_grow_edits(General_Memory *general, Edit_Stack *stack){ + i32 old_max = stack->edit_max; + Edit_Step *old_eds = stack->edits; + i32 new_max = old_max*2 + 2; + Edit_Step *new_eds = (Edit_Step*)general_memory_reallocate(general, old_eds, old_max*sizeof(Edit_Step), new_max*sizeof(Edit_Step)); + stack->edits = new_eds; + stack->edit_max = new_max; +} + +internal void +child_stack_grow_string(General_Memory *general, Small_Edit_Stack *stack, i32 extra_size){ + i32 old_max = stack->max; + u8 *old_str = stack->strings; + i32 new_max = old_max*2 + extra_size; + u8 *new_str = (u8*)general_memory_reallocate(general, old_str, old_max, new_max); + stack->strings = new_str; + stack->max = new_max; +} + +internal void +child_stack_grow_edits(General_Memory *general, Small_Edit_Stack *stack, i32 amount){ + i32 old_max = stack->edit_max; + Buffer_Edit *old_eds = stack->edits; + i32 new_max = old_max*2 + amount; + Buffer_Edit *new_eds = (Buffer_Edit*)general_memory_reallocate(general, old_eds, old_max*sizeof(Buffer_Edit), new_max*sizeof(Buffer_Edit)); + stack->edits = new_eds; + stack->edit_max = new_max; +} + +internal i32 +undo_children_push(General_Memory *general, Small_Edit_Stack *children, Buffer_Edit *edits, i32 edit_count, u8 *strings, i32 string_size){ + i32 result = children->edit_count; + if (children->edit_count + edit_count > children->edit_max){ + child_stack_grow_edits(general, children, edit_count); + } + + if (children->size + string_size > children->max){ + child_stack_grow_string(general, children, string_size); + } + + memcpy(children->edits + children->edit_count, edits, edit_count*sizeof(Buffer_Edit)); + memcpy(children->strings + children->size, strings, string_size); + + Buffer_Edit *edit = children->edits + children->edit_count; + i32 start_pos = children->size; + for (i32 i = 0; i < edit_count; ++i, ++edit){ + edit->str_start += start_pos; + } + + children->edit_count += edit_count; + children->size += string_size; + + return result; +} + +internal Edit_Step* +file_post_undo(General_Memory *general, Editing_File *file, Edit_Step step, b32 do_merge, b32 can_merge){ + if (step.type == ED_NORMAL){ + file->state.undo.redo.size = 0; + file->state.undo.redo.edit_count = 0; + } + + Edit_Stack *undo = &file->state.undo.undo; + Edit_Step *result = 0; + + if (step.child_count == 0){ + if (step.edit.end - step.edit.start + undo->size > undo->max){ + undo_stack_grow_string(general, undo, step.edit.end - step.edit.start); + } + + Buffer_Edit inv; + buffer_invert_edit(&file->state.buffer, step.edit, &inv, (char*)undo->strings, &undo->size, undo->max); + + Edit_Step inv_step = {}; + inv_step.edit = inv; + inv_step.can_merge = (b8)can_merge; + inv_step.type = ED_UNDO; + + b32 did_merge = 0; + if (do_merge && undo->edit_count > 0){ + Edit_Step prev = undo->edits[undo->edit_count-1]; + if (prev.can_merge && inv_step.edit.len == 0 && prev.edit.len == 0){ + if (prev.edit.end == inv_step.edit.start){ + did_merge = 1; + inv_step.edit.start = prev.edit.start; + } + } + } + + if (did_merge){ + result = undo->edits + (undo->edit_count-1); + *result = inv_step; + } + else{ + if (undo->edit_count == undo->edit_max){ + undo_stack_grow_edits(general, undo); + } + + result = undo->edits + (undo->edit_count++); + *result = inv_step; + } + } + else{ + Edit_Step inv_step = {}; + inv_step.type = ED_UNDO; + inv_step.first_child = step.inverse_first_child; + inv_step.inverse_first_child = step.first_child; + inv_step.special_type = step.special_type; + inv_step.child_count = step.inverse_child_count; + inv_step.inverse_child_count = step.child_count; + + if (undo->edit_count == undo->edit_max){ + undo_stack_grow_edits(general, undo); + } + result = undo->edits + (undo->edit_count++); + *result = inv_step; + } + return result; +} + +inline void +undo_stack_pop(Edit_Stack *stack){ + if (stack->edit_count > 0){ + Edit_Step *edit = stack->edits + (--stack->edit_count); + if (edit->child_count == 0){ + stack->size -= edit->edit.len; + } + } +} + +internal void +file_post_redo(General_Memory *general, Editing_File *file, Edit_Step step){ + Edit_Stack *redo = &file->state.undo.redo; + + if (step.child_count == 0){ + if (step.edit.end - step.edit.start + redo->size > redo->max){ + undo_stack_grow_string(general, redo, step.edit.end - step.edit.start); + } + + Buffer_Edit inv; + buffer_invert_edit(&file->state.buffer, step.edit, &inv, (char*)redo->strings, &redo->size, redo->max); + + Edit_Step inv_step = {}; + inv_step.edit = inv; + inv_step.type = ED_REDO; + + if (redo->edit_count == redo->edit_max){ + undo_stack_grow_edits(general, redo); + } + redo->edits[redo->edit_count++] = inv_step; + } + else{ + Edit_Step inv_step = {}; + inv_step.type = ED_REDO; + inv_step.first_child = step.inverse_first_child; + inv_step.inverse_first_child = step.first_child; + inv_step.special_type = step.special_type; + inv_step.child_count = step.inverse_child_count; + inv_step.inverse_child_count = step.child_count; + + if (redo->edit_count == redo->edit_max){ + undo_stack_grow_edits(general, redo); + } + redo->edits[redo->edit_count++] = inv_step; + } +} + +inline void +file_post_history_block(Editing_File *file, i32 pos){ + Assert(file->state.undo.history_head_block < pos); + Assert(pos < file->state.undo.history.edit_count); + + Edit_Step *history = file->state.undo.history.edits; + Edit_Step *step = history + file->state.undo.history_head_block; + step->next_block = pos; + step = history + pos; + step->prev_block = file->state.undo.history_head_block; + file->state.undo.history_head_block = pos; + ++file->state.undo.history_block_count; +} + +inline void +file_unpost_history_block(Editing_File *file){ + Assert(file->state.undo.history_block_count > 1); + --file->state.undo.history_block_count; + Edit_Step *old_head = file->state.undo.history.edits + file->state.undo.history_head_block; + file->state.undo.history_head_block = old_head->prev_block; +} + +internal Edit_Step* +file_post_history(General_Memory *general, Editing_File *file, Edit_Step step, b32 do_merge, b32 can_merge){ + Edit_Stack *history = &file->state.undo.history; + Edit_Step *result = 0; + + local_persist Edit_Type reverse_types[4]; + if (reverse_types[ED_UNDO] == 0){ + reverse_types[ED_NORMAL] = ED_REVERSE_NORMAL; + reverse_types[ED_REVERSE_NORMAL] = ED_NORMAL; + reverse_types[ED_UNDO] = ED_REDO; + reverse_types[ED_REDO] = ED_UNDO; + } + + if (step.child_count == 0){ + if (step.edit.end - step.edit.start + history->size > history->max){ + undo_stack_grow_string(general, history, step.edit.end - step.edit.start); + } + + Buffer_Edit inv; + buffer_invert_edit(&file->state.buffer, step.edit, &inv, + (char*)history->strings, &history->size, history->max); + + Edit_Step inv_step = {}; + inv_step.edit = inv; + inv_step.can_merge = (b8)can_merge; + inv_step.type = reverse_types[step.type]; + + b32 did_merge = 0; + if (do_merge && history->edit_count > 0){ + Edit_Step prev = history->edits[history->edit_count-1]; + if (prev.can_merge && inv_step.edit.len == 0 && prev.edit.len == 0){ + if (prev.edit.end == inv_step.edit.start){ + did_merge = 1; + inv_step.edit.start = prev.edit.start; + } + } + } + + if (did_merge){ + result = history->edits + (history->edit_count-1); + } + else{ + if (history->edit_count == history->edit_max){ + undo_stack_grow_edits(general, history); + } + result = history->edits + (history->edit_count++); + } + + *result = inv_step; + } + else{ + Edit_Step inv_step = {}; + inv_step.type = reverse_types[step.type]; + inv_step.first_child = step.inverse_first_child; + inv_step.inverse_first_child = step.first_child; + inv_step.special_type = step.special_type; + inv_step.inverse_child_count = step.child_count; + inv_step.child_count = step.inverse_child_count; + + if (history->edit_count == history->edit_max){ + undo_stack_grow_edits(general, history); + } + result = history->edits + (history->edit_count++); + *result = inv_step; + } + + return(result); +} + +// TODO(allen): // TODO(allen): // TODO(allen): // TODO(allen): // TODO(allen): +// TODO(allen): burn this shit to the ground yo! +inline void +file_view_nullify_file(View *view){ + view->transient.file_data = null_file_viewing_data; +} + +internal void +update_view_line_height(System_Functions *system, Models *models, View *view, Face_ID font_id){ + Font_Pointers font = system->font.get_pointers_by_id(font_id); + Assert(font.valid); + view->transient.line_height = font.metrics->height; +} + +inline void +view_cursor_move(View *view, Full_Cursor cursor){ + Editing_File *file = view->transient.file_data.file; + Assert(file != 0); + view_set_cursor(view, cursor, 1, file->settings.unwrapped_lines); + view->transient.file_data.show_temp_highlight = 0; +} + +inline void +view_cursor_move(System_Functions *system, View *view, i32 pos){ + Editing_File *file = view->transient.file_data.file; + Full_Cursor cursor = file_compute_cursor(system, file, seek_pos(pos), 0); + view_cursor_move(view, cursor); +} + +inline void +view_cursor_move(System_Functions *system, View *view, f32 x, f32 y, b32 round_down = 0){ + Buffer_Seek seek; + if (view->transient.file_data.file->settings.unwrapped_lines){ + seek = seek_unwrapped_xy(x, y, round_down); + } + else{ + seek = seek_wrapped_xy(x, y, round_down); + } + Editing_File *file = view->transient.file_data.file; + Full_Cursor cursor = file_compute_cursor(system, file, seek, 0); + view_cursor_move(view, cursor); +} + +inline void +view_cursor_move(System_Functions *system, View *view, i32 line, i32 character){ + Editing_File *file = view->transient.file_data.file; + Full_Cursor cursor = file_compute_cursor(system, file, seek_line_char(line, character), 0); + view_cursor_move(view, cursor); +} + +// TODO(allen): Eliminate models. +inline void +view_show_file(View *view, Models *models){ + Editing_File *file = view->transient.file_data.file; + Assert(file != 0); + view->transient.map = file->settings.base_map_id; + if (view->transient.showing_ui != VUI_None){ + view->transient.showing_ui = VUI_None; + view->transient.changed_context_in_step = 1; + } +} + +internal void +view_set_file(System_Functions *system, View *view, Editing_File *file, Models *models){ + Assert(file != 0); + + if (view->transient.file_data.file != 0){ + touch_file(&models->working_set, view->transient.file_data.file); + } + + File_Edit_Positions *edit_pos = view->transient.edit_pos; + + if (edit_pos != 0){ + edit_pos_unset(view->transient.file_data.file, edit_pos); + edit_pos = 0; + } + + file_view_nullify_file(view); + view->transient.file_data.file = file; + + edit_pos = edit_pos_get_new(file, view->persistent.id); + view->transient.edit_pos = edit_pos; + + update_view_line_height(system, models, view, file->settings.font_id); + + if (edit_pos->cursor.line == 0){ + view_cursor_move(system, view, 0); + } + + if (view->transient.showing_ui == VUI_None){ + view_show_file(view, models); + } +} + +internal Relative_Scrolling +view_get_relative_scrolling(View *view){ + Relative_Scrolling result = {0}; + if (view->transient.edit_pos != 0){ + Vec2 cursor = view_get_cursor_xy(view); + result.scroll_y = cursor.y - view->transient.edit_pos->scroll.scroll_y; + result.target_y = cursor.y - view->transient.edit_pos->scroll.target_y; + } + return(result); +} + +internal void +view_set_relative_scrolling(View *view, Relative_Scrolling scrolling){ + Vec2 cursor = view_get_cursor_xy(view); + if (view->transient.edit_pos != 0){ + view->transient.edit_pos->scroll.scroll_y = cursor.y - scrolling.scroll_y; + view->transient.edit_pos->scroll.target_y = round32(clamp_bottom(0.f, cursor.y - scrolling.target_y)); + } +} + +inline i32_Rect +view_widget_rect(View *view, i32 line_height){ + Assert(view->transient.file_data.file); + Panel *panel = view->transient.panel; + i32_Rect result = panel->inner; + result.y0 = result.y0 + line_height + 2; + return(result); +} + +internal void +file_update_history_before_edit(Mem_Options *mem, Editing_File *file, Edit_Step step, u8 *str, History_Mode history_mode){ + if (!file->state.undo.undo.edits) return; + General_Memory *general = &mem->general; + + b32 can_merge = 0, do_merge = 0; + switch (step.type){ + case ED_NORMAL: + { + if (step.edit.len == 1 && str && char_is_alpha_numeric(*str)){ + can_merge = 1; + } + if (step.edit.len == 1 && str && (can_merge || char_is_whitespace(*str))){ + do_merge = 1; + } + + if (history_mode != hist_forward){ + file_post_history(general, file, step, do_merge, can_merge); + } + + file_post_undo(general, file, step, do_merge, can_merge); + }break; + + case ED_REVERSE_NORMAL: + { + if (history_mode != hist_forward){ + file_post_history(general, file, step, do_merge, can_merge); + } + + undo_stack_pop(&file->state.undo.undo); + + b32 restore_redos = 0; + Edit_Step *redo_end = 0; + + if (history_mode == hist_backward && file->state.undo.edit_history_cursor > 0){ + restore_redos = 1; + redo_end = file->state.undo.history.edits + (file->state.undo.edit_history_cursor - 1); + } + else if (history_mode == hist_forward && file->state.undo.history.edit_count > 0){ + restore_redos = 1; + redo_end = file->state.undo.history.edits + (file->state.undo.history.edit_count - 1); + } + + if (restore_redos){ + Edit_Step *redo_start = redo_end; + i32 steps_of_redo = 0; + i32 strings_of_redo = 0; + { + i32 undo_count = 0; + while (redo_start->type == ED_REDO || redo_start->type == ED_UNDO){ + if (redo_start->type == ED_REDO){ + if (undo_count > 0){ + --undo_count; + } + else{ + ++steps_of_redo; + strings_of_redo += redo_start->edit.len; + } + } + else{ + ++undo_count; + } + --redo_start; + } + } + + if (redo_start < redo_end){ + ++redo_start; + ++redo_end; + + if (file->state.undo.redo.edit_count + steps_of_redo > file->state.undo.redo.edit_max) + undo_stack_grow_edits(general, &file->state.undo.redo); + + if (file->state.undo.redo.size + strings_of_redo > file->state.undo.redo.max) + undo_stack_grow_string(general, &file->state.undo.redo, strings_of_redo); + + u8 *str_src = file->state.undo.history.strings + redo_end->edit.str_start; + u8 *str_dest_base = file->state.undo.redo.strings; + i32 str_redo_pos = file->state.undo.redo.size + strings_of_redo; + + Edit_Step *edit_src = redo_end; + Edit_Step *edit_dest = file->state.undo.redo.edits + file->state.undo.redo.edit_count + steps_of_redo; + + { + i32 undo_count = 0; + for (i32 i = 0; i < steps_of_redo;){ + --edit_src; + str_src -= edit_src->edit.len; + if (edit_src->type == ED_REDO){ + if (undo_count > 0){ + --undo_count; + } + else{ + ++i; + + --edit_dest; + *edit_dest = *edit_src; + + str_redo_pos -= edit_dest->edit.len; + edit_dest->edit.str_start = str_redo_pos; + + memcpy(str_dest_base + str_redo_pos, str_src, edit_dest->edit.len); + } + } + else{ + ++undo_count; + } + } + Assert(undo_count == 0); + } + + file->state.undo.redo.size += strings_of_redo; + file->state.undo.redo.edit_count += steps_of_redo; + } + } + }break; + + case ED_UNDO: + { + if (history_mode != hist_forward){ + file_post_history(general, file, step, do_merge, can_merge); + } + file_post_redo(general, file, step); + undo_stack_pop(&file->state.undo.undo); + }break; + + case ED_REDO: + { + if (step.edit.len == 1 && str && char_is_alpha_numeric(*str)) can_merge = 1; + if (step.edit.len == 1 && str && (can_merge || char_is_whitespace(*str))) do_merge = 1; + + if (history_mode != hist_forward){ + file_post_history(general, file, step, do_merge, can_merge); + } + + file_post_undo(general, file, step, do_merge, can_merge); + undo_stack_pop(&file->state.undo.redo); + }break; + } + + if (history_mode != hist_forward){ + if (step.type == ED_UNDO || step.type == ED_REDO){ + if (file->state.undo.current_block_normal){ + file_post_history_block(file, file->state.undo.history.edit_count - 1); + file->state.undo.current_block_normal = 0; + } + } + else{ + if (!file->state.undo.current_block_normal){ + file_post_history_block(file, file->state.undo.history.edit_count - 1); + file->state.undo.current_block_normal = 1; + } + } + } + else{ + if (file->state.undo.history_head_block == file->state.undo.history.edit_count){ + file_unpost_history_block(file); + file->state.undo.current_block_normal = !file->state.undo.current_block_normal; + } + } + + if (history_mode == hist_normal){ + file->state.undo.edit_history_cursor = file->state.undo.history.edit_count; + } +} + +inline void +file_pre_edit_maintenance(System_Functions *system, General_Memory *general, Editing_File *file){ + if (file->state.still_lexing){ + system->cancel_job(BACKGROUND_THREADS, file->state.lex_job); + if (file->state.swap_array.tokens){ + general_memory_free(general, file->state.swap_array.tokens); + file->state.swap_array.tokens = 0; + } + file->state.still_lexing = 0; + } + file_mark_dirty(file); +} + +internal void +file_edit_cursor_fix(System_Functions *system, Models *models, Editing_File *file, Editing_Layout *layout, Cursor_Fix_Descriptor desc){ + + Partition *part = &models->mem.part; + + Temp_Memory cursor_temp = begin_temp_memory(part); + i32 cursor_max = layout->panel_max_count * 3; + cursor_max += file->markers.marker_count; + Cursor_With_Index *cursors = push_array(part, Cursor_With_Index, cursor_max); + Cursor_With_Index *r_cursors = push_array(part, Cursor_With_Index, cursor_max); + Assert(cursors != 0); + + i32 cursor_count = 0; + i32 r_cursor_count = 0; + + for (Panel *panel = layout->used_sentinel.next; + panel != &layout->used_sentinel; + panel = panel->next){ + View *view = panel->view; + if (view->transient.file_data.file == file){ + Assert(view->transient.edit_pos != 0); + write_cursor_with_index(cursors, &cursor_count, view->transient.edit_pos->cursor.pos); + write_cursor_with_index(cursors, &cursor_count, view->transient.edit_pos->mark); + write_cursor_with_index(cursors, &cursor_count, view->transient.edit_pos->scroll_i); + } + } + + for (Marker_Array *marker_it = file->markers.sentinel.next; + marker_it != &file->markers.sentinel; + marker_it = marker_it->next){ + u32 count = marker_it->count; + Marker *markers = MarkerArrayBase(marker_it); + for (u32 i = 0; i < count; ++i){ + if (markers[i].lean_right){ + write_cursor_with_index(r_cursors, &r_cursor_count, markers[i].pos); + } + else{ + write_cursor_with_index(cursors, &cursor_count, markers[i].pos); + } + } + } + + if (cursor_count > 0 || r_cursor_count > 0){ + buffer_sort_cursors(cursors, cursor_count); + if (desc.is_batch){ + buffer_batch_edit_update_cursors(cursors, cursor_count, desc.batch, desc.batch_size, false); + buffer_batch_edit_update_cursors(r_cursors, r_cursor_count, desc.batch, desc.batch_size, true); + } + else{ + buffer_update_cursors(cursors, cursor_count, desc.start, desc.end, desc.shift_amount + (desc.end - desc.start), false); + buffer_update_cursors(r_cursors, r_cursor_count, desc.start, desc.end, desc.shift_amount + (desc.end - desc.start), true); + } + buffer_unsort_cursors(cursors, cursor_count); + + cursor_count = 0; + r_cursor_count = 0; + for (Panel *panel = layout->used_sentinel.next; + panel != &layout->used_sentinel; + panel = panel->next){ + View *view = panel->view; + if (view->transient.file_data.file == file){ + Assert(view->transient.edit_pos != 0); + + i32 cursor_pos = cursors[cursor_count++].pos; + Editing_File *file = view->transient.file_data.file; + Full_Cursor new_cursor = file_compute_cursor(system, file, seek_pos(cursor_pos), 0); + + GUI_Scroll_Vars scroll = view->transient.edit_pos->scroll; + + view->transient.edit_pos->mark = cursors[cursor_count++].pos; + i32 new_scroll_i = cursors[cursor_count++].pos; + if (view->transient.edit_pos->scroll_i != new_scroll_i){ + view->transient.edit_pos->scroll_i = new_scroll_i; + + Full_Cursor temp_cursor = file_compute_cursor(system, file, seek_pos(view->transient.edit_pos->scroll_i), 0); + + f32 y_offset = MOD(view->transient.edit_pos->scroll.scroll_y, view->transient.line_height); + f32 y_position = temp_cursor.wrapped_y; + if (file->settings.unwrapped_lines){ + y_position = temp_cursor.unwrapped_y; + } + y_position += y_offset; + + scroll.target_y += round32(y_position - scroll.scroll_y); + scroll.scroll_y = y_position; + } + + view_set_cursor_and_scroll(view, new_cursor, 1, view->transient.file_data.file->settings.unwrapped_lines, scroll); + } + } + + for (Marker_Array *marker_it = file->markers.sentinel.next; + marker_it != &file->markers.sentinel; + marker_it = marker_it->next){ + u32 count = marker_it->count; + Marker *markers = MarkerArrayBase(marker_it); + for (u32 i = 0; i < count; ++i){ + if (markers[i].lean_right){ + markers[i].pos = r_cursors[r_cursor_count++].pos; + } + else{ + markers[i].pos = cursors[cursor_count++].pos; + } + } + } + } + + end_temp_memory(cursor_temp); +} + +internal void +file_do_single_edit(System_Functions *system, Models *models, Editing_File *file, Edit_Spec spec, History_Mode history_mode){ + + Mem_Options *mem = &models->mem; + Editing_Layout *layout = &models->layout; + + // NOTE(allen): fixing stuff beforewards???? + file_update_history_before_edit(mem, file, spec.step, spec.str, history_mode); + file_pre_edit_maintenance(system, &mem->general, file); + + // NOTE(allen): actual text replacement + i32 shift_amount = 0; + General_Memory *general = &mem->general; + Partition *part = &mem->part; + + char *str = (char*)spec.str; + i32 start = spec.step.edit.start; + i32 end = spec.step.edit.end; + i32 str_len = spec.step.edit.len; + + i32 scratch_size = partition_remaining(part); + + Assert(scratch_size > 0); + i32 request_amount = 0; + Assert(end <= buffer_size(&file->state.buffer)); + while (buffer_replace_range(&file->state.buffer, start, end, str, str_len, &shift_amount, part->base + part->pos, scratch_size, &request_amount)){ + void *new_data = 0; + if (request_amount > 0){ + new_data = general_memory_allocate(general, request_amount); + } + void *old_data = buffer_edit_provide_memory(&file->state.buffer, new_data, request_amount); + if (old_data){ + general_memory_free(general, old_data); + } + } + + // NOTE(allen): token fixing + if (file->settings.tokens_exist){ + if (!file->settings.virtual_white){ + file_relex_parallel(system, models, file, start, end, shift_amount); + } + else{ + file_relex_serial(models, file, start, end, shift_amount); + } + } + + // NOTE(allen): meta data + Gap_Buffer *buffer = &file->state.buffer; + i32 line_start = buffer_get_line_number(&file->state.buffer, start); + i32 line_end = buffer_get_line_number(&file->state.buffer, end); + i32 replaced_line_count = line_end - line_start; + i32 new_line_count = buffer_count_newlines(&file->state.buffer, start, start+str_len); + i32 line_shift = new_line_count - replaced_line_count; + + Font_Pointers font = system->font.get_pointers_by_id(file->settings.font_id); + Assert(font.valid); + + file_grow_starts_as_needed(general, buffer, line_shift); + buffer_remeasure_starts(buffer, line_start, line_end, line_shift, shift_amount); + + file_allocate_character_starts_as_needed(general, file); + buffer_remeasure_character_starts(system, font, buffer, line_start, line_end, line_shift, file->state.character_starts, 0, file->settings.virtual_white); + + file_measure_wraps(system, models, file, font); + + // NOTE(allen): cursor fixing + Cursor_Fix_Descriptor desc = {0}; + desc.start = start; + desc.end = end; + desc.shift_amount = shift_amount; + file_edit_cursor_fix(system, models, file, layout, desc); +} + +internal void +file_do_batch_edit(System_Functions *system, Models *models, Editing_File *file, Edit_Spec spec, History_Mode history_mode, i32 batch_type){ + + Mem_Options *mem = &models->mem; + General_Memory *general = &mem->general; + Partition *part = &mem->part; + Editing_Layout *layout = &models->layout; + + // NOTE(allen): fixing stuff "beforewards"??? + Assert(spec.str == 0); + file_update_history_before_edit(mem, file, spec.step, 0, history_mode); + file_pre_edit_maintenance(system, &mem->general, file); + + // NOTE(allen): actual text replacement + u8 *str_base = file->state.undo.children.strings; + i32 batch_size = spec.step.child_count; + Buffer_Edit *batch = file->state.undo.children.edits + spec.step.first_child; + + Assert(spec.step.first_child < file->state.undo.children.edit_count); + Assert(batch_size >= 0); + + i32 scratch_size = partition_remaining(part); + Buffer_Batch_State state = {}; + i32 request_amount = 0; + while (buffer_batch_edit_step(&state, &file->state.buffer, batch, + (char*)str_base, batch_size, part->base + part->pos, + scratch_size, &request_amount)){ + void *new_data = 0; + if (request_amount > 0){ + new_data = general_memory_allocate(general, request_amount); + } + void *old_data = buffer_edit_provide_memory(&file->state.buffer, new_data, request_amount); + if (old_data){ + general_memory_free(general, old_data); + } + } + + i32 shift_total = state.shift_total; + + // NOTE(allen): token fixing + switch (batch_type){ + case BatchEdit_Normal: + { + if (file->settings.tokens_exist){ + // TODO(allen): Write a smart fast one here someday. + Buffer_Edit *first_edit = batch; + Buffer_Edit *last_edit = batch + batch_size - 1; + + if (!file->settings.virtual_white){ + file_relex_parallel(system, models, file, first_edit->start, last_edit->end, shift_total); + } + else{ + file_relex_serial(models, file, first_edit->start, last_edit->end, shift_total); + } + } + }break; + + case BatchEdit_PreserveTokens: + { + if (file->state.tokens_complete){ + Cpp_Token_Array tokens = file->state.token_array; + Cpp_Token *token = tokens.tokens; + Cpp_Token *end_token = tokens.tokens + tokens.count; + Cpp_Token original = {(Cpp_Token_Type)0}; + + Buffer_Edit *edit = batch; + Buffer_Edit *end_edit = batch + batch_size; + + i32 shift_amount = 0; + i32 local_shift = 0; + + for (; token < end_token; ++token){ + original = *token; + for (; edit < end_edit && edit->start <= original.start; ++edit){ + local_shift = (edit->len - (edit->end - edit->start)); + shift_amount += local_shift; + } + token->start += shift_amount; + local_shift = 0; + for (; edit < end_edit && edit->start < original.start + original.size; ++edit){ + local_shift += (edit->len - (edit->end - edit->start)); + } + token->size += local_shift; + shift_amount += local_shift; + } + } + }break; + } + + // TODO(allen): Let's try to switch to remeasuring here moron! + // We'll need to get the total shift from the actual batch edit state + // instead of from the cursor fixing. The only reason we're getting + // it from cursor fixing is because you're a lazy asshole. + + // NOTE(allen): meta data + file_measure_starts(general, &file->state.buffer); + + Font_Pointers font = system->font.get_pointers_by_id(file->settings.font_id); + Assert(font.valid); + + // TODO(allen): write the remeasurement version + file_allocate_character_starts_as_needed(general, file); + buffer_measure_character_starts(system, font, &file->state.buffer, file->state.character_starts, 0, file->settings.virtual_white); + + file_measure_wraps(system, models, file, font); + + // NOTE(allen): cursor fixing + Cursor_Fix_Descriptor desc = {0}; + desc.is_batch = 1; + desc.batch = batch; + desc.batch_size = batch_size; + file_edit_cursor_fix(system, models, file, layout, desc); +} + +inline void +file_replace_range(System_Functions *system, Models *models, Editing_File *file, i32 start, i32 end, char *str, i32 len){ + Edit_Spec spec = {}; + spec.step.type = ED_NORMAL; + spec.step.edit.start = start; + spec.step.edit.end = end; + spec.step.edit.len = len; + spec.str = (u8*)str; + file_do_single_edit(system, models, file, spec, hist_normal); +} + +inline void +file_clear(System_Functions *system, Models *models, Editing_File *file){ + if (models->hook_end_file != 0){ + models->hook_end_file(&models->app_links, file->id.id); + } + file_replace_range(system, models, file, 0, buffer_size(&file->state.buffer), 0, 0); +} + +inline void +view_post_paste_effect(View *view, f32 seconds, i32 start, i32 size, u32 color){ + Editing_File *file = view->transient.file_data.file; + file->state.paste_effect.start = start; + file->state.paste_effect.end = start + size; + file->state.paste_effect.color = color; + file->state.paste_effect.seconds_down = seconds; + file->state.paste_effect.seconds_max = seconds; +} + +internal Style* +get_style(Models *models, i32 i){ + return (&models->styles.styles[i]); +} + +internal Style* +main_style(Models *models){ + return (get_style(models, 0)); +} + +internal void +apply_history_edit(System_Functions *system, Models *models, Editing_File *file, View *view, Edit_Stack *stack, Edit_Step step, History_Mode history_mode){ + Edit_Spec spec = {}; + spec.step = step; + + if (step.child_count == 0){ + spec.step.edit.str_start = 0; + spec.str = stack->strings + step.edit.str_start; + + file_do_single_edit(system, models, file, spec, history_mode); + + if (view){ + view_cursor_move(system, view, step.edit.start + step.edit.len); + view->transient.edit_pos->mark = view->transient.edit_pos->cursor.pos; + + Style *style = main_style(models); + view_post_paste_effect(view, 0.333f, step.edit.start, step.edit.len, style->main.undo_color); + } + } + else{ + file_do_batch_edit(system, models, view->transient.file_data.file, spec, hist_normal, spec.step.special_type); + } +} + +internal void +view_undo_redo(System_Functions *system, Models *models, View *view, Edit_Stack *stack, Edit_Type expected_type){ + Editing_File *file = view->transient.file_data.file; + Assert(file != 0); + Assert(view->transient.edit_pos != 0); + if (stack->edit_count > 0){ + Edit_Step step = stack->edits[stack->edit_count - 1]; + Assert(step.type == expected_type); + apply_history_edit(system, models, file, view, stack, step, hist_normal); + } +} + +internal void +view_history_step(System_Functions *system, Models *models, View *view, History_Mode history_mode){ + Assert(history_mode != hist_normal); + + Editing_File *file = view->transient.file_data.file; + Assert(file != 0); + Assert(view->transient.edit_pos != 0); + b32 do_history_step = 0; + Edit_Step step = {}; + if (history_mode == hist_backward){ + if (file->state.undo.edit_history_cursor > 0){ + do_history_step = 1; + step = file->state.undo.history.edits[--file->state.undo.edit_history_cursor]; + } + } + else{ + if (file->state.undo.edit_history_cursor < file->state.undo.history.edit_count){ + Assert(((file->state.undo.history.edit_count - file->state.undo.edit_history_cursor) & 1) == 0); + step = file->state.undo.history.edits[--file->state.undo.history.edit_count]; + file->state.undo.history.size -= step.edit.len; + ++file->state.undo.edit_history_cursor; + do_history_step = 1; + } + } + + if (do_history_step){ + apply_history_edit(system, models, + file, view, + &file->state.undo.history, step, history_mode); + } +} + +internal String* +working_set_next_clipboard_string(General_Memory *general, Working_Set *working, i32 str_size){ + String *result = 0; + i32 clipboard_current = working->clipboard_current; + if (working->clipboard_size == 0){ + clipboard_current = 0; + working->clipboard_size = 1; + } + else{ + ++clipboard_current; + if (clipboard_current >= working->clipboard_max_size){ + clipboard_current = 0; + } + else if (working->clipboard_size <= clipboard_current){ + working->clipboard_size = clipboard_current+1; + } + } + result = &working->clipboards[clipboard_current]; + working->clipboard_current = clipboard_current; + working->clipboard_rolling = clipboard_current; + char *new_str; + if (result->str){ + new_str = (char*)general_memory_reallocate(general, result->str, result->size, str_size); + } + else{ + new_str = (char*)general_memory_allocate(general, str_size+1); + } + // TODO(allen): What if new_str == 0? + *result = make_string_cap(new_str, 0, str_size); + return result; +} + +internal String* +working_set_clipboard_index(Working_Set *working, i32 index){ + String *result = 0; + i32 size = working->clipboard_size; + i32 current = working->clipboard_current; + if (index >= 0 && size > 0){ + index = index % size; + index = current + size - index; + index = index % size; + result = &working->clipboards[index]; + } + return(result); +} + +internal String* +working_set_clipboard_head(Working_Set *working){ + String *result = 0; + if (working->clipboard_size > 0){ + working->clipboard_rolling = 0; + result = working_set_clipboard_index(working, working->clipboard_rolling); + } + return(result); +} + +internal String* +working_set_clipboard_roll_down(Working_Set *working){ + String *result = 0; + if (working->clipboard_size > 0){ + i32 clipboard_index = working->clipboard_rolling; + ++clipboard_index; + working->clipboard_rolling = clipboard_index; + result = working_set_clipboard_index(working, working->clipboard_rolling); + } + return(result); +} + +internal Edit_Spec +file_compute_edit(Mem_Options *mem, Editing_File *file, Buffer_Edit *edits, char *str_base, i32 str_size, Buffer_Edit *inverse_array, char *inv_str, i32 inv_max, i32 edit_count, i32 batch_type){ + General_Memory *general = &mem->general; + + i32 inv_str_pos = 0; + Buffer_Invert_Batch state = {}; + if (buffer_invert_batch(&state, &file->state.buffer, edits, edit_count, + inverse_array, inv_str, &inv_str_pos, inv_max)){ + Assert(0); + } + + i32 first_child = undo_children_push(general, &file->state.undo.children, edits, edit_count, (u8*)(str_base), str_size); + i32 inverse_first_child = undo_children_push(general, &file->state.undo.children, inverse_array, edit_count, (u8*)(inv_str), inv_str_pos); + + Edit_Spec spec = {}; + spec.step.type = ED_NORMAL; + spec.step.first_child = first_child; + spec.step.inverse_first_child = inverse_first_child; + spec.step.special_type = batch_type; + spec.step.child_count = edit_count; + spec.step.inverse_child_count = edit_count; + + return(spec); +} + +internal u32* +style_get_color(Style *style, Cpp_Token token){ + u32 *result; + if (token.flags & CPP_TFLAG_IS_KEYWORD){ + if (token.type == CPP_TOKEN_BOOLEAN_CONSTANT){ + result = &style->main.bool_constant_color; + } + else{ + result = &style->main.keyword_color; + } + } + else if(token.flags & CPP_TFLAG_PP_DIRECTIVE){ + result = &style->main.preproc_color; + } + else{ + switch (token.type){ + case CPP_TOKEN_COMMENT: + result = &style->main.comment_color; + break; + + case CPP_TOKEN_STRING_CONSTANT: + result = &style->main.str_constant_color; + break; + + case CPP_TOKEN_CHARACTER_CONSTANT: + result = &style->main.char_constant_color; + break; + + case CPP_TOKEN_INTEGER_CONSTANT: + result = &style->main.int_constant_color; + break; + + case CPP_TOKEN_FLOATING_CONSTANT: + result = &style->main.float_constant_color; + break; + + case CPP_PP_INCLUDE_FILE: + result = &style->main.include_color; + break; + + default: + result = &style->main.default_color; + break; + } + } + return result; +} + +internal void +file_full_remeasure(System_Functions *system, Models *models, Editing_File *file){ + Face_ID font_id = file->settings.font_id; + Font_Pointers font = system->font.get_pointers_by_id(font_id); + file_measure_wraps_and_fix_cursor(system, models, file, font); + + Editing_Layout *layout = &models->layout; + + for (Panel *panel = layout->used_sentinel.next; + panel != &layout->used_sentinel; + panel = panel->next){ + View *view = panel->view; + if (view->transient.file_data.file != file){ + continue; + } + update_view_line_height(system, models, view, font_id); + } +} + +internal void +file_set_font(System_Functions *system, Models *models, Editing_File *file, Face_ID font_id){ + file->settings.font_id = font_id; + file_full_remeasure(system, models, file); +} + +internal void +global_set_font(System_Functions *system, Models *models, Face_ID font_id){ + for (File_Node *node = models->working_set.used_sentinel.next; + node != &models->working_set.used_sentinel; + node = node->next){ + Editing_File *file = (Editing_File*)node; + file_set_font(system, models, file, font_id); + } + models->global_font_id = font_id; +} + +internal b32 +alter_font(System_Functions *system, Models *models, Face_ID font_id, Font_Settings *new_settings){ + b32 success = false; + + if (system->font.face_change_settings(font_id, new_settings)){ + success = true; + + for (File_Node *node = models->working_set.used_sentinel.next; + node != &models->working_set.used_sentinel; + node = node->next){ + Editing_File *file = (Editing_File*)node; + if (file->settings.font_id == font_id){ + file_full_remeasure(system, models, file); + } + } + } + + return(success); +} + +internal b32 +release_font(System_Functions *system, Models *models, Face_ID font_id, Face_ID replacement_id){ + b32 success = false; + + if (system->font.face_release(font_id)){ + Font_Pointers font = system->font.get_pointers_by_id(replacement_id); + if (!font.valid){ + Face_ID largest_id = system->font.get_largest_id(); + for (replacement_id = 1; replacement_id <= largest_id && replacement_id > 0; ++replacement_id){ + font = system->font.get_pointers_by_id(replacement_id); + if (font.valid){ + break; + } + } + Assert(replacement_id <= largest_id && replacement_id > 0); + } + + success = true; + for (File_Node *node = models->working_set.used_sentinel.next; + node != &models->working_set.used_sentinel; + node = node->next){ + Editing_File *file = (Editing_File*)node; + if (file->settings.font_id == font_id){ + file_set_font(system, models, file, replacement_id); + } + } + } + + return(success); +} + +inline void +view_show_GUI(View *view, Models *models, View_UI ui){ + view->transient.map = mapid_ui; + view->transient.showing_ui = ui; + view->transient.changed_context_in_step = true; +} + +inline void +view_show_interactive(System_Functions *system, View *view, Models *models, Interactive_Action action, Interactive_Interaction interaction, String query){ + view->transient.showing_ui = VUI_Interactive; + view->transient.action = action; + view->transient.interaction = interaction; + view->transient.dest = make_fixed_width_string(view->transient.dest_); + view->transient.list_i = 0; + + view->transient.map = mapid_ui; + + hot_directory_clean_end(&models->hot_directory); + hot_directory_reload(system, &models->hot_directory); + view->transient.changed_context_in_step = true; +} + +inline void +view_show_theme(View *view, Models *models){ + view->transient.map = mapid_ui; + view->transient.showing_ui = VUI_Theme; + view->transient.color_mode = CV_Mode_Library; + view->transient.color = super_color_create(0xFF000000); + view->transient.current_color_editing = 0; + view->transient.changed_context_in_step = true; +} + +internal String +make_string_terminated(Partition *part, char *str, i32 len){ + char *space = (char*)push_array(part, char, len + 1); + String string = make_string_cap(str, len, len+1); + copy_fast_unsafe_cs(space, string); + string.str = space; + terminate_with_null(&string); + return(string); +} + +internal void +init_normal_file(System_Functions *system, Models *models, Editing_File *file, char *buffer, i32 size){ + PRFL_FUNC_GROUP(); + + String val = make_string(buffer, size); + file_create_from_string(system, models, file, val, 0); + + if (file->settings.tokens_exist && file->state.token_array.tokens == 0){ + if (!file->settings.virtual_white){ + file_first_lex_parallel(system, models, file); + } + else{ + file_first_lex_serial(models, file); + } + } +} + +internal void +init_read_only_file(System_Functions *system, Models *models, Editing_File *file){ + String val = null_string; + file_create_from_string(system, models, file, val, FileCreateFlag_ReadOnly); + + if (file->settings.tokens_exist && file->state.token_array.tokens == 0){ + if (!file->settings.virtual_white){ + file_first_lex_parallel(system, models, file); + } + else{ + file_first_lex_serial(models, file); + } + } +} + +internal char* +make_string_part(Partition *scratch, String src, int32_t *size_out){ + char *r = {0}; + if (src.size > 0){ + r = push_array(scratch, char, src.size); + *size_out = src.size; + memcpy(r, src.str, *size_out); + } + return(r); +} + +internal char* +make_string_part(Partition *scratch, String src, int32_t cap, int32_t *size_out, int32_t *cap_out){ + cap = clamp_bottom(src.size, cap); + char *r = 0; + if (src.size > 0){ + r = push_array(scratch, char, cap); + *size_out = src.size; + *cap_out = cap; + memcpy(r, src.str, *size_out); + } + return(r); +} + +internal void +buffer_bind_name(Models *models, General_Memory *general, Partition *scratch, Working_Set *working_set, Editing_File *file, String file_name){ + String base_name = front_of_directory(file_name); + + Temp_Memory temp = begin_temp_memory(scratch); + + // List of conflict files. + Editing_File **conflict_file_ptrs = push_array(scratch, Editing_File*, 0); + int32_t conflict_count = 0; + + { + ++conflict_count; + Editing_File **new_file_ptr = push_array(scratch, Editing_File*, 1); + *new_file_ptr = file; + } + + File_Node *used_nodes = &working_set->used_sentinel; + for (File_Node *node = used_nodes->next; node != used_nodes; node = node->next){ + Editing_File *file_ptr = (Editing_File*)node; + if (file_is_ready(file_ptr) && match(base_name, file_ptr->base_name.name)){ + ++conflict_count; + Editing_File **new_file_ptr = push_array(scratch, Editing_File*, 1); + *new_file_ptr = file_ptr; + } + } + + // Fill conflict array. + Buffer_Name_Conflict_Entry *conflicts = push_array(scratch, Buffer_Name_Conflict_Entry, conflict_count); + + for (int32_t i = 0; i < conflict_count; ++i){ + Editing_File *file_ptr = conflict_file_ptrs[i]; + Buffer_Name_Conflict_Entry *entry = &conflicts[i]; + entry->buffer_id = file_ptr->id.id; + entry->file_name = make_string_part(scratch, file_ptr->canon.name, &entry->file_name_len); + entry->base_name = make_string_part(scratch, base_name, &entry->base_name_len); + + String b = base_name; + if (i > 0){ + b = file_ptr->unique_name.name; + } + entry->unique_name_in_out = make_string_part(scratch, b, 256, &entry->unique_name_len_in_out, &entry->unique_name_capacity); + } + + // Get user's resolution data. + if (models->buffer_name_resolver != 0){ + models->buffer_name_resolver(&models->app_links, conflicts, conflict_count); + } + + // Re-bind all of the files + for (int32_t i = 0; i < conflict_count; ++i){ + Editing_File *file_ptr = conflict_file_ptrs[i]; + if (file_ptr->unique_name.name.str != 0){ + buffer_unbind_name_low_level(working_set, file_ptr); + } + } + for (int32_t i = 0; i < conflict_count; ++i){ + Editing_File *file_ptr = conflict_file_ptrs[i]; + Buffer_Name_Conflict_Entry *entry = &conflicts[i]; + + String unique_name = make_string(entry->unique_name_in_out, entry->unique_name_len_in_out); + + buffer_bind_name_low_level(general, working_set, file_ptr, base_name, unique_name); + } + + end_temp_memory(temp); +} + +internal Editing_File* +open_file(System_Functions *system, Models *models, String filename){ + Working_Set *working_set = &models->working_set; + Editing_File *file = 0; + + if (terminate_with_null(&filename)){ + Editing_File_Name canon_name = {0}; + if (get_canon_name(system, &canon_name, filename)){ + file = working_set_contains_canon(working_set, canon_name.name); + + if (file == 0){ + Plat_Handle handle; + if (system->load_handle(canon_name.name.str, &handle)){ + Mem_Options *mem = &models->mem; + General_Memory *general = &mem->general; + Partition *part = &mem->part; + + file = working_set_alloc_always(working_set, general); + + buffer_bind_file(system, general, working_set, file, canon_name.name); + buffer_bind_name(models, general, part, working_set, file, filename); + + i32 size = system->load_size(handle); + char *buffer = 0; + b32 gen_buffer = 0; + + Temp_Memory temp = begin_temp_memory(part); + + buffer = push_array(part, char, size); + if (buffer == 0){ + buffer = (char*)general_memory_allocate(general, size); + Assert(buffer); + gen_buffer = 1; + } + + if (system->load_file(handle, buffer, size)){ + system->load_close(handle); + init_normal_file(system, models, file, buffer, size); + } + else{ + system->load_close(handle); + } + + if (gen_buffer){ + general_memory_free(general, buffer); + } + + end_temp_memory(temp); + } + } + } + } + + return(file); +} + +internal void +view_open_file(System_Functions *system, Models *models, View *view, String filename){ + Editing_File *file = open_file(system, models, filename); + if (file){ + view_set_file(system, view, file, models); + } +} + +internal void +view_interactive_new_file(System_Functions *system, Models *models, View *view, String filename){ + Working_Set *working_set = &models->working_set; + Editing_File *file = 0; + + if (terminate_with_null(&filename)){ + Editing_File_Name canon_name = {0}; + if (get_canon_name(system, &canon_name, filename)){ + file = working_set_contains_canon(working_set, canon_name.name); + + if (file != 0){ + file_clear(system, models, file); + } + else{ + Mem_Options *mem = &models->mem; + General_Memory *general = &mem->general; + Partition *part = &mem->part; + + file = working_set_alloc_always(working_set, general); + + buffer_bind_file(system, general, working_set, file, canon_name.name); + buffer_bind_name(models, general, part, working_set, file, filename); + + init_normal_file(system, models, file, 0, 0); + } + } + } + + if (file){ + view_set_file(system, view, file, models); + } +} + +internal void +kill_file(System_Functions *system, Models *models, Editing_File *file){ + Working_Set *working_set = &models->working_set; + + if (file != 0 && !file->settings.never_kill){ + if (models->hook_end_file != 0){ + models->hook_end_file(&models->app_links, file->id.id); + } + + buffer_unbind_name_low_level(working_set, file); + if (file->canon.name.size != 0){ + buffer_unbind_file(system, working_set, file); + } + file_close(system, &models->mem.general, file); + working_set_free_file(&models->mem.general, working_set, file); + + File_Node *used = &models->working_set.used_sentinel; + File_Node *node = used->next; + + + for (Panel *panel = models->layout.used_sentinel.next; + panel != &models->layout.used_sentinel; + panel = panel->next){ + View *view = panel->view; + if (view->transient.file_data.file != file){ + continue; + } + if (node != used){ + view->transient.file_data.file = 0; + view_set_file(system, view, (Editing_File*)node, models); + node = node->next; + } + else{ + view->transient.file_data.file = 0; + view_set_file(system, view, 0, models); + } + } + } +} + +internal void +kill_file_by_name(System_Functions *system, Models *models, String name){ + Editing_File *file = working_set_contains_name(&models->working_set, name); + kill_file(system, models, file); +} + +internal void +save_file_by_name(System_Functions *system, Models *models, String name){ + Editing_File *file = working_set_contains_name(&models->working_set, name); + if (file != 0){ + save_file(system, models, file); + } +} + +internal void +interactive_begin_sure_to_kill(System_Functions *system, View *view, Models *models, Editing_File *file){ + view_show_interactive(system, view, models, IAct_Sure_To_Kill, IInt_Sure_To_Kill, make_lit_string("Are you sure?")); + copy_ss(&view->transient.dest, file->unique_name.name); +} + +internal Try_Kill_Result +interactive_try_kill_file(System_Functions *system, Models *models, Editing_File *file){ + Try_Kill_Result result = TryKill_CannotKill; + + if (!file->settings.never_kill){ + if (buffer_needs_save(file)){ + result = TryKill_NeedDialogue; + } + else{ + kill_file(system, models, file); + result = TryKill_Success; + } + } + + return(result); +} + +internal b32 +interactive_try_kill_file(System_Functions *system, Models *models, View *view, Editing_File *file){ + Try_Kill_Result kill_result = interactive_try_kill_file(system, models, file); + b32 result = (kill_result == TryKill_NeedDialogue); + if (result){ + interactive_begin_sure_to_kill(system, view, models, file); + } + return(result); +} + +internal b32 +interactive_try_kill_file_by_name(System_Functions *system, Models *models, View *view, String name){ + b32 kill_dialogue = 0; + + Editing_File *file = working_set_contains_name(&models->working_set, name); + if (file){ + kill_dialogue = interactive_try_kill_file(system, models, view, file); + } + + return(kill_dialogue); +} + +internal void +interactive_view_complete(System_Functions *system, View *view, Models *models, String dest, i32 user_action){ + switch (view->transient.action){ + case IAct_Open: + { + view_open_file(system, models, view, dest); + view_show_file(view, models); + }break; + + case IAct_New: + if (dest.size > 0 && !char_is_slash(dest.str[dest.size-1])){ + view_interactive_new_file(system, models, view, dest); + view_show_file(view, models); + if (models->hook_new_file != 0){ + Editing_File *file = view->transient.file_data.file; + models->hook_new_file(&models->app_links, file->id.id); + } + }break; + + case IAct_OpenOrNew: + { + InvalidCodePath; + }break; + + case IAct_Switch: + { + Editing_File *file = working_set_contains_name(&models->working_set, dest); + if (file){ + view_set_file(system, view, file, models); + } + view_show_file(view, models); + }break; + + case IAct_Kill: + if (!interactive_try_kill_file_by_name(system, models, view, dest)){ + view_show_file(view, models); + }break; + + case IAct_Sure_To_Close: + switch (user_action){ + case 0: + { + models->keep_playing = 0; + }break; + + case 1: + { + view_show_file(view, models); + }break; + + case 2: // TODO(allen): Save all and close. + break; + }break; + + case IAct_Sure_To_Kill: + switch (user_action){ + case 0: + { + kill_file_by_name(system, models, dest); + view_show_file(view, models); + }break; + + case 1: + { + view_show_file(view, models); + }break; + + case 2: + { + save_file_by_name(system, models, dest); + kill_file_by_name(system, models, dest); + view_show_file(view, models); + }break; + }break; + } +} + +internal void +intbar_draw_string(System_Functions *system, Render_Target *target, File_Bar *bar, String str, u32 char_color){ + draw_string(system, target, bar->font_id, str, (i32)(bar->pos_x + bar->text_shift_x), (i32)(bar->pos_y + bar->text_shift_y), char_color); + bar->pos_x += font_string_width(system, target, bar->font_id, str); +} + +internal GUI_Scroll_Vars +view_reinit_scrolling(View *view){ + + view->transient.reinit_scrolling = false; + + i32 target_x = 0; + i32 target_y = 0; + Editing_File *file = view->transient.file_data.file; + Assert(file != 0); + if (file_is_ready(file)){ + Vec2 cursor = view_get_cursor_xy(view); + + f32 w = view_width(view); + f32 h = view_height(view); + + if (cursor.x >= target_x + w){ + target_x = round32(cursor.x - w*.35f); + } + + target_y = clamp_bottom(0, floor32(cursor.y - h*.5f)); + } + + GUI_Scroll_Vars scroll = {0}; + + scroll.target_y = target_y; + scroll.scroll_y = (f32)target_y; + scroll.prev_target_y = -1000; + + scroll.target_x = target_x; + scroll.scroll_x = (f32)target_x; + scroll.prev_target_x = -1000; + + return(scroll); +} + +internal b32 +file_step(View *view, i32_Rect region, Input_Summary *user_input, b32 is_active, b32 *consumed_l){ + b32 is_animating = false; + Editing_File *file = view->transient.file_data.file; + Assert(file != 0); + if (!file->is_loading){ + if (file->state.paste_effect.seconds_down > 0.f){ + file->state.paste_effect.seconds_down -= user_input->dt; + is_animating = true; + } + } + return(is_animating); +} + +internal void +do_widget(View *view, GUI_Target *target){ + Query_Slot *slot; + Query_Bar *bar; + + // NOTE(allen): A temporary measure... although in + // general we maybe want the user to be able to ask + // how large a particular section of the GUI turns + // out to be after layout? + f32 height = 0.f; + + for (slot = view->transient.query_set.used_slot; slot != 0; slot = slot->next){ + bar = slot->query_bar; + gui_do_text_field(target, bar->prompt, bar->string); + height += view->transient.line_height + 2; + } + + view->transient.widget_height = height; +} + +internal void +begin_exhaustive_loop(Exhaustive_File_Loop *loop, Hot_Directory *hdir){ + loop->front_name = make_fixed_width_string(loop->front_name_); + loop->full_path = make_fixed_width_string(loop->full_path_); + + loop->infos = hdir->file_list.infos; + loop->count = hdir->file_list.count; + + copy_ss(&loop->front_name, front_of_directory(hdir->string)); + get_absolutes(loop->front_name, &loop->absolutes, 1, 1); + copy_ss(&loop->full_path, hdir->canon_dir); + loop->r = loop->full_path.size; +} + +internal Exhaustive_File_Info +get_exhaustive_info(System_Functions *system, Working_Set *working_set, Exhaustive_File_Loop *loop, i32 i){ + local_persist String message_loaded = make_lit_string(" LOADED"); + local_persist String message_unsaved = make_lit_string(" LOADED *"); + local_persist String message_unsynced = make_lit_string(" LOADED !"); + + Exhaustive_File_Info result = {0}; + + result.info = loop->infos + i; + loop->full_path.size = loop->r; + append_sc(&loop->full_path, result.info->filename); + terminate_with_null(&loop->full_path); + + Editing_File *file = working_set_contains_canon(working_set, loop->full_path); + + String filename = make_string_cap(result.info->filename, result.info->filename_len, result.info->filename_len+1); + + result.is_folder = (result.info->folder != 0); + result.name_match = (wildcard_match_s(&loop->absolutes, filename, 0) != 0); + result.is_loaded = (file != 0 && file_is_ready(file)); + + result.message = null_string; + if (result.is_loaded){ + switch (file_get_sync(file)){ + case DirtyState_UpToDate: result.message = message_loaded; break; + case DirtyState_UnsavedChanges: result.message = message_unsaved; break; + case DirtyState_UnloadedChanges: result.message = message_unsynced; break; + } + } + + return(result); +} + +static Style_Color_Edit colors_to_edit[] = { + {Stag_Back, Stag_Default, Stag_Back, make_lit_string("Background")}, + {Stag_Margin, Stag_Default, Stag_Margin, make_lit_string("Margin")}, + {Stag_Margin_Hover, Stag_Default, Stag_Margin_Hover, make_lit_string("Margin Hover")}, + {Stag_Margin_Active, Stag_Default, Stag_Margin_Active, make_lit_string("Margin Active")}, + + {Stag_Cursor, Stag_At_Cursor, Stag_Cursor, make_lit_string("Cursor")}, + {Stag_At_Cursor, Stag_At_Cursor, Stag_Cursor, make_lit_string("Text At Cursor")}, + {Stag_Mark, Stag_Mark, Stag_Back, make_lit_string("Mark")}, + + {Stag_Highlight, Stag_At_Highlight, Stag_Highlight, make_lit_string("Highlight")}, + {Stag_At_Highlight, Stag_At_Highlight, Stag_Highlight, make_lit_string("Text At Highlight")}, + + {Stag_Default, Stag_Default, Stag_Back, make_lit_string("Text Default")}, + {Stag_Comment, Stag_Comment, Stag_Back, make_lit_string("Comment")}, + {Stag_Keyword, Stag_Keyword, Stag_Back, make_lit_string("Keyword")}, + {Stag_Str_Constant, Stag_Str_Constant, Stag_Back, make_lit_string("String Constant")}, + {Stag_Char_Constant, Stag_Char_Constant, Stag_Back, make_lit_string("Character Constant")}, + {Stag_Int_Constant, Stag_Int_Constant, Stag_Back, make_lit_string("Integer Constant")}, + {Stag_Float_Constant, Stag_Float_Constant, Stag_Back, make_lit_string("Float Constant")}, + {Stag_Bool_Constant, Stag_Bool_Constant, Stag_Back, make_lit_string("Boolean Constant")}, + {Stag_Preproc, Stag_Preproc, Stag_Back, make_lit_string("Preprocessor")}, + {Stag_Special_Character, Stag_Special_Character, Stag_Back, make_lit_string("Special Character")}, + + {Stag_Highlight_Junk, Stag_Default, Stag_Highlight_Junk, make_lit_string("Junk Highlight")}, + {Stag_Highlight_White, Stag_Default, Stag_Highlight_White, make_lit_string("Whitespace Highlight")}, + + {Stag_Paste, Stag_Paste, Stag_Back, make_lit_string("Paste Color")}, + + {Stag_Bar, Stag_Base, Stag_Bar, make_lit_string("Bar")}, + {Stag_Base, Stag_Base, Stag_Bar, make_lit_string("Bar Text")}, + {Stag_Pop1, Stag_Pop1, Stag_Bar, make_lit_string("Bar Pop 1")}, + {Stag_Pop2, Stag_Pop2, Stag_Bar, make_lit_string("Bar Pop 2")}, }; -struct Live_Views{ - View *views; - View free_sentinel; - i32 count, max; -}; +internal Single_Line_Input_Step +app_single_line_input_core(System_Functions *system, Working_Set *working_set, Key_Event_Data key, Single_Line_Mode mode){ + Single_Line_Input_Step result = {0}; + + b8 ctrl = key.modifiers[MDFR_CONTROL_INDEX]; + b8 cmnd = key.modifiers[MDFR_COMMAND_INDEX]; + b8 alt = key.modifiers[MDFR_ALT_INDEX]; + + if (key.keycode == key_back){ + result.hit_backspace = true; + if (mode.string->size > 0){ + result.made_a_change = true; + --mode.string->size; + switch (mode.type){ + case SINGLE_LINE_STRING: + { + mode.string->str[mode.string->size] = 0; + }break; + + case SINGLE_LINE_FILE: + { + if (!ctrl && !cmnd && !alt){ + char end_character = mode.string->str[mode.string->size]; + if (char_is_slash(end_character)){ + mode.string->size = reverse_seek_slash(*mode.string) + 1; + mode.string->str[mode.string->size] = 0; + hot_directory_set(system, mode.hot_directory, *mode.string); + } + else{ + mode.string->str[mode.string->size] = 0; + } + } + else{ + mode.string->str[mode.string->size] = 0; + } + }break; + } + } + } + + else if (key.character == '\n' || key.character == '\t'){ + // NOTE(allen): do nothing! + } + + else if (key.keycode == key_esc){ + result.hit_esc = true; + result.made_a_change = true; + } + + else if (key.character){ + result.hit_a_character = true; + if (!ctrl && !cmnd && !alt){ + if (mode.string->size + 1 < mode.string->memory_size){ + u8 new_character = (u8)key.character; + mode.string->str[mode.string->size] = new_character; + mode.string->size++; + mode.string->str[mode.string->size] = 0; + if (mode.type == SINGLE_LINE_FILE && char_is_slash(new_character)){ + hot_directory_set(system, mode.hot_directory, *mode.string); + } + result.made_a_change = true; + } + } + else{ + result.did_command = true; + } + } + + return result; +} +inline Single_Line_Input_Step +app_single_line_input_step(System_Functions *system, Key_Event_Data key, String *string){ + Single_Line_Mode mode = {}; + mode.type = SINGLE_LINE_STRING; + mode.string = string; + return app_single_line_input_core(system, 0, key, mode); +} + +inline Single_Line_Input_Step +app_single_file_input_step(System_Functions *system, + Working_Set *working_set, Key_Event_Data key, + String *string, Hot_Directory *hot_directory, + b32 try_to_match, b32 case_sensitive){ + Single_Line_Mode mode = {}; + mode.type = SINGLE_LINE_FILE; + mode.string = string; + mode.hot_directory = hot_directory; + mode.try_to_match = try_to_match; + mode.case_sensitive = case_sensitive; + return app_single_line_input_core(system, working_set, key, mode); +} + +inline Single_Line_Input_Step +app_single_number_input_step(System_Functions *system, Key_Event_Data key, String *string){ + Single_Line_Input_Step result = {}; + Single_Line_Mode mode = {}; + mode.type = SINGLE_LINE_STRING; + mode.string = string; + + char c = (char)key.character; + if (c == 0 || c == '\n' || char_is_numeric(c)) + result = app_single_line_input_core(system, 0, key, mode); + return result; +} + +internal void +append_label(String *string, i32 indent_level, char *message){ + i32 r = 0; + for (r = 0; r < indent_level; ++r){ + append_s_char(string, '>'); + } + append_sc(string, message); +} + +internal void +show_gui_line(GUI_Target *target, String *string, i32 indent_level, i32 h_align, char *message, char *follow_up){ + string->size = 0; + append_label(string, indent_level, message); + if (follow_up){ + append_padding(string, '-', h_align); + append_s_char(string, ' '); + append_sc(string, follow_up); + } + gui_do_text_field(target, *string, null_string); +} + +internal void +show_gui_int(GUI_Target *target, String *string, + i32 indent_level, i32 h_align, char *message, i32 x){ + string->size = 0; + append_label(string, indent_level, message); + append_padding(string, '-', h_align); + append_s_char(string, ' '); + append_int_to_str(string, x); + gui_do_text_field(target, *string, null_string); +} + +internal void +show_gui_u64(GUI_Target *target, String *string, + i32 indent_level, i32 h_align, char *message, u64 x){ + string->size = 0; + append_label(string, indent_level, message); + append_padding(string, '-', h_align); + append_s_char(string, ' '); + append_u64_to_str(string, x); + gui_do_text_field(target, *string, null_string); +} + +internal void +show_gui_int_int(GUI_Target *target, String *string, + i32 indent_level, i32 h_align, char *message, i32 x, i32 m){ + string->size = 0; + append_label(string, indent_level, message); + append_padding(string, '-', h_align); + append_s_char(string, ' '); + append_int_to_str(string, x); + append_s_char(string, '/'); + append_int_to_str(string, m); + gui_do_text_field(target, *string, null_string); +} + +internal void +show_gui_id(GUI_Target *target, String *string, + i32 indent_level, i32 h_align, char *message, GUI_id id){ + string->size = 0; + append_label(string, indent_level, message); + append_padding(string, '-', h_align); + append_ss(string, make_lit_string(" [0]: ")); + append_u64_to_str(string, id.id[0]); + append_padding(string, ' ', h_align + 26); + append_ss(string, make_lit_string(" [1]: ")); + append_u64_to_str(string, id.id[1]); + gui_do_text_field(target, *string, null_string); +} + +internal void +show_gui_float(GUI_Target *target, String *string, + i32 indent_level, i32 h_align, char *message, float x){ + string->size = 0; + append_label(string, indent_level, message); + append_padding(string, '-', h_align); + append_s_char(string, ' '); + append_float_to_str(string, x); + gui_do_text_field(target, *string, null_string); +} + +internal void +show_gui_scroll(GUI_Target *target, String *string, + i32 indent_level, i32 h_align, char *message, + GUI_Scroll_Vars scroll){ + show_gui_line (target, string, indent_level, 0, message, 0); + show_gui_float(target, string, indent_level+1, h_align, " scroll_y ", scroll.scroll_y); + show_gui_int (target, string, indent_level+1, h_align, " target_y ", scroll.target_y); + show_gui_int (target, string, indent_level+1, h_align, " prev_target_y ", scroll.prev_target_y); + show_gui_float(target, string, indent_level+1, h_align, " scroll_x ", scroll.scroll_x); + show_gui_int (target, string, indent_level+1, h_align, " target_x ", scroll.target_x); + show_gui_int (target, string, indent_level+1, h_align, " prev_target_x ", scroll.prev_target_x); +} + +internal void +show_gui_cursor(GUI_Target *target, String *string, + i32 indent_level, i32 h_align, char *message, + Full_Cursor cursor){ + show_gui_line (target, string, indent_level, 0, message, 0); + show_gui_int (target, string, indent_level+1, h_align, " pos ", cursor.pos); + show_gui_int (target, string, indent_level+1, h_align, " line ", cursor.line); + show_gui_int (target, string, indent_level+1, h_align, " column ", cursor.character); + show_gui_float(target, string, indent_level+1, h_align, " unwrapped_x ", cursor.unwrapped_x); + show_gui_float(target, string, indent_level+1, h_align, " unwrapped_y ", cursor.unwrapped_y); + show_gui_float(target, string, indent_level+1, h_align, " wrapped_x ", cursor.wrapped_x); + show_gui_float(target, string, indent_level+1, h_align, " wrapped_y ", cursor.wrapped_y); +} + +internal void +show_gui_region(GUI_Target *target, String *string, + i32 indent_level, i32 h_align, char *message, + i32_Rect region){ + show_gui_line(target, string, indent_level, 0, message, 0); + show_gui_int (target, string, indent_level+1, h_align, " x0 ", region.x0); + show_gui_int (target, string, indent_level+1, h_align, " y0 ", region.y0); + show_gui_int (target, string, indent_level+1, h_align, " x1 ", region.x1); + show_gui_int (target, string, indent_level+1, h_align, " y1 ", region.y1); +} + +inline void +gui_show_mouse(GUI_Target *target, String *string, i32 mx, i32 my){ + string->size = 0; + append_ss(string, make_lit_string("mouse: (")); + append_int_to_str(string, mx); + append_s_char(string, ','); + append_int_to_str(string, my); + append_s_char(string, ')'); + + gui_do_text_field(target, *string, null_string); +} + +internal View_Step_Result +step_file_view(System_Functions *system, View *view, Models *models, View *active_view, Input_Summary input){ + View_Step_Result result = {0}; + GUI_Target *target = &view->transient.gui_target; + Key_Input_Data keys = input.keys; + + b32 show_scrollbar = !view->transient.hide_scrollbar; + + if (view->transient.showing_ui != VUI_None){ + b32 did_esc = false; + for (i32 i = 0; i < keys.count; ++i){ + Key_Event_Data key = keys.keys[i]; + if (key.keycode == key_esc){ + did_esc = 1; + break; + } + } + + if (did_esc){ + view_show_file(view, models); + result.consume_esc = true; + } + } + + gui_begin_top_level(target, input); + { + if (view->transient.showing_ui != VUI_Debug && !view->transient.hide_file_bar){ + gui_do_top_bar(target); + } + do_widget(view, target); + + if (view->transient.showing_ui == VUI_None){ + + gui_begin_serial_section(target); + { + i32 delta = 9*view->transient.line_height; + GUI_id scroll_context = {0}; + scroll_context.id[1] = view->transient.showing_ui; + scroll_context.id[0] = (u64)(view->transient.file_data.file); + + Assert(view->transient.file_data.file != 0); + Assert(view->transient.edit_pos != 0); + + GUI_Scroll_Vars *scroll = &view->transient.edit_pos->scroll; + gui_begin_scrollable(target, scroll_context, *scroll, + delta, show_scrollbar); + + gui_do_file(target); + gui_end_scrollable(target); + } + gui_end_serial_section(target); + } + else{ + switch (view->transient.showing_ui){ + case VUI_Theme: + { + if (view != active_view){ + view->transient.hot_file_view = active_view; + } + + String message = {0}; + String empty_string = {0}; + + GUI_id id = {0}; + id.id[1] = VUI_Theme + ((u64)view->transient.color_mode << 32); + + GUI_id scroll_context = {0}; + scroll_context.id[0] = 0; + scroll_context.id[1] = VUI_Theme + ((u64)view->transient.color_mode << 32); + + switch (view->transient.color_mode){ + case CV_Mode_Library: + { + message = make_lit_string("Current Theme - Click to Edit"); + gui_do_text_field(target, message, empty_string); + + id.id[0] = (u64)(main_style(models)); + if (gui_do_style_preview(target, id, 0)){ + view->transient.color_mode = CV_Mode_Adjusting; + } + + Assert(view->transient.file_data.file != 0); + + message = make_lit_string("Set Font"); + id.id[0] = (u64)(&view->transient.file_data.file->settings.font_id); + if (gui_do_button(target, id, message)){ + view->transient.color_mode = CV_Mode_Font; + } + + + message = make_lit_string("Set Global Font"); + id.id[0] = (u64)(&models->global_font_id); + + if (gui_do_button(target, id, message)){ + view->transient.color_mode = CV_Mode_Global_Font; + } + + message = make_lit_string("Theme Library - Click to Select"); + gui_do_text_field(target, message, empty_string); + + gui_begin_scrollable(target, scroll_context, view->transient.gui_scroll, + 9*view->transient.line_height, show_scrollbar); + + { + i32 count = models->styles.count; + for (i32 i = 1; i < count; ++i){ + Style *style = get_style(models, i); + id.id[0] = (u64)(style); + if (gui_do_style_preview(target, id, i)){ + style_copy(main_style(models), style); + } + } + } + + gui_end_scrollable(target); + } + break; + + case CV_Mode_Font: + case CV_Mode_Global_Font: + { + Editing_File *file = view->transient.file_data.file; + Assert(file != 0); + + id.id[0] = 0; + if (gui_do_button(target, id, make_lit_string("Back"))){ + view->transient.color_mode = CV_Mode_Library; + } + + gui_begin_scrollable(target, scroll_context, view->transient.gui_scroll, 9*view->transient.line_height, show_scrollbar); + + Face_ID font_id = file->settings.font_id; + Face_ID new_font_id = 0; + + Face_ID largest_id = system->font.get_largest_id(); + for (Face_ID i = 1; i <= largest_id; ++i){ + Font_Pointers font = system->font.get_pointers_by_id(i); + if (font.valid){ + Font_Settings *settings = font.settings; + Font_Metrics *metrics = font.metrics; + + char space[512]; + String m = make_fixed_width_string(space); + if (i == font_id){ + append(&m, "*"); + } + + append(&m, " \""); + append(&m, make_string(metrics->name, metrics->name_len)); + append(&m, "\" "); + append_int_to_str(&m, settings->parameters.pt_size); + append(&m, " "); + append(&m, (char*)(settings->parameters.italics?"italics ":"")); + append(&m, (char*)(settings->parameters.bold?"bold ":"")); + append(&m, (char*)(settings->parameters.underline?"underline ":"")); + append(&m, (char*)(settings->parameters.use_hinting?"hinting ":"")); + + if (i == font_id){ + append(&m, "*"); + } + + id.id[0] = i*2 + 0; + if (gui_do_button(target, id, m)){ + if (new_font_id == 0){ + new_font_id = i; + } + } + + id.id[0] = i*2 + 1; + if (gui_do_button(target, id, make_lit_string("edit"))){ + view->transient.font_edit_id = i; + if (view->transient.color_mode == CV_Mode_Font){ + view->transient.color_mode = CV_Mode_Font_Editing; + } + else{ + view->transient.color_mode = CV_Mode_Global_Font_Editing; + } + } + } + } + + id.id[0] = largest_id*2 + 2; + if (gui_do_button(target, id, make_lit_string("new face"))){ + if (new_font_id == 0){ + Font_Pointers font = system->font.get_pointers_by_id(font_id); + view->transient.font_edit_id = system->font.face_allocate_and_init(font.settings); + if (view->transient.color_mode == CV_Mode_Font){ + view->transient.color_mode = CV_Mode_Font_Editing; + } + else{ + view->transient.color_mode = CV_Mode_Global_Font_Editing; + } + } + } + + if (new_font_id != 0){ + if (view->transient.color_mode == CV_Mode_Font && new_font_id != font_id){ + file_set_font(system, models, file, new_font_id); + } + else if (new_font_id != font_id || new_font_id != models->global_font_id){ + global_set_font(system, models, new_font_id); + } + } + + gui_end_scrollable(target); + }break; + + case CV_Mode_Font_Editing: + case CV_Mode_Global_Font_Editing: + { + id.id[0] = 0; + if (gui_do_button(target, id, make_lit_string("Back"))){ + if (view->transient.color_mode == CV_Mode_Font_Editing){ + view->transient.color_mode = CV_Mode_Font; + } + else{ + view->transient.color_mode = CV_Mode_Global_Font; + } + } + + gui_begin_scrollable(target, scroll_context, view->transient.gui_scroll, 9*view->transient.line_height, show_scrollbar); + + Face_ID font_edit_id = view->transient.font_edit_id; + Font_Pointers font = system->font.get_pointers_by_id(font_edit_id); + Font_Settings *settings = font.settings; + Font_Metrics *metrics = font.metrics; + Font_Settings new_settings = *settings; + b32 has_new_settings = false; + + char space[128]; + String m = make_fixed_width_string(space); + copy(&m, "Size Up ("); + append_int_to_str(&m, settings->parameters.pt_size); + append(&m, ")"); + ++id.id[0]; + if (gui_do_button(target, id, m)){ + if (!has_new_settings){ + has_new_settings = true; + ++new_settings.parameters.pt_size; + } + } + + copy(&m, "Size Down ("); + append_int_to_str(&m, settings->parameters.pt_size); + append(&m, ")"); + ++id.id[0]; + if (gui_do_button(target, id, m)){ + if (!has_new_settings){ + has_new_settings = true; + --new_settings.parameters.pt_size; + } + } + + copy(&m, "Italics ["); + append(&m, (char*)(settings->parameters.italics?"+":" ")); + append(&m, "]"); + ++id.id[0]; + if (gui_do_button(target, id, m)){ + if (!has_new_settings){ + has_new_settings = true; + new_settings.parameters.italics = !new_settings.parameters.italics; + } + } + + copy(&m, "Bold ["); + append(&m, (char*)(settings->parameters.bold?"+":" ")); + append(&m, "]"); + ++id.id[0]; + if (gui_do_button(target, id, m)){ + if (!has_new_settings){ + has_new_settings = true; + new_settings.parameters.bold = !new_settings.parameters.bold; + } + } + + copy(&m, "Underline ["); + append(&m, (char*)(settings->parameters.underline?"+":" ")); + append(&m, "]"); + ++id.id[0]; + if (gui_do_button(target, id, m)){ + if (!has_new_settings){ + has_new_settings = true; + new_settings.parameters.underline = !new_settings.parameters.underline; + } + } + + copy(&m, "Hinting ["); + append(&m, (char*)(settings->parameters.use_hinting?"+":" ")); + append(&m, "]"); + ++id.id[0]; + if (gui_do_button(target, id, m)){ + if (!has_new_settings){ + has_new_settings = true; + new_settings.parameters.use_hinting = !new_settings.parameters.use_hinting; + } + } + + copy(&m, "Current Family: "); + append(&m, make_string(metrics->name, metrics->name_len)); + ++id.id[0]; + gui_do_button(target, id, m); + + i32 total_count = system->font.get_loadable_count(); + for (i32 i = 0; i < total_count; ++i){ + Font_Loadable_Description loadable = {0}; + system->font.get_loadable(i, &loadable); + + if (loadable.valid){ + String name = make_string(loadable.display_name, loadable.display_len); + ++id.id[0]; + if (gui_do_button(target, id, name)){ + if (!has_new_settings){ + has_new_settings = true; + memcpy(&new_settings.stub, &loadable.stub, sizeof(loadable.stub)); + } + } + } + } + + gui_end_scrollable(target); + + if (has_new_settings){ + alter_font(system, models, font_edit_id, &new_settings); + } + }break; + + case CV_Mode_Adjusting: + { + Style *style = main_style(models); + u32 *edit_color = 0; + u32 *fore = 0, *back = 0; + i32 i = 0; + + message = make_lit_string("Back"); + + id.id[0] = 0; + if (gui_do_button(target, id, message)){ + view->transient.color_mode = CV_Mode_Library; + } + + gui_begin_scrollable(target, scroll_context, view->transient.gui_scroll, 9*view->transient.line_height, show_scrollbar); + + i32 next_color_editing = view->transient.current_color_editing; + + for (i = 0; i < ArrayCount(colors_to_edit); ++i){ + edit_color = style_index_by_tag(&style->main, colors_to_edit[i].target); + id.id[0] = (u64)(edit_color); + + fore = style_index_by_tag(&style->main, colors_to_edit[i].fore); + back = style_index_by_tag(&style->main, colors_to_edit[i].back); + + if (gui_do_color_button(target, id, *fore, *back, colors_to_edit[i].text)){ + next_color_editing = i; + view->transient.color_cursor = 0; + } + + if (view->transient.current_color_editing == i){ + GUI_Item_Update update = {0}; + char text_space[7]; + String text = make_fixed_width_string(text_space); + + color_to_hexstr(&text, *edit_color); + if (gui_do_text_with_cursor(target, view->transient.color_cursor, text, &update)){ + b32 r = false; + i32 j = 0; + + for (j = 0; j < keys.count; ++j){ + Key_Code key = keys.keys[j].keycode; + switch (key){ + case key_left: + { + --view->transient.color_cursor; + r = true; + result.consume_keys = 1; + }break; + case key_right: + { + ++view->transient.color_cursor; + r = true; + result.consume_keys = 1; + }break; + + case key_up: + { + if (next_color_editing > 0){ + --next_color_editing; + } + result.consume_keys = 1; + }break; + + case key_down: + { + if (next_color_editing <= ArrayCount(colors_to_edit)-1){ + ++next_color_editing; + } + result.consume_keys = 1; + }break; + + default: + { + if ((key >= '0' && key <= '9') || (key >= 'a' && key <= 'f') || (key >= 'A' && key <= 'F')){ + text.str[view->transient.color_cursor] = (char)key; + r = true; + result.consume_keys = 1; + } + }break; + } + + if (view->transient.color_cursor < 0){ + view->transient.color_cursor = 0; + } + if (view->transient.color_cursor >= 6){ + view->transient.color_cursor = 5; + } + } + + if (r){ + hexstr_to_color(text, edit_color); + gui_rollback(target, &update); + gui_do_text_with_cursor(target, view->transient.color_cursor, text, 0); + } + } + } + } + + if (view->transient.current_color_editing != next_color_editing){ + view->transient.current_color_editing = next_color_editing; + view->transient.color_cursor = 0; + } + + gui_end_scrollable(target); + }break; + } + }break; + + case VUI_Interactive: + { + b32 complete = 0; + char comp_dest_space[1024]; + String comp_dest = make_fixed_width_string(comp_dest_space); + i32 comp_action = 0; + + GUI_id id = {0}; + id.id[1] = VUI_Interactive + ((u64)view->transient.interaction << 32); + + GUI_id scroll_context = {0}; + scroll_context.id[1] = VUI_Interactive + ((u64)view->transient.interaction << 32); + + Key_Code user_up_key = models->user_up_key; + Key_Code user_down_key = models->user_down_key; + Key_Modifier user_up_key_modifier = models->user_up_key_modifier; + Key_Modifier user_down_key_modifier = models->user_down_key_modifier; + + switch (view->transient.interaction){ + case IInt_Sys_File_List: + { + b32 autocomplete_with_enter = true; + b32 activate_directly = false; + + if (view->transient.action == IAct_New){ + autocomplete_with_enter = false; + } + + String message = null_string; + switch (view->transient.action){ + case IAct_OpenOrNew: + case IAct_Open: + { + message = make_lit_string("Open: "); + }break; + + case IAct_New: + { + message = make_lit_string("New: "); + }break; + } + + GUI_Item_Update update = {0}; + Hot_Directory *hdir = &models->hot_directory; + + b32 do_open_or_new = false; + for (i32 i = 0; i < keys.count; ++i){ + Key_Event_Data key = keys.keys[i]; + Single_Line_Input_Step step = app_single_file_input_step(system, &models->working_set, key, + &hdir->string, hdir, 1, 0); + + if (step.made_a_change){ + view->transient.list_i = 0; + result.consume_keys = true; + } + + if (key.keycode == '\n'){ + if (!autocomplete_with_enter){ + activate_directly = true; + result.consume_keys = true; + } + else if (view->transient.action == IAct_OpenOrNew){ + do_open_or_new = true; + result.consume_keys = true; + } + } + } + + gui_do_text_field(target, message, hdir->string); + + b32 snap_into_view = false; + scroll_context.id[0] = (u64)(hdir); + if (gui_scroll_was_activated(target, scroll_context)){ + snap_into_view = true; + } + gui_begin_scrollable(target, scroll_context, view->transient.gui_scroll, 9*view->transient.line_height, show_scrollbar); + + id.id[0] = (u64)(hdir) + 1; + + if (gui_begin_list(target, id, view->transient.list_i, 0, snap_into_view, &update)){ + // TODO(allen): Allow me to handle key consumption correctly here! + gui_standard_list(target, id, &view->transient.gui_scroll, view->transient.scroll_region, &keys, &view->transient.list_i, &update, user_up_key, user_up_key_modifier, user_down_key, user_down_key_modifier); + } + + b32 do_new_directory = false; + Exhaustive_File_Loop loop; + begin_exhaustive_loop(&loop, hdir); + for (i32 i = 0; i < loop.count; ++i){ + Exhaustive_File_Info file_info = get_exhaustive_info(system, &models->working_set, &loop, i); + + if (file_info.name_match){ + id.id[0] = (u64)(file_info.info); + + char *str = file_info.info->filename; + i32 len = file_info.info->filename_len; + String filename = make_string_cap(str, len, len + 1); + + if (gui_do_file_option(target, id, filename, file_info.is_folder, file_info.message)){ + if (file_info.is_folder){ + set_last_folder_sc(&hdir->string, file_info.info->filename, '/'); + do_new_directory = true; + } + + else if (autocomplete_with_enter){ + complete = true; + copy_ss(&comp_dest, loop.full_path); + if (view->transient.action == IAct_OpenOrNew){ + view->transient.action = IAct_Open; + } + } + } + + if (do_open_or_new){ + do_open_or_new = false; + } + } + } + + gui_end_list(target); + + if (activate_directly || do_open_or_new){ + complete = true; + copy_ss(&comp_dest, hdir->string); + if (do_open_or_new){ + view->transient.action = IAct_New; + } + } + + if (do_new_directory){ + hot_directory_reload(system, hdir); + } + + gui_end_scrollable(target); + }break; + + case IInt_Live_File_List: + { + local_persist String message_unsaved = make_lit_string(" *"); + local_persist String message_unsynced = make_lit_string(" !"); + + String message = null_string; + switch (view->transient.action){ + case IAct_Switch: + { + message = make_lit_string("Switch: "); + }break; + + case IAct_Kill: + { + message = make_lit_string("Kill: "); + }break; + } + + Working_Set *working_set = &models->working_set; + Editing_Layout *layout = &models->layout; + GUI_Item_Update update = {0}; + + for (i32 i = 0; i < keys.count; ++i){ + Key_Event_Data key = keys.keys[i]; + Single_Line_Input_Step step = app_single_line_input_step(system, key, &view->transient.dest); + if (step.made_a_change){ + view->transient.list_i = 0; + result.consume_keys = 1; + } + } + + Absolutes absolutes = {0}; + get_absolutes(view->transient.dest, &absolutes, 1, 1); + + gui_do_text_field(target, message, view->transient.dest); + + b32 snap_into_view = 0; + scroll_context.id[0] = (u64)(working_set); + if (gui_scroll_was_activated(target, scroll_context)){ + snap_into_view = 1; + } + gui_begin_scrollable(target, scroll_context, view->transient.gui_scroll, 9*view->transient.line_height, show_scrollbar); + + id.id[0] = (u64)(working_set) + 1; + if (gui_begin_list(target, id, view->transient.list_i, 0, snap_into_view, &update)){ + gui_standard_list(target, id, &view->transient.gui_scroll, view->transient.scroll_region, &keys, &view->transient.list_i, &update, user_up_key, user_up_key_modifier, user_down_key, user_down_key_modifier); + } + + { + Partition *part = &models->mem.part; + Temp_Memory temp = begin_temp_memory(part); + i32 reserved_top = 0, i = 0; + + partition_align(part, 8); + Editing_File **reserved_files = push_array(part, Editing_File*, 0); + + for (File_Node *node = working_set->used_sentinel.next; + node != &working_set->used_sentinel; + node = node->next){ + Editing_File *file = (Editing_File*)node; + Assert(!file->is_dummy); + + if (wildcard_match_s(&absolutes, file->unique_name.name, 0) != 0){ + b32 is_viewed = file_is_viewed(layout, file); + + if (is_viewed){ + reserved_files[reserved_top++] = file; + } + else{ + if (file->unique_name.name.str[0] == '*'){ + reserved_files[reserved_top++] = file; + } + else{ + message = null_string; + if (!file->settings.unimportant){ + switch (file_get_sync(file)){ + case DirtyState_UnloadedChanges: message = message_unsynced; break; + case DirtyState_UnsavedChanges: message = message_unsaved; break; + } + } + + id.id[0] = (u64)(file); + if (gui_do_file_option(target, id, file->unique_name.name, 0, message)){ + complete = 1; + copy_ss(&comp_dest, file->unique_name.name); + } + } + } + } + } + + for (i = 0; i < reserved_top; ++i){ + Editing_File *file = reserved_files[i]; + + message = null_string; + if (!file->settings.unimportant){ + switch (file_get_sync(file)){ + case DirtyState_UnloadedChanges: message = message_unsynced; break; + case DirtyState_UnsavedChanges: message = message_unsaved; break; + } + } + + id.id[0] = (u64)(file); + if (gui_do_file_option(target, id, file->unique_name.name, 0, message)){ + complete = 1; + copy_ss(&comp_dest, file->unique_name.name); + } + } + + end_temp_memory(temp); + } + + gui_end_list(target); + + gui_end_scrollable(target); + }break; + + case IInt_Sure_To_Close: + { + i32 action = -1; + + String empty_str = {0}; + String message = make_lit_string("There is one or more files unsaved changes, close anyway?"); + + gui_do_text_field(target, message, empty_str); + + id.id[0] = (u64)('y'); + message = make_lit_string("(Y)es"); + if (gui_do_fixed_option(target, id, message, 'y')){ + action = 0; + } + + id.id[0] = (u64)('n'); + message = make_lit_string("(N)o"); + if (gui_do_fixed_option(target, id, message, 'n')){ + action = 1; + } + + if (action != -1){ + complete = 1; + copy_ss(&comp_dest, view->transient.dest); + comp_action = action; + } + }break; + + case IInt_Sure_To_Kill: + { + i32 action = -1; + + String empty_str = {0}; + String message = make_lit_string("There are unsaved changes, close anyway?"); + + gui_do_text_field(target, message, empty_str); + + id.id[0] = (u64)('y'); + message = make_lit_string("(Y)es"); + if (gui_do_fixed_option(target, id, message, 'y')){ + action = 0; + } + + id.id[0] = (u64)('n'); + message = make_lit_string("(N)o"); + if (gui_do_fixed_option(target, id, message, 'n')){ + action = 1; + } + + id.id[0] = (u64)('s'); + message = make_lit_string("(S)ave and kill"); + if (gui_do_fixed_option(target, id, message, 's')){ + action = 2; + } + + if (action != -1){ + complete = 1; + copy_ss(&comp_dest, view->transient.dest); + comp_action = action; + } + }break; + } + + if (complete){ + terminate_with_null(&comp_dest); + interactive_view_complete(system, view, models, comp_dest, comp_action); + } + }break; + + case VUI_Debug: + { + GUI_id scroll_context = {0}; + scroll_context.id[1] = VUI_Debug + ((u64)view->transient.debug_vars.mode << 32); + + GUI_id id = {0}; + id.id[1] = VUI_Debug + ((u64)view->transient.debug_vars.mode << 32); + + // TODO(allen): + // + Incoming input + // + Memory info + // + Thread info + // + View inspection + // - auto generate? + // - expand/collapse sections + // - Buffer inspection + // - Command maps inspection + // - Clipboard inspection + + String empty_str = null_string; + + char space1[512]; + String string = make_fixed_width_string(space1); + + // Time Watcher + { + string.size = 0; + u64 time = system->now_time(); + + append_ss(&string, make_lit_string("last redraw: ")); + append_u64_to_str(&string, time); + + gui_do_text_field(target, string, empty_str); + } + + { + i32 prev_mode = view->transient.debug_vars.mode; + for (i32 i = 0; i < keys.count; ++i){ + Key_Event_Data key = keys.keys[i]; + if (key.modifiers[MDFR_CONTROL_INDEX] == 0 && + key.modifiers[MDFR_ALT_INDEX] == 0){ + if (key.keycode == 'i'){ + view->transient.debug_vars.mode = DBG_Input; + } + if (key.keycode == 'm'){ + view->transient.debug_vars.mode = DBG_Threads_And_Memory; + } + if (key.keycode == 'v'){ + view->transient.debug_vars.mode = DBG_View_Inspection; + } + } + } + if (prev_mode != view->transient.debug_vars.mode){ + result.consume_keys = 1; + } + } + + gui_begin_scrollable(target, scroll_context, view->transient.gui_scroll, + 9*view->transient.line_height, show_scrollbar); + + switch (view->transient.debug_vars.mode){ + case DBG_Input: + { + Debug_Data *debug = &models->debug; + + gui_show_mouse(target, &string, input.mouse.x, input.mouse.y); + + Debug_Input_Event *input_event = debug->input_events; + for (i32 i = 0; + i < ArrayCount(debug->input_events); + ++i, ++input_event){ + string.size = 0; + + if (input_event->is_hold){ + append_ss(&string, make_lit_string("hold: ")); + } + else{ + append_ss(&string, make_lit_string("press: ")); + } + + if (input_event->is_ctrl){ + append_ss(&string, make_lit_string("ctrl-")); + } + else{ + append_ss(&string, make_lit_string(" -")); + } + + if (input_event->is_alt){ + append_ss(&string, make_lit_string("alt-")); + } + else{ + append_ss(&string, make_lit_string(" -")); + } + + if (input_event->is_shift){ + append_ss(&string, make_lit_string("shift ")); + } + else{ + append_ss(&string, make_lit_string(" ")); + } + + if (input_event->key > ' ' && input_event->key <= '~'){ + append_ss(&string, make_string(&input_event->key, 1)); + } + else if (input_event->key == ' '){ + append_ss(&string, make_lit_string("space")); + } + else if (input_event->key == '\n'){ + append_ss(&string, make_lit_string("\\n")); + } + else if (input_event->key == '\t'){ + append_ss(&string, make_lit_string("\\t")); + } + else{ + String str; + str.str = global_key_name(input_event->key, &str.size); + if (str.str){ + str.memory_size = str.size + 1; + append_ss(&string, str); + } + else{ + append_ss(&string, make_lit_string("unrecognized!")); + } + } + + if (input_event->consumer[0] != 0){ + append_padding(&string, ' ', 40); + append_sc(&string, input_event->consumer); + } + + gui_do_text_field(target, string, empty_str); + } + }break; + + case DBG_Threads_And_Memory: + { + b8 threads[4]; + i32 pending = 0; + + if (system->internal_get_thread_states){ + system->internal_get_thread_states(BACKGROUND_THREADS, + threads, &pending); + + string.size = 0; + append_ss(&string, make_lit_string("pending jobs: ")); + append_int_to_str(&string, pending); + gui_do_text_field(target, string, empty_str); + + for (i32 i = 0; i < 4; ++i){ + string.size = 0; + append_ss(&string, make_lit_string("thread ")); + append_int_to_str(&string, i); + append_ss(&string, make_lit_string(": ")); + + if (threads[i]){ + append_ss(&string, make_lit_string("running")); + } + else{ + append_ss(&string, make_lit_string("waiting")); + } + + gui_do_text_field(target, string, empty_str); + } + } + + Partition *part = &models->mem.part; + + string.size = 0; + append_ss(&string, make_lit_string("part memory: ")); + append_int_to_str(&string, part->pos); + append_s_char(&string, '/'); + append_int_to_str(&string, part->max); + gui_do_text_field(target, string, empty_str); + +#if !defined(FED_DEBUG_MEM_H) + General_Memory *general = &models->mem.general; + Bubble *bubble = 0, *sentinel = &general->sentinel; + for (dll_items(bubble, sentinel)){ + string.size = 0; + if (bubble->flags & MEM_BUBBLE_USED){ + append_ss(&string, make_lit_string(" used: ")); + } + else{ + append_ss(&string, make_lit_string(" free: ")); + } + + append_int_to_str(&string, bubble->size); + append_padding(&string, ' ', 40); + gui_do_text_field(target, string, empty_str); + } #endif + }break; + + case DBG_View_Inspection: + { + i32 inspecting_id = view->transient.debug_vars.inspecting_view_id; + View *views_to_inspect[16]; + i32 view_count = 0; + b32 low_detail = true; + + if (inspecting_id == 0){ + Editing_Layout *layout = &models->layout; + + for (Panel *panel = layout->used_sentinel.next; + panel != &layout->used_sentinel; + panel = panel->next){ + View *view_ptr = panel->view; + views_to_inspect[view_count++] = view_ptr; + } + } + else if (inspecting_id >= 1 && inspecting_id <= 16){ + Live_Views *live_set = &models->live_set; + View *view_ptr = live_set->views + inspecting_id - 1; + views_to_inspect[view_count++] = view_ptr; + low_detail = false; + } + + for (i32 i = 0; i < view_count; ++i){ + View *view_ptr = views_to_inspect[i]; + + string.size = 0; + append_ss(&string, make_lit_string("view: ")); + append_int_to_str(&string, view_ptr->persistent.id + 1); + gui_do_text_field(target, string, empty_str); + + string.size = 0; + Editing_File *file = view_ptr->transient.file_data.file; + Assert(file != 0); + append_ss(&string, make_lit_string(" > buffer: ")); + append_ss(&string, file->unique_name.name); + gui_do_text_field(target, string, empty_str); + string.size = 0; + append_ss(&string, make_lit_string(" >> buffer-slot-id: ")); + append_int_to_str(&string, file->id.id); + + if (low_detail){ + string.size = 0; + append_ss(&string, make_lit_string("inspect this")); + + id.id[0] = (u64)(view_ptr->persistent.id); + if (gui_do_button(target, id, string)){ + view->transient.debug_vars.inspecting_view_id = view_ptr->persistent.id + 1; + } + } + else{ + + gui_show_mouse(target, &string, input.mouse.x, input.mouse.y); + +#define SHOW_GUI_BLANK(n) show_gui_line(target, &string, n, 0, "", 0) +#define SHOW_GUI_LINE(n, str) show_gui_line(target, &string, n, 0, " " str, 0) +#define SHOW_GUI_STRING(n, h, str, mes) show_gui_line(target, &string, n, h, " " str " ", mes) +#define SHOW_GUI_INT(n, h, str, v) show_gui_int(target, &string, n, h, " " str " ", v) +#define SHOW_GUI_INT_INT(n, h, str, v, m) show_gui_int_int(target, &string, n, h, " " str " ", v, m) +#define SHOW_GUI_U64(n, h, str, v) show_gui_u64(target, &string, n, h, " " str " ", v) +#define SHOW_GUI_ID(n, h, str, v) show_gui_id(target, &string, n, h, " " str, v) +#define SHOW_GUI_FLOAT(n, h, str, v) show_gui_float(target, &string, n, h, " " str " ", v) +#define SHOW_GUI_BOOL(n, h, str, v) do { if (v) { show_gui_line(target, &string, n, h, " " str " ", "true"); }\ + else { show_gui_line(target, &string, n, h, " " str " ", "false"); } } while(0) + +#define SHOW_GUI_SCROLL(n, h, str, v) show_gui_scroll(target, &string, n, h, " " str, v) +#define SHOW_GUI_CURSOR(n, h, str, v) show_gui_cursor(target, &string, n, h, " " str, v) +#define SHOW_GUI_REGION(n, h, str, v) show_gui_region(target, &string, n, h, " " str, v) + + i32 h_align = 31; + + SHOW_GUI_BLANK(0); + { + i32 map = view_ptr->transient.map; +#define MAP_LABEL "command map" + if (map == mapid_global){ + SHOW_GUI_STRING(1, h_align, MAP_LABEL, "global"); + } + else if (map == mapid_file){ + SHOW_GUI_STRING(1, h_align, MAP_LABEL, "file"); + } + else if (map == mapid_ui){ + SHOW_GUI_STRING(1, h_align, MAP_LABEL, "gui"); + } + else if (map == mapid_nomap){ + SHOW_GUI_STRING(1, h_align, MAP_LABEL, "nomap"); + } + else{ + SHOW_GUI_STRING(1, h_align, MAP_LABEL, "user"); + SHOW_GUI_INT(2, h_align, "custom map id", map); + } + } + + SHOW_GUI_BLANK(0); + SHOW_GUI_LINE(1, "file data:"); + SHOW_GUI_BOOL(2, h_align, "has file", view_ptr->transient.file_data.file); + SHOW_GUI_BOOL(2, h_align, "show temp highlight", view_ptr->transient.file_data.show_temp_highlight); + SHOW_GUI_INT (2, h_align, "start temp highlight", view_ptr->transient.file_data.temp_highlight.pos); + SHOW_GUI_INT (2, h_align, "end temp highlight", view_ptr->transient.file_data.temp_highlight_end_pos); + + SHOW_GUI_BOOL(2, h_align, "show whitespace", view_ptr->transient.file_data.show_whitespace); + SHOW_GUI_BOOL(2, h_align, "locked", view_ptr->transient.file_data.file_locked); + + SHOW_GUI_BLANK(0); + SHOW_GUI_REGION(1, h_align, "scroll region", view_ptr->transient.scroll_region); + + SHOW_GUI_BLANK(0); + SHOW_GUI_LINE(1, "file editing positions"); + { + File_Edit_Positions *edit_pos = view_ptr->transient.edit_pos; + + Assert(edit_pos != 0); + SHOW_GUI_SCROLL(2, h_align, "scroll:", edit_pos->scroll); + SHOW_GUI_BLANK (2); + SHOW_GUI_CURSOR(2, h_align, "cursor:", edit_pos->cursor); + SHOW_GUI_BLANK (2); + SHOW_GUI_INT (2, h_align, "mark", edit_pos->mark); + SHOW_GUI_FLOAT (2, h_align, "preferred_x", edit_pos->preferred_x); + SHOW_GUI_INT (2, h_align, "scroll_i", edit_pos->scroll_i); + } + + SHOW_GUI_BLANK (0); + SHOW_GUI_SCROLL(1, h_align, "gui scroll:", view_ptr->transient.gui_scroll); + + SHOW_GUI_BLANK (0); + SHOW_GUI_LINE (1, "gui target"); + SHOW_GUI_INT_INT(2, h_align, "gui partition", + view_ptr->transient.gui_target.push.pos, + view_ptr->transient.gui_target.push.max); + + SHOW_GUI_BLANK (2); + SHOW_GUI_ID (2, h_align, "active", view_ptr->transient.gui_target.active); + SHOW_GUI_ID (2, h_align, "mouse_hot", view_ptr->transient.gui_target.mouse_hot); + SHOW_GUI_ID (2, h_align, "auto_hot", view_ptr->transient.gui_target.auto_hot); + SHOW_GUI_ID (2, h_align, "hover", view_ptr->transient.gui_target.hover); + SHOW_GUI_ID (2, h_align, "scroll_id", view_ptr->transient.gui_target.scroll_id); + + SHOW_GUI_BLANK (2); + SHOW_GUI_SCROLL (2, h_align, "scroll_original", view_ptr->transient.gui_target.scroll_original); + SHOW_GUI_REGION (2, h_align, "region_original", view_ptr->transient.gui_target.region_original); + + SHOW_GUI_BLANK (2); + SHOW_GUI_REGION (2, h_align, "region_updated", view_ptr->transient.gui_target.region_updated); + + + SHOW_GUI_BLANK (1); + SHOW_GUI_SCROLL (1, h_align, "gui scroll", view_ptr->transient.gui_scroll); + } + } + }break; + } + + gui_end_scrollable(target); + }break; + } + } + } + gui_end_top_level(target); + + result.animating = target->animating; + return(result); +} + +internal f32 +view_get_scroll_y(View *view){ + f32 v = 0; + if (view->transient.showing_ui == VUI_None){ + File_Edit_Positions *edit_pos = view->transient.edit_pos; + TentativeAssert(edit_pos != 0); + if (edit_pos != 0){ + v = edit_pos->scroll.scroll_y; + } + } + else{ + v = view->transient.gui_scroll.scroll_y; + } + return(v); +} + +internal b32 +click_button_input(GUI_Target *target, GUI_Session *session, b32 in_scroll, i32_Rect scroll_rect, Input_Summary *user_input, GUI_Interactive *b, b32 *is_animating){ + b32 result = 0; + i32 mx = user_input->mouse.x; + i32 my = user_input->mouse.y; + + b32 in_sub_region = true; + if (in_scroll && !hit_check(mx, my, scroll_rect)){ + in_sub_region = false; + } + + if (in_sub_region && hit_check(mx, my, session->rect)){ + target->hover = b->id; + if (user_input->mouse.press_l){ + target->mouse_hot = b->id; + *is_animating = 1; + result = 1; + } + if (user_input->mouse.release_l && gui_id_eq(target->mouse_hot, b->id)){ + target->active = b->id; + target->mouse_hot = gui_id_zero(); + *is_animating = 1; + } + } + else if (gui_id_eq(target->hover, b->id)){ + target->hover = gui_id_zero(); + } + + return(result); +} + +internal b32 +scroll_button_input(GUI_Target *target, GUI_Session *session, Input_Summary *user_input, GUI_id id, b32 *is_animating){ + b32 result = 0; + i32 mx = user_input->mouse.x; + i32 my = user_input->mouse.y; + + if (hit_check(mx, my, session->rect)){ + target->hover = id; + if (user_input->mouse.l){ + target->mouse_hot = id; + gui_activate_scrolling(target); + *is_animating = 1; + result = 1; + } + } + else if (gui_id_eq(target->hover, id)){ + target->hover = gui_id_zero(); + } + return(result); +} + +static u32 +to_writable_character(Key_Code long_character, u8 *character){ + u32 result = 0; + if (long_character != 0){ + u32_to_utf8_unchecked(long_character, character, &result); + } + return(result); +} + +internal Input_Process_Result +do_step_file_view(System_Functions *system, View *view, Models *models, i32_Rect rect, b32 is_active, Input_Summary *user_input, GUI_Scroll_Vars vars, i32_Rect region, i32 max_y){ + Input_Process_Result result = {0}; + b32 is_file_scroll = 0; + + GUI_Session gui_session = {0}; + GUI_Header *h = 0; + GUI_Target *target = &view->transient.gui_target; + GUI_Interpret_Result interpret_result = {0}; + + vars.target_y = clamp(0, vars.target_y, max_y); + + result.vars = vars; + result.region = region; + + target->active = gui_id_zero(); + + // HACK(allen): UI sucks! Now just forcing it to + // not have the bug where it clicks buttons behind the + // header buttons before the scrollable section. + b32 in_scroll = false; + i32_Rect scroll_rect = {0}; + i32 prev_bottom = 0; + + if (target->push.pos > 0){ + gui_session_init(&gui_session, target, rect, view->transient.line_height); + + for (h = (GUI_Header*)target->push.base; + h->type; + h = NextHeader(h)){ + interpret_result = gui_interpret(target, &gui_session, h, + result.vars, result.region, max_y); + + if (interpret_result.has_region){ + result.region = interpret_result.region; + } + + switch (h->type){ + case guicom_file_option: + case guicom_fixed_option: + case guicom_fixed_option_checkbox: + { + GUI_Interactive *b = (GUI_Interactive*)h; + + if (interpret_result.auto_activate){ + target->auto_hot = gui_id_zero(); + target->active = b->id; + result.is_animating = 1; + } + else if (interpret_result.auto_hot){ + if (!gui_id_eq(target->auto_hot, b->id)){ + target->auto_hot = b->id; + result.is_animating = 1; + } + } + }break; + } + + if (interpret_result.has_info){ + switch (h->type){ + case guicom_top_bar: break; + + case guicom_file: + { + // NOTE(allen): Set the file region first because the + // computation of view_compute_max_target_y depends on it. + view->transient.file_region = gui_session.rect; + + if (view->transient.reinit_scrolling){ + result.vars = view_reinit_scrolling(view); + result.is_animating = 1; + } + + if (file_step(view, gui_session.rect, user_input, is_active, &result.consumed_l)){ + result.is_animating = 1; + } + is_file_scroll = 1; + }break; + + case guicom_color_button: + case guicom_font_button: + case guicom_button: + case guicom_file_option: + case guicom_style_preview: + { + GUI_Interactive *b = (GUI_Interactive*)h; + + if (click_button_input(target, &gui_session, in_scroll, scroll_rect, user_input, b, &result.is_animating)){ + result.consumed_l = true; + } + + prev_bottom = gui_session.rect.y1; + }break; + + case guicom_fixed_option: + case guicom_fixed_option_checkbox: + { + GUI_Interactive *b = (GUI_Interactive*)h; + + if (click_button_input(target, &gui_session, in_scroll, scroll_rect, user_input, b, &result.is_animating)){ + result.consumed_l = true; + } + + { + Key_Input_Data *keys = &user_input->keys; + + void *ptr = (b + 1); + String string = gui_read_string(&ptr); + AllowLocal(string); + + char activation_key = *(char*)ptr; + activation_key = char_to_upper(activation_key); + + if (activation_key != 0){ + i32 count = keys->count; + for (i32 i = 0; i < count; ++i){ + Key_Event_Data key = keys->keys[i]; + + u8 character[4]; + u32 length = to_writable_character(key.character, character); + if (length == 1){ + if (char_to_upper(character[0]) == activation_key){ + target->active = b->id; + result.is_animating = 1; + break; + } + } + } + } + } + }break; + + case guicom_scrollable_slider: + { + GUI_id id = gui_id_scrollbar_slider(); + i32 mx = user_input->mouse.x; + i32 my = user_input->mouse.y; + f32 v = 0; + + if (hit_check(mx, my, gui_session.rect)){ + target->hover = id; + if (user_input->mouse.press_l){ + target->mouse_hot = id; + result.is_animating = 1; + result.consumed_l = 1; + } + } + else if (gui_id_eq(target->hover, id)){ + target->hover = gui_id_zero(); + } + + if (gui_id_eq(target->mouse_hot, id)){ + v = unlerp(gui_session.scroll_top, (f32)my, + gui_session.scroll_bottom); + v = clamp(0.f, v, 1.f); + result.vars.target_y = round32(lerp(0.f, v, (f32)max_y)); + + gui_activate_scrolling(target); + result.is_animating = 1; + } + } + // NOTE(allen): NO BREAK HERE!! + + case guicom_scrollable_invisible: + { + if (user_input->mouse.wheel != 0){ + result.vars.target_y += user_input->mouse.wheel; + + result.vars.target_y = + clamp(0, result.vars.target_y, max_y); + gui_activate_scrolling(target); + result.is_animating = 1; + } + }break; + + case guicom_scrollable_top: + { + GUI_id id = gui_id_scrollbar_top(); + + if (scroll_button_input(target, &gui_session, user_input, id, &result.is_animating)){ + result.vars.target_y -= clamp_bottom(1, target->delta >> 2); + result.vars.target_y = clamp_bottom(0, result.vars.target_y); + result.consumed_l = 1; + } + }break; + + case guicom_scrollable_bottom: + { + GUI_id id = gui_id_scrollbar_bottom(); + + if (scroll_button_input(target, &gui_session, user_input, id, &result.is_animating)){ + result.vars.target_y += clamp_bottom(1, target->delta >> 2); + result.vars.target_y = clamp_top(result.vars.target_y, max_y); + result.consumed_l = 1; + } + }break; + + case guicom_begin_scrollable_section: + { + in_scroll = true; + scroll_rect.x0 = region.x0; + scroll_rect.y0 = prev_bottom; + scroll_rect.x1 = region.x1; + scroll_rect.y1 = region.y1; + }break; + + case guicom_end_scrollable_section: + { + in_scroll = false; + if (!is_file_scroll){ + result.has_max_y_suggestion = 1; + result.max_y = gui_session.suggested_max_y; + } + }break; + } + } + } + + if (!user_input->mouse.l){ + if (!gui_id_is_null(target->mouse_hot)){ + target->mouse_hot = gui_id_zero(); + result.is_animating = 1; + } + } + + { + GUI_Scroll_Vars scroll_vars = result.vars; + b32 is_new_target = 0; + if (scroll_vars.target_x != scroll_vars.prev_target_x || + scroll_vars.target_y != scroll_vars.prev_target_y){ + is_new_target = 1; + } + + f32 target_x = (f32)scroll_vars.target_x; + f32 target_y = (f32)scroll_vars.target_y; + + if (models->scroll_rule(target_x, target_y, &scroll_vars.scroll_x, &scroll_vars.scroll_y, (view->persistent.id) + 1, is_new_target, user_input->dt)){ + result.is_animating = true; + } + + scroll_vars.prev_target_x = scroll_vars.target_x; + scroll_vars.prev_target_y = scroll_vars.target_y; + + result.vars = scroll_vars; + } + } + + return(result); +} + +internal i32 +draw_file_loaded(System_Functions *system, View *view, Models *models, i32_Rect rect, b32 is_active, Render_Target *target){ + Editing_File *file = view->transient.file_data.file; + Style *style = main_style(models); + i32 line_height = view->transient.line_height; + + f32 max_x = (f32)file->settings.display_width; + i32 max_y = rect.y1 - rect.y0 + line_height; + + Assert(file != 0); + Assert(!file->is_dummy); + Assert(buffer_good(&file->state.buffer)); + Assert(view->transient.edit_pos != 0); + + b32 tokens_use = 0; + Cpp_Token_Array token_array = {}; + if (file){ + tokens_use = file->state.tokens_complete && (file->state.token_array.count > 0); + token_array = file->state.token_array; + } + + Partition *part = &models->mem.part; + Temp_Memory temp = begin_temp_memory(part); + + partition_align(part, 4); + + f32 left_side_space = 0; + + i32 max = partition_remaining(part) / sizeof(Buffer_Render_Item); + Buffer_Render_Item *items = push_array(part, Buffer_Render_Item, max); + + Face_ID font_id = file->settings.font_id; + Font_Pointers font = system->font.get_pointers_by_id(font_id); + + f32 scroll_x = view->transient.edit_pos->scroll.scroll_x; + f32 scroll_y = view->transient.edit_pos->scroll.scroll_y; + + // NOTE(allen): For now we will temporarily adjust scroll_y to try + // to prevent the view moving around until floating sections are added + // to the gui system. + scroll_y += view->transient.widget_height; + + Full_Cursor render_cursor; + if (!file->settings.unwrapped_lines){ + render_cursor = file_compute_cursor(system, file, seek_wrapped_xy(0, scroll_y, 0), true); + } + else{ + render_cursor = file_compute_cursor(system, file, seek_unwrapped_xy(0, scroll_y, 0), true); + } + + view->transient.edit_pos->scroll_i = render_cursor.pos; + + i32 count = 0; + { + b32 wrapped = !file->settings.unwrapped_lines; + + Buffer_Render_Params params; + params.buffer = &file->state.buffer; + params.items = items; + params.max = max; + params.count = &count; + params.port_x = (f32)rect.x0 + left_side_space; + params.port_y = (f32)rect.y0; + params.clip_w = view_width(view) - left_side_space; + params.scroll_x = scroll_x; + params.scroll_y = scroll_y; + params.width = max_x; + params.height = (f32)max_y; + params.start_cursor = render_cursor; + params.wrapped = wrapped; + params.system = system; + params.font = font; + params.virtual_white = file->settings.virtual_white; + params.wrap_slashes = file->settings.wrap_indicator; + + Buffer_Render_State state = {0}; + Buffer_Layout_Stop stop = {0}; + + f32 line_shift = 0.f; + b32 do_wrap = 0; + i32 wrap_unit_end = 0; + + b32 first_wrap_determination = 1; + i32 wrap_array_index = 0; + + do{ + stop = buffer_render_data(&state, params, line_shift, do_wrap, wrap_unit_end); + switch (stop.status){ + case BLStatus_NeedWrapDetermination: + { + if (first_wrap_determination){ + wrap_array_index = binary_search(file->state.wrap_positions, stop.pos, 0, file->state.wrap_position_count); + ++wrap_array_index; + if (file->state.wrap_positions[wrap_array_index] == stop.pos){ + do_wrap = 1; + wrap_unit_end = file->state.wrap_positions[wrap_array_index]; + } + else{ + do_wrap = 0; + wrap_unit_end = file->state.wrap_positions[wrap_array_index]; + } + first_wrap_determination = 0; + } + else{ + Assert(stop.pos == wrap_unit_end); + do_wrap = 1; + ++wrap_array_index; + wrap_unit_end = file->state.wrap_positions[wrap_array_index]; + } + }break; + + case BLStatus_NeedWrapLineShift: + case BLStatus_NeedLineShift: + { + line_shift = file->state.line_indents[stop.wrap_line_index]; + }break; + } + }while(stop.status != BLStatus_Finished); + } + + i32 cursor_begin = 0, cursor_end = 0; + u32 cursor_color = 0, at_cursor_color = 0; + if (view->transient.file_data.show_temp_highlight){ + cursor_begin = view->transient.file_data.temp_highlight.pos; + cursor_end = view->transient.file_data.temp_highlight_end_pos; + cursor_color = style->main.highlight_color; + at_cursor_color = style->main.at_highlight_color; + } + else{ + cursor_begin = view->transient.edit_pos->cursor.pos; + cursor_end = cursor_begin + 1; + cursor_color = style->main.cursor_color; + at_cursor_color = style->main.at_cursor_color; + } + + i32 token_i = 0; + u32 main_color = style->main.default_color; + u32 special_color = style->main.special_character_color; + u32 ghost_color = style->main.ghost_character_color; + if (tokens_use){ + Cpp_Get_Token_Result result = cpp_get_token(token_array, items->index); + main_color = *style_get_color(style, token_array.tokens[result.token_index]); + token_i = result.token_index + 1; + } + + u32 mark_color = style->main.mark_color; + Buffer_Render_Item *item = items; + Buffer_Render_Item *item_end = item + count; + i32 prev_ind = -1; + u32 highlight_color = 0; + u32 highlight_this_color = 0; + + for (; item < item_end; ++item){ + i32 ind = item->index; + highlight_this_color = 0; + if (tokens_use && ind != prev_ind){ + Cpp_Token current_token = token_array.tokens[token_i-1]; + + if (token_i < token_array.count){ + if (ind >= token_array.tokens[token_i].start){ + for (;token_i < token_array.count && ind >= token_array.tokens[token_i].start; ++token_i){ + main_color = *style_get_color(style, token_array.tokens[token_i]); + current_token = token_array.tokens[token_i]; + } + } + else if (ind >= current_token.start + current_token.size){ + main_color = style->main.default_color; + } + } + + if (current_token.type == CPP_TOKEN_JUNK && ind >= current_token.start && ind < current_token.start + current_token.size){ + highlight_color = style->main.highlight_junk_color; + } + else{ + highlight_color = 0; + } + } + + u32 char_color = main_color; + if (item->flags & BRFlag_Special_Character){ + char_color = special_color; + } + else if (item->flags & BRFlag_Ghost_Character){ + char_color = ghost_color; + } + + f32_Rect char_rect = f32R(item->x0, item->y0, item->x1, item->y1); + + if (view->transient.file_data.show_whitespace && highlight_color == 0 && codepoint_is_whitespace(item->codepoint)){ + highlight_this_color = style->main.highlight_white_color; + } + else{ + highlight_this_color = highlight_color; + } + + if (cursor_begin <= ind && ind < cursor_end && (ind != prev_ind || cursor_begin < ind)){ + if (is_active){ + draw_rectangle(target, char_rect, cursor_color); + char_color = at_cursor_color; + } + else{ + if (!view->transient.file_data.show_temp_highlight){ + draw_rectangle_outline(target, char_rect, cursor_color); + } + } + } + else if (highlight_this_color){ + draw_rectangle(target, char_rect, highlight_this_color); + } + + u32 fade_color = 0xFFFF00FF; + f32 fade_amount = 0.f; + + if (file->state.paste_effect.seconds_down > 0.f && + file->state.paste_effect.start <= ind && + ind < file->state.paste_effect.end){ + fade_color = file->state.paste_effect.color; + fade_amount = file->state.paste_effect.seconds_down; + fade_amount /= file->state.paste_effect.seconds_max; + } + + char_color = color_blend(char_color, fade_amount, fade_color); + + if (ind == view->transient.edit_pos->mark && prev_ind != ind){ + draw_rectangle_outline(target, char_rect, mark_color); + } + if (item->codepoint != 0){ + draw_font_glyph(target, font_id, item->codepoint, item->x0, item->y0, char_color); + } + prev_ind = ind; + } + + end_temp_memory(temp); + + return(0); +} + +internal void +draw_text_field(System_Functions *system, Render_Target *target, View *view, Models *models, Face_ID font_id, i32_Rect rect, String p, String t){ + Style *style = main_style(models); + + u32 back_color = style->main.margin_color; + u32 text1_color = style->main.default_color; + u32 text2_color = style->main.file_info_style.pop1_color; + + i32 x = rect.x0; + i32 y = rect.y0 + 2; + + if (target){ + draw_rectangle(target, rect, back_color); + x = ceil32(draw_string(system, target, font_id, p, x, y, text2_color)); + draw_string(system, target, font_id, t, x, y, text1_color); + } +} + +internal void +draw_text_with_cursor(System_Functions *system, Render_Target *target, View *view, Models *models, Face_ID font_id, i32_Rect rect, String s, i32 pos){ + Style *style = main_style(models); + + u32 back_color = style->main.margin_color; + u32 text_color = style->main.default_color; + u32 cursor_color = style->main.cursor_color; + u32 at_cursor_color = style->main.at_cursor_color; + + f32 x = (f32)rect.x0; + i32 y = rect.y0 + 2; + + if (target){ + draw_rectangle(target, rect, back_color); + + if (pos >= 0 && pos < s.size){ + Font_Pointers font = system->font.get_pointers_by_id(font_id); + + String part1 = substr(s, 0, pos); + String part2 = substr(s, pos, 1); + String part3 = substr(s, pos+1, s.size-pos-1); + + x = draw_string(system, target, font_id, part1, floor32(x), y, text_color); + + f32 adv = font_get_glyph_advance(system, font.settings, font.metrics, font.pages, s.str[pos]); + i32_Rect cursor_rect; + cursor_rect.x0 = floor32(x); + cursor_rect.x1 = floor32(x) + ceil32(adv); + cursor_rect.y0 = y; + cursor_rect.y1 = y + view->transient.line_height; + draw_rectangle(target, cursor_rect, cursor_color); + x = draw_string(system, target, font_id, part2, floor32(x), y, at_cursor_color); + + draw_string(system, target, font_id, part3, floor32(x), y, text_color); + } + else{ + draw_string(system, target, font_id, s, floor32(x), y, text_color); + } + } +} + +internal void +draw_file_bar(System_Functions *system, Render_Target *target, View *view, Models *models, Editing_File *file, i32_Rect rect){ + File_Bar bar; + Style *style = main_style(models); + Interactive_Style bar_style = style->main.file_info_style; + + u32 back_color = bar_style.bar_color; + u32 base_color = bar_style.base_color; + u32 pop1_color = bar_style.pop1_color; + u32 pop2_color = bar_style.pop2_color; + + bar.rect = rect; + + if (target){ + bar.font_id = file->settings.font_id; + bar.pos_x = (f32)bar.rect.x0; + bar.pos_y = (f32)bar.rect.y0; + bar.text_shift_y = 2; + bar.text_shift_x = 0; + + draw_rectangle(target, bar.rect, back_color); + + Assert(file); + + intbar_draw_string(system, target, &bar, file->unique_name.name, base_color); + intbar_draw_string(system, target, &bar, make_lit_string(" -"), base_color); + + if (file->is_loading){ + intbar_draw_string(system, target, &bar, make_lit_string(" loading"), base_color); + } + else{ + char bar_space[526]; + String bar_text = make_fixed_width_string(bar_space); + append_ss (&bar_text, make_lit_string(" L#")); + append_int_to_str(&bar_text, view->transient.edit_pos->cursor.line); + append_ss (&bar_text, make_lit_string(" C#")); + append_int_to_str(&bar_text, view->transient.edit_pos->cursor.character); + + append_ss(&bar_text, make_lit_string(" -")); + + if (file->settings.dos_write_mode){ + append_ss(&bar_text, make_lit_string(" dos")); + } + else{ + append_ss(&bar_text, make_lit_string(" nix")); + } + + intbar_draw_string(system, target, &bar, bar_text, base_color); + + + if (file->state.still_lexing){ + intbar_draw_string(system, target, &bar, make_lit_string(" parsing"), pop1_color); + } + + if (!file->settings.unimportant){ + switch (file_get_sync(file)){ + case DirtyState_UnloadedChanges: + { + local_persist String out_of_sync = make_lit_string(" !"); + intbar_draw_string(system, target, &bar, out_of_sync, pop2_color); + }break; + + case DirtyState_UnsavedChanges: + { + local_persist String out_of_sync = make_lit_string(" *"); + intbar_draw_string(system, target, &bar, out_of_sync, pop2_color); + }break; + } + } + } + } +} + +u32 +get_margin_color(i32 active_level, Style *style){ + u32 margin = 0xFFFFFFFF; + + switch (active_level){ + default: + margin = style->main.list_item_color; + break; + + case 1: case 2: + margin = style->main.list_item_hover_color; + break; + + case 3: case 4: + margin = style->main.list_item_active_color; + break; + } + + return(margin); +} + +internal void +draw_color_button(System_Functions *system, GUI_Target *gui_target, Render_Target *target, View *view, Face_ID font_id, i32_Rect rect, GUI_id id, u32 fore, u32 back, String text){ + i32 active_level = gui_active_level(gui_target, id); + + if (active_level > 0){ + Swap(u32, back, fore); + } + + draw_rectangle(target, rect, back); + draw_string(system, target, font_id, text, rect.x0, rect.y0 + 1, fore); +} + +internal void +draw_font_button(System_Functions *system, GUI_Target *gui_target, Render_Target *target, View *view, Models *models, i32_Rect rect, GUI_id id, Face_ID font_id, String text){ + Style *style = main_style(models); + + i32 active_level = gui_active_level(gui_target, id); + + u32 margin_color = get_margin_color(active_level, style); + u32 back_color = style->main.back_color; + u32 text_color = style->main.default_color; + + draw_rectangle(target, rect, back_color); + draw_rectangle_outline(target, rect, margin_color); + draw_string(system, target, font_id, text, rect.x0, rect.y0 + 1, text_color); +} + +internal void +draw_fat_option_block(System_Functions *system, GUI_Target *gui_target, Render_Target *target, View *view, Models *models, Face_ID font_id, i32_Rect rect, GUI_id id, String text, String pop, i8 checkbox = -1){ + Style *style = main_style(models); + + i32 active_level = gui_active_level(gui_target, id); + + i32_Rect inner = get_inner_rect(rect, 3); + + u32 margin = get_margin_color(active_level, style); + u32 back = style->main.back_color; + u32 text_color = style->main.default_color; + u32 pop_color = style->main.file_info_style.pop2_color; + + i32 h = view->transient.line_height; + i32 x = inner.x0 + 3; + i32 y = inner.y0 + h/2 - 1; + + draw_rectangle(target, inner, back); + draw_margin(target, rect, inner, margin); + + if (checkbox != -1){ + u32 checkbox_color = style->main.margin_active_color; + i32_Rect checkbox_rect = get_inner_rect(inner, (inner.y1 - inner.y0 - h)/2); + checkbox_rect.x1 = checkbox_rect.x0 + (checkbox_rect.y1 - checkbox_rect.y0); + + if (checkbox == 0){ + draw_rectangle_outline(target, checkbox_rect, checkbox_color); + } + else{ + draw_rectangle(target, checkbox_rect, checkbox_color); + } + + x = checkbox_rect.x1 + 3; + } + + x = ceil32(draw_string(system, target, font_id, text, x, y, text_color)); + draw_string(system, target, font_id, pop, x, y, pop_color); +} + +internal void +draw_button(System_Functions *system, GUI_Target *gui_target, Render_Target *target, View *view, Models *models, Face_ID font_id, i32_Rect rect, GUI_id id, String text){ + Style *style = main_style(models); + + i32 active_level = gui_active_level(gui_target, id); + + i32_Rect inner = get_inner_rect(rect, 3); + + u32 margin = style->main.default_color; + u32 back = get_margin_color(active_level, style); + u32 text_color = style->main.default_color; + + i32 h = view->transient.line_height; + i32 y = inner.y0 + h/2 - 1; + + i32 w = (i32)font_string_width(system, target, font_id, text); + i32 x = (inner.x1 + inner.x0 - w)/2; + + draw_rectangle(target, inner, back); + draw_rectangle_outline(target, inner, margin); + + draw_string(system, target, font_id, text, x, y, text_color); +} + +internal void +draw_style_preview(System_Functions *system, GUI_Target *gui_target, Render_Target *target, View *view, Models *models, Face_ID font_id, i32_Rect rect, GUI_id id, Style *style){ + i32 active_level = gui_active_level(gui_target, id); + char font_name_space[256]; + String font_name = make_fixed_width_string(font_name_space); + font_name.size = system->font.get_name_by_id(font_id, font_name.str, font_name.memory_size); + Font_Pointers font = system->font.get_pointers_by_id(font_id); + + i32_Rect inner = get_inner_rect(rect, 3); + + u32 margin_color = get_margin_color(active_level, style); + u32 back = style->main.back_color; + u32 text_color = style->main.default_color; + u32 keyword_color = style->main.keyword_color; + u32 int_constant_color = style->main.int_constant_color; + u32 comment_color = style->main.comment_color; + + draw_margin(target, rect, inner, margin_color); + draw_rectangle(target, inner, back); + + i32 y = inner.y0; + i32 x = inner.x0; + x = ceil32(draw_string(system, target, font_id, style->name.str, x, y, text_color)); + i32 font_x = (i32)(inner.x1 - font_string_width(system, target, font_id, font_name)); + if (font_x > x + 10){ + draw_string(system, target, font_id, font_name, font_x, y, text_color); + } + + i32 height = font.metrics->height; + x = inner.x0; + y += height; + x = ceil32(draw_string(system, target, font_id, "if", x, y, keyword_color)); + x = ceil32(draw_string(system, target, font_id, "(x < ", x, y, text_color)); + x = ceil32(draw_string(system, target, font_id, "0", x, y, int_constant_color)); + x = ceil32(draw_string(system, target, font_id, ") { x = ", x, y, text_color)); + x = ceil32(draw_string(system, target, font_id, "0", x, y, int_constant_color)); + x = ceil32(draw_string(system, target, font_id, "; } ", x, y, text_color)); + x = ceil32(draw_string(system, target, font_id, "// comment", x, y, comment_color)); + + x = inner.x0; + y += height; + draw_string(system, target, font_id, "[] () {}; * -> +-/ <>= ! && || % ^", x, y, text_color); +} + +internal i32 +do_render_file_view(System_Functions *system, View *view, Models *models, GUI_Scroll_Vars *scroll, View *active, i32_Rect rect, b32 is_active, Render_Target *target, Input_Summary *user_input){ + + Editing_File *file = view->transient.file_data.file; + Assert(file != 0); + + i32 result = 0; + + GUI_Session gui_session = {0}; + GUI_Header *h = 0; + GUI_Target *gui_target = &view->transient.gui_target; + GUI_Interpret_Result interpret_result = {0}; + + f32 v = {0}; + i32 max_y = view_compute_max_target_y(view); + + Face_ID font_id = file->settings.font_id; + if (gui_target->push.pos > 0){ + gui_session_init(&gui_session, gui_target, rect, view->transient.line_height); + + v = view_get_scroll_y(view); + + i32_Rect clip_rect = rect; + draw_push_clip(target, clip_rect); + + for (h = (GUI_Header*)gui_target->push.base; + h->type; + h = NextHeader(h)){ + interpret_result = gui_interpret(gui_target, &gui_session, h, + *scroll, view->transient.scroll_region, max_y); + + if (interpret_result.has_info){ + if (gui_session.clip_y > clip_rect.y0){ + clip_rect.y0 = gui_session.clip_y; + draw_change_clip(target, clip_rect); + } + + switch (h->type){ + case guicom_top_bar: + { + draw_file_bar(system, target, view, models, file, gui_session.rect); + }break; + + case guicom_file: + { + if (file_is_ready(file)){ + result = draw_file_loaded(system, view, models, gui_session.rect, is_active, target); + } + }break; + + case guicom_text_field: + { + void *ptr = (h+1); + String p = gui_read_string(&ptr); + String t = gui_read_string(&ptr); + draw_text_field(system, target, view, models, font_id, gui_session.rect, p, t); + }break; + + case guicom_text_with_cursor: + { + void *ptr = (h+1); + String s = gui_read_string(&ptr); + i32 pos = gui_read_integer(&ptr); + + draw_text_with_cursor(system, target, view, models, font_id, gui_session.rect, s, pos); + }break; + + case guicom_color_button: + { + GUI_Interactive *b = (GUI_Interactive*)h; + void *ptr = (b + 1); + u32 fore = (u32)gui_read_integer(&ptr); + u32 back = (u32)gui_read_integer(&ptr); + String t = gui_read_string(&ptr); + + draw_color_button(system, gui_target, target, view, font_id, gui_session.rect, b->id, fore, back, t); + }break; + + case guicom_font_button: + { + GUI_Interactive *b = (GUI_Interactive*)h; + void *ptr = (b + 1); + Face_ID this_font_id = (Face_ID)gui_read_integer(&ptr); + String t = gui_read_string(&ptr); + + draw_font_button(system, gui_target, target, view, models, gui_session.rect, b->id, this_font_id, t); + }break; + + case guicom_file_option: + { + GUI_Interactive *b = (GUI_Interactive*)h; + void *ptr = (b + 1); + b32 folder = gui_read_integer(&ptr); + String f = gui_read_string(&ptr); + String m = gui_read_string(&ptr); + + if (folder){ + append_s_char(&f, '/'); + } + + draw_fat_option_block(system, gui_target, target, view, models, font_id, gui_session.rect, b->id, f, m); + }break; + + case guicom_style_preview: + { + GUI_Interactive *b = (GUI_Interactive*)h; + i32 style_index = *(i32*)(b + 1); + Style *style = get_style(models, style_index); + + draw_style_preview(system, gui_target, target, view, models, font_id, gui_session.rect, b->id, style); + }break; + + case guicom_fixed_option: + case guicom_fixed_option_checkbox: + { + GUI_Interactive *b = (GUI_Interactive*)h; + void *ptr = (b + 1); + String f = gui_read_string(&ptr); + String m = {0}; + i8 status = -1; + if (h->type == guicom_fixed_option_checkbox){ + gui_read_byte(&ptr); + status = (i8)gui_read_byte(&ptr); + } + + draw_fat_option_block(system, gui_target, target, view, models, font_id, gui_session.rect, b->id, f, m, status); + }break; + + case guicom_button: + { + GUI_Interactive *b = (GUI_Interactive*)h; + void *ptr = (b + 1); + String t = gui_read_string(&ptr); + + draw_button(system, gui_target, target, view, models, font_id, gui_session.rect, b->id, t); + }break; + + case guicom_scrollable_bar: + { + Style *style = main_style(models); + + u32 back; + u32 outline; + + i32_Rect bar = gui_session.rect; + + back = style->main.back_color; + if (is_active){ + outline = style->main.margin_active_color; + } + else{ + outline = style->main.margin_color; + } + + draw_rectangle(target, bar, back); + draw_rectangle_outline(target, bar, outline); + }break; + + case guicom_scrollable_top: + case guicom_scrollable_slider: + case guicom_scrollable_bottom: + { + GUI_id id; + Style *style = main_style(models); + i32_Rect box = gui_session.rect; + + i32 active_level; + + u32 back; + u32 outline; + + switch (h->type){ + case guicom_scrollable_top: id = gui_id_scrollbar_top(); break; + case guicom_scrollable_bottom: id = gui_id_scrollbar_bottom(); break; + default: id = gui_id_scrollbar_slider(); break; + } + + active_level = gui_active_level(gui_target, id); + + switch (active_level){ + case 0: back = style->main.back_color; break; + case 1: back = style->main.margin_hover_color; break; + default: back = style->main.margin_active_color; break; + } + + if (is_active){ + outline = style->main.margin_active_color; + } + else{ + outline = style->main.margin_color; + } + + draw_rectangle(target, box, back); + draw_margin(target, box, get_inner_rect(box, 2), outline); + }break; + + case guicom_begin_scrollable_section: + clip_rect.x1 = Min(gui_session.scroll_region.x1, clip_rect.x1); + draw_push_clip(target, clip_rect); + break; + + case guicom_end_scrollable_section: + clip_rect = draw_pop_clip(target); + break; + } + } + } + + draw_pop_clip(target); + } + + return(result); +} + +inline void +file_view_free_buffers(View *view, Models *models){ + General_Memory *general = &models->mem.general; + general_memory_free(general, view->transient.gui_mem); + view->transient.gui_mem = 0; +} + +internal View_And_ID +live_set_alloc_view(Live_Views *live_set, Panel *panel, Models *models){ + View_And_ID result = {}; + + Assert(live_set->count < live_set->max); + ++live_set->count; + + result.view = live_set->free_sentinel.transient.next; + result.id = (i32)(result.view - live_set->views); + Assert(result.id == result.view->persistent.id); + + //dll_remove(&result.view->transient)); + result.view->transient.next->transient.prev = result.view->transient.prev; + result.view->transient.prev->transient.next = result.view->transient.next; + memset(&result.view->transient, 0, sizeof(result.view->transient)); + + result.view->transient.in_use = true; + panel->view = result.view; + result.view->transient.panel = panel; + + init_query_set(&result.view->transient.query_set); + + i32 gui_mem_size = KB(512); + void *gui_mem = general_memory_allocate(&models->mem.general, gui_mem_size + 8); + result.view->transient.gui_mem = gui_mem; + gui_mem = advance_to_alignment(gui_mem); + result.view->transient.gui_target.push = make_part(gui_mem, gui_mem_size); + + return(result); +} + +inline void +live_set_free_view(Live_Views *live_set, View *view, Models *models){ + Assert(live_set->count > 0); + --live_set->count; + file_view_free_buffers(view, models); + //dll_insert(&live_set->free_sentinel, view); + view->transient.next = live_set->free_sentinel.transient.next; + view->transient.prev = &live_set->free_sentinel; + live_set->free_sentinel.transient.next = view; + view->transient.next->transient.prev = view; + view->transient.in_use = false; +} // BOTTOM diff --git a/4ed_view.h b/4ed_view.h new file mode 100644 index 00000000..e6937b9f --- /dev/null +++ b/4ed_view.h @@ -0,0 +1,367 @@ +/* + * Mr. 4th Dimention - Allen Webster + * + * 17.07.2017 + * + * File editing view for 4coder. + * + */ + +// TOP + +#if !defined(FRED_VIEW_H) +#define FRED_VIEW_H + +struct View_Persistent{ + i32 id; + Coroutine_Head *coroutine; + Event_Message message_passing_slot; +}; + +struct File_Viewing_Data{ + Editing_File *file; + + Full_Cursor temp_highlight; + i32 temp_highlight_end_pos; + b32 show_temp_highlight; + + b32 show_whitespace; + b32 file_locked; +}; +global File_Viewing_Data null_file_viewing_data = {0}; + +enum Interactive_Action{ + IAct_Open, + IAct_New, + IAct_OpenOrNew, + IAct_Switch, + IAct_Kill, + IAct_Sure_To_Kill, + IAct_Sure_To_Close +}; + +enum Interactive_Interaction{ + IInt_Sys_File_List, + IInt_Live_File_List, + IInt_Sure_To_Kill, + IInt_Sure_To_Close +}; + +enum View_UI{ + VUI_None, + VUI_Theme, + VUI_Interactive, + VUI_Debug +}; + +enum Debug_Mode{ + DBG_Input, + DBG_Threads_And_Memory, + DBG_View_Inspection +}; + +enum Color_View_Mode{ + CV_Mode_Library, + CV_Mode_Font, + CV_Mode_Global_Font, + CV_Mode_Font_Editing, + CV_Mode_Global_Font_Editing, + CV_Mode_Adjusting, +}; + +struct Scroll_Context{ + Editing_File *file; + GUI_id scroll; + View_UI mode; +}; + +struct Debug_Vars{ + i32 mode; + i32 inspecting_view_id; +}; +global_const Debug_Vars null_debug_vars = {0}; + +struct View_Transient{ + struct View *next; + struct View *prev; + struct Panel *panel; + b32 in_use; + i32 map; + + File_Viewing_Data file_data; + + i32_Rect file_region_prev; + i32_Rect file_region; + + i32_Rect scroll_region; + File_Edit_Positions *edit_pos; + + View_UI showing_ui; + GUI_Target gui_target; + void *gui_mem; + GUI_Scroll_Vars gui_scroll; + i32 gui_max_y; + i32 list_i; + + b32 hide_scrollbar; + b32 hide_file_bar; + + // interactive stuff + Interactive_Interaction interaction; + Interactive_Action action; + + char dest_[256]; + String dest; + + b32 changed_context_in_step; + + // theme stuff + View *hot_file_view; + u32 *palette; + Color_View_Mode color_mode; + Face_ID font_edit_id; + Super_Color color; + b32 p4c_only; + Style_Library inspecting_styles; + b8 import_export_check[64]; + i32 import_file_id; + i32 current_color_editing; + i32 color_cursor; + + // misc + + // TODO(allen): Can we burn line_height to the ground now? + // It's what I've always wanted!!!! :D + i32 line_height; + + // TODO(allen): Do I still use mode? + Query_Set query_set; + f32 widget_height; + + b32 reinit_scrolling; + + Debug_Vars debug_vars; +}; + +struct View{ + // TODO(allen): Why is this this way? + View_Persistent persistent; + View_Transient transient; +}; + +struct Live_Views{ + View *views; + View free_sentinel; + i32 count, max; +}; + +struct Cursor_Limits{ + f32 min, max; + f32 delta; +}; + +struct View_And_ID{ + View *view; + i32 id; +}; + +enum{ + GROW_FAILED, + GROW_NOT_NEEDED, + GROW_SUCCESS, +}; + +struct Code_Wrap_X{ + f32 base_x; + f32 paren_nesting[32]; + i32 paren_safe_top; + i32 paren_top; +}; +global Code_Wrap_X null_wrap_x = {0}; + +struct Code_Wrap_State{ + Cpp_Token_Array token_array; + Cpp_Token *token_ptr; + Cpp_Token *end_token; + + Code_Wrap_X wrap_x; + + b32 in_pp_body; + Code_Wrap_X plane_wrap_x; + + i32 *line_starts; + i32 line_count; + i32 line_index; + i32 next_line_start; + + f32 x; + b32 consume_newline; + + Gap_Buffer_Stream stream; + i32 size; + i32 i; + + Font_Pointers font; + f32 tab_indent_amount; + f32 byte_advance; + + Translation_State tran; + Translation_Emits emits; + u32 J; + Buffer_Model_Step step; + Buffer_Model_Behavior behavior; +}; + +struct Code_Wrap_Step{ + i32 position_start; + i32 position_end; + + f32 start_x; + f32 final_x; + + Cpp_Token *this_token; +}; + +struct Wrap_Indent_Pair{ + i32 wrap_position; + f32 line_shift; +}; + +struct Potential_Wrap_Indent_Pair{ + i32 wrap_position; + f32 line_shift; + + f32 wrap_x; + i32 wrappable_score; + + b32 adjust_top_to_this; +}; + +struct Wrap_Current_Shift{ + f32 shift; + b32 adjust_top_to_this; +}; + +struct Shift_Information{ + i32 start, end, amount; +}; + +struct Edit_Spec{ + u8 *str; + Edit_Step step; +}; + +struct Relative_Scrolling{ + f32 scroll_x, scroll_y; + f32 target_x, target_y; +}; + +struct Cursor_Fix_Descriptor{ + b32 is_batch; + union{ + struct{ + Buffer_Edit *batch; + i32 batch_size; + }; + struct{ + i32 start, end; + i32 shift_amount; + }; + }; +}; + +struct File_Bar{ + f32 pos_x, pos_y; + f32 text_shift_x, text_shift_y; + i32_Rect rect; + Face_ID font_id; +}; + +struct Exhaustive_File_Loop{ + char front_name_[256]; + char full_path_[256]; + String front_name, full_path; + + Absolutes absolutes; + + File_Info *infos; + i32 count, r; +}; + +struct Exhaustive_File_Info{ + File_Info *info; + String message; + b8 is_folder; + b8 name_match; + b8 is_loaded; +}; + +struct Style_Color_Edit{ + Style_Tag target; + Style_Tag fore; + Style_Tag back; + String text; +}; + +struct Single_Line_Input_Step{ + b8 hit_newline; + b8 hit_ctrl_newline; + b8 hit_a_character; + b8 hit_backspace; + b8 hit_esc; + b8 made_a_change; + b8 did_command; + b8 no_file_match; +}; + +enum Single_Line_Input_Type{ + SINGLE_LINE_STRING, + SINGLE_LINE_FILE +}; + +struct Single_Line_Mode{ + Single_Line_Input_Type type; + String *string; + Hot_Directory *hot_directory; + b32 fast_folder_select; + b32 try_to_match; + b32 case_sensitive; +}; + +struct View_Step_Result{ + b32 animating; + b32 consume_keys; + b32 consume_esc; +}; + +struct Input_Process_Result{ + GUI_Scroll_Vars vars; + i32_Rect region; + b32 is_animating; + b32 consumed_l; + b32 consumed_r; + + b32 has_max_y_suggestion; + i32 max_y; +}; + +enum{ + FileCreateFlag_ReadOnly = 1, +}; + +enum History_Mode{ + hist_normal, + hist_backward, + hist_forward +}; + +enum Try_Kill_Result{ + TryKill_CannotKill, + TryKill_NeedDialogue, + TryKill_Success +}; + +#endif + +// BOTTOM + diff --git a/4ed_working_set.cpp b/4ed_working_set.cpp index d087ac3c..3398d381 100644 --- a/4ed_working_set.cpp +++ b/4ed_working_set.cpp @@ -98,9 +98,8 @@ working_set_alloc(Working_Set *working_set){ dll_remove(node); Buffer_Slot_ID id = result->id; - *result = null_editing_file; + memset(result, 0, sizeof(*result)); result->id = id; - //result->unique_buffer_id = ++working_set->unique_file_counter; dll_insert(&working_set->used_sentinel, node); result->settings.display_width = working_set->default_display_width; result->settings.minimum_base_display_width = working_set->default_minimum_base_display_width; @@ -305,41 +304,39 @@ internal Editing_File* working_set_lookup_file(Working_Set *working_set, String string){ Editing_File *file = 0; - { - // TODO(allen): use the name table for this - File_Node *node, *used_nodes; - used_nodes = &working_set->used_sentinel; - for (dll_items(node, used_nodes)){ - file = (Editing_File*)node; - if (string.size == 0 || match_ss(string, file->unique_name.name)){ + // TODO(allen): use the name table for this + for (File_Node *node = working_set->used_sentinel.next; + node != &working_set->used_sentinel; + node = node->next){ + Editing_File *nfile = (Editing_File*)node; + if (string.size == 0 || match_ss(string, nfile->unique_name.name)){ + file = nfile; + break; + } + } + + if (file == 0){ + for (File_Node *node = working_set->used_sentinel.next; + node = &working_set->used_sentinel; + node = node->next){ + Editing_File *nfile = (Editing_File*)node; + if (string.size == 0 || has_substr_s(nfile->unique_name.name, string)){ + file = nfile; break; } } - if (node == used_nodes) file = 0; } - if (!file){ - File_Node *node, *used_nodes; - used_nodes = &working_set->used_sentinel; - for (dll_items(node, used_nodes)){ - file = (Editing_File*)node; - if (string.size == 0 || has_substr_s(file->unique_name.name, string)){ - break; - } - } - if (node == used_nodes) file = 0; - } - - return (file); + return(file); } internal void touch_file(Working_Set *working_set, Editing_File *file){ - if (file){ - Assert(!file->is_dummy); - dll_remove(&file->node); - dll_insert(&working_set->used_sentinel, &file->node); - } + TentativeAssert(file != 0); + Assert(!file->is_dummy); + dll_remove(&file->node); + dll_insert(&working_set->used_sentinel, &file->node); + } diff --git a/build_tests.bat b/build_tests.bat index eab90302..24775d2b 100644 --- a/build_tests.bat +++ b/build_tests.bat @@ -26,9 +26,7 @@ cl %opts% %inc% %code%\meta\4ed_test_builder.cpp /Zi /Fe%name% popd pushd %data% -%full_name% %scripts%\test_full_click.4is -%full_name% %scripts%\test_write_4coder_awesomeness.4is -%full_name% %scripts%\test_bootstrap.4is +%full_name% %scripts%\*.4is %full_name% %scripts%\generated\*.4is popd diff --git a/generate_tests.bat b/generate_tests.bat index 2c1acf6e..9e43bfa2 100644 --- a/generate_tests.bat +++ b/generate_tests.bat @@ -22,6 +22,4 @@ pushd %build% cl %opts% %inc% %code%\meta\4ed_test_generator.cpp /Zi /Fe%name% popd -pushd %scripts% -%full_name% -popd +%full_name% %code% diff --git a/meta/4ed_build.cpp b/meta/4ed_build.cpp index bc7ea8f5..2e4b7fe4 100644 --- a/meta/4ed_build.cpp +++ b/meta/4ed_build.cpp @@ -583,8 +583,8 @@ internal void standard_build(char *cdir, u32 flags, u32 arch){ fsm_generator(cdir); metagen(cdir); - //do_buildsuper(cdir, fm_str(custom_files[Custom_Default]), arch); - do_buildsuper(cdir, fm_str(custom_files[Custom_Experiments]), arch); + do_buildsuper(cdir, fm_str(custom_files[Custom_Default]), arch); + //do_buildsuper(cdir, fm_str(custom_files[Custom_Experiments]), arch); //do_buildsuper(cdir, fm_str(custom_files[Custom_Casey]), arch); //do_buildsuper(cdir, fm_str(custom_files[Custom_ChronalVim]), arch); build_main(cdir, true, flags, arch); diff --git a/meta/4ed_metagen.cpp b/meta/4ed_metagen.cpp index 40cdccbc..95c55444 100644 --- a/meta/4ed_metagen.cpp +++ b/meta/4ed_metagen.cpp @@ -14,9 +14,6 @@ #define API_H "4coder_generated/app_functions.h" #define REMAPPING_FILE "4coder_generated/remapping.h" - -#define OS_API_H "4ed_os_custom_api.h" - #include "../4ed_defines.h" #include "4ed_meta_defines.h" #include "../4coder_API/version.h" @@ -187,7 +184,7 @@ generate_style(){ Temp temp = fm_begin_temp(); char filename_4coder[] = STYLE_FILE; - char filename_4ed[] = "4ed_style.h"; + char filename_4ed[] = "4ed_generated_style.h"; String out = str_alloc(10 << 20);; @@ -354,31 +351,6 @@ generate_custom_headers(){ String out = str_alloc(10 << 20); // NOTE(allen): Custom API headers - i32 main_api_count = unit_custom.parse[0].item_count; - i32 os_api_count = unit_custom.parse[1].item_count; - append(&out, "struct Application_Links;\n"); - - for (i32 i = main_api_count; i < os_api_count; ++i){ - append(&out, "#define "); - append(&out, func_4ed_names.names[i].macro); - append(&out, "(n) "); - append(&out, unit_custom.set.items[i].ret); - append(&out, " n"); - append(&out, unit_custom.set.items[i].args); - append_s_char(&out, '\n'); - } - - for (i32 i = main_api_count; i < os_api_count; ++i){ - append(&out, "typedef "); - append(&out, func_4ed_names.names[i].macro); - append_s_char(&out, '('); - append(&out, unit_custom.set.items[i].name); - append(&out, "_Function);\n"); - } - - fm_write_file(OS_API_H, out.str, out.size); - out.size = 0; - append(&out, "struct Application_Links;\n"); for (i32 i = 0; i < unit_custom.set.count; ++i){ @@ -401,7 +373,6 @@ generate_custom_headers(){ append(&out, "struct Application_Links{\n"); - append(&out, "#if defined(ALLOW_DEP_4CODER)\n"); for (i32 i = 0; i < unit_custom.set.count; ++i){ append(&out, unit_custom.set.items[i].name); @@ -731,7 +702,7 @@ generate_remapping_code_and_data(){ bind(mappings, 'x', MDFR_ALT, execute_arbitrary_command); - bind(mappings, 's', MDFR_ALT, show_scrollbar); + bind(mappings, 'W', MDFR_ALT, show_scrollbar); bind(mappings, 'w', MDFR_ALT, hide_scrollbar); bind(mappings, 'b', MDFR_ALT, toggle_filebar); @@ -824,6 +795,7 @@ generate_remapping_code_and_data(){ bind(mappings, 'q', MDFR_ALT , query_replace_selection); bind(mappings, 'r', MDFR_CTRL, reverse_search); bind(mappings, 's', MDFR_CTRL, save); + bind(mappings, 's', MDFR_ALT , save_to_query); bind(mappings, 't', MDFR_CTRL, search_identifier); bind(mappings, 'T', MDFR_CTRL, list_all_locations_of_identifier); bind(mappings, 'u', MDFR_CTRL, to_uppercase); @@ -930,7 +902,7 @@ generate_remapping_code_and_data(){ bind(mappings, 'x', MDFR_CTRL, execute_arbitrary_command); - bind(mappings, 's', MDFR_CTRL, show_scrollbar); + bind(mappings, 'W', MDFR_CTRL, show_scrollbar); bind(mappings, 'w', MDFR_CTRL, hide_scrollbar); bind(mappings, 'b', MDFR_CTRL, toggle_filebar); @@ -1020,6 +992,7 @@ generate_remapping_code_and_data(){ bind(mappings, 'Q', MDFR_CMND, query_replace_identifier); bind(mappings, 'r', MDFR_CMND, reverse_search); bind(mappings, 's', MDFR_CMND, save); + bind(mappings, 's', MDFR_CTRL, save_to_query); bind(mappings, 't', MDFR_CMND, search_identifier); bind(mappings, 'T', MDFR_CMND, list_all_locations_of_identifier); bind(mappings, 'u', MDFR_CMND, to_uppercase); diff --git a/meta/4ed_test_builder.cpp b/meta/4ed_test_builder.cpp index 97cf3432..636acd9c 100644 --- a/meta/4ed_test_builder.cpp +++ b/meta/4ed_test_builder.cpp @@ -223,6 +223,32 @@ require_unquoted_string(Line_Parse_Context context, i32 index, String *str_out){ return(result); } +internal bool32 +require_unquoted_multi_string(Line_Parse_Context context, i32 start_index, String *str_out){ + bool32 result = false; + if (start_index < context.words.count){ + String str = context.words.strings[start_index]; + if (str.str[0] != '"'){ + String last_word = context.words.strings[context.words.count - 1]; + char *end = last_word.str + last_word.size; + str.size = (i32)(end - str.str); + *str_out = str; + result = true; + } + else{ + show_error(context, + context.words.strings[context.words.count - 1].str, + "expected a simple word (a simple word must be unquoted)"); + } + } + else{ + show_error(context, + context.words.strings[context.words.count - 1].str, + "expected another word"); + } + return(result); +} + internal bool32 require_any_string(Line_Parse_Context context, i32 index, String *str_out){ bool32 result = require_unquoted_string(context, index, str_out); @@ -323,6 +349,7 @@ process_script__inner(Partition *scratch, char *name){ Simulation_Event *events = push_array(scratch, Simulation_Event, 0); i32 event_count = 0; + i32 standard_time_increment = 0; i32 time_counter = 0; for (i32 i = 0; i < lines.count; ++i){ @@ -378,6 +405,17 @@ process_script__inner(Partition *scratch, char *name){ } } + else if (match(first_word, "basewait")){ + i32 increment = 0; + if (require_integer(context, 1, &increment) && + require_blank(context, 2)){ + standard_time_increment = increment; + } + else{ + return; + } + } + else if (match(first_word, "key")){ String key_name = {0}; String mod_name = {0}; @@ -407,8 +445,7 @@ process_script__inner(Partition *scratch, char *name){ i32 increment = 0; String string = {0}; if (require_integer(context, 1, &increment) && - require_unquoted_string(context, 2, &string) && - require_blank(context, 3)){ + require_unquoted_multi_string(context, 2, &string)){ emit_type = true; type_increment = increment; type_string = string; @@ -543,6 +580,7 @@ process_script__inner(Partition *scratch, char *name){ memset(new_event, 0, sizeof(*new_event)); *new_event = event; event_count += 1; + time_counter += standard_time_increment; } if (emit_type){ @@ -554,8 +592,11 @@ process_script__inner(Partition *scratch, char *name){ new_event->key.code = type_string.str[j]; new_event->key.modifiers = MDFR_NONE; event_count += 1; - time_counter += type_increment; + if (j + 1 < type_string.size){ + time_counter += type_increment; + } } + time_counter += standard_time_increment; } if (emit_invoke){ @@ -584,7 +625,7 @@ process_script__inner(Partition *scratch, char *name){ } } if (count > 0){ - time_counter = events[count - 1].counter_index; + time_counter = events[count - 1].counter_index + standard_time_increment; } end_temp_memory(invoke_temp); diff --git a/meta/4ed_test_generator.cpp b/meta/4ed_test_generator.cpp index cd39f965..0a607186 100644 --- a/meta/4ed_test_generator.cpp +++ b/meta/4ed_test_generator.cpp @@ -11,19 +11,143 @@ #include "4ed_defines.h" #include "4coder_lib/4coder_string.h" +#include "4coder_lib/4coder_mem.h" #include "4coder_file.h" #include +//////////////////////////////// + +global char hot_directory_space[4096]; +global String hot_directory = {hot_directory_space, 0, sizeof(hot_directory_space)}; + internal void -print_usage(char *name){ - fprintf(stdout, "usage: %s\n", name); +init_hot_directory(char *dir){ + copy(&hot_directory, dir); + replace_char(&hot_directory, '\\', '/'); + if (hot_directory.str[hot_directory.size - 1] != '/'){ + append(&hot_directory, "/"); + } +} + +internal void +set_hot_directory(String str){ + copy(&hot_directory, str); +} + +internal void +set_hot_directory(char *str){ + copy(&hot_directory, str); +} + +internal void +push_folder_hot_directory(String str){ + append(&hot_directory, str); + append(&hot_directory, "/"); +} + +internal void +push_folder_hot_directory(char *str){ + append(&hot_directory, str); + append(&hot_directory, "/"); +} + +internal void +pop_folder_hot_directory(void){ + remove_last_folder(&hot_directory); +} + +internal String +get_hot_directory(Partition *part){ + String hot; + hot.str = push_array(part, char, hot_directory.size); + hot.size = hot_directory.size; + hot.memory_size = hot_directory.size; + memcpy(hot.str, hot_directory.str, hot_directory.size); + return(hot); } internal FILE* -try_open_output(char *name){ - FILE *out = fopen(name, "wb"); +fopen_hot_directory(Partition *scratch, char *file_name, char *flags){ + Temp_Memory temp = begin_temp_memory(scratch); + + char *full_name = push_array(scratch, char, hot_directory.size); + memcpy(full_name, hot_directory.str, hot_directory.size); + + i32 file_name_length = str_size(file_name); + char *full_name_file_portion = push_array(scratch, char, file_name_length); + memcpy(full_name_file_portion, file_name, file_name_length); + + char *terminator = push_array(scratch, char, 1); + *terminator = 0; + + FILE *result = fopen(full_name, flags); + + end_temp_memory(temp); + + return(result); +} + +//////////////////////////////// + +struct Test_Node{ + Test_Node *next; + String name; +}; + +struct Test_List{ + Partition *part; + + Test_Node *first; + Test_Node *last; + i32 count; +}; + +#define zdll_push(f,l,cptr,n) (((f) == 0)?((f) = (l) = (n)):((l)->next = (n), (l) = (n))), (*(cptr)) += 1 + +internal void +push_test(Test_List *list, String name){ + Test_Node *node = push_array(list->part, Test_Node, 1); + node->name = make_string_cap(push_array(list->part, char, name.size), 0, name.size); + push_align(list->part, 8); + copy(&node->name, name); + zdll_push(list->first, list->last, &list->count, node); +} + +internal void +push_test(Test_List *list, char *name){ + push_test(list, make_string_slowly(name)); +} + +//////////////////////////////// + +global String code_root; +global String script_root; +global String sample_root; + +typedef u32 Generate_Flag; +enum{ + GenFlag_RebuildSamples = 1, + GenFlag_RebuildScripts = 2, + GenFlag_OutputTestNames = 4, +}; +enum{ + GenFlag_DoAll = GenFlag_RebuildSamples|GenFlag_RebuildScripts|GenFlag_OutputTestNames, +}; + +#define DoSamples(f) (((f) & GenFlag_RebuildSamples) != 0) +#define DoScripts(f) (((f) & GenFlag_RebuildScripts) != 0) +#define DoTestNames(f) (((f) & GenFlag_OutputTestNames) != 0) + +internal void +print_usage(char *name){ + fprintf(stdout, "usage: %s code-root-directory\n", name); +} + +internal FILE* +try_open_output(Partition *scratch, char *name){ + FILE *out = fopen_hot_directory(scratch, name, "wb"); if (out == 0){ fprintf(stdout, "Could not open output file %s\n", name); } @@ -31,99 +155,299 @@ try_open_output(char *name){ } internal void -write_open_test_file_default_bindings(FILE *out, - char *src_name, char *dst_name){ - fprintf(out, - "wait 1\n" - "key o MDFR_CTRL\n" - "wait 1\n" - "type 1 %s\n" - "wait 1\n" - "key key_newline MDFR_NONE\n" - "wait 1\n" - "key key_space MDFR_CTRL\n" - "wait 1\n" - "key key_page_down MDFR_CTRL\n" - "wait 1\n" - "key c MDFR_CTRL\n" - "wait 1\n" - "key K MDFR_CTRL\n" - "wait 1\n" - "key o MDFR_CTRL\n" - "wait 1\n" - "key key_back MDFR_NONE\n" - "wait 1\n" - "type 1 output/%s\n" - "wait 1\n" - "key key_newline MDFR_NONE\n" - "wait 1\n" - "key key_space MDFR_CTRL\n" - "wait 1\n" - "key key_page_down MDFR_CTRL\n" - "wait 1\n" - "key d MDFR_CTRL\n" - "wait 1\n" - "key v MDFR_CTRL\n" - "wait 1\n" - "key m MDFR_CTRL\n" - "wait 1\n" - "key key_space MDFR_CTRL\n", - src_name, dst_name); +generate_run_script(Partition *scratch, Test_List list){ + Temp_Memory temp = begin_temp_memory(scratch); + + set_hot_directory(code_root); + FILE *out = try_open_output(scratch, "run_regression_tests.bat"); + if (out != 0){ + fprintf(out, + "@echo off\n" + + "pushd ..\\4coder-non-source\\test_data\n" + "set run_path=%%cd%%\\sample_files\n" + "set data_path=%%cd%%\\input_data\n" + "popd\n" + + "pushd ..\\build\n" + "set build=%%cd%%\n" + "popd\n" + + "pushd %%run_path%%\n"); + + for (Test_Node *node = list.first; + node != 0; + node = node->next){ + fprintf(out, "%%build%%\\4ed -T %%data_path%%\\%.*s\n", + node->name.size, node->name.str); + } + + fprintf(out, "popd\n"); + fclose(out); + } + + end_temp_memory(temp); } internal void -generate_capacity_stresser_1(void){ - FILE *out = try_open_output("gentest_capstress1.4is"); - if (out == 0){ - return; - } - - fprintf(out, "mouse_xy 20 20\n"); - write_open_test_file_default_bindings(out, - "small.cpp", "small.cpp"); - +write_open_test_file_default_bindings(FILE *out, + char *src_name, char *dst_name){ fprintf(out, - "wait 1\n" - "key g MDFR_CTRL\n" - "wait 1\n" - "key 6 MDFR_NONE\n" - "wait 1\n" - "key key_newline MDFR_NONE\n"); + "key o MDFR_CTRL\n" + "type 1 %s\n" + "key key_newline MDFR_NONE\n" + "key o MDFR_CTRL\n" + "key key_back MDFR_NONE\n" + "type 1 output/\n" + "key key_esc MDFR_NONE\n" + "key s MDFR_ALT\n" + "type 1 %s\n" + "key key_newline MDFR_NONE\n", + src_name, dst_name); +} + +internal String +generate_token_test_sample_file(Partition *part, i32 index, i32 token_target, Generate_Flag flags){ + i32 name_cap = 512; + String sample_name = make_string_cap(push_array(part, char, name_cap), 0, name_cap); + append(&sample_name, "gentokentest"); + append_int_to_str(&sample_name, index + 1); + append(&sample_name, ".cpp"); + bool32 string_build_success = terminate_with_null(&sample_name); + Assert(string_build_success); - for (i32 i = 0; i < 20; ++i){ - fprintf(out, - "wait 1\n" - "type 1 int\n" - "wait 1\n" - "key key_space MDFR_NONE\n" - "wait 1\n" - "type 1 foo%d\n" - "wait 1\n" - "key key_space MDFR_NONE\n" - "wait 1\n" - "key = MDFR_NONE\n" - "wait 1\n" - "key key_space MDFR_NONE\n" - "wait 1\n" - "type 1 %d;\n" - "wait 1\n" - "key key_newline MDFR_NONE\n", - i, i); + set_hot_directory(sample_root); + if (DoSamples(flags)){ + FILE *out = try_open_output(part, sample_name.str); + if (out != 0){ + fprintf(out, + "int foo(){\n" + "\n"); + i32 token_count = 6; + Assert(token_count < token_target); + for (;token_count + 10 < token_target;){ + fprintf(out, "int x = 0;\n"); + token_count += 5; + } + Assert(token_count < token_target); + fprintf(out, "}\n"); + fclose(out); + } + else{ + sample_name.str = 0; + sample_name.size = 0; + sample_name.memory_size = 0; + } } - fprintf(out, "exit\n"); + return(sample_name); +} + +internal void +generate_token_tests(Partition *scratch, Test_List *test_list, Generate_Flag flags){ + Temp_Memory temp = begin_temp_memory(scratch); - fclose(out); + for (i32 size = 1024, i = 0; i < 5; size <<= 1, ++i){ + char test_name_space[512]; + String test_name = make_fixed_width_string(test_name_space); + append(&test_name, "gentest_capstress"); + append_int_to_str(&test_name, i + 1); + append(&test_name, ".4is"); + bool32 string_build_success = terminate_with_null(&test_name); + Assert(string_build_success); + + String sample_name = generate_token_test_sample_file(scratch, i, size, flags); + + if (sample_name.str != 0){ + set_hot_directory(script_root); + if (DoScripts(flags)){ + FILE *out = try_open_output(scratch, test_name.str); + if (out != 0){ + fprintf(out, + "mouse_xy 20 20\n" + "key P MDFR_CTRL\n" + "basewait 1\n"); + write_open_test_file_default_bindings(out, sample_name.str, sample_name.str); + + fprintf(out, "key key_down MDFR_CTRL\n"); + + for (i32 i = 0; i < 5; ++i){ + fprintf(out, + "type 1 int x = 0;\n" + "key key_newline MDFR_NONE\n"); + } + + fprintf(out, + "key s MDFR_CTRL\n" + "exit\n" + "key Y MDFR_NONE"); + + fclose(out); + } + } + + if (DoTestNames(flags)){ + remove_extension(&test_name); + append(&test_name, "4id"); + bool32 string_build_success = terminate_with_null(&test_name); + Assert(string_build_success); + push_test(test_list, test_name); + } + } + } + + end_temp_memory(temp); +} + +internal String +generate_dupline_test_sample_file(Partition *part, i32 line_count, bool32 always_newline, Generate_Flag flags){ + i32 name_cap = 512; + String sample_name = make_string_cap(push_array(part, char, name_cap), 0, name_cap); + append(&sample_name, "genduplinetest"); + append_int_to_str(&sample_name, line_count); + append(&sample_name, "_"); + append_int_to_str(&sample_name, always_newline); + append(&sample_name, ".cpp"); + bool32 string_build_success = terminate_with_null(&sample_name); + Assert(string_build_success); + + if (DoSamples(flags)){ + set_hot_directory(sample_root); + FILE *out = try_open_output(part, sample_name.str); + if (out != 0){ + for (i32 i = 0; i < line_count; ++i){ + fprintf(out, "abcd"); + if (i + 1 < line_count || always_newline){ + fprintf(out, "\n"); + } + } + fclose(out); + } + } + + return(sample_name); +} + +internal void +generate_dupline_specific_test(Partition *scratch, Test_List *test_list, Generate_Flag flags, + i32 line_count, bool32 always_newline, bool32 read_only){ + char test_name_space[512]; + String test_name = make_fixed_width_string(test_name_space); + if (read_only){ + append(&test_name, "gentest_dupline_readonly.4is"); + } + else{ + append(&test_name, "gentest_dupline"); + append_int_to_str(&test_name, line_count); + append(&test_name, "_"); + append_int_to_str(&test_name, always_newline); + append(&test_name, ".4is"); + } + bool32 string_build_success = terminate_with_null(&test_name); + Assert(string_build_success); + + String sample_name; + if (read_only){ + sample_name = make_lit_string("*messages*"); + } + else{ + sample_name = generate_dupline_test_sample_file(scratch, line_count, always_newline, flags); + } + + if (sample_name.str != 0){ + set_hot_directory(script_root); + if (DoScripts(flags)){ + FILE *out = try_open_output(scratch, test_name.str); + if (out != 0){ + fprintf(out, + "mouse_xy 20 20\n" + "key P MDFR_CTRL\n" + "basewait 1\n"); + if (read_only){ + fprintf(out, + "key i MDFR_CTRL\n" + "type 1 *messages*\n" + "key key_newline MDFR_NONE\n" + "key L MDFR_CTRL\n" + "key s MDFR_CTRL\n" + "exit\n"); + } + else{ + write_open_test_file_default_bindings(out, sample_name.str, sample_name.str); + fprintf(out, + "key L MDFR_CTRL\n" + "key s MDFR_CTRL\n" + "exit\n"); + } + fclose(out); + } + } + + if (DoTestNames(flags)){ + remove_extension(&test_name); + append(&test_name, "4id"); + bool32 string_build_success = terminate_with_null(&test_name); + Assert(string_build_success); + push_test(test_list, test_name); + } + } +} + +internal void +generate_dupline_tests(Partition *scratch, Test_List *test_list, Generate_Flag flags){ + Temp_Memory temp = begin_temp_memory(scratch); + for (i32 line_count = 0; line_count < 2; ++line_count){ + for (bool32 always_newline = 0; always_newline <= 1; ++always_newline){ + generate_dupline_specific_test(scratch, test_list, flags, + line_count, always_newline, false); + } + } + generate_dupline_specific_test(scratch, test_list, flags, 0, false, true); + end_temp_memory(temp); } int main(int argc, char **argv){ - if (argc > 1){ + if (argc != 2){ print_usage(argv[0]); exit(1); } - generate_capacity_stresser_1(); + // NOTE(allen): Init the hot directory + init_hot_directory(argv[1]); + + // NOTE(allen): Init the partition + i32 memory_size = MB(8); + Partition part_ = make_part(malloc(memory_size), memory_size); + Partition *part = &part_; + + // NOTE(allen): Get various root paths + code_root = get_hot_directory(part); + + push_folder_hot_directory("test_input_scripts/generated"); + script_root = get_hot_directory(part); + + set_hot_directory(code_root); + pop_folder_hot_directory(); + push_folder_hot_directory("4coder-non-source/test_data/sample_files"); + sample_root = get_hot_directory(part); + + // NOTE(allen): Setup the test list + i32 test_list_size = MB(8); + Partition test_list_part = make_part(malloc(test_list_size), test_list_size); + + Test_List test_list = {0}; + test_list.part = &test_list_part; + + // NOTE(allen): Tests + push_test(&test_list, "test_load_FONT_COURIER_NEW_28_c.4id"); + generate_token_tests(part, &test_list, GenFlag_DoAll); + //generate_token_tests(part, &test_list, GenFlag_OutputTestNames); + generate_dupline_tests(part, &test_list, GenFlag_DoAll); + //generate_dupline_tests(part, &test_list, GenFlag_OutputTestNames); + + // NOTE(allen): Generate the run test script + generate_run_script(part, test_list); return(0); } diff --git a/platform_linux/linux_4ed.cpp b/platform_linux/linux_4ed.cpp index a9fb5d1e..69338c3c 100644 --- a/platform_linux/linux_4ed.cpp +++ b/platform_linux/linux_4ed.cpp @@ -28,7 +28,6 @@ # include "4coder_lib/4coder_mem.h" # include "4coder_API/types.h" -# include "4ed_os_custom_api.h" #else # include "4coder_default_bindings.cpp" diff --git a/platform_mac/mac_4ed.cpp b/platform_mac/mac_4ed.cpp index 06b270a3..c22dd065 100644 --- a/platform_mac/mac_4ed.cpp +++ b/platform_mac/mac_4ed.cpp @@ -25,7 +25,6 @@ # include "4coder_lib/4coder_mem.h" # include "4coder_API/types.h" -# include "4ed_os_custom_api.h" #else # include "4coder_default_bindings.cpp" diff --git a/platform_win32/win32_4ed.cpp b/platform_win32/win32_4ed.cpp index 171993be..43e8c456 100644 --- a/platform_win32/win32_4ed.cpp +++ b/platform_win32/win32_4ed.cpp @@ -36,7 +36,6 @@ # include "4coder_lib/4coder_mem.h" # include "4coder_API/types.h" -# include "4ed_os_custom_api.h" #else # include "4coder_default_bindings.cpp" diff --git a/project.4coder b/project.4coder index 545d591c..bc56ef3c 100644 --- a/project.4coder +++ b/project.4coder @@ -6,11 +6,15 @@ fkey_command_win[2] = {"build_site.bat" , "*site*" , false, fkey_command_win[3] = {"build_string.bat" , "*compilation*" , true , true }; fkey_command_win[4] = {"echo build: x86 & build.bat /DDEV_BUILD_X86" , "*compilation*", true, true }; fkey_command_win[5] = {"build_metadata.bat" , "*compilation*" , true , true }; -fkey_command_win[6] = {"run_regression_tests.bat" , "*test*" , false, false}; +fkey_command_win[6] = {"run_regression_tests.bat" , "*test*" , false, true }; fkey_command_win[7] = {"build_tests.bat" , "*compilation*" , true , true }; fkey_command_win[8] = {"generate_tests.bat" , "*compilation*" , true , true }; fkey_command_win[12] = {"package.bat" , "*package*" , false, true }; +fkey_command_win[1] = {"build_tests.bat" , "*compilation*" , true , true }; +fkey_command_win[2] = {"generate_tests.bat" , "*compilation*" , true , true }; +fkey_command_win[3] = {"run_regression_tests.bat" , "*test*" , false, true }; + fkey_command_linux[1] = {"echo build: x64 & ./build.sh", "*compilation*" , true , true }; fkey_command_linux[2] = {"build_site.sh" , "*site*" , false, true }; fkey_command_linux[3] = {"build_string.sh" , "*compilation*" , true , true }; diff --git a/run_regression_tests.bat b/run_regression_tests.bat index ad626b7f..e7dd6671 100644 --- a/run_regression_tests.bat +++ b/run_regression_tests.bat @@ -1,16 +1,21 @@ @echo off - pushd ..\4coder-non-source\test_data set run_path=%cd%\sample_files set data_path=%cd%\input_data popd - pushd ..\build set build=%cd% popd - pushd %run_path% -rem %build%\4ed -T %data_path%\test_bootstrap.4id +%build%\4ed -T %data_path%\test_load_FONT_COURIER_NEW_28_c.4id %build%\4ed -T %data_path%\gentest_capstress1.4id +%build%\4ed -T %data_path%\gentest_capstress2.4id +%build%\4ed -T %data_path%\gentest_capstress3.4id +%build%\4ed -T %data_path%\gentest_capstress4.4id +%build%\4ed -T %data_path%\gentest_capstress5.4id +%build%\4ed -T %data_path%\gentest_dupline0_0.4id +%build%\4ed -T %data_path%\gentest_dupline0_1.4id +%build%\4ed -T %data_path%\gentest_dupline1_0.4id +%build%\4ed -T %data_path%\gentest_dupline1_1.4id +%build%\4ed -T %data_path%\gentest_dupline_readonly.4id popd - diff --git a/site/source_material/home.txt b/site/source_material/home.txt index 1b32b5e7..e2bbb03b 100644 --- a/site/source_material/home.txt +++ b/site/source_material/home.txt @@ -5,7 +5,7 @@ If you cannot find what you are looking for please contact \STYLE{code} editor@4coder.net \END with questions. -\LINK{!http://patreon.com/mr4thdimention} Support Development \END +\LINK{!http://4coder.itch.io/4coder} Get Builds and Support Development \END \LINK{document:features} Feature List \END diff --git a/test_input_scripts/test_bootstrap.4is b/test_input_scripts/test_bootstrap.4is deleted file mode 100644 index b9e30fb0..00000000 --- a/test_input_scripts/test_bootstrap.4is +++ /dev/null @@ -1,41 +0,0 @@ - -mouse_xy 20 20 - -wait 45 -debug_number 1 -key _ MDFR_CTRL - -wait 5 -debug_number 2 -invoke test_write_4coder_awesomeness.4id - -wait 30 -invoke test_full_click.4id - -wait 10 -mouse_left_press - -wait 10 -mouse_xy 50 20 -mouse_left_release - -wait 40 -key key_back MDFR_CTRL -key key_end MDFR_NONE - -wait 10 -key o MDFR_CTRL - -wait 10 -type 5 unicode.txt -wait 25 -key key_newline MDFR_NONE -wait 25 -type 5 hello -key key_newline MDFR_NONE - -wait 60 -exit - -wait 60 -key Y MDFR_NONE diff --git a/test_input_scripts/test_full_click.4is b/test_input_scripts/test_full_click.4is deleted file mode 100644 index 9770ee65..00000000 --- a/test_input_scripts/test_full_click.4is +++ /dev/null @@ -1,6 +0,0 @@ - -mouse_left_press -wait 2 -mouse_left_release - -exit diff --git a/test_input_scripts/test_load_FONT_COURIER_NEW_28_c.4is b/test_input_scripts/test_load_FONT_COURIER_NEW_28_c.4is new file mode 100644 index 00000000..b85d5b7a --- /dev/null +++ b/test_input_scripts/test_load_FONT_COURIER_NEW_28_c.4is @@ -0,0 +1,6 @@ + +basewait 1 +key o MDFR_CTRL +type 1 FONT_COURIER_NEW_28 +key key_newline MDFR_NONE +exit diff --git a/test_input_scripts/test_write_4coder_awesomeness.4is b/test_input_scripts/test_write_4coder_awesomeness.4is deleted file mode 100644 index b011dc27..00000000 --- a/test_input_scripts/test_write_4coder_awesomeness.4is +++ /dev/null @@ -1,6 +0,0 @@ - -type 2 4coder -key key_tab MDFR_NONE -wait 2 -type 2 awesomeness -exit \ No newline at end of file