diff --git a/4coder_custom.cpp b/4coder_custom.cpp index 923f15d2..8dbddd71 100644 --- a/4coder_custom.cpp +++ b/4coder_custom.cpp @@ -3,59 +3,148 @@ */ // NOTE(allen): NEW THINGS TO LOOK FOR: -// MAPID_USER_CUSTOM - define maps other than the built in GLOBAL/FILE maps -// inherit_map +// mapid_user_custom - define maps other than the built in global and file maps +// +// inherit_map - override bindings or add new bindings in a new map with another +// +// set_hook - if you were using start_hook, it is still available if you use set_hook +// +// push_parameter - see description of parameter stack immediately below +// clear_parameters +// exec_command_keep_stack +// +// THE PARAMETER STACK: +// In this version I have introduced a parameter stack. +// Calls to commands through exec_command can now be parameterized +// by first pushing parameters onto that stack. This is achieved through +// the push_parameter. If you look at the signature for the function it +// uses a "Dynamic" struct, but 4coder_helper.h has overrides that accept +// ints or strings. The helper functions also take care of copying the +// strings inline into the stack so that you don't have to maintain your copy. +// +// If you'd like to optimize out the extra copy it will work, just use the +// main app.push_parameter and keep the memory of the string alive until +// the stack is cleared. +// +// A call to exec_command executes the command with the current stack, +// then clears the stack. To keep the stack use exec_command_keep_stack. +// +// If you would like to allocate your own memory, you can tie your memory +// to the parameter stack by calling push_memory which takes a cmd_context and len. +// It will return a char* to your memory block. Until you call clear_parameters +// or exec_command the memory will remain on the stack for you to use. Memory +// chunks on the stack are ignored by commands that use parameters, so your memory +// will not influence the behavior of any commands. // -// get_settings #include "4coder_custom.h" #include "4coder_helper.h" -#define exec_command app.exec_command -#define fulfill_interaction app.fulfill_interaction +#define exec_command_keep_stack app.exec_command_keep_stack +#define clear_parameters app.clear_parameters +#define get_active_buffer app.get_active_buffer -extern "C" START_HOOK_SIG(start_hook){ +#define exec_command(cmd_context, id) \ + exec_command_keep_stack(cmd_context, id); \ + clear_parameters(cmd_context) +#define push_parameter(cmd_context, ...) push_parameter_helper(cmd_context, app, __VA_ARGS__) +#define push_memory(cmd_context, len) app.push_memory(cmd_context, len) + +#define literal(s) s, (sizeof(s)-1) + +// NOTE(allen): All of your custom ids should be >= mapid_user_custom. +// I recommend enumerating your own map ids as shown here. +enum My_Maps{ + my_code_map = mapid_user_custom, +}; + +HOOK_SIG(my_start){ exec_command(cmd_context, cmdid_open_panel_vsplit); exec_command(cmd_context, cmdid_change_active_panel); } +CUSTOM_COMMAND_SIG(open_my_files){ + // NOTE(allen): The command cmdid_interactive_open has is now able to + // open a file specified on the parameter stack. If the file does not + // exist cmdid_interactive_open behaves as usual. + push_parameter(cmd_context, par_name, literal("w:/4ed/data/test/basic.cpp")); + exec_command(cmd_context, cmdid_interactive_open); + + exec_command(cmd_context, cmdid_change_active_panel); + + char my_file[256]; + int my_file_len; + + my_file_len = sizeof("w:/4ed/data/test/basic.txt") - 1; + for (int i = 0; i < my_file_len; ++i){ + my_file[i] = ("w:/4ed/data/test/basic.txt")[i]; + } + + // NOTE(allen): null terminators are not needed for strings. + push_parameter(cmd_context, par_name, my_file, my_file_len); + exec_command(cmd_context, cmdid_interactive_open); + + exec_command(cmd_context, cmdid_change_active_panel); +} + +char *get_extension(const char *filename, int len, int *extension_len){ + char *c = (char*)(filename + len - 1); + char *end = c; + while (*c != '.' && c > filename) --c; + *extension_len = (int)(end - c); + return c+1; +} + +bool str_match(const char *a, int len_a, const char *b, int len_b){ + bool result = 0; + if (len_a == len_b){ + char *end = (char*)(a + len_a); + while (a < end && *a == *b){ + ++a; ++b; + } + if (a == end) result = 1; + } + return result; +} + +HOOK_SIG(my_file_settings){ + Buffer_Summary buffer = get_active_buffer(cmd_context); + + int treat_as_code = 0; + + // NOTE(allen): This checks buffer.file_name just in case get_active_buffer returns back + // a null buffer (where every member is 0). + if (buffer.file_name && buffer.size < (16 << 20)){ + int extension_len; + char *extension = get_extension(buffer.file_name, buffer.file_name_len, &extension_len); + if (str_match(extension, extension_len, literal("cpp"))) treat_as_code = 1; + else if (str_match(extension, extension_len, literal("h"))) treat_as_code = 1; + else if (str_match(extension, extension_len, literal("c"))) treat_as_code = 1; + else if (str_match(extension, extension_len, literal("hpp"))) treat_as_code = 1; + } + + push_parameter(cmd_context, par_lex_as_cpp_file, treat_as_code); + push_parameter(cmd_context, par_wrap_lines, !treat_as_code); + push_parameter(cmd_context, par_key_mapid, (treat_as_code)?(my_code_map):(mapid_file)); + push_parameter(cmd_context, par_end_line_mode, EOL_USE_CRLF); + exec_command(cmd_context, cmdid_set_settings); +} + CUSTOM_COMMAND_SIG(open_in_other){ exec_command(cmd_context, cmdid_change_active_panel); exec_command(cmd_context, cmdid_interactive_open); } -extern "C" GET_BINDING_DATA(get_binding){ +extern "C" GET_BINDING_DATA(get_bindings){ Bind_Helper context_actual = begin_bind_helper(data, size); Bind_Helper *context = &context_actual; - begin_settings_group(context); + // NOTE(allen): Right now hooks have no loyalties to maps, all hooks are + // global and once set they always apply. + set_hook(context, hook_start, my_start); + set_hook(context, hook_open_file, my_file_settings); - use_when(context, when_default, 1); - - set(context, set_lex_as_cpp_file, 0); - set(context, set_wrap_lines, 1); - set(context, set_key_mapid, MAPID_FILE); - - // NOTE(allen): options include EOL_USE_CRLF, EOL_USE_CR_USE_LF, EOL_SHOW_CR_USE_LF - // EOL_USE_CRLF - treats a crlf and lf as newline markers, renders lone cr as special character "\r" - // EOL_USE_CR_USE_LF - treats both as separate newline markers - // EOL_SHOW_CR_USE_LF - treats lf as newline marker, renders cr as special character "\r" - set(context, set_end_line_mode, EOL_USE_CRLF); - - end_group(context); - - - begin_settings_group(context); - - use_when(context, when_extension, "cpp"); - use_when(context, when_extension, "hpp"); - use_when(context, when_extension, "c"); - use_when(context, when_extension, "h"); - - set(context, set_lex_as_cpp_file, 1); - set(context, set_key_mapid, MAPID_USER_CUSTOM + 0); - - begin_map(context, MAPID_GLOBAL); + begin_map(context, mapid_global); bind(context, 'p', MDFR_CTRL, cmdid_open_panel_vsplit); bind(context, '-', MDFR_CTRL, cmdid_open_panel_hsplit); @@ -68,19 +157,22 @@ extern "C" GET_BINDING_DATA(get_binding){ bind(context, 'c', MDFR_ALT, cmdid_open_color_tweaker); bind(context, 'x', MDFR_ALT, cmdid_open_menu); bind_me(context, 'o', MDFR_ALT, open_in_other); + // NOTE(allen): Go look at open_my_files, that's the only point of this being here. + bind_me(context, 'M', MDFR_ALT | MDFR_CTRL, open_my_files); end_map(context); - - begin_map(context, MAPID_USER_CUSTOM + 0); - - // NOTE(allen): Set this map (MAPID_USER_CUSTOM + 0) to - // inherit from MAPID_FILE. When searching if a key is bound - // in this map, if it is not found here it will then search MAPID_FILE. + + begin_map(context, my_code_map); + + // NOTE(allen): Set this map (my_code_map == mapid_user_custom) to + // inherit from mapid_file. When searching if a key is bound + // in this map, if it is not found here it will then search mapid_file. // - // If this is not set, it defaults to MAPID_GLOBAL. - inherit_map(context, MAPID_FILE); - + // If this is not set, it defaults to mapid_global. + inherit_map(context, mapid_file); + + // NOTE(allen): This demonstrates that children can override parent's bindings. bind(context, codes->right, MDFR_CTRL, cmdid_seek_alphanumeric_or_camel_right); bind(context, codes->left, MDFR_CTRL, cmdid_seek_alphanumeric_or_camel_left); @@ -90,7 +182,7 @@ extern "C" GET_BINDING_DATA(get_binding){ end_map(context); - begin_map(context, MAPID_FILE); + begin_map(context, mapid_file); // NOTE(allen): Binding this essentially binds all key combos that // would normally insert a character into a buffer. @@ -121,15 +213,21 @@ extern "C" GET_BINDING_DATA(get_binding){ bind(context, 'x', MDFR_CTRL, cmdid_cut); bind(context, 'v', MDFR_CTRL, cmdid_paste); bind(context, 'V', MDFR_CTRL, cmdid_paste_next); + bind(context, 'Z', MDFR_CTRL, cmdid_timeline_scrub); bind(context, 'z', MDFR_CTRL, cmdid_undo); bind(context, 'y', MDFR_CTRL, cmdid_redo); + bind(context, codes->left, MDFR_ALT, cmdid_increase_rewind_speed); + bind(context, codes->right, MDFR_ALT, cmdid_increase_fastforward_speed); + bind(context, codes->down, MDFR_ALT, cmdid_stop_rewind_fastforward); + bind(context, 'h', MDFR_CTRL, cmdid_history_backward); + bind(context, 'H', MDFR_CTRL, cmdid_history_forward); bind(context, 'd', MDFR_CTRL, cmdid_delete_chunk); bind(context, 'l', MDFR_CTRL, cmdid_toggle_line_wrap); bind(context, 'L', MDFR_CTRL, cmdid_toggle_endline_mode); bind(context, 'u', MDFR_CTRL, cmdid_to_uppercase); bind(context, 'j', MDFR_CTRL, cmdid_to_lowercase); bind(context, '?', MDFR_CTRL, cmdid_toggle_show_whitespace); - + // NOTE(allen): These whitespace manipulators are not currently functional bind(context, '`', MDFR_CTRL, cmdid_clean_line); bind(context, '~', MDFR_CTRL, cmdid_clean_all_lines); diff --git a/4coder_custom.h b/4coder_custom.h index d26117a9..8af73613 100644 --- a/4coder_custom.h +++ b/4coder_custom.h @@ -70,8 +70,14 @@ enum Command_ID{ cmdid_paste, cmdid_paste_next, cmdid_delete_chunk, + cmdid_timeline_scrub, cmdid_undo, cmdid_redo, + cmdid_increase_rewind_speed, + cmdid_increase_fastforward_speed, + cmdid_stop_rewind_fastforward, + cmdid_history_backward, + cmdid_history_forward, cmdid_interactive_new, cmdid_interactive_open, cmdid_reopen, @@ -108,106 +114,202 @@ enum Command_ID{ cmdid_close_minor_view, cmdid_cursor_mark_swap, cmdid_open_menu, + cmdid_set_settings, // cmdid_count }; +enum Param_ID{ + par_name, + par_lex_as_cpp_file, + par_wrap_lines, + par_key_mapid, + par_end_line_mode, + // never below this + par_type_count +}; + +enum Hook_ID{ + hook_start, + hook_open_file, + // never below this + hook_type_count +}; + +enum Dynamic_Type{ + dynamic_type_int, + dynamic_type_string, + // never below this + dynamic_type_count +}; + +struct Dynamic{ + int type; + union{ + struct{ + int str_len; + char *str_value; + }; + int int_value; + }; +}; + +inline Dynamic +dynamic_int(int x){ + Dynamic result; + result.type = dynamic_type_int; + result.int_value = x; + return result; +} + +inline Dynamic +dynamic_string(const char *string, int len){ + Dynamic result; + result.type = dynamic_type_string; + result.str_len = len; + result.str_value = (char*)(string); + return result; +} + +inline int +dynamic_to_int(Dynamic *dynamic){ + int result = 0; + if (dynamic->type == dynamic_type_int){ + result = dynamic->int_value; + } + return result; +} + +inline char* +dynamic_to_string(Dynamic *dynamic, int *len){ + char *result = 0; + if (dynamic->type == dynamic_type_string){ + result = dynamic->str_value; + *len = dynamic->str_len; + } + return result; +} + +inline int +dynamic_to_bool(Dynamic *dynamic){ + int result = 0; + if (dynamic->type == dynamic_type_int){ + result = (dynamic->int_value != 0); + } + else{ + result = 1; + } + return result; +} + struct Extra_Font{ char file_name[256]; char font_name[24]; int size; }; -#define GET_BINDING_DATA(name) int name(void *data, int size, Key_Codes *codes) -#define SET_EXTRA_FONT_SIG(name) void name(Extra_Font *font_out) -#define CUSTOM_COMMAND_SIG(name) void name(void *cmd_context, struct Application_Links app) -#define START_HOOK_SIG(name) void name(void *cmd_context, struct Application_Links app) - -extern "C"{ - typedef CUSTOM_COMMAND_SIG(Custom_Command_Function); - typedef GET_BINDING_DATA(Get_Binding_Data_Function); - typedef SET_EXTRA_FONT_SIG(Set_Extra_Font_Function); - typedef START_HOOK_SIG(Start_Hook_Function); -} - -#define EXECUTE_COMMAND_SIG(name) void name(void *cmd_context, int command_id) -#define FULFILL_INTERACTION_SIG(name) void name(void *cmd_context, char *data, bool full_set) - -extern "C"{ - typedef EXECUTE_COMMAND_SIG(Exec_Command_Function); - typedef FULFILL_INTERACTION_SIG(Fulfill_Interaction_Function); -} - -struct Application_Links{ - Exec_Command_Function *exec_command; - Fulfill_Interaction_Function *fulfill_interaction; -}; - -enum Settings_Unit_Type{ - SUNIT_HEADER, - SUNIT_GROUP, - SUNIT_USE_CLAUSE, - SUNIT_USE_CLAUSE_STRING, - SUNIT_SETTING -}; - -enum Setting_ID{ - set_lex_as_cpp_file, - set_wrap_lines, - set_key_mapid, - set_end_line_mode -}; - -enum Setting_When_Type{ - when_default, - when_extension -}; - -enum End_Of_Line_Options{ +enum EOL_Option{ EOL_USE_CRLF, EOL_USE_CR_USE_LF, EOL_SHOW_CR_USE_LF }; +struct Buffer_Summary{ + // NOTE(allen): None of these members nor any of the data pointed to + // by these members should be modified. + int file_id; + + int size; + const char *data; + + int file_name_len; + int buffer_name_len; + const char *file_name; + const char *buffer_name; + + int file_cursor_pos; + + EOL_Option eol_mode; + int is_lexed; + + int map_id; +}; + +#define GET_BINDING_DATA(name) int name(void *data, int size, Key_Codes *codes) +#define SET_EXTRA_FONT_SIG(name) void name(Extra_Font *font_out) +#define CUSTOM_COMMAND_SIG(name) void name(void *cmd_context, struct Application_Links app) +#define HOOK_SIG(name) void name(void *cmd_context, struct Application_Links app) + +extern "C"{ + typedef CUSTOM_COMMAND_SIG(Custom_Command_Function); + typedef GET_BINDING_DATA(Get_Binding_Data_Function); + typedef SET_EXTRA_FONT_SIG(Set_Extra_Font_Function); + typedef HOOK_SIG(Hook_Function); +} + +#define PUSH_PARAMETER_SIG(name) void name(void *cmd_context, Dynamic param, Dynamic value) +#define PUSH_MEMORY_SIG(name) char* name(void *cmd_context, int len) +#define EXECUTE_COMMAND_SIG(name) void name(void *cmd_context, int command_id) +#define CLEAR_PARAMETERS_SIG(name) void name(void *cmd_context) +#define GET_ACTIVE_BUFFER_SIG(name) Buffer_Summary name(void *cmd_context) + +extern "C"{ + typedef EXECUTE_COMMAND_SIG(Exec_Command_Function); + typedef PUSH_PARAMETER_SIG(Push_Parameter_Function); + typedef PUSH_MEMORY_SIG(Push_Memory_Function); + typedef CLEAR_PARAMETERS_SIG(Clear_Parameters_Function); + typedef GET_ACTIVE_BUFFER_SIG(Get_Active_Buffer_Function); +} + +struct Application_Links{ + Exec_Command_Function *exec_command_keep_stack; + Push_Parameter_Function *push_parameter; + Push_Memory_Function *push_memory; + Clear_Parameters_Function *clear_parameters; + Get_Active_Buffer_Function *get_active_buffer; +}; + +// NOTE(allen): definitions for the buffer that communicates to 4ed.exe + enum Binding_Unit_Type{ - UNIT_HEADER, - UNIT_MAP_BEGIN, - UNIT_BINDING, - UNIT_CALLBACK, - UNIT_INHERIT, - UNIT_SETTINGS_BEGIN, - UNIT_USE_CLAUSE, - UNIT_USE_CLAUSE_STRING, - UNIT_SETTING + unit_header, + unit_map_begin, + unit_binding, + unit_callback, + unit_inherit, + unit_hook }; enum Map_ID{ - MAPID_GLOBAL, - MAPID_FILE, - MAPID_USER_CUSTOM + mapid_global, + mapid_file, + // NOTE(allen): mapid_nomap will remain empty even if you attempt to fill it + // it is for setting a map's parent to nothing + mapid_nomap, + mapid_user_custom = 100 }; struct Binding_Unit{ Binding_Unit_Type type; union{ - struct{ int total_size; int map_count; int group_count; int error; } header; + struct{ int total_size; int user_map_count; int error; } header; struct{ int mapid; int bind_count; } map_begin; struct{ int mapid; } map_inherit; struct{ - int command_id; short code; unsigned char modifiers; + int command_id; } binding; struct{ - Custom_Command_Function *func; short code; unsigned char modifiers; + Custom_Command_Function *func; } callback; - - struct{ int clause_type; int value; } use_clause; - struct{ int clause_type; int len; char *value; } use_clause_string; - struct{ int setting_id; int value; } setting; + struct{ + int hook_id; + Custom_Command_Function *func; + } hook; }; }; diff --git a/4coder_custom_vim.cpp b/4coder_custom_vim.cpp deleted file mode 100644 index 81a0348d..00000000 --- a/4coder_custom_vim.cpp +++ /dev/null @@ -1,135 +0,0 @@ -/* - * This is an example of how vim-keys might start - * to work in 4coder, through the customization API. - */ - -#include "4coder_custom.h" -#include "4coder_helper.h" - -#define exec_command app.exec_command -#define fulfill_interaction app.fulfill_interaction - -extern "C" START_HOOK_SIG(start_hook){ - exec_command(cmd_context, cmdid_open_panel_vsplit); - exec_command(cmd_context, cmdid_change_active_panel); -} - - -extern "C" GET_BINDING_DATA(get_binding){ - Bind_Helper context_actual = begin_bind_helper(data, size); - Bind_Helper *context = &context_actual; - - begin_settings_group(context); - - use_when(context, when_default, 1); - - set(context, set_lex_as_cpp_file, 0); - set(context, set_wrap_lines, 1); - set(context, set_key_mapid, MAPID_FILE); - - // NOTE(allen): options include EOL_USE_CRLF, EOL_USE_CR_USE_LF, EOL_SHOW_CR_USE_LF - // EOL_USE_CRLF - treats a crlf and lf as newline markers, renders lone cr as special character "\r" - // EOL_USE_CR_USE_LF - treats both as separate newline markers - // EOL_SHOW_CR_USE_LF - treats lf as newline marker, renders cr as special character "\r" - set(context, set_end_line_mode, EOL_USE_CRLF); - - end_group(context); - - - begin_settings_group(context); - - use_when(context, when_extension, "cpp"); - use_when(context, when_extension, "hpp"); - use_when(context, when_extension, "c"); - use_when(context, when_extension, "h"); - - set(context, set_lex_as_cpp_file, 1); - set(context, set_key_mapid, MAPID_USER_CUSTOM + 0); - - begin_map(context, MAPID_GLOBAL); - - bind(context, 'p', MDFR_CTRL, cmdid_open_panel_vsplit); - bind(context, '-', MDFR_CTRL, cmdid_open_panel_hsplit); - bind(context, 'P', MDFR_CTRL, cmdid_close_panel); - bind(context, 'n', MDFR_CTRL, cmdid_interactive_new); - bind(context, 'o', MDFR_CTRL, cmdid_interactive_open); - bind(context, ',', MDFR_CTRL, cmdid_change_active_panel); - bind(context, 'k', MDFR_CTRL, cmdid_interactive_kill_buffer); - bind(context, 'i', MDFR_CTRL, cmdid_interactive_switch_buffer); - bind(context, 'c', MDFR_ALT, cmdid_open_color_tweaker); - bind(context, 'x', MDFR_ALT, cmdid_open_menu); - bind_me(context, 'o', MDFR_ALT, open_in_other); - - end_map(context); - - - begin_map(context, MAPID_USER_CUSTOM + 0); - inherit_map(context, MAPID_FILE); - end_map(context); - - int sub_id; - begin_map(context, MAPID_FILE); - - sub_id = begin_sub_map(context); - { - bind_vanilla_keys(context, cmdid_write_character); - - bind(context, codes->left, MDFR_NONE, cmdid_move_left); - bind(context, codes->right, MDFR_NONE, cmdid_move_right); - bind(context, codes->del, MDFR_NONE, cmdid_delete); - bind(context, codes->back, MDFR_NONE, cmdid_backspace); - bind(context, codes->up, MDFR_NONE, cmdid_move_up); - bind(context, codes->down, MDFR_NONE, cmdid_move_down); - bind(context, codes->end, MDFR_NONE, cmdid_seek_end_of_line); - bind(context, codes->home, MDFR_NONE, cmdid_seek_beginning_of_line); - bind(context, codes->page_up, MDFR_NONE, cmdid_page_up); - bind(context, codes->page_down, MDFR_NONE, cmdid_page_down); - } - end_sub_map(context); - - bind_params(context, 'i', MDFR_NONE, cmdid_use_sub_map); - fill_param(context, "sub_id", sub_id); - end_params(context); - - bind_multi(context, 'a', MDFR_NONE); - { - bind(context, 'a', MDFR_NONE, cmdid_move_left); - bind_params(context, 'a', MDFR_NONE, cmdid_use_sub_map); - fill_param(context, "sub_id", sub_id); - end_params(context); - } - end_multi(context); - - bind(context, 'b', cmdid_seek_alphanumeric_left); - bind(context, 'w', cmdid_seek_alphanumeric_right); - bind(context, 'e', cmdid_seek_white_or_token_right); - - // ???? this seems a bit off - bind_compound(context, 'g'); - { - bind(context, 'e', cmdid_seek_white_or_token_left); - } - end_compound(context); - - end_map(context); - end_bind_helper(context); - - return context->write_total; -} - -inline void -strset_(char *dst, char *src){ - do{ - *dst++ = *src++; - }while (*src); -} - -#define strset(d,s) if (sizeof(s) <= sizeof(d)) strset_(d,s) - -extern "C" SET_EXTRA_FONT_SIG(set_extra_font){ - strset(font_out->file_name, "liberation-mono.ttf"); - strset(font_out->font_name, "BIG"); - font_out->size = 25; -} - - diff --git a/4coder_helper.h b/4coder_helper.h index be5c3ac0..9e502b16 100644 --- a/4coder_helper.h +++ b/4coder_helper.h @@ -14,15 +14,8 @@ struct Bind_Helper{ #define BH_ERR_MISSING_BEGIN 2 #define BH_ERR_OUT_OF_MEMORY 3 -inline int -seek_null(char *str){ - char *start = str; - while (*str) ++str; - return (int)(str - start); -} - inline void -copy(char *dest, char *src, int len){ +copy(char *dest, const char *src, int len){ for (int i = 0; i < len; ++i){ *dest++ = *src++; } @@ -73,11 +66,10 @@ begin_bind_helper(void *data, int size){ result.end = result.start + size / sizeof(*result.cursor); Binding_Unit unit; - unit.type = UNIT_HEADER; + unit.type = unit_header; unit.header.total_size = sizeof(*result.header); result.header = write_unit(&result, unit); - result.header->header.map_count = 0; - result.header->header.group_count = 0; + result.header->header.user_map_count = 0; return result; } @@ -85,10 +77,10 @@ begin_bind_helper(void *data, int size){ inline void begin_map(Bind_Helper *helper, int mapid){ if (helper->group != 0 && helper->error == 0) helper->error = BH_ERR_MISSING_END; - if (!helper->error) ++helper->header->header.map_count; + if (!helper->error && mapid >= mapid_user_custom) ++helper->header->header.user_map_count; Binding_Unit unit; - unit.type = UNIT_MAP_BEGIN; + unit.type = unit_map_begin; unit.map_begin.mapid = mapid; helper->group = write_unit(helper, unit); helper->group->map_begin.bind_count = 0; @@ -106,7 +98,7 @@ bind(Bind_Helper *helper, short code, unsigned char modifiers, int cmdid){ if (!helper->error) ++helper->group->map_begin.bind_count; Binding_Unit unit; - unit.type = UNIT_BINDING; + unit.type = unit_binding; unit.binding.command_id = cmdid; unit.binding.code = code; unit.binding.modifiers = modifiers; @@ -120,7 +112,7 @@ bind_me(Bind_Helper *helper, short code, unsigned char modifiers, Custom_Command if (!helper->error) ++helper->group->map_begin.bind_count; Binding_Unit unit; - unit.type = UNIT_CALLBACK; + unit.type = unit_callback; unit.callback.func = func; unit.callback.code = code; unit.callback.modifiers = modifiers; @@ -141,14 +133,25 @@ bind_me_vanilla_keys(Bind_Helper *helper, Custom_Command_Function *func){ inline void inherit_map(Bind_Helper *helper, int mapid){ if (helper->group == 0 && helper->error == 0) helper->error = BH_ERR_MISSING_BEGIN; + if (!helper->error && mapid >= mapid_user_custom) ++helper->header->header.user_map_count; Binding_Unit unit; - unit.type = UNIT_INHERIT; + unit.type = unit_inherit; unit.map_inherit.mapid = mapid; write_unit(helper, unit); } +inline void +set_hook(Bind_Helper *helper, int hook_id, Custom_Command_Function *func){ + Binding_Unit unit; + unit.type = unit_hook; + unit.hook.hook_id = hook_id; + unit.hook.func = func; + + write_unit(helper, unit); +} + inline void end_bind_helper(Bind_Helper *helper){ if (helper->header){ @@ -157,72 +160,32 @@ end_bind_helper(Bind_Helper *helper){ } } +// NOTE(allen): Useful functions and overloads on app links inline void -begin_settings_group(Bind_Helper *helper){ - if (helper->group != 0 && helper->error == 0) helper->error = BH_ERR_MISSING_END; - if (!helper->error) ++helper->header->header.group_count; - - Binding_Unit unit; - unit.type = UNIT_SETTINGS_BEGIN; - helper->group = write_unit(helper, unit); +push_parameter_helper(void *cmd_context, Application_Links app, int param, int value){ + app.push_parameter(cmd_context, dynamic_int(param), dynamic_int(value)); } inline void -end_group(Bind_Helper *helper){ - if (helper->group == 0 && helper->error == 0) helper->error = BH_ERR_MISSING_BEGIN; - helper->group = 0; +push_parameter_helper(void *cmd_context, Application_Links app, int param, const char *value, int value_len){ + char *value_copy = app.push_memory(cmd_context, value_len); + copy(value_copy, value, value_len); + app.push_parameter(cmd_context, dynamic_int(param), dynamic_string(value_copy, value_len)); } inline void -use_when(Bind_Helper *helper, int clause_type, int value){ - if (helper->group == 0 && helper->error == 0) helper->error = BH_ERR_MISSING_BEGIN; - - Binding_Unit unit; - unit.type = UNIT_USE_CLAUSE; - unit.use_clause.clause_type = clause_type; - unit.use_clause.value = value; - write_unit(helper, unit); +push_parameter_helper(void *cmd_context, Application_Links app, const char *param, int param_len, int value){ + char *param_copy = app.push_memory(cmd_context, param_len); + copy(param_copy, param, param_len); + app.push_parameter(cmd_context, dynamic_string(param_copy, param_len), dynamic_int(value)); } inline void -use_when(Bind_Helper *helper, int clause_type, char *value){ - if (helper->group == 0 && helper->error == 0) helper->error = BH_ERR_MISSING_BEGIN; - - Binding_Unit unit; - unit.type = UNIT_USE_CLAUSE; - unit.use_clause_string.clause_type = clause_type; - unit.use_clause_string.len = seek_null(value); - Binding_Unit *u = write_unit(helper, unit); - u->use_clause_string.value = write_inline_string(helper, value, unit.use_clause_string.len); -} - -inline void -use_when(Bind_Helper *helper, int clause_type, char *value, int len){ - if (helper->group == 0 && helper->error == 0) helper->error = BH_ERR_MISSING_BEGIN; - - Binding_Unit unit; - unit.type = UNIT_USE_CLAUSE; - unit.use_clause_string.clause_type = clause_type; - unit.use_clause_string.len = len; - unit.use_clause_string.value = value; - write_unit(helper, unit); -} - -inline void -set(Bind_Helper *helper, int setting_id, int value){ - if (helper->group == 0 && helper->error == 0) helper->error = BH_ERR_MISSING_BEGIN; - - Binding_Unit unit; - unit.type = UNIT_SETTING; - unit.setting.setting_id = setting_id; - unit.setting.value = value; - write_unit(helper, unit); -} - -inline void -end_settings_helper(Bind_Helper *helper){ - if (helper->header){ - helper->header->header.error = helper->error; - } +push_parameter_helper(void *cmd_context, Application_Links app, const char *param, int param_len, const char *value, int value_len){ + char *param_copy = app.push_memory(cmd_context, param_len); + char *value_copy = app.push_memory(cmd_context, value_len); + copy(param_copy, param, param_len); + copy(value_copy, value, value_len); + app.push_parameter(cmd_context, dynamic_string(param_copy, param_len), dynamic_string(value_copy, value_len)); } diff --git a/4ed.cpp b/4ed.cpp index a9fe27f7..1c3c5db6 100644 --- a/4ed.cpp +++ b/4ed.cpp @@ -103,6 +103,11 @@ struct App_Vars{ #if FRED_INTERNAL Command_Map map_debug; #endif + Command_Map *user_maps; + i32 *map_id_table; + i32 user_map_count; + + Custom_Command_Function *hooks[hook_type_count]; Font_Set fonts; @@ -132,6 +137,48 @@ struct App_Vars{ Panel *prev_mouse_panel; }; +internal i32 +app_get_or_add_map_index(App_Vars *vars, i32 mapid){ + i32 result; + i32 user_map_count = vars->user_map_count; + i32 *map_id_table = vars->map_id_table; + for (result = 0; result < user_map_count; ++result){ + if (map_id_table[result] == mapid) break; + if (map_id_table[result] == 0){ + map_id_table[result] = mapid; + break; + } + } + return result; +} + +internal i32 +app_get_map_index(App_Vars *vars, i32 mapid){ + i32 result; + i32 user_map_count = vars->user_map_count; + i32 *map_id_table = vars->map_id_table; + for (result = 0; result < user_map_count; ++result){ + if (map_id_table[result] == mapid) break; + if (map_id_table[result] == 0){ + result = user_map_count; + break; + } + } + return result; +} + +internal Command_Map* +app_get_map(App_Vars *vars, i32 mapid){ + Command_Map *map = 0; + if (mapid >= mapid_user_custom) + map = vars->user_maps + mapid - mapid_user_custom; + else if (mapid == mapid_global) + map = &vars->map_top; + else if (mapid == mapid_file) + map = &vars->map_file; + return map; +} + // Commands globalvar Application_Links app_links; @@ -148,7 +195,7 @@ globalvar Application_Links app_links; #define REQ_VIEW(n) View *n = command->view; if (!n) return #define REQ_FILE_VIEW(n) File_View *n = view_to_file_view(command->view); if (!n) return -#define REQ_FILE(n,v) Editing_File *n = (v)->file; if (!n || !n->data || n->is_dummy) return +#define REQ_FILE(n,v) Editing_File *n = (v)->file; if (!n || !n->buffer.data || n->is_dummy) return #define REQ_COLOR_VIEW(n) Color_View *n = view_to_color_view(command->view); if (!n) return #define REQ_DBG_VIEW(n) Debug_View *n = view_to_debug_view(command->view); if (!n) return @@ -167,8 +214,51 @@ struct Command_Data{ i32 screen_width, screen_height; Key_Single key; + + Partition part; }; +struct Command_Parameter{ + i32 type; + union{ + struct{ + Dynamic param; + Dynamic value; + } param; + struct{ + i32 len; + char *str; + } inline_string; + }; +}; + +inline Command_Parameter* +param_next(Command_Parameter *param, Command_Parameter *end){ + Command_Parameter *result = param; + if (result->type == 0){ + ++result; + } + while (result->type != 0 && result < end){ + i32 len = result->inline_string.len; + len += sizeof(*result) - 1; + len -= (len % sizeof(*result)); + result = (Command_Parameter*)((char*)result + len + sizeof(*result)); + } + return result; +} + +inline Command_Parameter* +param_stack_first(Partition *part, Command_Parameter *end){ + Command_Parameter *result = (Command_Parameter*)part->base; + if (result->type != 0) result = param_next(result, end); + return result; +} + +inline Command_Parameter* +param_stack_end(Partition *part){ + return (Command_Parameter*)((char*)part->base + part->pos); +} + COMMAND_DECL(null){ AllowLocal(command); } @@ -183,7 +273,7 @@ COMMAND_DECL(write_character){ u8 character = (u8)command->key.key.character; char str_space[2]; String string = make_string(str_space, 2); - if (character == '\n' && file->endline_mode == ENDLINE_RN_COMBINED){ + if (character == '\n' && file->endline_mode == EOL_USE_CRLF){ str_space[0] = '\r'; str_space[1] = '\n'; string.size = 2; @@ -232,7 +322,7 @@ COMMAND_DECL(seek_whitespace_right){ REQ_FILE(file, view); i32 pos = seek_whitespace_right( - file->data, file->size, view->cursor.pos); + (u8*)file->buffer.data, file->buffer.size, view->cursor.pos); view_cursor_move(view, pos); } @@ -243,7 +333,7 @@ COMMAND_DECL(seek_whitespace_left){ REQ_FILE(file, view); i32 pos = seek_whitespace_left( - file->data, view->cursor.pos); + (u8*)file->buffer.data, view->cursor.pos); view_cursor_move(view, pos); } @@ -253,7 +343,7 @@ COMMAND_DECL(seek_whitespace_up){ REQ_FILE_VIEW(view); REQ_FILE(file, view); - u8* data = file->data; + char* data = file->buffer.data; u32 pos = view->cursor.pos; while (pos > 0 && char_is_whitespace(data[pos])){ --pos; @@ -281,8 +371,8 @@ COMMAND_DECL(seek_whitespace_up){ ++pos; } - if (file->endline_mode == ENDLINE_RN_COMBINED){ - pos = pos_adjust_to_self(pos, data, file->size); + if (file->endline_mode == EOL_USE_CRLF){ + pos = pos_adjust_to_self(pos, (u8*)data, file->buffer.size); } view_cursor_move(view, pos); @@ -293,8 +383,8 @@ COMMAND_DECL(seek_whitespace_down){ REQ_FILE_VIEW(view); REQ_FILE(file, view); - i32 size = file->size; - u8* data = file->data; + i32 size = file->buffer.size; + char* data = file->buffer.data; i32 pos = view->cursor.pos; while (pos < size && char_is_whitespace(data[pos])){ ++pos; @@ -387,11 +477,11 @@ COMMAND_DECL(seek_white_or_token_right){ REQ_FILE(file, view); i32 token_pos, white_pos; - token_pos = file->size; + token_pos = file->buffer.size; if (file->tokens_complete){ token_pos = seek_token_right(&file->token_stack, view->cursor.pos); } - white_pos = seek_whitespace_right(file->data, file->size, view->cursor.pos); + white_pos = seek_whitespace_right((u8*)file->buffer.data, file->buffer.size, view->cursor.pos); view_cursor_move(view, Min(token_pos, white_pos)); } @@ -401,11 +491,11 @@ COMMAND_DECL(seek_white_or_token_left){ REQ_FILE(file, view); i32 token_pos, white_pos; - token_pos = file->size; + token_pos = file->buffer.size; if (file->tokens_complete){ token_pos = seek_token_left(&file->token_stack, view->cursor.pos); } - white_pos = seek_whitespace_left(file->data, view->cursor.pos); + white_pos = seek_whitespace_left((u8*)file->buffer.data, view->cursor.pos); view_cursor_move(view, Max(token_pos, white_pos)); } @@ -439,7 +529,7 @@ COMMAND_DECL(seek_alphanumeric_right){ REQ_FILE(file, view); i32 pos = seek_alphanumeric_right( - file->data, file->size, view->cursor.pos); + (u8*)file->buffer.data, file->buffer.size, view->cursor.pos); view_cursor_move(view, pos); } @@ -449,7 +539,7 @@ COMMAND_DECL(seek_alphanumeric_left){ REQ_FILE(file, view); i32 pos = seek_alphanumeric_left( - file->data, view->cursor.pos); + (u8*)file->buffer.data, view->cursor.pos); view_cursor_move(view, pos); } @@ -458,11 +548,11 @@ COMMAND_DECL(seek_alphanumeric_or_camel_right){ REQ_FILE_VIEW(view); REQ_FILE(file, view); - u8 *data = file->data; + char *data = file->buffer.data; i32 an_pos, camel_pos; an_pos = seek_alphanumeric_right( - file->data, file->size, view->cursor.pos); + (u8*)file->buffer.data, file->buffer.size, view->cursor.pos); u8 curr_char; u8 prev_char = data[view->cursor.pos + 1]; @@ -482,7 +572,7 @@ COMMAND_DECL(seek_alphanumeric_or_camel_left){ REQ_FILE_VIEW(view); REQ_FILE(file, view); - u8 *data = file->data; + u8 *data = (u8*)file->buffer.data; i32 an_pos, camel_pos; an_pos = seek_alphanumeric_left( @@ -554,7 +644,7 @@ COMMAND_DECL(copy){ Range range = get_range(view->cursor.pos, view->mark); if (range.start < range.end){ - u8 *data = file->data; + u8 *data = (u8*)file->buffer.data; clipboard_copy(&mem->general, working_set, data, range); } } @@ -569,14 +659,14 @@ COMMAND_DECL(cut){ Range range = get_range(view->cursor.pos, view->mark); if (range.start < range.end){ - u8 *data = file->data; + u8 *data = (u8*)file->buffer.data; i32 next_cursor_pos = range.start; clipboard_copy(&mem->general, working_set, data, range); view_replace_range(mem, view, layout, range.start, range.end, 0, 0, next_cursor_pos); view->mark = pos_universal_fix(range.start, - file->data, file->size, + (u8*)file->buffer.data, file->buffer.size, file->endline_mode); view_measure_wraps(&mem->general, view); view_cursor_move(view, next_cursor_pos); @@ -602,7 +692,7 @@ COMMAND_DECL(paste){ view_cursor_move(view, next_cursor_pos); view->mark = pos_universal_fix(pos_left, - file->data, file->size, + (u8*)file->buffer.data, file->buffer.size, file->endline_mode); Editing_Layout *layout = command->layout; @@ -639,7 +729,7 @@ COMMAND_DECL(paste_next){ view_cursor_move(view, next_cursor_pos); view->mark = pos_universal_fix(range.start, - file->data, file->size, + (u8*)file->buffer.data, file->buffer.size, file->endline_mode); Editing_Layout *layout = command->layout; @@ -674,17 +764,19 @@ COMMAND_DECL(delete_chunk){ view_measure_wraps(&mem->general, view); view_cursor_move(view, next_cursor_pos); view->mark = pos_universal_fix(range.start, - file->data, file->size, + (u8*)file->buffer.data, file->buffer.size, file->endline_mode); } } -COMMAND_DECL(undo_scrub){ +COMMAND_DECL(timeline_scrub){ ProfileMomentFunction(); REQ_FILE_VIEW(view); REQ_FILE(file, view); - view_set_widget(view, FWIDG_UNDOING); + view_set_widget(view, FWIDG_TIMELINES); + view->widget.timeline.undo_line = 1; + view->widget.timeline.history_line = 1; } COMMAND_DECL(undo){ @@ -731,21 +823,13 @@ COMMAND_DECL(increase_fastforward_speed){ view->rewind_speed = -neg_rewind_speed * 0.25f; } -COMMAND_DECL(stop_rewind_and_fastforward){ +COMMAND_DECL(stop_rewind_fastforward){ ProfileMomentFunction(); REQ_FILE_VIEW(view); view->rewind_speed = 0; } -COMMAND_DECL(history_scrub){ - ProfileMomentFunction(); - REQ_FILE_VIEW(view); - REQ_FILE(file, view); - - view_set_widget(view, FWIDG_RESTORING); -} - COMMAND_DECL(history_backward){ ProfileMomentFunction(); REQ_FILE_VIEW(view); @@ -788,6 +872,60 @@ COMMAND_DECL(interactive_new){ copy(&int_view->query, "New: "); } +internal File_View* +app_open_file(App_Vars *vars, General_Memory *general, Panel *panel, + Working_Set *working_set, String *string, Style *style, + Live_Views *live_set, Command_Data *command_data){ + File_View *result = 0; + Editing_File *target_file = 0; + bool32 created_file = 0; + + target_file = working_set_contains(working_set, *string); + if (!target_file){ + Get_File_Result file = working_set_get_available_file(working_set); + if (file.file){ + buffer_get_dummy(file.file); + created_file = buffer_create(general, file.file, (u8*)string->str, style->font); + table_add(&working_set->table, file.file->source_path, file.index); + if (created_file){ + target_file = file.file; + } + } + } + + if (target_file){ + View *new_view = live_set_alloc_view(live_set, &vars->mem); + + view_replace_major(new_view, panel, live_set); + + File_View *file_view = file_view_init(new_view, &vars->delay, &vars->layout); + result = file_view; + + View *old_view = command_data->view; + command_data->view = new_view; + + Partition old_part = command_data->part; + Temp_Memory temp = begin_temp_memory(&vars->mem.part); + command_data->part = partition_sub_part(&vars->mem.part, 16 << 10); + + view_set_file(file_view, target_file, style, + vars->hooks[hook_open_file], command_data, app_links); + + command_data->part = old_part; + end_temp_memory(temp); + + command_data->view = old_view; + + new_view->map = app_get_map(vars, target_file->base_map_id); + + if (created_file && target_file->tokens_exist) + buffer_first_lex_parallel(general, target_file); + } + + return result; +} + + COMMAND_DECL(interactive_open){ ProfileMomentFunction(); USE_VARS(vars); @@ -798,16 +936,41 @@ COMMAND_DECL(interactive_open){ USE_STYLE(style); USE_DELAY(delay); - View *new_view = live_set_alloc_view(live_set, mem); - view_replace_minor(new_view, panel, live_set); + char *filename = 0; + int filename_len = 0; - new_view->map = &vars->map_ui; - Interactive_View *int_view = - interactive_view_init(new_view, &vars->hot_directory, style, - working_set, delay); - int_view->interaction = INTV_SYS_FILE_LIST; - int_view->action = INTV_OPEN; - copy(&int_view->query, "Open: "); + Command_Parameter *end = param_stack_end(&command->part); + Command_Parameter *param = param_stack_first(&command->part, end); + for (; param < end; param = param_next(param, end)){ + if (param->param.param.type == dynamic_type_int && + param->param.param.int_value == par_name && + param->param.value.type == dynamic_type_string){ + filename = param->param.value.str_value; + filename_len = param->param.value.str_len; + break; + } + } + + bool32 made_file = 0; + + if (filename){ + String string = make_string(filename, filename_len); + if (app_open_file(vars, &mem->general, panel, working_set, + &string, style, live_set, command)) made_file = 1; + } + + if (!made_file){ + View *new_view = live_set_alloc_view(live_set, mem); + view_replace_minor(new_view, panel, live_set); + + new_view->map = &vars->map_ui; + Interactive_View *int_view = + interactive_view_init(new_view, &vars->hot_directory, style, + working_set, delay); + int_view->interaction = INTV_SYS_FILE_LIST; + int_view->action = INTV_OPEN; + copy(&int_view->query, "Open: "); + } } // TODO(allen): Improvements to reopen @@ -821,6 +984,7 @@ COMMAND_DECL(reopen){ USE_STYLE(style); USE_LAYOUT(layout); USE_MEM(mem); + USE_VARS(vars); Editing_File temp_file; if (buffer_create(&mem->general, &temp_file, (u8*)make_c_str(file->source_path), style->font)){ @@ -831,14 +995,24 @@ COMMAND_DECL(reopen){ if (file->tokens_exist) buffer_first_lex_parallel(&mem->general, file); + Partition old_part = command->part; + Temp_Memory temp = begin_temp_memory(&vars->mem.part); + command->part = partition_sub_part(&vars->mem.part, 16 << 10); + + view_set_file(view, file, style, + vars->hooks[hook_open_file], command, app_links); + + command->part = old_part; + end_temp_memory(temp); + i32 panel_count = layout->panel_count; Panel *panels = layout->panels; for (i32 i = 0; i < panel_count; ++i){ Panel *current_panel = panels + i; View *current_view_ = current_panel->view; File_View *current_view = view_to_file_view(current_view_); - if (current_view && current_view->file == file){ - view_set_file(current_view, current_view->file, style); + if (current_view && current_view != view && current_view->file == file){ + view_set_file(current_view, current_view->file, style, 0, command, app_links); } } } @@ -851,7 +1025,7 @@ COMMAND_DECL(save){ String *file_path = &file->source_path; if (file_path->size > 0){ - buffer_save(file, (u8*)file_path->str); + file_save(file, (u8*)file_path->str); } } @@ -967,27 +1141,27 @@ COMMAND_DECL(toggle_endline_mode){ USE_MEM(mem); switch (file->endline_mode){ - case ENDLINE_RN_COMBINED: + case EOL_USE_CRLF: { - file->endline_mode = ENDLINE_RN_SEPARATE; + file->endline_mode = EOL_USE_CR_USE_LF; }break; - case ENDLINE_RN_SEPARATE: + case EOL_USE_CR_USE_LF: { - file->endline_mode = ENDLINE_RN_SHOWALLR; + file->endline_mode = EOL_SHOW_CR_USE_LF; }break; - case ENDLINE_RN_SHOWALLR: + case EOL_SHOW_CR_USE_LF: { - view->cursor.pos = pos_adjust_to_self(view->cursor.pos, view->file->data, - view->file->size); - file->endline_mode = ENDLINE_RN_COMBINED; + view->cursor.pos = pos_adjust_to_self(view->cursor.pos, (u8*)view->file->buffer.data, + view->file->buffer.size); + file->endline_mode = EOL_USE_CRLF; }break; } view->cursor = view_compute_cursor_from_pos(view, view->cursor.pos); - buffer_measure_starts(&mem->general, view->file); + file_measure_starts(&mem->general, view->file); } COMMAND_DECL(toggle_show_whitespace){ @@ -1018,11 +1192,19 @@ COMMAND_DECL(to_uppercase){ Range range = get_range(view->cursor.pos, view->mark); if (range.start < range.end){ + Edit_Spec spec = {}; + spec.type = ED_NORMAL; + spec.start = range.start; + spec.end = range.end; + spec.str_len = range.end - range.start; + spec.next_cursor_pos = view->cursor.pos; + view_pre_replace_range(mem, file, spec); + if (file->still_lexing){ system_cancel_job(BACKGROUND_THREADS, file->lex_job); } - u8 *data = file->data; + u8 *data = (u8*)file->buffer.data; for (i32 i = range.start; i < range.end; ++i){ if (data[i] >= 'a' && data[i] <= 'z'){ data[i] += (u8)('A' - 'a'); @@ -1043,11 +1225,19 @@ COMMAND_DECL(to_lowercase){ Range range = get_range(view->cursor.pos, view->mark); if (range.start < range.end){ + Edit_Spec spec = {}; + spec.type = ED_NORMAL; + spec.start = range.start; + spec.end = range.end; + spec.str_len = range.end - range.start; + spec.next_cursor_pos = view->cursor.pos; + view_pre_replace_range(mem, file, spec); + if (file->still_lexing){ system_cancel_job(BACKGROUND_THREADS, file->lex_job); } - u8 *data = file->data; + u8 *data = (u8*)file->buffer.data; for (i32 i = range.start; i < range.end; ++i){ if (data[i] >= 'A' && data[i] <= 'Z'){ data[i] -= (u8)('A' - 'a'); @@ -1312,12 +1502,12 @@ COMMAND_DECL(move_left){ REQ_FILE_VIEW(view); REQ_FILE(file, view); - u8 *data = file->data; + u8 *data = (u8*)file->buffer.data; i32 pos = view->cursor.pos; if (pos > 0){ --pos; - if (file->endline_mode == ENDLINE_RN_COMBINED){ - pos = pos_adjust_to_self(pos, data, file->size); + if (file->endline_mode == EOL_USE_CRLF){ + pos = pos_adjust_to_self(pos, data, file->buffer.size); } } @@ -1329,11 +1519,11 @@ COMMAND_DECL(move_right){ REQ_FILE_VIEW(view); REQ_FILE(file, view); - i32 size = file->size; - u8* data = file->data; + i32 size = file->buffer.size; + u8* data = (u8*)file->buffer.data; i32 pos = view->cursor.pos; if (pos < size){ - if (file->endline_mode == ENDLINE_RN_COMBINED){ + if (file->endline_mode == EOL_USE_CRLF){ pos = pos_adjust_to_right(pos, data, size); ++pos; } @@ -1353,11 +1543,11 @@ COMMAND_DECL(delete){ USE_MEM(mem); i32 cursor_pos = view->cursor.pos; - u8 *data = file->data; - if (file->size > 0 && cursor_pos < file->size){ + u8 *data = (u8*)file->buffer.data; + if (file->buffer.size > 0 && cursor_pos < file->buffer.size){ i32 start, end; start = cursor_pos; - if (file->endline_mode == ENDLINE_RN_COMBINED && + if (file->endline_mode == EOL_USE_CRLF && data[cursor_pos] == '\r' && data[cursor_pos+1] == '\n'){ end = cursor_pos+2; } @@ -1383,12 +1573,12 @@ COMMAND_DECL(backspace){ USE_MEM(mem); i32 cursor_pos = view->cursor.pos; - u8 *data = file->data; - if (cursor_pos > 0 && cursor_pos <= (i32)file->size){ + u8 *data = (u8*)file->buffer.data; + if (cursor_pos > 0 && cursor_pos <= (i32)file->buffer.size){ i32 start, end; end = cursor_pos; - if (file->endline_mode == ENDLINE_RN_COMBINED && + if (file->endline_mode == EOL_USE_CRLF && cursor_pos > 1 && data[cursor_pos-1] == '\n' && data[cursor_pos-2] == '\r'){ @@ -1585,42 +1775,81 @@ COMMAND_DECL(cursor_mark_swap){ view->mark = pos; } -internal bool32 -fulfill_interaction_intview(Command_Data *command, char *data, bool32 full_set){ - bool32 result = 0; - Panel *panel = command->panel; - View *view = panel->view; - Interactive_View *int_view = view_to_interactive_view(view); - if (int_view){ - result = 1; - - String *dest = 0; - switch (int_view->interaction){ - case INTV_SYS_FILE_LIST: - dest = &int_view->hot_directory->string; - break; - case INTV_LIVE_FILE_LIST: - dest = &int_view->dest; - break; - } - if (full_set) dest->size = 0; - append(dest, data); - - interactive_view_complete(int_view); - } - - return result; -} - COMMAND_DECL(user_callback){ ProfileMomentFunction(); if (binding.custom) binding.custom(command, app_links); } +COMMAND_DECL(set_settings){ + ProfileMomentFunction(); + REQ_FILE_VIEW(view); + REQ_FILE(file, view); + USE_VARS(vars); + USE_MEM(mem); + + Command_Parameter *end = param_stack_end(&command->part); + Command_Parameter *param = param_stack_first(&command->part, end); + for (; param < end; param = param_next(param, end)){ + int p = dynamic_to_int(¶m->param.param); + switch (p){ + case par_lex_as_cpp_file: + { + int v = dynamic_to_bool(¶m->param.value); + if (file->tokens_exist){ + if (!v) buffer_kill_tokens(&mem->general, file); + } + else{ + if (v) buffer_first_lex_parallel(&mem->general, file); + } + }break; + + case par_wrap_lines: + { + int v = dynamic_to_bool(¶m->param.value); + if (view->unwrapped_lines){ + if (v){ + Relative_Scrolling scrolling = view_get_relative_scrolling(view); + view->unwrapped_lines = 0; + view->target_x = 0; + view->cursor = + view_compute_cursor_from_pos(view, view->cursor.pos); + view_set_relative_scrolling(view, scrolling); + } + } + else{ + if (!v){ + Relative_Scrolling scrolling = view_get_relative_scrolling(view); + view->unwrapped_lines = 1; + view->cursor = + view_compute_cursor_from_pos(view, view->cursor.pos); + view_set_relative_scrolling(view, scrolling); + } + } + }break; + + case par_key_mapid: + { + int v = dynamic_to_int(¶m->param.value); + Command_Map *map = 0; + if (v == mapid_global) map = &vars->map_top; + else if (v == mapid_file) map = &vars->map_file; + else if (v >= mapid_user_custom){ + int index = app_get_map_index(vars, v); + if (index < vars->user_map_count) map = vars->user_maps + index; + else map = 0; + } + }break; + + case par_end_line_mode: + break; + } + } +} + globalvar Command_Function command_table[cmdid_count]; extern "C"{ - EXECUTE_COMMAND_SIG(execute_command_from_id){ + EXECUTE_COMMAND_SIG(external_exec_command_keep_stack){ Command_Data *cmd = (Command_Data*)cmd_context; Command_Function function = command_table[command_id]; Command_Binding binding; @@ -1641,27 +1870,79 @@ extern "C"{ command_data.screen_width = cmd->screen_width; command_data.screen_height = cmd->screen_height; command_data.key = cmd->key; + command_data.part = cmd->part; *cmd = command_data; } - FULFILL_INTERACTION_SIG(fulfill_interaction_external){ + PUSH_PARAMETER_SIG(external_push_parameter){ Command_Data *cmd = (Command_Data*)cmd_context; - bool32 handled = 0; - handled = fulfill_interaction_intview(cmd, data, full_set); + Partition *part = &cmd->part; + Command_Parameter *cmd_param = push_struct(part, Command_Parameter); + cmd_param->type = 0; + cmd_param->param.param = param; + cmd_param->param.value = value; + } + + PUSH_MEMORY_SIG(external_push_memory){ + Command_Data *cmd = (Command_Data*)cmd_context; + Partition *part = &cmd->part; + Command_Parameter *base = push_struct(part, Command_Parameter); + char *result = push_array(part, char, len); + int full_len = len + sizeof(Command_Parameter) - 1; + full_len -= (full_len % sizeof(Command_Parameter)); + part->pos += full_len - len; + base->type = 1; + base->inline_string.str = result; + base->inline_string.len = len; + return result; + } + + CLEAR_PARAMETERS_SIG(external_clear_parameters){ + Command_Data *cmd = (Command_Data*)cmd_context; + cmd->part.pos = 0; + } + + GET_ACTIVE_BUFFER_SIG(external_get_active_buffer){ + Command_Data *cmd = (Command_Data*)cmd_context; + Buffer_Summary buffer = {}; + + File_View *view = view_to_file_view(cmd->view); + if (view){ + Editing_File *file = view->file; + if (file && !file->is_dummy){ + Working_Set *working_set = cmd->working_set; + buffer.file_id = (int)(file - working_set->files); + buffer.size = file->buffer.size; + buffer.data = (const char*)file->buffer.data; + buffer.file_name_len = file->source_path.size; + buffer.buffer_name_len = file->live_name.size; + buffer.file_name = file->source_path.str; + buffer.buffer_name = file->live_name.str; + buffer.file_cursor_pos = file->cursor_pos; + buffer.eol_mode = file->endline_mode; + buffer.is_lexed = file->tokens_exist; + buffer.map_id = file->base_map_id; + } + } + + return buffer; } } inline void app_links_init(){ - app_links.exec_command = execute_command_from_id; - app_links.fulfill_interaction = fulfill_interaction_external; + app_links.exec_command_keep_stack = external_exec_command_keep_stack; + app_links.push_parameter = external_push_parameter; + app_links.push_memory = external_push_memory; + app_links.clear_parameters = external_clear_parameters; + app_links.get_active_buffer = external_get_active_buffer; } #if FRED_INTERNAL internal void -setup_debug_commands(Command_Map *commands, Key_Codes *codes){ - map_init(commands); +setup_debug_commands(Command_Map *commands, Partition *part, Key_Codes *codes, Command_Map *parent){ + map_init(commands, part, 6, parent); map_add(commands, 'm', MDFR_NONE, command_debug_memory); map_add(commands, 'o', MDFR_NONE, command_debug_os_events); @@ -1670,8 +1951,8 @@ setup_debug_commands(Command_Map *commands, Key_Codes *codes){ #endif internal void -setup_ui_commands(Command_Map *commands, Key_Codes *codes){ - map_init(commands); +setup_ui_commands(Command_Map *commands, Partition *part, Key_Codes *codes, Command_Map *parent){ + map_init(commands, part, 12, parent); commands->vanilla_keyboard_default.function = command_null; @@ -1684,8 +1965,8 @@ setup_ui_commands(Command_Map *commands, Key_Codes *codes){ } internal void -setup_file_commands(Command_Map *commands, Key_Codes *codes){ - map_init(commands); +setup_file_commands(Command_Map *commands, Partition *part, Key_Codes *codes, Command_Map *parent){ + map_init(commands, part, 101, parent); commands->vanilla_keyboard_default.function = command_write_character; @@ -1713,13 +1994,12 @@ setup_file_commands(Command_Map *commands, Key_Codes *codes){ map_add(commands, 'V', MDFR_CTRL, command_paste_next); map_add(commands, 'z', MDFR_CTRL, command_undo); map_add(commands, 'y', MDFR_CTRL, command_redo); - map_add(commands, 'Z', MDFR_CTRL, command_undo_scrub); + map_add(commands, 'Z', MDFR_CTRL, command_timeline_scrub); map_add(commands, codes->left, MDFR_ALT, command_increase_rewind_speed); map_add(commands, codes->right, MDFR_ALT, command_increase_fastforward_speed); - map_add(commands, codes->down, MDFR_ALT, command_stop_rewind_and_fastforward); + map_add(commands, codes->down, MDFR_ALT, command_stop_rewind_fastforward); map_add(commands, 'h', MDFR_CTRL, command_history_backward); map_add(commands, 'H', MDFR_CTRL, command_history_forward); - map_add(commands, 'J', MDFR_CTRL, command_history_scrub); map_add(commands, 'd', MDFR_CTRL, command_delete_chunk); map_add(commands, 'l', MDFR_CTRL, command_toggle_line_wrap); map_add(commands, 'L', MDFR_CTRL, command_toggle_endline_mode); @@ -1744,8 +2024,8 @@ setup_file_commands(Command_Map *commands, Key_Codes *codes){ } internal void -setup_top_commands(Command_Map *commands, Key_Codes *codes){ - map_init(commands); +setup_top_commands(Command_Map *commands, Partition *part, Key_Codes *codes, Command_Map *parent){ + map_init(commands, part, 51, parent); #if FRED_INTERNAL map_add(commands, 'd', MDFR_ALT, command_open_debug_view); @@ -1807,8 +2087,14 @@ setup_command_table(){ SET(paste); SET(paste_next); SET(delete_chunk); + SET(timeline_scrub); SET(undo); SET(redo); + SET(increase_rewind_speed); + SET(increase_fastforward_speed); + SET(stop_rewind_fastforward); + SET(history_backward); + SET(history_forward); SET(interactive_new); SET(interactive_open); SET(reopen); @@ -1845,6 +2131,7 @@ setup_command_table(){ SET(close_minor_view); SET(cursor_mark_swap); SET(open_menu); + SET(set_settings); #undef SET } @@ -2168,6 +2455,51 @@ app_load_font(Font *font, char *filename, i32 size, void *memory, return font->loaded; } +char *_4coder_get_extension(const char *filename, int len, int *extension_len){ + char *c = (char*)(filename + len - 1); + char *end = c; + while (*c != '.' && c > filename) --c; + *extension_len = (int)(end - c); + return c+1; +} + +bool _4coder_str_match(const char *a, int len_a, const char *b, int len_b){ + bool result = 0; + if (len_a == len_b){ + char *end = (char*)(a + len_a); + while (a < end && *a == *b){ + ++a; ++b; + } + if (a == end) result = 1; + } + return result; +} + +#define literal(s) s, (sizeof(s) - 1) + +HOOK_SIG(default_open_file_hook){ + Buffer_Summary buffer = app.get_active_buffer(cmd_context); + + int treat_as_code = 0; + + if (buffer.file_name && buffer.size < (16 << 20)){ + int extension_len; + char *extension = _4coder_get_extension(buffer.file_name, buffer.file_name_len, &extension_len); + if (_4coder_str_match(extension, extension_len, literal("cpp"))) treat_as_code = 1; + else if (_4coder_str_match(extension, extension_len, literal("h"))) treat_as_code = 1; + else if (_4coder_str_match(extension, extension_len, literal("c"))) treat_as_code = 1; + else if (_4coder_str_match(extension, extension_len, literal("hpp"))) treat_as_code = 1; + } + + app.push_parameter(cmd_context, dynamic_int(par_lex_as_cpp_file), dynamic_int(treat_as_code)); + app.push_parameter(cmd_context, dynamic_int(par_wrap_lines), dynamic_int(!treat_as_code)); + app.push_parameter(cmd_context, dynamic_int(par_key_mapid), dynamic_int(mapid_file)); + app.push_parameter(cmd_context, dynamic_int(par_end_line_mode), dynamic_int(EOL_USE_CRLF)); + + app.exec_command_keep_stack(cmd_context, cmdid_set_settings); + app.clear_parameters(cmd_context); +} + internal bool32 app_init(Thread_Context *thread, Application_Memory *memory, Key_Codes *loose_codes, Clipboard_Contents clipboard){ @@ -2229,42 +2561,77 @@ app_init(Thread_Context *thread, Application_Memory *memory, vars->live_set.free_view = (View*)views_; setup_command_table(); - + + Command_Map *global = &vars->map_top; if (TEMP.get_bindings){ i32 size = partition_remaining(partition); void *data = partition_current(partition); - + + // TODO(allen): Use a giant bubble of general memory for this. + // So that it doesn't interfere with the command maps as they allocate + // their own memory. i32 wanted_size = TEMP.get_bindings(data, size, loose_codes); bool32 did_top = 0; bool32 did_file = 0; - if (wanted_size <= size){ + partition_allocate(partition, wanted_size); + Binding_Unit *unit = (Binding_Unit*)data; - if (unit->type == UNIT_HEADER && unit->header.error == 0){ + if (unit->type == unit_header && unit->header.error == 0){ Binding_Unit *end = unit + unit->header.total_size; + i32 user_map_count = unit->header.user_map_count; + + vars->map_id_table = + push_array(&vars->mem.part, i32, user_map_count); + + vars->user_maps = + push_array(&vars->mem.part, Command_Map, user_map_count); + + vars->user_map_count = user_map_count; + Command_Map *mapptr = 0; - int mapid = -1; for (++unit; unit < end; ++unit){ switch (unit->type){ - case UNIT_MAP_BEGIN: + case unit_map_begin: { - mapid = unit->map_begin.mapid; - if (mapid == MAPID_GLOBAL){ + int table_max = unit->map_begin.bind_count * 3 / 2; + int mapid = unit->map_begin.mapid; + if (mapid == mapid_global){ mapptr = &vars->map_top; - map_init(mapptr); + map_init(mapptr, &vars->mem.part, table_max, global); did_top = 1; } - else if (mapid == MAPID_FILE){ + else if (mapid == mapid_file){ mapptr = &vars->map_file; - map_init(mapptr); + map_init(mapptr, &vars->mem.part, table_max, global); did_file = 1; } + else if (mapid >= mapid_user_custom){ + i32 index = app_get_or_add_map_index(vars, mapid); + Assert(index < user_map_count); + mapptr = vars->user_maps + index; + map_init(mapptr, &vars->mem.part, table_max, global); + } else mapptr = 0; }break; - - case UNIT_BINDING: + + case unit_inherit: + if (mapptr){ + Command_Map *parent = 0; + int mapid = unit->map_inherit.mapid; + if (mapid == mapid_global) parent = &vars->map_top; + else if (mapid == mapid_file) parent = &vars->map_file; + else if (mapid >= mapid_user_custom){ + i32 index = app_get_or_add_map_index(vars, mapid); + if (index < user_map_count) parent = vars->user_maps + index; + else parent = 0; + } + mapptr->parent = parent; + }break; + + case unit_binding: if (mapptr){ Command_Function func = 0; if (unit->binding.command_id >= 0 && unit->binding.command_id < cmdid_count) @@ -2280,7 +2647,7 @@ app_init(Thread_Context *thread, Application_Memory *memory, } break; - case UNIT_CALLBACK: + case unit_callback: if (mapptr){ Command_Function func = command_user_callback; Custom_Command_Function *custom = unit->callback.func; @@ -2295,24 +2662,36 @@ app_init(Thread_Context *thread, Application_Memory *memory, } } break; + + case unit_hook: + { + int hook_id = unit->hook.hook_id; + if (hook_id >= 0 && hook_id < hook_type_count){ + vars->hooks[hook_id] = unit->hook.func; + } + }break; } } } } - if (!did_top) setup_top_commands(&vars->map_top, loose_codes); - if (!did_file) setup_file_commands(&vars->map_file, loose_codes); + if (!did_top) setup_top_commands(&vars->map_top, &vars->mem.part, loose_codes, global); + if (!did_file) setup_file_commands(&vars->map_file, &vars->mem.part, loose_codes, global); } else{ - setup_top_commands(&vars->map_top, loose_codes); - setup_file_commands(&vars->map_file, loose_codes); + setup_top_commands(&vars->map_top, &vars->mem.part, loose_codes, global); + setup_file_commands(&vars->map_file, &vars->mem.part, loose_codes, global); } - setup_ui_commands(&vars->map_ui, loose_codes); + setup_ui_commands(&vars->map_ui, &vars->mem.part, loose_codes, global); #if FRED_INTERNAL - setup_debug_commands(&vars->map_debug, loose_codes); + setup_debug_commands(&vars->map_debug, &vars->mem.part, loose_codes, global); #endif + if (vars->hooks[hook_open_file] == 0){ + vars->hooks[hook_open_file] = default_open_file_hook; + } + if (!font_init()) return 0; vars->fonts.max = 6; @@ -2732,22 +3111,45 @@ app_step(Thread_Context *thread, Key_Codes *codes, command_data.screen_width = target->width; command_data.screen_height = target->height; - if (first_step && TEMP.start_hook){ - TEMP.start_hook(&command_data, app_links); + Temp_Memory param_stack_temp = begin_temp_memory(&vars->mem.part); + command_data.part = partition_sub_part(&vars->mem.part, 16 << 10); + + if (first_step && vars->hooks[hook_start]){ + vars->hooks[hook_start](&command_data, app_links); + command_data.part.pos = 0; } // NOTE(allen): command input to active view for (i32 key_i = 0; key_i < key_data.count; ++key_i){ Command_Binding cmd = {}; - Command_Map *override_map = 0; + Command_Map *map = 0; View *view = active_panel->view; Key_Single key = get_single_key(&key_data, key_i); command_data.key = key; - if (view) override_map = view->map; - if (override_map) cmd = map_extract(override_map, key); - if (cmd.function == 0) cmd = map_extract(&vars->map_top, key); + Command_Map *visited_maps[16] = {}; + i32 visited_top = 0; + + if (view) map = view->map; + if (map == 0) map = &vars->map_top; + while (map){ + cmd = map_extract(map, key); + if (cmd.function == 0){ + if (visited_top < ArrayCount(visited_maps)){ + visited_maps[visited_top++] = map; + map = map->parent; + for (i32 i = 0; i < visited_top; ++i){ + if (map == visited_maps[i]){ + map = 0; + break; + } + } + } + else map = 0; + } + else map = 0; + } switch (vars->state){ case APP_STATE_EDIT: @@ -2835,33 +3237,8 @@ app_step(Thread_Context *thread, Key_Codes *codes, switch (act->type){ case DACT_OPEN: { - Editing_File *target_file = 0; - bool32 created_file = 0; - - target_file = working_set_contains(working_set, *string); - if (!target_file){ - Get_File_Result file = working_set_get_available_file(working_set); - if (file.file){ - buffer_get_dummy(file.file); - created_file = buffer_create(general, file.file, (u8*)string->str, style->font); - table_add(&working_set->table, file.file->source_path, file.index); - if (created_file){ - target_file = file.file; - } - } - } - - if (target_file){ - View *new_view = live_set_alloc_view(live_set, &vars->mem); - - new_view->map = &vars->map_file; - view_replace_major(new_view, panel, live_set); - - File_View *file_view = file_view_init(new_view, &vars->delay, &vars->layout); - view_set_file(file_view, target_file, style); - if (created_file && target_file->tokens_exist) - buffer_first_lex_parallel(general, target_file); - } + command_data.view = (View*) + app_open_file(vars, general,panel, working_set, string, style, live_set, &command_data); }break; case DACT_SAVE_AS: @@ -2873,7 +3250,7 @@ app_step(Thread_Context *thread, Key_Codes *codes, if (fview){ Editing_File *file = fview->file; if (file && !file->is_dummy){ - buffer_save_and_set_names(file, (u8*)string->str); + file_save_and_set_names(file, (u8*)string->str); } } }break; @@ -2882,7 +3259,7 @@ app_step(Thread_Context *thread, Key_Codes *codes, { Editing_File *file = working_set_lookup_file(working_set, *string); if (file && !file->is_dummy){ - buffer_save(file, (u8*)file->source_path.str); + file_save(file, (u8*)file->source_path.str); } }break; @@ -2894,10 +3271,12 @@ app_step(Thread_Context *thread, Key_Codes *codes, View *new_view = live_set_alloc_view(live_set, mem); view_replace_major(new_view, panel, live_set); - new_view->map = &vars->map_file; File_View *file_view = file_view_init(new_view, &vars->delay, &vars->layout); - view_set_file(file_view, file.file, style); + command_data.view = (View*)file_view; + view_set_file(file_view, file.file, style, + vars->hooks[hook_open_file], &command_data, app_links); + new_view->map = app_get_map(vars, file.file->base_map_id); if (file.file->tokens_exist) buffer_first_lex_parallel(general, file.file); }break; @@ -2907,10 +3286,12 @@ app_step(Thread_Context *thread, Key_Codes *codes, if (file){ View *new_view = live_set_alloc_view(live_set, mem); view_replace_major(new_view, panel, live_set); - new_view->map = &vars->map_file; - + File_View *file_view = file_view_init(new_view, &vars->delay, &vars->layout); - view_set_file(file_view, file, style); + command_data.view = (View*)file_view; + view_set_file(file_view, file, style, + vars->hooks[hook_open_file], &command_data, app_links); + new_view->map = app_get_map(vars, file->base_map_id); } }break; @@ -2975,6 +3356,8 @@ app_step(Thread_Context *thread, Key_Codes *codes, } ProfileEnd(delayed_actions); + end_temp_memory(param_stack_temp); + ProfileStart(resize); // NOTE(allen): send resize messages to panels that have changed size { @@ -3007,7 +3390,7 @@ app_step(Thread_Context *thread, Key_Codes *codes, Editing_File *file = vars->working_set.files; for (i32 i = vars->working_set.file_index_count; i > 0; --i, ++file){ - if (file->data && !file->is_dummy){ + if (file->buffer.data && !file->is_dummy){ buffer_measure_widths(&vars->mem.general, file, vars->style.font); } } diff --git a/4ed.h b/4ed.h index 8a3b4382..f3ed376a 100644 --- a/4ed.h +++ b/4ed.h @@ -3,7 +3,7 @@ * * 12.12.2014 * - * Win32 Layer for project codename "4ed" + * Application Layer for project codename "4ed" * */ @@ -15,7 +15,7 @@ struct Partition{ i32 pos, max; }; -internal Partition +inline Partition partition_open(void *memory, i32 size){ Partition partition; partition.base = (u8*)memory;; @@ -24,16 +24,21 @@ partition_open(void *memory, i32 size){ return partition; } -internal void* +inline void* partition_allocate(Partition *data, i32 size){ void *ret = 0; - if (size > 0 && data->pos + size < data->max){ + if (size > 0 && data->pos + size <= data->max){ ret = data->base + data->pos; data->pos += size; } return ret; } +inline void +partition_align(Partition *data, u32 boundary){ + data->pos = (data->pos + (boundary - 1)) & (~boundary); +} + inline void* partition_current(Partition *data){ return data->base + data->pos; @@ -44,6 +49,14 @@ partition_remaining(Partition *data){ return data->max - data->pos; } +inline Partition +partition_sub_part(Partition *data, i32 size){ + Partition result = {}; + void *d = partition_allocate(data, size); + if (d) result = partition_open(d, size); + return result; +} + #define push_struct(part, T) (T*)partition_allocate(part, sizeof(T)) #define push_array(part, T, size) (T*)partition_allocate(part, sizeof(T)*(size)) #define push_block(part, size) partition_allocate(part, size) diff --git a/4ed_color_view.cpp b/4ed_color_view.cpp index 7c002712..09f7c734 100644 --- a/4ed_color_view.cpp +++ b/4ed_color_view.cpp @@ -123,15 +123,6 @@ view_to_color_view(View *view){ return (Color_View*)view; } -internal real32 -font_string_width(Font *font, char *str){ - real32 x = 0; - for (i32 i = 0; str[i]; ++i){ - x += font_get_glyph_width(font, str[i]); - } - return x; -} - internal void draw_gradient_slider(Render_Target *target, Vec4 base, i32 channel, i32 steps, real32 top, real32_Rect slider, bool32 hsla){ @@ -213,51 +204,6 @@ do_label(UI_State *state, UI_Layout *layout, char *text, real32 height = 2.f){ } } -internal bool32 -do_button(i32 id, UI_State *state, UI_Layout *layout, char *text, - bool32 is_toggle = 0, bool32 on = 0){ - bool32 result = 0; - Font *font = state->font; - i32 character_h = font->height; - - i32_Rect btn_rect = layout_rect(layout, character_h * 2); - btn_rect = get_inner_rect(btn_rect, 2); - - Widget_ID wid = make_id(state, id); - - if (state->input_stage){ - if (ui_do_button_input(state, btn_rect, wid, 0)){ - result = 1; - } - } - else{ - Render_Target *target = state->target; - UI_Style ui_style = get_ui_style(state->style); - u32 back, fore, outline; - outline = ui_style.bright; - get_colors(state, &back, &fore, wid, ui_style); - - draw_rectangle(target, btn_rect, back); - draw_rectangle_outline(target, btn_rect, outline); - real32 text_width = font_string_width(font, text); - i32 box_width = btn_rect.x1 - btn_rect.x0; - i32 box_height = btn_rect.y1 - btn_rect.y0; - i32 x_pos = TRUNC32(btn_rect.x0 + (box_width - text_width)*.5f); - draw_string(target, font, text, x_pos, btn_rect.y0 + (box_height - character_h) / 2, fore); - - if (is_toggle){ - i32_Rect on_box = get_inner_rect(btn_rect, character_h/2); - on_box.x1 = on_box.x0 + (on_box.y1 - on_box.y0); - - if (on) draw_rectangle(target, on_box, fore); - else draw_rectangle(target, on_box, back); - draw_rectangle_outline(target, on_box, fore); - } - } - - return result; -} - internal void do_scroll_bar(UI_State *state, i32_Rect rect){ i32 id = 1; @@ -1051,7 +997,7 @@ step_draw_adjusting(Color_View *color_view, i32_Rect rect, View_Message message, ui.layout.rect.x1 -= 20; if (!ui.state.input_stage) draw_push_clip(target, ui.layout.rect); - if (do_button(-1, &ui.state, &ui.layout, "Back to Library")){ + if (do_button(-1, &ui.state, &ui.layout, "Back to Library", 2)){ color_view->mode = CV_MODE_LIBRARY; ui.state.view_y = 0; } @@ -1174,7 +1120,7 @@ update_highlighting(Color_View *color_view){ Editing_File *file = file_view->file; i32 pos = view_get_cursor_pos(file_view); - char c = file->data[pos]; + char c = file->buffer.data[pos]; if (c == '\r'){ color_view->highlight.ids[0] = @@ -1607,19 +1553,19 @@ step_draw_library(Color_View *color_view, i32_Rect rect, View_Message message, begin_row(&ui.layout, 3); if (ui.state.style->name.size >= 1){ - if (do_button(-2, &ui.state, &ui.layout, "Save")){ + if (do_button(-2, &ui.state, &ui.layout, "Save", 2)){ style_library_add(ui.styles, ui.state.style); } } else{ - do_button(-2, &ui.state, &ui.layout, "~Need's Name~"); + do_button(-2, &ui.state, &ui.layout, "~Need's Name~", 2); } - if (do_button(-3, &ui.state, &ui.layout, "Import")){ + if (do_button(-3, &ui.state, &ui.layout, "Import", 2)){ color_view->mode = CV_MODE_IMPORT_FILE; hot_directory_clean_end(color_view->hot_directory); hot_directory_reload(color_view->hot_directory, color_view->working_set); } - if (do_button(-4, &ui.state, &ui.layout, "Export")){ + if (do_button(-4, &ui.state, &ui.layout, "Export", 2)){ color_view->mode = CV_MODE_EXPORT; hot_directory_clean_end(color_view->hot_directory); hot_directory_reload(color_view->hot_directory, color_view->working_set); @@ -1647,10 +1593,10 @@ step_draw_library(Color_View *color_view, i32_Rect rect, View_Message message, do_label(&ui.state, &ui.layout, "Import Which File?"); begin_row(&ui.layout, 2); - if (do_button(-2, &ui.state, &ui.layout, "*.p4c only", 1, color_view->p4c_only)){ + if (do_button(-2, &ui.state, &ui.layout, "*.p4c only", 2, 1, color_view->p4c_only)){ color_view->p4c_only = !color_view->p4c_only; } - if (do_button(-3, &ui.state, &ui.layout, "Cancel")){ + if (do_button(-3, &ui.state, &ui.layout, "Cancel", 2)){ color_view->mode = CV_MODE_LIBRARY; } @@ -1689,10 +1635,10 @@ step_draw_library(Color_View *color_view, i32_Rect rect, View_Message message, do_label(&ui.state, &ui.layout, "Export File Name?"); begin_row(&ui.layout, 2); - if (do_button(-2, &ui.state, &ui.layout, "Finish Export")){ + if (do_button(-2, &ui.state, &ui.layout, "Finish Export", 2)){ file_selected = 1; } - if (do_button(-3, &ui.state, &ui.layout, "Cancel")){ + if (do_button(-3, &ui.state, &ui.layout, "Cancel", 2)){ color_view->mode = CV_MODE_LIBRARY; } @@ -1740,14 +1686,14 @@ step_draw_library(Color_View *color_view, i32_Rect rect, View_Message message, do_label(&ui.state, &ui.layout, "Pack"); begin_row(&ui.layout, 2); - if (do_button(-2, &ui.state, &ui.layout, "Finish Import")){ + if (do_button(-2, &ui.state, &ui.layout, "Finish Import", 2)){ Style *style = styles; for (i32 i = 0; i < style_count; ++i, ++style){ if (import_check[i]) style_library_add(ui.styles, style); } color_view->mode = CV_MODE_LIBRARY; } - if (do_button(-3, &ui.state, &ui.layout, "Cancel")){ + if (do_button(-3, &ui.state, &ui.layout, "Cancel", 2)){ color_view->mode = CV_MODE_LIBRARY; } @@ -1767,10 +1713,10 @@ step_draw_library(Color_View *color_view, i32_Rect rect, View_Message message, do_label(&ui.state, &ui.layout, "Export Which Themes?"); begin_row(&ui.layout, 2); - if (do_button(-2, &ui.state, &ui.layout, "Export")){ + if (do_button(-2, &ui.state, &ui.layout, "Export", 2)){ color_view->mode = CV_MODE_EXPORT_FILE; } - if (do_button(-3, &ui.state, &ui.layout, "Cancel")){ + if (do_button(-3, &ui.state, &ui.layout, "Cancel", 2)){ color_view->mode = CV_MODE_LIBRARY; } diff --git a/4ed_command.cpp b/4ed_command.cpp index cf7f0eff..8a1c050b 100644 --- a/4ed_command.cpp +++ b/4ed_command.cpp @@ -18,8 +18,9 @@ struct Command_Binding{ }; struct Command_Map{ + Command_Map *parent; Command_Binding vanilla_keyboard_default; - Command_Binding commands[101]; + Command_Binding *commands; i32 count, max; }; @@ -93,10 +94,12 @@ map_drop(Command_Map *map, u16 event_code, u8 modifiers){ } internal void -map_init(Command_Map *commands){ +map_init(Command_Map *commands, Partition *part, i32 max, Command_Map *parent){ + commands->parent = parent; + commands->commands = push_array(part, Command_Binding, max); + memset(commands->commands, 0, max*sizeof(*commands->commands)); commands->vanilla_keyboard_default = {}; - memset(commands->commands, 0, sizeof(commands->commands)); - commands->max = ArrayCount(commands->commands); + commands->max = max; commands->count = 0; } diff --git a/4ed_file_view.cpp b/4ed_file_view.cpp index 758eeb95..12173fec 100644 --- a/4ed_file_view.cpp +++ b/4ed_file_view.cpp @@ -9,11 +9,25 @@ // TOP -enum Endline_Mode{ - ENDLINE_RN_COMBINED, - ENDLINE_RN_SEPARATE, - ENDLINE_RN_SHOWALLR -}; +#define GOLDEN_ARRAY 0 +#define ROPES 1 +#define GAP_BUFFER 2 +#define MULTI_GAP_BUFFER 3 +#define SCOPING_BUFFER 4 + +#define USE_BUFFER_TYPE GOLDEN_ARRAY + +#if USE_BUFFER_TYPE == GOLDEN_ARRAY +#include "buffer/4coder_golden_array.cpp" +#elif USE_BUFFER_TYPE == ROPES +#include "buffer/4coder_ropes.cpp" +#elif USE_BUFFER_TYPE == GAP_BUFFER +#include "buffer/4coder_gap_buffer.cpp" +#elif USE_BUFFER_TYPE == MULTI_GAP_BUFFER +#include "buffer/4coder_multi_gap_buffer.cpp" +#elif USE_BUFFER_TYPE == SCOPING_BUFFER +#include "buffer/4coder_scoping_buffer.cpp" +#endif struct Range{ i32 start, end; @@ -51,12 +65,11 @@ struct Undo_Data{ Edit_Step *history; i32 edit_history, edit_history_max; i32 edit_history_cursor; - i32 pending_block; + i32 current_block_normal; }; struct Editing_File{ - i32 size, max_size; - u8 *data; + Buffer buffer; Undo_Data undo; @@ -67,7 +80,7 @@ struct Editing_File{ i32 width_count, width_max; real32 *line_width; - Endline_Mode endline_mode; + EOL_Option endline_mode; i32 cursor_pos; bool32 is_dummy; @@ -83,6 +96,7 @@ struct Editing_File{ bool32 tokens_exist; bool32 still_lexing; u32 lex_job; + i32 base_map_id; u64 last_4ed_write_time; u64 last_4ed_edit_time; @@ -989,16 +1003,19 @@ enum File_View_Widget_Type{ FWIDG_NONE, FWIDG_SEARCH, FWIDG_GOTO_LINE, - FWIDG_UNDOING, - FWIDG_RESTORING, + FWIDG_TIMELINES, // never below this FWIDG_TYPE_COUNT }; struct File_View_Widget{ + UI_State state; File_View_Widget_Type type; i32 height; - UI_State state; + struct{ + bool32 undo_line; + bool32 history_line; + } timeline; }; struct File_View{ @@ -1087,26 +1104,26 @@ pos_adjust_to_self(i32 pos, u8 *data, i32 size){ } inline i32 -pos_universal_fix(i32 pos, u8 *data, i32 size, Endline_Mode mode){ - if (mode == ENDLINE_RN_COMBINED) +pos_universal_fix(i32 pos, u8 *data, i32 size, EOL_Option mode){ + if (mode == EOL_USE_CRLF) pos = pos_adjust_to_self(pos, data, size); return pos; } inline i32 -starts_new_line(u8 character, Endline_Mode mode){ - return (character == '\n' || (mode == ENDLINE_RN_SEPARATE && character == '\r')); +starts_new_line(u8 character, EOL_Option mode){ + return (character == '\n' || (mode == EOL_USE_CR_USE_LF && character == '\r')); } inline void -buffer_init_strings(Editing_File *file){ +file_init_strings(Editing_File *file){ file->source_path = make_fixed_width_string(file->source_path_); file->live_name = make_fixed_width_string(file->live_name_); file->extension = make_fixed_width_string(file->extension_); } inline void -buffer_set_name(Editing_File *file, u8 *filename){ +file_set_name(Editing_File *file, u8 *filename){ String f, ext; f = make_string_slowly((char*)filename); copy_checked(&file->source_path, f); @@ -1117,7 +1134,7 @@ buffer_set_name(Editing_File *file, u8 *filename){ } inline void -buffer_synchronize_times(Editing_File *file, u8 *filename){ +file_synchronize_times(Editing_File *file, u8 *filename){ Time_Stamp stamp = system_file_time_stamp(filename); if (stamp.success){ file->last_4ed_write_time = stamp.time; @@ -1127,55 +1144,48 @@ buffer_synchronize_times(Editing_File *file, u8 *filename){ } inline bool32 -buffer_save(Editing_File *file, u8 *filename){ - bool32 result; - result = system_save_file(filename, file->data, file->size); - buffer_synchronize_times(file, filename); +file_save(Editing_File *file, u8 *filename){ + bool32 result = 0; + i32 do_once = 0; + for (Buffer_Save_Loop loop = buffer_save_loop(&file->buffer); + buffer_save_good(&loop); + buffer_save_next(&loop)){ + Assert(do_once == 0); + result = system_save_file(filename, loop.data, loop.size); + ++do_once; + } + file_synchronize_times(file, filename); return result; } inline bool32 -buffer_save_and_set_names(Editing_File *file, u8 *filename){ +file_save_and_set_names(Editing_File *file, u8 *filename){ bool32 result = 0; - if (buffer_save(file, filename)){ + if (file_save(file, filename)){ result = 1; - buffer_set_name(file, filename); + file_set_name(file, filename); } return result; } -internal i32 -buffer_count_newlines(Editing_File *file, i32 start, i32 end){ +inline i32 +file_count_newlines(Editing_File *file, i32 start, i32 end){ i32 count = 0; - - u8 *data = file->data; - Endline_Mode end_mode = file->endline_mode; - - for (i32 i = start; i < end; ++i){ - bool32 new_line = 0; - switch(data[i]){ - case '\n': - { - new_line = 1; - }break; - case '\r': - { - if (end_mode == ENDLINE_RN_SEPARATE){ - new_line = 1; - } - }break; - } - count += new_line; + switch (file->endline_mode){ + case EOL_USE_CRLF: + case EOL_SHOW_CR_USE_LF: + count = buffer_count_newlines(&file->buffer, start, end, 0, 1); break; + case EOL_USE_CR_USE_LF: + count = buffer_count_newlines(&file->buffer, start, end, 1, 1); break; } - return count; } internal bool32 -buffer_check_newline(Endline_Mode end_mode, u8 character){ +file_check_newline(EOL_Option end_mode, u8 character){ bool32 result = 0; if (character == '\n' || - (end_mode == ENDLINE_RN_SEPARATE && character == '\r')){ + (end_mode == EOL_USE_CR_USE_LF && character == '\r')){ result = 1; } return result; @@ -1200,15 +1210,16 @@ enum File_Bubble_Type{ #define GROW_SUCCESS 2 internal i32 -buffer_grow_starts_as_needed(General_Memory *general, Editing_File *file, i32 additional_lines){ +file_grow_starts_as_needed(General_Memory *general, Editing_File *file, i32 additional_lines){ bool32 result = GROW_NOT_NEEDED; i32 max = file->line_max; i32 count = file->line_count; i32 target_lines = count + additional_lines; if (target_lines > max){ max <<= 1; - i32 *new_lines = (i32*)general_memory_reallocate(general, file->line_starts, - sizeof(i32)*count, sizeof(i32)*max, BUBBLE_STARTS); + i32 *new_lines = (i32*) + general_memory_reallocate(general, file->line_starts, + sizeof(i32)*count, sizeof(i32)*max, BUBBLE_STARTS); if (new_lines){ file->line_starts = new_lines; file->line_max = max; @@ -1222,36 +1233,37 @@ buffer_grow_starts_as_needed(General_Memory *general, Editing_File *file, i32 ad } internal void -buffer_measure_starts(General_Memory *general, Editing_File *file){ +file_measure_starts(General_Memory *general, Editing_File *file){ ProfileMomentFunction(); if (!file->line_starts){ i32 max = file->line_max = Kbytes(1); file->line_starts = (i32*)general_memory_allocate(general, max*sizeof(i32), BUBBLE_STARTS); + TentativeAssert(file->line_starts); // TODO(allen): when unable to allocate? } - Endline_Mode mode = file->endline_mode; - i32 size = file->size; - u8 *data = file->data; - - file->line_count = 0; - i32 start = 0; - for (i32 i = 0; i < size; ++i){ - if (buffer_check_newline(mode, data[i])){ - // TODO(allen): when unable to grow? - buffer_grow_starts_as_needed(general, file, 1); - file->line_starts[file->line_count++] = start; - start = i + 1; - } + // TODO(allen): handle endline modes + Buffer_Measure_Starts state = {}; + while (buffer_measure_starts_(&state, &file->buffer, file->line_starts, file->line_max, 0, 1)){ + i32 max = file->line_max; + i32 count = state.count; + i32 target_lines = count + 1; + + max = (target_lines << 1); + i32 *new_lines = (i32*) + general_memory_reallocate(general, file->line_starts, + sizeof(i32)*count, sizeof(i32)*max, BUBBLE_STARTS); + + // TODO(allen): when unable to grow? + TentativeAssert(new_lines); + file->line_starts = new_lines; + file->line_max = max; } - - // TODO(allen): when unable to grow? - buffer_grow_starts_as_needed(general, file, 1); - file->line_starts[file->line_count++] = start; + file->line_count = state.count; } internal real32 -measure_character(Font *font, Endline_Mode end_mode, bool32 *new_line, +measure_character(Font *font, EOL_Option end_mode, bool32 *new_line, u8 character, u8 next){ real32 width = 0; switch (character){ @@ -1270,7 +1282,7 @@ measure_character(Font *font, Endline_Mode end_mode, bool32 *new_line, case '\r': { switch (end_mode){ - case ENDLINE_RN_COMBINED: + case EOL_USE_CRLF: { if (next == '\n'){ // DO NOTHING @@ -1281,13 +1293,13 @@ measure_character(Font *font, Endline_Mode end_mode, bool32 *new_line, } }break; - case ENDLINE_RN_SEPARATE: + case EOL_USE_CR_USE_LF: { width = font->chardata[character].xadvance; *new_line = 1; }break; - case ENDLINE_RN_SHOWALLR: + case EOL_SHOW_CR_USE_LF: { width = font->chardata['\\'].xadvance; width += font->chardata['r'].xadvance; @@ -1320,9 +1332,9 @@ buffer_measure_widths(General_Memory *general, Editing_File *file, Font *font){ file->width_max = new_max; } - Endline_Mode mode = file->endline_mode; - i32 size = file->size; - u8 *data = file->data; + EOL_Option mode = file->endline_mode; + i32 size = file->buffer.size; + char *data = file->buffer.data; real32 *line_widths = file->line_width; // TODO(allen): Does this help at all with widths??? @@ -1352,7 +1364,7 @@ buffer_remeasure_starts(General_Memory *general, Editing_File *file, i32 character_shift){ ProfileMomentFunction(); Assert(file->line_starts); - buffer_grow_starts_as_needed(general, file, line_shift); + file_grow_starts_as_needed(general, file, line_shift); i32 *lines = file->line_starts; if (line_shift != 0){ @@ -1369,12 +1381,12 @@ buffer_remeasure_starts(General_Memory *general, Editing_File *file, } } - i32 size = file->size; - u8 *data = file->data; + i32 size = file->buffer.size; + char *data = file->buffer.data; i32 char_i = lines[line_start]; i32 line_i = line_start; - Endline_Mode end_mode = file->endline_mode; + EOL_Option end_mode = file->endline_mode; i32 start = char_i; for (; char_i <= size; ++char_i){ @@ -1386,7 +1398,7 @@ buffer_remeasure_starts(General_Memory *general, Editing_File *file, character = data[char_i]; } - if (buffer_check_newline(end_mode, character)){ + if (file_check_newline(end_mode, character)){ if (line_i > line_end && start == lines[line_i]){ break; } @@ -1502,18 +1514,20 @@ buffer_create_from_string(General_Memory *general, Editing_File *file, u8 *filen Assert(data); *file = {}; - file->data = data; - file->size = val.size; - file->max_size = request_size; + file->buffer.data = (char*)data; + file->buffer.size = val.size; + file->buffer.max = request_size; if (val.size > 0) memcpy(data, val.str, val.size); data[val.size] = 0; - buffer_synchronize_times(file, filename); - buffer_init_strings(file); - buffer_set_name(file, filename); + file_synchronize_times(file, filename); + file_init_strings(file); + file_set_name(file, filename); - buffer_measure_starts(general, file); + file->base_map_id = mapid_file; + + file_measure_starts(general, file); buffer_measure_widths(general, file, font); file->font = font; @@ -1533,11 +1547,7 @@ buffer_create_from_string(General_Memory *general, Editing_File *file, u8 *filen file->undo.history_block_count = 1; file->undo.history_head_block = 0; - file->undo.pending_block = 0; - - if (!match(file->extension, make_lit_string("txt"))){ - file->tokens_exist = 1; - } + file->undo.current_block_normal = 1; } internal bool32 @@ -1598,7 +1608,7 @@ buffer_close(General_Memory *general, Editing_File *file){ if (file->token_stack.tokens){ general_memory_free(general, file->token_stack.tokens); } - general_memory_free(general, file->data); + general_memory_free(general, file->buffer.data); general_memory_free(general, file->line_starts); general_memory_free(general, file->line_width); } @@ -1606,7 +1616,7 @@ buffer_close(General_Memory *general, Editing_File *file){ internal void buffer_get_dummy(Editing_File *buffer){ *buffer = {}; - buffer->data = (u8*)&buffer->size; + buffer->buffer.data = (char*)&buffer->buffer.size; buffer->is_dummy = 1; } @@ -1620,8 +1630,8 @@ JOB_CALLBACK(job_full_lex){ General_Memory *general = (General_Memory*)data[1]; Cpp_File cpp_file; - cpp_file.data = (char*)file->data; - cpp_file.size = file->size; + cpp_file.data = file->buffer.data; + cpp_file.size = file->buffer.size; Cpp_Token_Stack tokens; tokens.tokens = (Cpp_Token*)memory->data; @@ -1719,8 +1729,8 @@ buffer_relex_parallel(Mem_Options *mem, Editing_File *file, bool32 inline_lex = !file->still_lexing; if (inline_lex){ Cpp_File cpp_file; - cpp_file.data = (char*)file->data; - cpp_file.size = file->size; + cpp_file.data = file->buffer.data; + cpp_file.size = file->buffer.size; Cpp_Token_Stack *stack = &file->token_stack; @@ -1795,14 +1805,15 @@ buffer_relex_parallel(Mem_Options *mem, Editing_File *file, internal bool32 buffer_grow_as_needed(General_Memory *general, Editing_File *file, i32 additional_size){ bool32 result = 1; - i32 target_size = file->size + additional_size + 1; - if (target_size >= file->max_size){ + i32 target_size = file->buffer.size + additional_size + 1; + if (target_size >= file->buffer.max){ i32 request_size = LargeRoundUp(target_size*2, Kbytes(256)); - u8 *new_data = (u8*)general_memory_reallocate(general, file->data, file->size, request_size, BUBBLE_BUFFER); + char *new_data = (char*) + general_memory_reallocate(general, file->buffer.data, file->buffer.size, request_size, BUBBLE_BUFFER); if (new_data){ - new_data[file->size] = 0; - file->data = new_data; - file->max_size = request_size; + new_data[file->buffer.size] = 0; + file->buffer.data = new_data; + file->buffer.max = request_size; } else{ result = 0; @@ -1880,7 +1891,7 @@ internal Edit_Step* buffer_post_undo(General_Memory *general, Editing_File *file, i32 start, i32 end, i32 str_len, bool32 do_merge, bool32 can_merge, bool32 clear_redo, i32 pre_pos = -1, i32 post_pos = -1){ - String replaced = make_string((char*)file->data + start, end - start); + String replaced = make_string(file->buffer.data + start, end - start); if (clear_redo){ file->undo.str_redo = file->undo.str_max; @@ -1941,7 +1952,7 @@ buffer_unpost_undo(Editing_File *file){ internal void buffer_post_redo(General_Memory *general, Editing_File *file, i32 start, i32 end, i32 str_len, i32 pre_pos, i32 post_pos){ - String replaced = make_string((char*)file->data + start, end - start); + String replaced = make_string((char*)file->buffer.data + start, end - start); if (file->undo.str_redo - replaced.size < file->undo.str_size) buffer_grow_undo_string(general, file, replaced.size); @@ -1982,7 +1993,9 @@ buffer_post_history_block(Editing_File *file, i32 pos){ inline void buffer_unpost_history_block(Editing_File *file){ Assert(file->undo.history_block_count > 1); - + --file->undo.history_block_count; + Edit_Step *old_head = file->undo.history + file->undo.history_head_block; + file->undo.history_head_block = old_head->prev_block; } internal Edit_Step* @@ -1990,7 +2003,7 @@ buffer_post_history(General_Memory *general, Editing_File *file, i32 start, i32 end, i32 str_len, bool32 do_merge, bool32 can_merge, Edit_Type reverse_type, i32 pre_pos = -1, i32 post_pos = -1){ - String replaced = make_string((char*)file->data + start, end - start); + String replaced = make_string(file->buffer.data + start, end - start); if (file->undo.str_history_max < file->undo.str_history + replaced.size) buffer_grow_history_string(general, file, replaced.size); @@ -2041,13 +2054,13 @@ buffer_text_replace(General_Memory *general, Editing_File *file, i32 shift_amount = (str_len - (end - start)); buffer_grow_as_needed(general, file, shift_amount); - Assert(shift_amount + file->size + 1 <= file->max_size); + Assert(shift_amount + file->buffer.size + 1 <= file->buffer.max); - i32 size = file->size; - u8 *data = (u8*)file->data; + i32 size = file->buffer.size; + char *data = (char*)file->buffer.data; memmove(data + end + shift_amount, data + end, size - end); - file->size += shift_amount; - file->data[file->size] = 0; + file->buffer.size += shift_amount; + file->buffer.data[file->buffer.size] = 0; memcpy(data + start, str, str_len); @@ -2074,7 +2087,7 @@ buffer_replace_range(Mem_Options *mem, Editing_File *file, i32 line_start = buffer_get_line_index(file, start); i32 line_end = buffer_get_line_index(file, end); i32 replaced_line_count = line_end - line_start; - i32 new_line_count = buffer_count_newlines(file, start, start+str_len); + i32 new_line_count = file_count_newlines(file, start, start+str_len); i32 line_shift = new_line_count - replaced_line_count; buffer_remeasure_starts(general, file, line_start, line_end, line_shift, shift_amount); @@ -2089,23 +2102,6 @@ buffer_replace_range(Mem_Options *mem, Editing_File *file, return shift; } -#if 0 -inline internal void -buffer_delete(Mem_Options *mem, Editing_File *file, i32 pos){ - buffer_replace_range(mem, file, pos, pos+1, 0, 0); -} - -inline internal void -buffer_delete_range(Mem_Options *mem, Editing_File *file, i32 smaller, i32 larger){ - buffer_replace_range(mem, file, smaller, larger, 0, 0); -} - -inline internal void -buffer_delete_range(Mem_Options *mem, Editing_File *file, Range range){ - buffer_replace_range(mem, file, range.smaller, range.larger, 0, 0); -} -#endif - enum Endline_Convert_Type{ ENDLINE_RN, ENDLINE_N, @@ -2175,7 +2171,7 @@ seek_line_char(i32 line, i32 character){ internal View_Cursor_Data view_cursor_seek(u8 *data, i32 size, Panel_Seek seek, - Endline_Mode endline_mode, + EOL_Option endline_mode, real32 max_width, Font *font, View_Cursor_Data hint = {}){ View_Cursor_Data cursor = hint; @@ -2191,17 +2187,17 @@ view_cursor_seek(u8 *data, i32 size, Panel_Seek seek, case '\r': { switch (endline_mode){ - case ENDLINE_RN_COMBINED: + case EOL_USE_CRLF: { if (next_c != '\n'){ do_slashr = 1; } }break; - case ENDLINE_RN_SEPARATE: + case EOL_USE_CR_USE_LF: { do_newline = 1; }break; - case ENDLINE_RN_SHOWALLR: + case EOL_SHOW_CR_USE_LF: { do_slashr = 1; }break; @@ -2303,7 +2299,7 @@ view_cursor_seek(u8 *data, i32 size, Panel_Seek seek, } } - if (endline_mode == ENDLINE_RN_COMBINED){ + if (endline_mode == EOL_USE_CRLF){ cursor.pos = pos_adjust_to_self(cursor.pos, data, size); } @@ -2332,7 +2328,7 @@ view_compute_cursor_from_pos(File_View *view, i32 pos){ hint.wrapped_x = 0; real32 max_width = view_compute_width(view); - result = view_cursor_seek(file->data, file->size, seek_pos(pos), + result = view_cursor_seek((u8*)file->buffer.data, file->buffer.size, seek_pos(pos), file->endline_mode, max_width, font, hint); return result; @@ -2348,6 +2344,7 @@ view_compute_cursor_from_unwrapped_xy(File_View *view, real32 seek_x, real32 see i32 line_index = FLOOR32(seek_y / font->height); if (line_index >= file->line_count) line_index = file->line_count - 1; + if (line_index < 0) line_index = 0; View_Cursor_Data hint; hint.pos = file->line_starts[line_index]; @@ -2359,7 +2356,7 @@ view_compute_cursor_from_unwrapped_xy(File_View *view, real32 seek_x, real32 see hint.wrapped_x = 0; real32 max_width = view_compute_width(view); - result = view_cursor_seek(file->data, file->size, + result = view_cursor_seek((u8*)file->buffer.data, file->buffer.size, seek_unwrapped_xy(seek_x, seek_y, round_down), file->endline_mode, max_width, font, hint); @@ -2411,7 +2408,7 @@ view_compute_cursor_from_wrapped_xy(File_View *view, real32 seek_x, real32 seek_ real32 max_width = view_compute_width(view); - result = view_cursor_seek(file->data, file->size, + result = view_cursor_seek((u8*)file->buffer.data, file->buffer.size, seek_wrapped_xy(seek_x, seek_y, round_down), file->endline_mode, max_width, font, hint); @@ -2488,7 +2485,8 @@ view_get_cursor_y(File_View *view){ } internal void -view_set_file(File_View *view, Editing_File *file, Style *style){ +view_set_file(File_View *view, Editing_File *file, Style *style, + Custom_Command_Function *open_hook, void *cmd_context, Application_Links app){ Panel *panel = view->view_base.panel; view->file = file; @@ -2531,6 +2529,8 @@ view_set_file(File_View *view, Editing_File *file, Style *style){ view->vel_y = 1.f; view->vel_x = 1.f; + + if (open_hook) open_hook(cmd_context, app); } struct Relative_Scrolling{ @@ -2603,8 +2603,7 @@ view_widget_height(File_View *view, Font *font){ case FWIDG_NONE: break; case FWIDG_SEARCH: result = font->height + 2; break; case FWIDG_GOTO_LINE: result = font->height + 2; break; - case FWIDG_UNDOING: result = font->height*2; break; - case FWIDG_RESTORING: result = font->height*2; break; + case FWIDG_TIMELINES: result = view->widget.height; break; } return result; } @@ -2633,8 +2632,7 @@ struct Edit_Spec{ }; internal void -view_pre_replace_range(Mem_Options *mem, File_View *view, Editing_File *file, - Editing_Layout *layout, Edit_Spec spec){ +view_pre_replace_range(Mem_Options *mem, Editing_File *file, Edit_Spec spec){ General_Memory *general = &mem->general; bool32 can_merge = 0, do_merge = 0; @@ -2664,39 +2662,72 @@ view_pre_replace_range(Mem_Options *mem, File_View *view, Editing_File *file, buffer_unpost_undo(file); + bool32 restore_redos = 0; + Edit_Step *redo_end = 0; + if (spec.in_history == 1 && file->undo.edit_history_cursor > 0){ - Edit_Step *redo_end = file->undo.history + (file->undo.edit_history_cursor - 1); + restore_redos = 1; + redo_end = file->undo.history + (file->undo.edit_history_cursor - 1); + } + else if (spec.in_history == 2 && file->undo.edit_history > 0){ + restore_redos = 1; + redo_end = file->undo.history + (file->undo.edit_history - 1); + } + + if (restore_redos){ Edit_Step *redo_start = redo_end; - while (redo_start->reverse_type == ED_REDO) --redo_start; + i32 steps_of_redo = 0; + i32 undo_count = 0; + while (redo_start->reverse_type == ED_REDO || redo_start->reverse_type == ED_UNDO){ + if (redo_start->reverse_type == ED_REDO){ + if (undo_count > 0) --undo_count; + else ++steps_of_redo; + } + else{ + ++undo_count; + } + --redo_start; + } ++redo_start; ++redo_end; if (redo_start < redo_end){ - i32 steps_of_redo = (i32)(redo_end - redo_start); - u8 *str_dest_base = file->undo.strings; u8 *str_src = file->undo.history_strings + redo_start->str_start; i32 str_redo_pos = file->undo.str_redo; Edit_Step *edit_dest = file->undo.edits + file->undo.edit_redo; - Edit_Step *edit_src = redo_start; + Edit_Step *edit_src = redo_end-1; - for (i32 i = 0; i < steps_of_redo; ++i){ - --edit_dest; - edit_dest->range = edit_src->range; - edit_dest->pre_pos = edit_src->pre_pos; - edit_dest->post_pos = edit_src->post_pos; - edit_dest->can_merge = edit_src->can_merge; - edit_dest->reverse_type = edit_src->reverse_type; - - edit_dest->str_len = edit_src->str_len; - str_redo_pos -= edit_dest->str_len; - edit_dest->str_start = str_redo_pos; - - memcpy(str_dest_base + str_redo_pos, str_src, edit_dest->str_len); + i32 undo_count = 0; + for (i32 i = 0; i < steps_of_redo;){ + if (edit_src->reverse_type == ED_REDO){ + if (undo_count > 0){ + --undo_count; + } + else{ + ++i; + --edit_dest; + edit_dest->range = edit_src->range; + edit_dest->pre_pos = edit_src->pre_pos; + edit_dest->post_pos = edit_src->post_pos; + edit_dest->can_merge = edit_src->can_merge; + edit_dest->reverse_type = edit_src->reverse_type; + + edit_dest->str_len = edit_src->str_len; + str_redo_pos -= edit_dest->str_len; + edit_dest->str_start = str_redo_pos; + + memcpy(str_dest_base + str_redo_pos, str_src, edit_dest->str_len); + } + } + else{ + ++undo_count; + } str_src += edit_dest->str_len; - ++edit_src; + --edit_src; } + Assert(undo_count == 0); file->undo.str_redo = str_redo_pos; file->undo.edit_redo -= steps_of_redo; @@ -2717,6 +2748,7 @@ view_pre_replace_range(Mem_Options *mem, File_View *view, Editing_File *file, case ED_REDO: { + if (spec.str_len == 1 && char_is_alpha_numeric(*spec.str)) can_merge = 1; if (spec.str_len == 1 && (can_merge || char_is_whitespace(*spec.str))) do_merge = 1; @@ -2730,23 +2762,23 @@ view_pre_replace_range(Mem_Options *mem, File_View *view, Editing_File *file, } if (spec.in_history != 2){ - if (spec.type == ED_UNDO){ - if (!file->undo.pending_block){ - file->undo.pending_block = file->undo.edit_history - 1; - buffer_post_history_block(file, file->undo.pending_block); + if (spec.type == ED_UNDO || spec.type == ED_REDO){ + if (file->undo.current_block_normal){ + buffer_post_history_block(file, file->undo.edit_history - 1); + file->undo.current_block_normal = 0; } } else{ - if (file->undo.pending_block){ + if (!file->undo.current_block_normal){ buffer_post_history_block(file, file->undo.edit_history - 1); - file->undo.pending_block = 0; + file->undo.current_block_normal = 1; } } } else{ - if (file->undo.pending_block == file->undo.edit_history){ + if (file->undo.history_head_block == file->undo.edit_history){ buffer_unpost_history_block(file); - file->undo.pending_block = 0; + file->undo.current_block_normal = !file->undo.current_block_normal; } } @@ -2755,26 +2787,27 @@ view_pre_replace_range(Mem_Options *mem, File_View *view, Editing_File *file, internal void view_post_replace_range(Mem_Options *mem, File_View *view, Editing_File *file, - Editing_Layout *layout, Edit_Spec spec){ + Editing_Layout *layout, Shift_Information shift){ Panel *current_panel = layout->panels; + General_Memory *general = &mem->general; i32 panel_count = layout->panel_count; for (i32 i = 0; i < panel_count; ++i, ++current_panel){ File_View *current_view = view_to_file_view(current_panel->view); if (current_view && current_view->file == file){ - view_measure_wraps(&mem->general, current_view); + view_measure_wraps(general, current_view); if (current_view != view){ - if (current_view->cursor.pos >= spec.end){ + if (current_view->cursor.pos >= shift.end){ view_cursor_move(current_view, current_view->cursor.pos+shift.amount); } - else if (current_view->cursor.pos > spec.start){ - view_cursor_move(current_view, spec.start); + else if (current_view->cursor.pos > shift.start){ + view_cursor_move(current_view, shift.start); } - if (current_view->mark >= spec.end){ + if (current_view->mark >= shift.end){ current_view->mark += shift.amount; } - else if (current_view->mark > spec.start){ - current_view->mark += spec.start; + else if (current_view->mark > shift.start){ + current_view->mark += shift.start; } current_view->preferred_x = view_get_cursor_x(current_view); } @@ -2786,19 +2819,10 @@ internal void view_replace_range(Mem_Options *mem, File_View *view, Editing_File *file, Editing_Layout *layout, Edit_Spec spec){ Assert(file); - - view_pre_replace_range(mem, view, file, layout, spec); - - Shift_Information shift; - if (!spec.skip_text_replace){ - shift = - buffer_replace_range(mem, file, spec.start, spec.end, spec.str, spec.str_len); - } - else{ - shift = spec.override_replace; - } - - view_post_replace_range(mem, view, file, layout, spec, shift); + view_pre_replace_range(mem, file, spec); + Shift_Information shift = + buffer_replace_range(mem, file, spec.start, spec.end, spec.str, spec.str_len); + view_post_replace_range(mem, view, file, layout, shift); } inline void @@ -2827,7 +2851,6 @@ view_range_undo(Mem_Options *mem, File_View *view, Editing_Layout *layout, spec.str_len = step.str_len; spec.pre_pos = step.pre_pos; spec.post_pos = step.post_pos; - spec.in_history = 0; view_replace_range(mem, view, file, layout, spec); } @@ -2844,7 +2867,6 @@ view_range_redo(Mem_Options *mem, File_View *view, Editing_Layout *layout, spec.str_len = step.str_len; spec.pre_pos = step.pre_pos; spec.post_pos = step.post_pos; - spec.in_history = 0; view_replace_range(mem, view, file, layout, spec); } @@ -2852,13 +2874,13 @@ view_range_redo(Mem_Options *mem, File_View *view, Editing_Layout *layout, internal i32 view_find_end_of_line(File_View *view, i32 pos){ Editing_File *file = view->file; - u8 *data = (u8*)file->data; - while (pos < file->size && + char *data = file->buffer.data; + while (pos < file->buffer.size && !starts_new_line(data[pos], file->endline_mode)){ ++pos; } - if (pos >= file->size){ - pos = file->size; + if (pos >= file->buffer.size){ + pos = file->buffer.size; } return pos; } @@ -2866,7 +2888,7 @@ view_find_end_of_line(File_View *view, i32 pos){ internal i32 view_find_beginning_of_line(File_View *view, i32 pos){ Editing_File *file = view->file; - u8 *data = (u8*)file->data; + char *data = file->buffer.data; if (pos > 0){ --pos; while (pos > 0 && !starts_new_line(data[pos], file->endline_mode)){ @@ -2882,12 +2904,12 @@ view_find_beginning_of_line(File_View *view, i32 pos){ internal i32 view_find_beginning_of_next_line(File_View *view, i32 pos){ Editing_File *file = view->file; - u8 *data = (u8*)file->data; - while (pos < file->size && + char *data = file->buffer.data; + while (pos < file->buffer.size && !starts_new_line(data[pos], file->endline_mode)){ ++pos; } - if (pos < file->size){ + if (pos < file->buffer.size){ ++pos; } return pos; @@ -3103,9 +3125,9 @@ buffer_find_hard_start(Editing_File *file, i32 line_start){ Line_Hard_Start result = {}; result.line_is_blank = 1; - u8 *data = file->data; + char *data = file->buffer.data; - for (i32 scan = line_start; scan < file->size; ++scan){ + for (i32 scan = line_start; scan < file->buffer.size; ++scan){ if (data[scan] == '\n'){ if (scan > 0 && data[scan-1] == '\r'){ scan -= 1; @@ -3636,19 +3658,68 @@ remeasure_file_view(View *view_, i32_Rect rect){ view_set_relative_scrolling(view, relative); } +internal bool32 +do_button(i32 id, UI_State *state, UI_Layout *layout, char *text, i32 height_mult, + bool32 is_toggle = 0, bool32 on = 0){ + bool32 result = 0; + Font *font = state->font; + i32 character_h = font->height; + + i32_Rect btn_rect = layout_rect(layout, character_h * height_mult); + if (height_mult > 1) btn_rect = get_inner_rect(btn_rect, 2); + else{ + btn_rect.x0 += 2; + btn_rect.x1 -= 2; + } + + Widget_ID wid = make_id(state, id); + + if (state->input_stage){ + if (ui_do_button_input(state, btn_rect, wid, 0)){ + result = 1; + } + } + else{ + Render_Target *target = state->target; + UI_Style ui_style = get_ui_style(state->style); + u32 back, fore, outline; + outline = ui_style.bright; + get_colors(state, &back, &fore, wid, ui_style); + + draw_rectangle(target, btn_rect, back); + draw_rectangle_outline(target, btn_rect, outline); + real32 text_width = font_string_width(font, text); + i32 box_width = btn_rect.x1 - btn_rect.x0; + i32 box_height = btn_rect.y1 - btn_rect.y0; + i32 x_pos = TRUNC32(btn_rect.x0 + (box_width - text_width)*.5f); + draw_string(target, font, text, x_pos, btn_rect.y0 + (box_height - character_h) / 2, fore); + + if (is_toggle){ + i32_Rect on_box = get_inner_rect(btn_rect, character_h/2); + on_box.x1 = on_box.x0 + (on_box.y1 - on_box.y0); + + if (on) draw_rectangle(target, on_box, fore); + else draw_rectangle(target, on_box, back); + draw_rectangle_outline(target, on_box, fore); + } + } + + return result; +} + internal bool32 do_undo_slider(Widget_ID wid, UI_State *state, UI_Layout *layout, i32 max, i32 v, Undo_Data *undo, i32 *out){ bool32 result = 0; Font *font = state->font; i32 character_h = font->height; - i32_Rect containing_rect = layout_rect(layout, character_h * 2); - + i32_Rect containing_rect = layout_rect(layout, character_h); + i32_Rect click_rect; click_rect.x0 = containing_rect.x0 + character_h - 1; click_rect.x1 = containing_rect.x1 - character_h + 1; - click_rect.y0 = containing_rect.y0 + (character_h/2) - 1; - click_rect.y1 = containing_rect.y1 - DIVCEIL32(character_h, 2) + 1; + click_rect.y0 = containing_rect.y0 + 2; + click_rect.y1 = containing_rect.y1 - 2; if (state->input_stage){ real32 l; @@ -3667,8 +3738,8 @@ do_undo_slider(Widget_ID wid, UI_State *state, UI_Layout *layout, i32 max, i32 v real32 L = unlerp(0.f, (real32)v, (real32)max); i32 x = FLOOR32(lerp((real32)click_rect.x0, L, (real32)click_rect.x1)); - i32 bar_top = containing_rect.y0 + character_h - 1; - i32 bar_bottom = containing_rect.y1 - character_h + 1; + i32 bar_top = ((click_rect.y0 + click_rect.y1) >> 1) - 1; + i32 bar_bottom = bar_top + 2; bool32 show_bar = 1; real32 tick_step = (click_rect.x1 - click_rect.x0) / (real32)max; @@ -3711,19 +3782,19 @@ do_undo_slider(Widget_ID wid, UI_State *state, UI_Layout *layout, i32 max, i32 v } } else{ - if (show_bar){ i32_Rect slider_rect; slider_rect.x0 = click_rect.x0; slider_rect.y0 = bar_top; slider_rect.y1 = bar_bottom; - + Edit_Step *history = undo->history; i32 block_count = undo->history_block_count; Edit_Step *step = history; for (i32 i = 0; i < block_count; ++i){ u32 color; - if (step->reverse_type == ED_REDO) color = ui_style.pop1; + if (step->reverse_type == ED_REDO || + step->reverse_type == ED_UNDO) color = ui_style.pop1; else color = ui_style.dim; real32 L; @@ -3733,11 +3804,14 @@ do_undo_slider(Widget_ID wid, UI_State *state, UI_Layout *layout, i32 max, i32 v step = history + step->next_block; L = unlerp(0.f, (real32)(step - history), (real32)max); } + if (L > 1.f) L = 1.f; i32 x = FLOOR32(lerp((real32)click_rect.x0, L, (real32)click_rect.x1)); slider_rect.x1 = x; draw_rectangle(target, slider_rect, color); slider_rect.x0 = slider_rect.x1; + + if (L == 1.f) break; } } @@ -3753,6 +3827,8 @@ do_undo_slider(Widget_ID wid, UI_State *state, UI_Layout *layout, i32 max, i32 v for (i32 i = 0; i <= max; ++i){ if (i != max){ if (history[i].reverse_type == ED_REDO) color = ui_style.pop1; + else if (history[i].reverse_type == ED_UNDO || + history[i].reverse_type == ED_NORMAL) color = ui_style.pop2; else color = ui_style.dim; } draw_rectangle(target, tick, color); @@ -3765,8 +3841,8 @@ do_undo_slider(Widget_ID wid, UI_State *state, UI_Layout *layout, i32 max, i32 v i32_Rect slider_handle; slider_handle.x0 = x - 2; slider_handle.x1 = x + 2; - slider_handle.y0 = bar_top - (character_h/2); - slider_handle.y1 = bar_bottom + (character_h/2); + slider_handle.y0 = click_rect.y0; + slider_handle.y1 = click_rect.y1; draw_rectangle(target, slider_handle, ui_style.bright); } @@ -3989,8 +4065,7 @@ step_file_view(Thread_Context *thread, View *view_, i32_Rect rect, total_count = undo_count + redo_count; switch (view->widget.type){ - case FWIDG_UNDOING: - case FWIDG_RESTORING: + case FWIDG_TIMELINES: { i32_Rect widg_rect = view_widget_rect(view, font); @@ -4001,37 +4076,62 @@ step_file_view(Thread_Context *thread, View *view_, i32_Rect rect, UI_Layout layout; begin_layout(&layout, widg_rect); - Widget_ID wid = make_id(&state, 1); - - if (view->widget.type == FWIDG_UNDOING){ - i32 new_count; - if (do_undo_slider(wid, &state, &layout, total_count, undo_count, 0, &new_count)){ - view->rewind_speed = 0; - view->rewind_amount = 0; - - for (i32 i = 0; i < scrub_max && new_count < undo_count; ++i){ - view_undo(view_->mem, view->layout, view); - --undo_count; - } - for (i32 i = 0; i < scrub_max && new_count > undo_count; ++i){ - view_redo(view_->mem, view->layout, view); - ++undo_count; + if (view->widget.timeline.undo_line){ + if (do_button(1, &state, &layout, "- Undo", 1)){ + view->widget.timeline.undo_line = 0; + } + + if (view->widget.timeline.undo_line){ + Widget_ID wid = make_id(&state, 2); + i32 new_count; + if (do_undo_slider(wid, &state, &layout, total_count, undo_count, 0, &new_count)){ + view->rewind_speed = 0; + view->rewind_amount = 0; + + for (i32 i = 0; i < scrub_max && new_count < undo_count; ++i){ + view_undo(view_->mem, view->layout, view); + --undo_count; + } + for (i32 i = 0; i < scrub_max && new_count > undo_count; ++i){ + view_redo(view_->mem, view->layout, view); + ++undo_count; + } } } } else{ - i32 new_count; - i32 mid = ((file->undo.edit_history + file->undo.edit_history_cursor) >> 1); - i32 count = file->undo.edit_history_cursor; - if (do_undo_slider(wid, &state, &layout, mid, count, &file->undo, &new_count)){ - for (i32 i = 0; i < scrub_max && new_count < count; ++i){ - view_history_step(view_->mem, view->layout, view, 1); - } - for (i32 i = 0; i < scrub_max && new_count > count; ++i){ - view_history_step(view_->mem, view->layout, view, 2); + if (do_button(1, &state, &layout, "+ Undo", 1)){ + view->widget.timeline.undo_line = 1; + } + } + + if (view->widget.timeline.history_line){ + if (do_button(3, &state, &layout, "- History", 1)){ + view->widget.timeline.history_line = 0; + } + + Widget_ID wid = make_id(&state, 4); + if (view->widget.timeline.history_line){ + i32 new_count; + i32 mid = ((file->undo.edit_history + file->undo.edit_history_cursor) >> 1); + i32 count = file->undo.edit_history_cursor; + if (do_undo_slider(wid, &state, &layout, mid, count, &file->undo, &new_count)){ + for (i32 i = 0; i < scrub_max && new_count < count; ++i){ + view_history_step(view_->mem, view->layout, view, 1); + } + for (i32 i = 0; i < scrub_max && new_count > count; ++i){ + view_history_step(view_->mem, view->layout, view, 2); + } } } } + else{ + if (do_button(3, &state, &layout, "+ History", 1)){ + view->widget.timeline.history_line = 1; + } + } + + view->widget.height = layout.y - widg_rect.y0; if (ui_finish_frame(&view->widget.state, &state, &layout, widg_rect, 0, 0)){ result = 1; @@ -4101,10 +4201,10 @@ draw_file_view(Thread_Context *thread, View *view_, i32_Rect rect, bool32 is_act i32 max_x = rect.x1 - rect.x0; i32 max_y = rect.y1 - rect.y0 + font->height; - Assert(file && file->data && !file->is_dummy); + Assert(file && file->buffer.data && !file->is_dummy); - i32 size = (i32)file->size; - u8 *data = (u8*)file->data; + i32 size = (i32)file->buffer.size; + u8 *data = (u8*)file->buffer.data; View_Cursor_Data start_cursor; start_cursor = view_compute_cursor_from_xy(view, 0, view->scroll_y); @@ -4209,7 +4309,7 @@ draw_file_view(Thread_Context *thread, View *view_, i32_Rect rect, bool32 is_act } if (highlight_color != 0){ - if (file->endline_mode == ENDLINE_RN_COMBINED && + if (file->endline_mode == EOL_USE_CRLF && to_render == '\r' && next_to_render == '\n'){ // DO NOTHING // NOTE(allen): relevant durring whitespace highlighting @@ -4259,20 +4359,20 @@ draw_file_view(Thread_Context *thread, View *view_, i32_Rect rect, bool32 is_act if (to_render == '\r'){ bool32 show_slashr = 0; switch (file->endline_mode){ - case ENDLINE_RN_COMBINED: + case EOL_USE_CRLF: { if (next_to_render != '\n'){ show_slashr = 1; } }break; - case ENDLINE_RN_SEPARATE: + case EOL_USE_CR_USE_LF: { pos_x = 0; pos_y += font->height; }break; - case ENDLINE_RN_SHOWALLR: + case EOL_SHOW_CR_USE_LF: { show_slashr = 1; }break; @@ -4338,11 +4438,12 @@ draw_file_view(Thread_Context *thread, View *view_, i32_Rect rect, bool32 is_act begin_layout(&layout, widg_rect); switch (view->widget.type){ - case FWIDG_UNDOING: - case FWIDG_RESTORING: + case FWIDG_TIMELINES: { - Widget_ID wid = make_id(&state, 1); - if (view->widget.type == FWIDG_UNDOING){ + if (view->widget.timeline.undo_line){ + do_button(1, &state, &layout, "- Undo", 1); + + Widget_ID wid = make_id(&state, 2); i32 undo_count, redo_count, total_count; undo_count = file->undo.edit_count; redo_count = file->undo.edit_max - file->undo.edit_redo; @@ -4350,10 +4451,20 @@ draw_file_view(Thread_Context *thread, View *view_, i32_Rect rect, bool32 is_act do_undo_slider(wid, &state, &layout, total_count, undo_count, 0, 0); } else{ + do_button(1, &state, &layout, "+ Undo", 1); + } + + if (view->widget.timeline.history_line){ + do_button(3, &state, &layout, "- History", 1); + + Widget_ID wid = make_id(&state, 4); i32 new_count; i32 mid = ((file->undo.edit_history + file->undo.edit_history_cursor) >> 1); do_undo_slider(wid, &state, &layout, mid, file->undo.edit_history_cursor, &file->undo, &new_count); } + else{ + do_button(3, &state, &layout, "+ History", 1); + } }break; case FWIDG_SEARCH: @@ -4450,8 +4561,7 @@ HANDLE_COMMAND_SIG(handle_command_file_view){ switch (file_view->widget.type){ case FWIDG_NONE: - case FWIDG_UNDOING: - case FWIDG_RESTORING: + case FWIDG_TIMELINES: { file_view->next_mode = {}; if (binding.function) binding.function(command, binding); @@ -4496,7 +4606,7 @@ HANDLE_COMMAND_SIG(handle_command_file_view){ } } - String file_string = make_string((char*)file->data, file->size); + String file_string = make_string(file->buffer.data, file->buffer.size); i32 pos; if (file_view->isearch.reverse){ if (result.hit_backspace){ @@ -4526,12 +4636,12 @@ HANDLE_COMMAND_SIG(handle_command_file_view){ } else{ pos = find_substr(file_string, start_pos + 1, *string); - if (pos < file->size){ + if (pos < file->buffer.size){ if (step_forward){ file_view->isearch.pos = pos; start_pos = pos; pos = find_substr(file_string, start_pos + 1, *string); - if (pos == file->size){ + if (pos == file->buffer.size){ pos = start_pos; } } diff --git a/4ed_rendering.cpp b/4ed_rendering.cpp index 4fd93310..29d77e2c 100644 --- a/4ed_rendering.cpp +++ b/4ed_rendering.cpp @@ -825,6 +825,15 @@ font_get_glyph_width(Font *font, u16 character){ return font->chardata[character].xadvance; } +internal real32 +font_string_width(Font *font, char *str){ + real32 x = 0; + for (i32 i = 0; str[i]; ++i){ + x += font_get_glyph_width(font, str[i]); + } + return x; +} + internal i32 draw_string(Render_Target *target, Font *font, char *str, i32 x_, i32 y, u32 color){ diff --git a/buffer/4coder_golden_array.cpp b/buffer/4coder_golden_array.cpp new file mode 100644 index 00000000..52c342bd --- /dev/null +++ b/buffer/4coder_golden_array.cpp @@ -0,0 +1,118 @@ +/* + * Mr. 4th Dimention - Allen Webster + * Four Tech + * + * 16.10.2015 + * + * Buffer data object + * type - Golden Array + * + */ + +#define inline_4tech inline +#define internal_4tech static + +typedef struct{ + char *data; + int size, max; +} Buffer; + +typedef struct{ + Buffer *buffer; + char *data; + int size; +} Buffer_Save_Loop; + +inline_4tech Buffer_Save_Loop +buffer_save_loop(Buffer *buffer){ + Buffer_Save_Loop result; + result.buffer = buffer; + result.data = buffer->data; + result.size = buffer->size; + return(result); +} + +inline_4tech int +buffer_save_good(Buffer_Save_Loop *loop){ + int result; + result = (loop->buffer != 0); + return(result); +} + +inline_4tech void +buffer_save_next(Buffer_Save_Loop *loop){ + loop->buffer = 0; +} + +internal_4tech int +buffer_count_newlines(Buffer *buffer, int start, int end, int CR, int LF){ + int new_line, count; + char *data; + int i; + + data = buffer->data; + new_line = 0; + count = 0; + + for (i = start; i < end; ++i){ + switch(data[i]){ + case '\n': new_line = LF; break; + case '\r': new_line = CR; break; + default: new_line = 0; break; + } + count += new_line; + } + + return (count); +} + +typedef struct{ + int i; + int count; + int start; +} Buffer_Measure_Starts; + +internal_4tech int +buffer_measure_starts_(Buffer_Measure_Starts *state, Buffer *buffer, int *starts, int max, int CR, int LF){ + char *data; + int size; + int start, count, i, new_line; + int result; + + data = buffer->data; + size = buffer->size; + + result = 0; + start = state->start; + count = state->count; + + for (i = state->i; i < size; ++i){ + switch (data[i]){ + case '\n': new_line = LF; break; + case '\r': new_line = CR; break; + default: new_line = 0; break; + } + + if (new_line){ + if (count == max){ + result = 1; + break; + } + + starts[count++] = start; + start = i + 1; + } + } + + if (i == size){ + if (count == max) result = 1; + else starts[count++] = start; + } + + state->i = i; + state->count = count; + state->start = start; + + return (result); +} + diff --git a/win32_4ed.cpp b/win32_4ed.cpp index 9c8cce70..9af60e83 100644 --- a/win32_4ed.cpp +++ b/win32_4ed.cpp @@ -72,7 +72,6 @@ struct TEMP_BACKDOOR{ Get_Binding_Data_Function *get_bindings; Set_Extra_Font_Function *set_extra_font; - Start_Hook_Function *start_hook; } TEMP; #if FRED_INTERNAL @@ -931,9 +930,6 @@ WinMain(HINSTANCE hInstance, TEMP.set_extra_font = (Set_Extra_Font_Function*) GetProcAddress(win32vars.custom, "set_extra_font"); - - TEMP.start_hook = (Start_Hook_Function*) - GetProcAddress(win32vars.custom, "start_hook"); } #endif