diff --git a/4coder_custom.h b/4coder_custom.h index d9378c70..4d36083b 100644 --- a/4coder_custom.h +++ b/4coder_custom.h @@ -46,6 +46,7 @@ enum Special_Hook_ID{ _hook_scroll_rule = hook_type_count, _hook_new_file, _hook_open_file, + _hook_save_file, _hook_command_caller, _hook_input_filter, }; diff --git a/4coder_custom_api.h b/4coder_custom_api.h index 732a2195..0e2ec256 100644 --- a/4coder_custom_api.h +++ b/4coder_custom_api.h @@ -12,7 +12,7 @@ #define BUFFER_REPLACE_RANGE_SIG(n) bool32 n(Application_Links *app, Buffer_Summary *buffer, int32_t start, int32_t end, char *str, int32_t len) #define BUFFER_COMPUTE_CURSOR_SIG(n) bool32 n(Application_Links *app, Buffer_Summary *buffer, Buffer_Seek seek, Partial_Cursor *cursor_out) #define BUFFER_BATCH_EDIT_SIG(n) bool32 n(Application_Links *app, Buffer_Summary *buffer, char *str, int32_t str_len, Buffer_Edit *edits, int32_t edit_count, Buffer_Batch_Edit_Type type) -#define BUFFER_GET_SETTING_SIG(n) int32_t n(Application_Links *app, Buffer_Summary *buffer, Buffer_Setting_ID setting, int32_t *value_out) +#define BUFFER_GET_SETTING_SIG(n) bool32 n(Application_Links *app, Buffer_Summary *buffer, Buffer_Setting_ID setting, int32_t *value_out) #define BUFFER_SET_SETTING_SIG(n) bool32 n(Application_Links *app, Buffer_Summary *buffer, Buffer_Setting_ID setting, int32_t value) #define BUFFER_TOKEN_COUNT_SIG(n) int32_t n(Application_Links *app, Buffer_Summary *buffer) #define BUFFER_READ_TOKENS_SIG(n) bool32 n(Application_Links *app, Buffer_Summary *buffer, int32_t start_token, int32_t end_token, Cpp_Token *tokens_out) @@ -360,7 +360,7 @@ static inline bool32 buffer_read_range(Application_Links *app, Buffer_Summary *b static inline bool32 buffer_replace_range(Application_Links *app, Buffer_Summary *buffer, int32_t start, int32_t end, char *str, int32_t len){return(app->buffer_replace_range(app, buffer, start, end, str, len));} static inline bool32 buffer_compute_cursor(Application_Links *app, Buffer_Summary *buffer, Buffer_Seek seek, Partial_Cursor *cursor_out){return(app->buffer_compute_cursor(app, buffer, seek, cursor_out));} static inline bool32 buffer_batch_edit(Application_Links *app, Buffer_Summary *buffer, char *str, int32_t str_len, Buffer_Edit *edits, int32_t edit_count, Buffer_Batch_Edit_Type type){return(app->buffer_batch_edit(app, buffer, str, str_len, edits, edit_count, type));} -static inline int32_t buffer_get_setting(Application_Links *app, Buffer_Summary *buffer, Buffer_Setting_ID setting, int32_t *value_out){return(app->buffer_get_setting(app, buffer, setting, value_out));} +static inline bool32 buffer_get_setting(Application_Links *app, Buffer_Summary *buffer, Buffer_Setting_ID setting, int32_t *value_out){return(app->buffer_get_setting(app, buffer, setting, value_out));} static inline bool32 buffer_set_setting(Application_Links *app, Buffer_Summary *buffer, Buffer_Setting_ID setting, int32_t value){return(app->buffer_set_setting(app, buffer, setting, value));} static inline int32_t buffer_token_count(Application_Links *app, Buffer_Summary *buffer){return(app->buffer_token_count(app, buffer));} static inline bool32 buffer_read_tokens(Application_Links *app, Buffer_Summary *buffer, int32_t start_token, int32_t end_token, Cpp_Token *tokens_out){return(app->buffer_read_tokens(app, buffer, start_token, end_token, tokens_out));} @@ -428,7 +428,7 @@ static inline bool32 buffer_read_range(Application_Links *app, Buffer_Summary *b static inline bool32 buffer_replace_range(Application_Links *app, Buffer_Summary *buffer, int32_t start, int32_t end, char *str, int32_t len){return(app->buffer_replace_range_(app, buffer, start, end, str, len));} static inline bool32 buffer_compute_cursor(Application_Links *app, Buffer_Summary *buffer, Buffer_Seek seek, Partial_Cursor *cursor_out){return(app->buffer_compute_cursor_(app, buffer, seek, cursor_out));} static inline bool32 buffer_batch_edit(Application_Links *app, Buffer_Summary *buffer, char *str, int32_t str_len, Buffer_Edit *edits, int32_t edit_count, Buffer_Batch_Edit_Type type){return(app->buffer_batch_edit_(app, buffer, str, str_len, edits, edit_count, type));} -static inline int32_t buffer_get_setting(Application_Links *app, Buffer_Summary *buffer, Buffer_Setting_ID setting, int32_t *value_out){return(app->buffer_get_setting_(app, buffer, setting, value_out));} +static inline bool32 buffer_get_setting(Application_Links *app, Buffer_Summary *buffer, Buffer_Setting_ID setting, int32_t *value_out){return(app->buffer_get_setting_(app, buffer, setting, value_out));} static inline bool32 buffer_set_setting(Application_Links *app, Buffer_Summary *buffer, Buffer_Setting_ID setting, int32_t value){return(app->buffer_set_setting_(app, buffer, setting, value));} static inline int32_t buffer_token_count(Application_Links *app, Buffer_Summary *buffer){return(app->buffer_token_count_(app, buffer));} static inline bool32 buffer_read_tokens(Application_Links *app, Buffer_Summary *buffer, int32_t start_token, int32_t end_token, Cpp_Token *tokens_out){return(app->buffer_read_tokens_(app, buffer, start_token, end_token, tokens_out));} diff --git a/4coder_default_bindings.cpp b/4coder_default_bindings.cpp index 9061745c..eed86a88 100644 --- a/4coder_default_bindings.cpp +++ b/4coder_default_bindings.cpp @@ -160,7 +160,7 @@ HOOK_SIG(my_view_adjust){ for (View_Summary view = get_view_first(app, AccessAll); view.exists; get_view_next(app, &view, AccessAll)){ - new_wrap_width += view.file_region.x1 - view.file_region.x0; + new_wrap_width += view.view_region.x1 - view.view_region.x0; ++count; } @@ -237,7 +237,7 @@ OPEN_FILE_HOOK_SIG(my_file_settings){ if (treat_as_code && enable_code_wrapping && buffer.size < (1 << 18)){ // NOTE(allen|a4.0.12): There is a little bit of grossness going on here. // If we set BufferSetting_Lex to true, it will launch a lexing job. - // If a lexing job is active when we set BufferSetting_VirtualWhitespace on that call can fail. + // If a lexing job is active when we set BufferSetting_VirtualWhitespace, the call can fail. // Unfortunantely without tokens virtual whitespace doesn't really make sense. // So for now I have it automatically turning on lexing when virtual whitespace is turned on. // Cleaning some of that up is a goal for future versions. @@ -253,6 +253,22 @@ OPEN_FILE_HOOK_SIG(my_file_settings){ return(0); } +OPEN_FILE_HOOK_SIG(my_file_save){ + uint32_t access = AccessAll; + Buffer_Summary buffer = get_buffer(app, buffer_id, access); + assert(buffer.exists); + + int32_t is_virtual = 0; + if (automatically_indent_text_on_save && buffer_get_setting(app, &buffer, BufferSetting_VirtualWhitespace, &is_virtual)){ + if (is_virtual){ + auto_tab_whole_file_by_summary(app, &buffer); + } + } + + // no meaning for return + return(0); +} + // NOTE(allen|a4.0.9): The input filter allows you to modify the input // to a frame before 4coder starts processing it at all. // @@ -292,6 +308,47 @@ CUSTOM_COMMAND_SIG(toggle_mouse){ set_mouse_suppression(app, !suppressing_mouse); } +CUSTOM_COMMAND_SIG(execute_arbitrary_command){ + // NOTE(allen): This isn't a super powerful version of this command, I will expand + // upon it so that it has all the cmdid_* commands by default. However, with this + // as an example you have everything you need to make it work already. You could + // even use app->memory to create a hash table in the start hook. + Query_Bar bar; + char space[1024]; + bar.prompt = make_lit_string("Command: "); + bar.string = make_fixed_width_string(space); + + if (!query_user_string(app, &bar)) return; + + // NOTE(allen): Here I chose to end this query bar because when I call another + // command it might ALSO have query bars and I don't want this one hanging + // around at that point. Since the bar exists on my stack the result of the query + // is still available in bar.string though. + end_query_bar(app, &bar, 0); + + if (match_ss(bar.string, make_lit_string("load project"))){ + exec_command(app, load_project); + } + else if (match_ss(bar.string, make_lit_string("open all code"))){ + exec_command(app, open_all_code); + } + else if(match_ss(bar.string, make_lit_string("close all code"))){ + exec_command(app, close_all_code); + } + else if (match_ss(bar.string, make_lit_string("open menu"))){ + exec_command(app, cmdid_open_menu); + } + else if (match_ss(bar.string, make_lit_string("dos lines"))){ + exec_command(app, eol_dosify); + } + else if (match_ss(bar.string, make_lit_string("nix lines"))){ + exec_command(app, eol_nixify); + } + else{ + print_message(app, literal("unrecognized command\n")); + } +} + void default_keys(Bind_Helper *context){ begin_map(context, mapid_global); @@ -326,10 +383,30 @@ default_keys(Bind_Helper *context){ bind(context, 's', MDFR_ALT, show_scrollbar); bind(context, 'w', MDFR_ALT, hide_scrollbar); - bind(context, key_f2, MDFR_NONE, toggle_mouse); + bind(context, key_f2, MDFR_CTRL, toggle_mouse); bind(context, key_page_up, MDFR_CTRL, toggle_fullscreen); bind(context, 'E', MDFR_ALT, exit_4coder); + bind(context, key_f1, MDFR_NONE, project_fkey_command); + bind(context, key_f2, MDFR_NONE, project_fkey_command); + bind(context, key_f3, MDFR_NONE, project_fkey_command); + bind(context, key_f4, MDFR_NONE, project_fkey_command); + + bind(context, key_f5, MDFR_NONE, project_fkey_command); + bind(context, key_f6, MDFR_NONE, project_fkey_command); + bind(context, key_f7, MDFR_NONE, project_fkey_command); + bind(context, key_f8, MDFR_NONE, project_fkey_command); + + bind(context, key_f9, MDFR_NONE, project_fkey_command); + bind(context, key_f10, MDFR_NONE, project_fkey_command); + bind(context, key_f11, MDFR_NONE, project_fkey_command); + bind(context, key_f12, MDFR_NONE, project_fkey_command); + + bind(context, key_f13, MDFR_NONE, project_fkey_command); + bind(context, key_f14, MDFR_NONE, project_fkey_command); + bind(context, key_f15, MDFR_NONE, project_fkey_command); + bind(context, key_f16, MDFR_NONE, project_fkey_command); + end_map(context); begin_map(context, my_code_map); @@ -481,6 +558,7 @@ get_bindings(void *data, int32_t size){ set_hook(context, hook_view_size_change, my_view_adjust); set_open_file_hook(context, my_file_settings); + set_save_file_hook(context, my_file_save); set_command_caller(context, default_command_caller); set_input_filter(context, my_suppress_mouse_filter); set_scroll_rule(context, smooth_scroll_rule); diff --git a/4coder_default_include.cpp b/4coder_default_include.cpp index 8e3ed3cb..57cfbb56 100644 --- a/4coder_default_include.cpp +++ b/4coder_default_include.cpp @@ -1036,15 +1036,17 @@ CUSTOM_COMMAND_SIG(auto_tab_line_at_cursor){ move_past_lead_whitespace(app, &view, &buffer); } +static void +auto_tab_whole_file_by_summary(Application_Links *app, Buffer_Summary *buffer){ + buffer_auto_indent(app, buffer, 0, buffer->size, DEF_TAB_WIDTH, DEFAULT_INDENT_FLAGS | AutoIndent_FullTokens); +} + CUSTOM_COMMAND_SIG(auto_tab_whole_file){ uint32_t access = AccessOpen; View_Summary view = get_active_view(app, access); Buffer_Summary buffer = get_buffer(app, view.buffer_id, access); - buffer_auto_indent(app, &buffer, - 0, buffer.size, - DEF_TAB_WIDTH, - DEFAULT_INDENT_FLAGS | AutoIndent_FullTokens); + auto_tab_whole_file_by_summary(app, &buffer); } CUSTOM_COMMAND_SIG(auto_tab_range){ @@ -2501,77 +2503,9 @@ CUSTOM_COMMAND_SIG(query_replace){ // Fast Buffer Management // -CUSTOM_COMMAND_SIG(close_all_code){ - String extension; - Buffer_Summary buffer; - - // TODO(allen): Get better memory constructs to the custom layer - // so that it doesn't have to rely on arbitrary limits like this one. - int32_t buffers_to_close[2048]; - int32_t buffers_to_close_count = 0; - - uint32_t access = AccessAll; - for (buffer = get_buffer_first(app, access); - buffer.exists; - get_buffer_next(app, &buffer, access)){ - - extension = file_extension(make_string(buffer.file_name, buffer.file_name_len)); - if (match_ss(extension, make_lit_string("cpp")) || - match_ss(extension, make_lit_string("hpp")) || - match_ss(extension, make_lit_string("c")) || - match_ss(extension, make_lit_string("h")) || - match_ss(extension, make_lit_string("cc"))){ - - buffers_to_close[buffers_to_close_count++] = buffer.buffer_id; - } - } - - for (int32_t i = 0; i < buffers_to_close_count; ++i){ - kill_buffer(app, buffer_identifier(buffers_to_close[i]), true, 0); - } -} - -CUSTOM_COMMAND_SIG(open_all_code){ - // NOTE(allen|a3.4.4): This method of getting the hot directory works - // because this custom.cpp gives no special meaning to app->memory - // and doesn't set up a persistent allocation system within app->memory. - // push_directory isn't a very good option since it's tied to the parameter - // stack, so I am phasing that idea out now. - String dir = make_string_cap(app->memory, 0, app->memory_size); - dir.size = directory_get_hot(app, dir.str, dir.memory_size); - int32_t dir_size = dir.size; - - // NOTE(allen|a3.4.4): Here we get the list of files in this directory. - // Notice that we free_file_list at the end. - File_List list = get_file_list(app, dir.str, dir.size); - - for (int32_t i = 0; i < list.count; ++i){ - File_Info *info = list.infos + i; - if (!info->folder){ - String extension = make_string_cap(info->filename, info->filename_len, info->filename_len+1); - extension = file_extension(extension); - if (match_ss(extension, make_lit_string("cpp")) || - match_ss(extension, make_lit_string("hpp")) || - match_ss(extension, make_lit_string("c")) || - match_ss(extension, make_lit_string("h")) || - match_ss(extension, make_lit_string("cc"))){ - // NOTE(allen): There's no way in the 4coder API to use relative - // paths at the moment, so everything should be full paths. Which is - // managable. Here simply set the dir string size back to where it - // was originally, so that new appends overwrite old ones. - dir.size = dir_size; - append_sc(&dir, info->filename); - create_buffer(app, dir.str, dir.size, 0); - } - } - } - - free_file_list(app, list); -} - -char out_buffer_space[1024]; -char command_space[1024]; -char hot_directory_space[1024]; +static char out_buffer_space[1024]; +static char command_space[1024]; +static char hot_directory_space[1024]; CUSTOM_COMMAND_SIG(execute_any_cli){ Query_Bar bar_out = {0}; @@ -2591,11 +2525,7 @@ CUSTOM_COMMAND_SIG(execute_any_cli){ uint32_t access = AccessAll; View_Summary view = get_active_view(app, access); - exec_system_command(app, &view, - buffer_identifier(bar_out.string.str, bar_out.string.size), - hot_directory.str, hot_directory.size, - bar_cmd.string.str, bar_cmd.string.size, - CLI_OverlapWithConflict | CLI_CursorAtEnd); + exec_system_command(app, &view, buffer_identifier(bar_out.string.str, bar_out.string.size), hot_directory.str, hot_directory.size, bar_cmd.string.str, bar_cmd.string.size, CLI_OverlapWithConflict | CLI_CursorAtEnd); } CUSTOM_COMMAND_SIG(execute_previous_cli){ @@ -2607,11 +2537,7 @@ CUSTOM_COMMAND_SIG(execute_previous_cli){ uint32_t access = AccessAll; View_Summary view = get_active_view(app, access); - exec_system_command(app, &view, - buffer_identifier(out_buffer.str, out_buffer.size), - hot_directory.str, hot_directory.size, - cmd.str, cmd.size, - CLI_OverlapWithConflict | CLI_CursorAtEnd); + exec_system_command(app, &view, buffer_identifier(out_buffer.str, out_buffer.size), hot_directory.str, hot_directory.size, cmd.str, cmd.size, CLI_OverlapWithConflict | CLI_CursorAtEnd); } } @@ -2698,8 +2624,7 @@ get_search_all_string(Application_Links *app, Query_Bar *bar){ } static void -generic_search_all_buffers(Application_Links *app, General_Memory *general, Partition *part, - String string, uint32_t match_flags){ +generic_search_all_buffers(Application_Links *app, General_Memory *general, Partition *part, String string, uint32_t match_flags){ Search_Set set = {0}; Search_Iter iter = {0}; @@ -2828,32 +2753,28 @@ CUSTOM_COMMAND_SIG(list_all_locations){ Query_Bar bar; get_search_all_string(app, &bar); if (bar.string.size == 0) return; - generic_search_all_buffers(app, &global_general, &global_part, - bar.string, SearchFlag_MatchWholeWord); + generic_search_all_buffers(app, &global_general, &global_part, bar.string, SearchFlag_MatchWholeWord); } CUSTOM_COMMAND_SIG(list_all_substring_locations){ Query_Bar bar; get_search_all_string(app, &bar); if (bar.string.size == 0) return; - generic_search_all_buffers(app, &global_general, &global_part, - bar.string, SearchFlag_MatchSubstring); + generic_search_all_buffers(app, &global_general, &global_part, bar.string, SearchFlag_MatchSubstring); } CUSTOM_COMMAND_SIG(list_all_locations_case_insensitive){ Query_Bar bar; get_search_all_string(app, &bar); if (bar.string.size == 0) return; - generic_search_all_buffers(app, &global_general, &global_part, - bar.string, SearchFlag_CaseInsensitive | SearchFlag_MatchWholeWord); + generic_search_all_buffers(app, &global_general, &global_part, bar.string, SearchFlag_CaseInsensitive | SearchFlag_MatchWholeWord); } CUSTOM_COMMAND_SIG(list_all_substring_locations_case_insensitive){ Query_Bar bar; get_search_all_string(app, &bar); if (bar.string.size == 0) return; - generic_search_all_buffers(app, &global_general, &global_part, - bar.string, SearchFlag_CaseInsensitive | SearchFlag_MatchSubstring); + generic_search_all_buffers(app, &global_general, &global_part, bar.string, SearchFlag_CaseInsensitive | SearchFlag_MatchSubstring); } CUSTOM_COMMAND_SIG(list_all_locations_of_identifier){ @@ -3132,12 +3053,7 @@ standard_build_search(Application_Links *app, View_Summary *view, Buffer_Summary append_s_char(&message, '\n'); print_message(app, message.str, message.size); - - exec_system_command(app, view, - buffer_identifier(literal("*compilation*")), - dir->str, dir->size, - command->str, command->size, - CLI_OverlapWithConflict); + exec_system_command(app, view, buffer_identifier(literal("*compilation*")), dir->str, dir->size, command->str, command->size, CLI_OverlapWithConflict); result = true; break; } @@ -3150,11 +3066,7 @@ standard_build_search(Application_Links *app, View_Summary *view, Buffer_Summary String backup_command = make_fixed_width_string(backup_space); append_ss(&backup_command, make_lit_string("echo could not find ")); append_ss(&backup_command, filename); - exec_system_command(app, view, - buffer_identifier(literal("*compilation*")), - dir->str, dir->size, - backup_command.str, backup_command.size, - CLI_OverlapWithConflict); + exec_system_command(app, view, buffer_identifier(literal("*compilation*")), dir->str, dir->size, backup_command.str, backup_command.size, CLI_OverlapWithConflict); } break; } @@ -3170,11 +3082,7 @@ static int32_t execute_standard_build_search(Application_Links *app, View_Summary *view, Buffer_Summary *active_buffer, String *dir, String *command, int32_t perform_backup){ - int32_t result = standard_build_search(app, view, - active_buffer, - dir, command, perform_backup, true, - make_lit_string("build.bat"), - make_lit_string("build")); + int32_t result = standard_build_search(app, view, active_buffer, dir, command, perform_backup, true, make_lit_string("build.bat"), make_lit_string("build")); return(result); } @@ -3183,7 +3091,6 @@ execute_standard_build_search(Application_Links *app, View_Summary *view, // NOTE(allen): Build search rule for linux. static int32_t execute_standard_build_search(Application_Links *app, View_Summary *view, Buffer_Summary *active_buffer, String *dir, String *command, bool32 perform_backup){ - char dir_space[512]; String dir_copy = make_fixed_width_string(dir_space); copy(&dir_copy, *dir); @@ -3205,8 +3112,7 @@ execute_standard_build_search(Application_Links *app, View_Summary *view, Buffer // NOTE(allen): This searches first using the active file's directory, // then if no build script is found, it searches from 4coders hot directory. static void -execute_standard_build(Application_Links *app, View_Summary *view, - Buffer_Summary *active_buffer){ +execute_standard_build(Application_Links *app, View_Summary *view, Buffer_Summary *active_buffer){ char dir_space[512]; String dir = make_fixed_width_string(dir_space); @@ -3216,8 +3122,7 @@ execute_standard_build(Application_Links *app, View_Summary *view, int32_t build_dir_type = get_build_directory(app, active_buffer, &dir); if (build_dir_type == BuildDir_AtFile){ - if (!execute_standard_build_search(app, view, active_buffer, - &dir, &command, false)){ + if (!execute_standard_build_search(app, view, active_buffer, &dir, &command, false)){ dir.size = 0; command.size = 0; build_dir_type = get_build_directory(app, 0, &dir); @@ -3225,8 +3130,7 @@ execute_standard_build(Application_Links *app, View_Summary *view, } if (build_dir_type == BuildDir_AtHot){ - execute_standard_build_search(app, view, active_buffer, - &dir, &command, true); + execute_standard_build_search(app, view, active_buffer, &dir, &command, true); } } @@ -3296,48 +3200,6 @@ CUSTOM_COMMAND_SIG(change_to_build_panel){ } } -// -// Other -// - -CUSTOM_COMMAND_SIG(execute_arbitrary_command){ - // NOTE(allen): This isn't a super powerful version of this command, I will expand - // upon it so that it has all the cmdid_* commands by default. However, with this - // as an example you have everything you need to make it work already. You could - // even use app->memory to create a hash table in the start hook. - Query_Bar bar; - char space[1024]; - bar.prompt = make_lit_string("Command: "); - bar.string = make_fixed_width_string(space); - - if (!query_user_string(app, &bar)) return; - - // NOTE(allen): Here I chose to end this query bar because when I call another - // command it might ALSO have query bars and I don't want this one hanging - // around at that point. Since the bar exists on my stack the result of the query - // is still available in bar.string though. - end_query_bar(app, &bar, 0); - - if (match_ss(bar.string, make_lit_string("open all code"))){ - exec_command(app, open_all_code); - } - else if(match_ss(bar.string, make_lit_string("close all code"))){ - exec_command(app, close_all_code); - } - else if (match_ss(bar.string, make_lit_string("open menu"))){ - exec_command(app, cmdid_open_menu); - } - else if (match_ss(bar.string, make_lit_string("dos lines"))){ - exec_command(app, eol_dosify); - } - else if (match_ss(bar.string, make_lit_string("nix lines"))){ - exec_command(app, eol_nixify); - } - else{ - print_message(app, literal("unrecognized command\n")); - } -} - // NOTE(allen|a4): scroll rule information // // The parameters: @@ -3454,6 +3316,7 @@ struct Config_Line{ struct Config_Item{ Config_Line line; + Cpp_Token_Array array; char *mem; String id; int32_t subscript_index; @@ -3461,6 +3324,7 @@ struct Config_Item{ }; struct Config_Array_Reader{ + Cpp_Token_Array array; char *mem; int32_t i; int32_t val_array_end; @@ -3484,7 +3348,7 @@ read_config_token(Cpp_Token_Array array, int32_t *i_ptr){ token = array.tokens[i]; } - i = *i_ptr; + *i_ptr = i; return(token); } @@ -3502,7 +3366,7 @@ read_config_line(Cpp_Token_Array array, int32_t *i_ptr){ Cpp_Token token = read_config_token(array, &i); bool32 subscript_success = 1; - if (token.type == CPP_TOKEN_BRACE_OPEN){ + if (token.type == CPP_TOKEN_BRACKET_OPEN){ subscript_success = 0; ++i; if (i < array.count){ @@ -3511,7 +3375,7 @@ read_config_line(Cpp_Token_Array array, int32_t *i_ptr){ ++i; if (i < array.count){ token = read_config_token(array, &i); - if (token.type == CPP_TOKEN_BRACE_CLOSE){ + if (token.type == CPP_TOKEN_BRACKET_CLOSE){ ++i; if (i < array.count){ token = read_config_token(array, &i); @@ -3540,7 +3404,10 @@ read_config_line(Cpp_Token_Array array, int32_t *i_ptr){ bool32 expecting_array_item = 1; for (; i < array.count; ++i){ Cpp_Token array_token = read_config_token(array, &i); - if (array_token.type != CPP_TOKEN_BRACE_CLOSE){ + if (array_token.size == 0){ + break; + } + if (array_token.type == CPP_TOKEN_BRACE_CLOSE){ config_line.val_array_end = i; array_success = 1; break; @@ -3596,11 +3463,14 @@ read_config_line(Cpp_Token_Array array, int32_t *i_ptr){ } static Config_Item -get_config_item(Config_Line line, char *mem){ +get_config_item(Config_Line line, char *mem, Cpp_Token_Array array){ Config_Item item = {0}; item.line = line; + item.array = array; item.mem = mem; - item.id = make_string(mem + line.id_token.start,line.id_token.size); + if (line.id_token.size != 0){ + item.id = make_string(mem + line.id_token.start, line.id_token.size); + } if (line.subscript_token.size != 0){ String subscript_str = make_string(mem + line.subscript_token.start,line.subscript_token.size); @@ -3625,6 +3495,7 @@ config_var(Config_Item item, char *var_name, int32_t *subscript, uint32_t token_ subscript_succes = 0; } } + if (subscript_succes){ if (var_out){ switch (token_type){ @@ -3647,6 +3518,7 @@ config_var(Config_Item item, char *var_name, int32_t *subscript, uint32_t token_ case CPP_TOKEN_BRACE_OPEN: { Config_Array_Reader *array_reader = (Config_Array_Reader*)var_out; + array_reader->array = item.array; array_reader->mem = item.mem; array_reader->i = item.line.val_array_start; array_reader->val_array_end = item.line.val_array_end; @@ -3687,9 +3559,37 @@ config_array_var(Config_Item item, char *var_name, int32_t *subscript, Config_Ar static bool32 config_array_next_item(Config_Array_Reader *array_reader, Config_Item *item){ + bool32 result = 0; + for (;array_reader->i < array_reader->val_array_end; + ++array_reader->i){ + Cpp_Token array_token = read_config_token(array_reader->array, &array_reader->i); + if (array_token.size == 0 || array_reader->i >= array_reader->val_array_end){ + break; + } + + if (array_token.type == CPP_TOKEN_BRACE_CLOSE){ + break; + } + + switch (array_token.type){ + case CPP_TOKEN_BOOLEAN_CONSTANT: + case CPP_TOKEN_INTEGER_CONSTANT: + case CPP_TOKEN_STRING_CONSTANT: + { + Config_Line line = {0}; + line.val_token = array_token; + line.read_success = 1; + *item = get_config_item(line, array_reader->mem, array_reader->array); + result = 1; + ++array_reader->i; + goto doublebreak; + }break; + } + } + doublebreak:; - bool32 result = (array_reader->good); + array_reader->good = result; return(result); } @@ -3705,6 +3605,8 @@ static bool32 enable_code_wrapping = 1; static bool32 automatically_adjust_wrapping = 1; static int32_t default_wrap_width = 672; static int32_t default_min_base_width = 550; +static bool32 automatically_indent_text_on_save = 1; + static String default_theme_name = make_lit_string("4coder"); static String default_font_name = make_lit_string("Liberation Sans"); @@ -3782,10 +3684,11 @@ process_config_file(Application_Links *app){ Config_Line config_line = read_config_line(array, &i); if (config_line.read_success){ - Config_Item item = get_config_item(config_line, mem); + Config_Item item = get_config_item(config_line, mem, array); config_bool_var(item, "enable_code_wrapping", 0, &enable_code_wrapping); config_bool_var(item, "automatically_adjust_wrapping", 0, &automatically_adjust_wrapping); + config_bool_var(item, "automatically_indent_text_on_save", 0, &automatically_indent_text_on_save); config_int_var(item, "default_wrap_width", 0, &new_wrap_width); config_int_var(item, "default_min_base_width", 0, &new_min_base_width); @@ -3807,7 +3710,223 @@ process_config_file(Application_Links *app){ // NOTE(allen): Project system setup -static char *loaded_project = 0; +static char *default_extensions[] = { + "cpp", + "hpp", + "c", + "h", + "cc" +}; + +struct Fkey_Command{ + char command[128]; + char out[128]; + bool32 use_build_panel; +}; + +struct Project{ + char dir_space[256]; + char *dir; + int32_t dir_len; + + char extension_space[256]; + char *extensions[94]; + int32_t extension_count; + + Fkey_Command fkey_commands[16]; + + bool32 close_all_code_when_this_project_closes; + bool32 close_all_files_when_project_opens; +}; + +static Project null_project = {}; +static Project current_project = {}; + +static void +set_project_extensions(Project *project, String src){ + int32_t mode = 0; + int32_t j = 0, k = 0; + for (int32_t i = 0; i < src.size; ++i){ + switch (mode){ + case 0: + { + if (src.str[i] == '.'){ + mode = 1; + project->extensions[k++] = &project->extension_space[j]; + } + }break; + + case 1: + { + if (src.str[i] == '.'){ + project->extension_space[j++] = 0; + project->extensions[k++] = &project->extension_space[j]; + } + else{ + project->extension_space[j++] = src.str[i]; + } + }break; + } + } + project->extension_space[j++] = 0; + project->extension_count = k; +} + +// TODO(allen): make this a string operation or a lexer operation or something +static void +interpret_escaped_string(char *dst, String src){ + int32_t mode = 0; + int32_t j = 0; + for (int32_t i = 0; i < src.size; ++i){ + switch (mode){ + case 0: + { + if (src.str[i] == '\\'){ + mode = 1; + } + else{ + dst[j++] = src.str[i]; + } + }break; + + case 1: + { + switch (src.str[i]){ + case '\\':{dst[j++] = '\\'; mode = 0;}break; + case 'n': {dst[j++] = '\n'; mode = 0;}break; + case 't': {dst[j++] = '\t'; mode = 0;}break; + case '"': {dst[j++] = '"'; mode = 0;}break; + case '0': {dst[j++] = '\0'; mode = 0;}break; + } + }break; +} + } + dst[j] = 0; +} + +static void +close_all_files_with_extension(Application_Links *app, Partition *scratch_part, char **extension_list, int32_t extension_count){ + Temp_Memory temp = begin_temp_memory(scratch_part); + + int32_t buffers_to_close_max = partition_remaining(scratch_part)/sizeof(int32_t); + int32_t *buffers_to_close = push_array(scratch_part, int32_t, buffers_to_close_max); + + int32_t buffers_to_close_count = 0; + bool32 do_repeat = 0; + do{ + buffers_to_close_count = 0; + do_repeat = 0; + + uint32_t access = AccessAll; + Buffer_Summary buffer = {0}; + for (buffer = get_buffer_first(app, access); + buffer.exists; + get_buffer_next(app, &buffer, access)){ + + bool32 is_match = 1; + if (extension_count > 0){ + String extension = file_extension(make_string(buffer.file_name, buffer.file_name_len)); + is_match = 0; + for (int32_t i = 0; i < extension_count; ++i){ + if (match(extension, extension_list[i])){ + is_match = 1; + break; + } + } + } + + if (is_match){ + if (buffers_to_close_count >= buffers_to_close_max){ + do_repeat = 1; + break; + } + buffers_to_close[buffers_to_close_count++] = buffer.buffer_id; + } + } + + for (int32_t i = 0; i < buffers_to_close_count; ++i){ + kill_buffer(app, buffer_identifier(buffers_to_close[i]), true, 0); + } +} + while(do_repeat); + + end_temp_memory(temp); +} + +static void +open_all_files_with_extension(Application_Links *app, Partition *scratch_part, char **extension_list, int32_t extension_count){ + Temp_Memory temp = begin_temp_memory(scratch_part); + + int32_t max_size = partition_remaining(scratch_part); + char *memory = push_array(scratch_part, char, max_size); + + String dir = make_string_cap(memory, 0, max_size); + dir.size = directory_get_hot(app, dir.str, dir.memory_size); + int32_t dir_size = dir.size; + + // NOTE(allen|a3.4.4): Here we get the list of files in this directory. + // Notice that we free_file_list at the end. + File_List list = get_file_list(app, dir.str, dir.size); + + for (int32_t i = 0; i < list.count; ++i){ + File_Info *info = list.infos + i; + if (!info->folder){ + bool32 is_match = 1; + + if (extension_count > 0){ + is_match = 0; + + String extension = make_string_cap(info->filename, info->filename_len, info->filename_len+1); + extension = file_extension(extension); + for (int32_t j = 0; j < extension_count; ++j){ + if (match(extension, extension_list[j])){ + is_match = 1; + break; + } + } + + if (is_match){ + // NOTE(allen): There's no way in the 4coder API to use relative + // paths at the moment, so everything should be full paths. Which is + // managable. Here simply set the dir string size back to where it + // was originally, so that new appends overwrite old ones. + dir.size = dir_size; + append_sc(&dir, info->filename); + create_buffer(app, dir.str, dir.size, 0); + } + } + } +} + + free_file_list(app, list); + +end_temp_memory(temp); +} + +static char** +get_standard_code_extensions(int32_t *extension_count_out){ + char **extension_list = default_extensions; + int32_t extension_count = ArrayCount(default_extensions); + if (current_project.dir != 0){ + extension_list = current_project.extensions; + extension_count = current_project.extension_count; + } + *extension_count_out = extension_count; + return(extension_list); +} + +// NOTE(allen|a4.0.14): open_all_code and close_all_code now use the extensions set in the loaded project. If there is no project loaded the extensions ".cpp.hpp.c.h.cc" are used. +CUSTOM_COMMAND_SIG(open_all_code){ + int32_t extension_count = 0; + char **extension_list = get_standard_code_extensions(&extension_count); + open_all_files_with_extension(app, &global_part, extension_list, extension_count); +} + +CUSTOM_COMMAND_SIG(close_all_code){ + int32_t extension_count = 0; + char **extension_list = get_standard_code_extensions(&extension_count); + close_all_files_with_extension(app, &global_part, extension_list, extension_count); +} CUSTOM_COMMAND_SIG(load_project){ Partition *part = &global_part; @@ -3820,114 +3939,255 @@ CUSTOM_COMMAND_SIG(load_project){ } if (project_name.size != 0){ - append_sc(&project_name, "/project.4coder"); - terminate_with_null(&project_name); - - FILE *file = fopen(project_name.str, "rb"); - if (file){ - Temp_Memory temp = begin_temp_memory(part); + int32_t original_size = project_name.size; + append_sc(&project_name, "project.4coder"); + terminate_with_null(&project_name); - char *mem = 0; - int32_t size = 0; - bool32 file_read_success = file_handle_dump(part, file, &mem, &size); - if (file_read_success){ - fclose(file); + // TODO(allen): make sure we do nothing when this project is already open + + FILE *file = fopen(project_name.str, "rb"); + if (file){ + project_name.size = original_size; + terminate_with_null(&project_name); - Cpp_Token_Array array; - array.count = 0; - array.max_count = (1 << 20)/sizeof(Cpp_Token); - array.tokens = push_array(&global_part, Cpp_Token, array.max_count); - - Cpp_Lex_Data S = cpp_lex_data_init(); - Cpp_Lex_Result result = cpp_lex_step(&S, mem, size+1, HAS_NULL_TERM, &array, NO_OUT_LIMIT); - - if (result == LexResult_Finished){ - for (int32_t i = 0; i < array.count; ++i){ - Config_Line config_line = read_config_line(array, &i); - if (config_line.read_success){ - Config_Item item = get_config_item(config_line, mem); - - { - String str = {0}; - if (config_string_var(item, "extensions", 0, &str)){ - // TODO(allen) - } - } + Temp_Memory temp = begin_temp_memory(part); + + char *mem = 0; + int32_t size = 0; + bool32 file_read_success = file_handle_dump(part, file, &mem, &size); + if (file_read_success){ + fclose(file); - { - #if WIN - #define FKEY_COMMAND "fkey_command_wnd" - #else -#define FKEY_COMMAND "fkey_command_linux" - #endif + Cpp_Token_Array array; + array.count = 0; + array.max_count = (1 << 20)/sizeof(Cpp_Token); + array.tokens = push_array(&global_part, Cpp_Token, array.max_count); + + Cpp_Lex_Data S = cpp_lex_data_init(); + Cpp_Lex_Result result = cpp_lex_step(&S, mem, size+1, HAS_NULL_TERM, &array, NO_OUT_LIMIT); + + if (result == LexResult_Finished){ + // Clear out current project + if (current_project.close_all_code_when_this_project_closes){ + exec_command(app, close_all_code); + } + current_project = null_project; - int32_t index = 0; - Config_Array_Reader array_reader = {0}; - if (config_array_var(item, FKEY_COMMAND, &index, &array_reader)){ - if (index >= 1 && index <= 16){ - Config_Item array_item = {0}; - int32_t item_index = 0; - - for (config_array_next_item(&array_reader, &array_item); - config_array_good(&array_reader); - config_array_next_item(&array_reader, &array_item)){ - if (item_index >= 2){ - break; + // Set new project directory + { + current_project.dir = current_project.dir_space; + String str = make_fixed_width_string(current_project.dir_space); + copy(&str, project_name); + terminate_with_null(&str); + current_project.dir_len = str.size; + } + + // Read the settings from project.4coder + for (int32_t i = 0; i < array.count; ++i){ + Config_Line config_line = read_config_line(array, &i); + if (config_line.read_success){ + Config_Item item = get_config_item(config_line, mem, array); + + { + String str = {0}; + if (config_string_var(item, "extensions", 0, &str)){ + if (str.size < sizeof(current_project.extension_space)){ + set_project_extensions(¤t_project, str); + print_message(app, str.str, str.size); + print_message(app, "\n", 1); + } + else{ + print_message(app, literal("STRING TOO LONG!\n")); + } + } } - switch (item_index){ - case 0: - { - if (config_int_var(array_item, 0, 0, 0)){ - // TODO(allen) - } - String str = {0}; - if (config_string_var(array_item, 0, 0, &str)){ - // TODO(allen) - } - }break; + { +#if defined(_WIN32) +#define FKEY_COMMAND "fkey_command_win" +#elif defined(__linux__) +#define FKEY_COMMAND "fkey_command_linux" +#else +#error no project configuration names for this platform +#endif - case 1: - { - if (config_int_var(array_item, 0, 0, 0)){ - // TODO(allen) + int32_t index = 0; + Config_Array_Reader array_reader = {0}; + if (config_array_var(item, FKEY_COMMAND, &index, &array_reader)){ + if (index >= 1 && index <= 16){ + Config_Item array_item = {0}; + int32_t item_index = 0; + + char space[256]; + String msg = make_fixed_width_string(space); + append(&msg, FKEY_COMMAND"["); + append_int_to_str(&msg, index); + append(&msg, "] = {"); + + for (config_array_next_item(&array_reader, &array_item); + config_array_good(&array_reader); + config_array_next_item(&array_reader, &array_item)){ + + if (item_index >= 3){ + break; + } + + append(&msg, "["); + append_int_to_str(&msg, item_index); + append(&msg, "] = "); + + bool32 read_string = 0; + bool32 read_bool = 0; + + char *dest_str = 0; + int32_t dest_str_size = 0; + + bool32 *dest_bool = 0; + + switch (item_index){ + case 0: + { + dest_str = current_project.fkey_commands[index-1].command; + dest_str_size = sizeof(current_project.fkey_commands[index-1].command); + read_string = 1; + }break; + + case 1: + { + dest_str = current_project.fkey_commands[index-1].out; + dest_str_size = sizeof(current_project.fkey_commands[index-1].out); + read_string = 1; + }break; + + case 2: + { + dest_bool = ¤t_project.fkey_commands[index-1].use_build_panel; + read_bool = 1; + }break; + } + + if (read_string){ + if (config_int_var(array_item, 0, 0, 0)){ + append(&msg, "NULL, "); + dest_str[0] = 0; + } + + String str = {0}; + if (config_string_var(array_item, 0, 0, &str)){ + if (str.size < dest_str_size){ + interpret_escaped_string(dest_str, str); + append(&msg, dest_str); + append(&msg, ", "); + } + else{ + append(&msg, "STRING TOO LONG!, "); + } + } } - String str = {0}; - if (config_string_var(array_item, 0, 0, &str)){ - // TODO(allen) + + if (read_bool){ + if (config_bool_var(array_item, 0, 0, dest_bool)){ + if (dest_bool){ + append(&msg, "true, "); + } + else{ + append(&msg, "false, "); + } + } } - }break; + + item_index++; + } + + append(&msg, "}\n"); + print_message(app, msg.str, msg.size); + } + } } - - item_index++; } } + + if (current_project.close_all_files_when_project_opens){ + close_all_files_with_extension(app, &global_part, 0, 0); } + + // Open all project files + exec_command(app, open_all_code); } + } + + end_temp_memory(temp); + } + else{ + char message_space[512]; + String message = make_fixed_width_string(message_space); + append_sc(&message, "Did not find project.4coder. "); + if (current_project.dir != 0){ + append_sc(&message, "Continuing with: "); + append_sc(&message, current_project.dir); + } + else{ + append_sc(&message, "Continuing without a project"); + } + print_message(app, message.str, message.size); + } + } + else{ + print_message(app, literal("Failed trying to get project file name")); + } +} + +CUSTOM_COMMAND_SIG(project_fkey_command){ + User_Input input = get_command_input(app); + if (input.type == UserInputKey){ + if (input.key.keycode >= key_f1 && input.key.keycode <= key_f16){ + int32_t ind = (input.key.keycode - key_f1); + + char *command = current_project.fkey_commands[ind].command; + char *out = current_project.fkey_commands[ind].out; + bool32 use_build_panel = current_project.fkey_commands[ind].use_build_panel; + + if (command[0] != 0){ + int32_t command_len = str_size(command); + + View_Summary view_ = {0}; + View_Summary *view = 0; + Buffer_Identifier buffer_id = {0}; + uint32_t flags = 0; + + bool32 set_fancy_font = 0; + + if (out[0] != 0){ + int32_t out_len = str_size(out); + buffer_id = buffer_identifier(out, out_len); + + view = &view_; + + if (use_build_panel){ + view_ = get_or_open_build_panel(app); + if (match(out, "*compilation*")){ + set_fancy_font = 1; + } + } + else{ + view_ = get_active_view(app, AccessAll); + } + + prev_location = null_location; + lock_jump_buffer(out, out_len); + } + else{ + // TODO(allen): fix the exec_system_command call so it can take a null buffer_id. + buffer_id = buffer_identifier(literal("*dump*")); + } + + exec_system_command(app, view, buffer_id, current_project.dir, current_project.dir_len, command, command_len, flags); + if (set_fancy_font){ + set_fancy_compilation_buffer_font(app); } } } } - - end_temp_memory(temp); - } - else{ - char message_space[512]; - String message = make_fixed_width_string(message_space); - append_sc(&message, "Did not find project.4coder. "); - if (loaded_project != 0){ - append_sc(&message, "Continuing with: "); - append_sc(&message, loaded_project); - } - else{ - append_sc(&message, "Continuing without a project"); - } - print_message(app, message.str, message.size); - } -} -else{ - print_message(app, literal("Failed trying to get project file name")); -} } #endif diff --git a/4coder_helper.h b/4coder_helper.h index cfd0dc46..1fb5eeca 100644 --- a/4coder_helper.h +++ b/4coder_helper.h @@ -179,6 +179,16 @@ set_open_file_hook(Bind_Helper *helper, Open_File_Hook_Function *func){ write_unit(helper, unit); } +inline void +set_save_file_hook(Bind_Helper *helper, Open_File_Hook_Function *func){ + Binding_Unit unit; + unit.type = unit_hook; + unit.hook.hook_id = _hook_save_file; + unit.hook.func = (void*) func; + + write_unit(helper, unit); +} + inline void set_command_caller(Bind_Helper *helper, Command_Caller_Hook_Function *func){ Binding_Unit unit; diff --git a/4coder_types.h b/4coder_types.h index bd0071ac..641d9d2d 100644 --- a/4coder_types.h +++ b/4coder_types.h @@ -668,7 +668,11 @@ STRUCT View_Summary{ bool32 show_whitespace; /* - DOC(If this is not a null summary, this describes the screen position in which this view's buffer is displayed.) + DOC(If this is not a null summary, this describes the screen position in which this view's is displayed.) + */ + i32_Rect view_region; + /* + DOC(If this is not a null summary, this describes the screen position in which this view's buffer is displayed. This is different from view_region, because it does not include any fixed height GUI at the top of the view.) */ i32_Rect file_region; /* DOC(If this is not a null summary, this describes the scrolling position inside the view.) */ diff --git a/4ed.cpp b/4ed.cpp index e9931a51..e399525d 100644 --- a/4ed.cpp +++ b/4ed.cpp @@ -414,7 +414,7 @@ COMMAND_DECL(save){ REQ_FILE(file, view); if (!file->is_dummy && file_is_ready(file) && buffer_can_save(file)){ - save_file(system, &models->mem, file); + save_file(system, models, file); } } @@ -422,9 +422,7 @@ COMMAND_DECL(save_as){ USE_VIEW(view); REQ_FILE(file, view); - view_show_interactive(system, view, - IAct_Save_As, IInt_Sys_File_List, - make_lit_string("Save As: ")); + view_show_interactive(system, view, IAct_Save_As, IInt_Sys_File_List, make_lit_string("Save As: ")); } COMMAND_DECL(change_active_panel){ @@ -1439,6 +1437,7 @@ App_Init_Sig(app_init){ models->scroll_rule = fallback_scroll_rule; models->hook_open_file = 0; models->hook_new_file = 0; + models->hook_save_file = 0; setup_command_table(); @@ -1578,6 +1577,10 @@ App_Init_Sig(app_init){ models->hook_new_file = (Open_File_Hook_Function*)unit->hook.func; break; + case _hook_save_file: + models->hook_save_file = (Open_File_Hook_Function*)unit->hook.func; + break; + case _hook_command_caller: models->command_caller = (Command_Caller_Hook_Function*)unit->hook.func; break; @@ -2564,6 +2567,8 @@ App_Step_Sig(app_step){ "-The 'config.4coder' file can now be placed with the 4ed executable file\n" "-New options in 'config.4coder' to specify the font and color theme\n" "-New built in project configuration system\n" + "-New on-save hooks allows custom behavior in the custom layer whenever a file is saved\n" + "-When using code wrapping, any saved file is automatically indented in the text format, this option can be turned off in config.4coder\n" "\n" "New in alpha 4.0.12 and 4.0.13:\n" "-Text files wrap lines at whitespace when possible\n" diff --git a/4ed_api_implementation.cpp b/4ed_api_implementation.cpp index f3547da1..71a49c52 100644 --- a/4ed_api_implementation.cpp +++ b/4ed_api_implementation.cpp @@ -87,7 +87,8 @@ fill_view_summary(View_Summary *view, View *vptr, Live_Views *live_set, Working_ view->cursor = vptr->edit_pos->cursor; view->preferred_x = vptr->edit_pos->preferred_x; - view->file_region = vptr->panel->inner; + view->view_region = vptr->panel->inner; + view->file_region = vptr->file_region; view->scroll_vars = vptr->edit_pos->scroll; } } @@ -118,7 +119,7 @@ imp_get_file(Command_Data *cmd, Buffer_Summary *buffer){ Editing_File *file = 0; Working_Set *working_set = &cmd->models->working_set;; - if (buffer->exists){ + if (buffer && buffer->exists){ file = working_set_get_active_file(working_set, buffer->buffer_id); if (file != 0 && !file_is_ready(file)){ file = 0; @@ -148,7 +149,7 @@ internal View* imp_get_view(Command_Data *cmd, View_Summary *view){ View *vptr = 0; - if (view->exists){ + if (view && view->exists){ vptr = imp_get_view(cmd, view->view_id); } @@ -185,28 +186,24 @@ DOC_SEE(Command_ID) API_EXPORT bool32 Exec_System_Command(Application_Links *app, View_Summary *view, Buffer_Identifier buffer, char *path, int32_t path_len, char *command, int32_t command_len, Command_Line_Interface_Flag flags) /* -DOC_PARAM(view, If the view parameter is non-null it specifies a view to display the command's output buffer.) +DOC_PARAM(view, If the view parameter is non-null it specifies a view to display the command's output buffer, otherwise the command will still work but if there is a buffer capturing the output it will not automatically be displayed.) DOC_PARAM(buffer, The buffer the command will output to is specified by the buffer parameter. -See Buffer_Identifier for information on how this type specifies a buffer.) +See Buffer_Identifier for information on how this type specifies a buffer. The command will cause a crash if no file is specified.) DOC_PARAM(path, The path parameter specifies the path in which the command shall be executed. The string need not be null terminated.) DOC_PARAM(path_len, The parameter path_len specifies the length of the path string.) DOC_PARAM(command, The command parameter specifies the command that shall be executed. The string need not be null terminated.) DOC_PARAM(command_len, The parameter command_len specifies the length of the command string.) DOC_PARAM(flags, Flags for the behavior of the call are specified in the flags parameter.) DOC_RETURN(This call returns non-zero on success.) -DOC -( -A call to exec_system_command executes a command as if called from the command line, and sends the output to -a buffer. The buffer identifier can either name a new buffer that does not exist, name a buffer that does -exist, or provide the id of a buffer that does exist. +DOC( +A call to exec_system_command executes a command as if called from the command line, and sends the output to a buffer. The buffer identifier can name a new buffer that does not exist, name a buffer that does exist, or provide the id of a buffer that does exist. If the buffer is not already in an open view and the view parameter is not NULL, then the provided view will display the output buffer. -If the view parameter is NULL, no view will switch to the output. -) +If the view parameter is NULL, no view will switch to the output.) DOC_SEE(Buffer_Identifier) -DOC_SEE(Command_Line_Input_Flag) +DOC_SEE(Command_Line_Interface_Flag) */{ Command_Data *cmd = (Command_Data*)app->cmd_context; System_Functions *system = cmd->system; @@ -231,6 +228,7 @@ DOC_SEE(Command_Line_Input_Flag) if (vars->cli_processes.count < vars->cli_processes.max){ file = get_file_from_identifier(system, working_set, buffer); + if (file){ if (file->settings.read_only == 0){ append_ss(&feedback_str, make_lit_string("ERROR: ")); @@ -252,13 +250,11 @@ DOC_SEE(Command_Line_Input_Flag) else if (buffer.name){ file = working_set_alloc_always(system, working_set, general); if (file == 0){ - append_ss(&feedback_str, - make_lit_string("ERROR: unable to allocate a new buffer\n")); + append_ss(&feedback_str, make_lit_string("ERROR: unable to allocate a new buffer\n")); do_feedback_message(system, models, feedback_str); result = false; goto done; } - String name = make_string_terminated(part, buffer.name, buffer.name_len); buffer_bind_name(system, general, working_set, file, name); init_read_only_file(system, models, file); @@ -707,7 +703,7 @@ DOC_SEE(Buffer_Batch_Edit_Type) return(result); } -API_EXPORT int32_t +API_EXPORT bool32 Buffer_Get_Setting(Application_Links *app, Buffer_Summary *buffer, Buffer_Setting_ID setting, int32_t *value_out) /* DOC_PARAM(buffer, the buffer from which to read a setting) @@ -717,7 +713,7 @@ DOC_RETURN(returns non-zero on success) */{ Command_Data *cmd = (Command_Data*)app->cmd_context; Editing_File *file = imp_get_file(cmd, buffer); - int32_t result = 0; + bool32 result = 0; if (file){ result = 1; @@ -1141,17 +1137,17 @@ DOC_RETURN(This call returns non-zero on success.) Command_Data *cmd = (Command_Data*)app->cmd_context; System_Functions *system = cmd->system; Models *models = cmd->models; - bool32 result = false; + bool32 result = 0; Editing_File *file = imp_get_file(cmd, buffer); if (file){ if (file_get_sync(file) != DirtyState_UpToDate){ - result = true; + result = 1; Partition *part = &models->mem.part; Temp_Memory temp = begin_temp_memory(part); String name = make_string_terminated(part, filename, filename_len); - save_file_to_name(system, &models->mem, file, name.str); + save_file_to_name(system, models, file, name.str); end_temp_memory(temp); } } diff --git a/4ed_app_models.h b/4ed_app_models.h index 32017f1e..f58c8277 100644 --- a/4ed_app_models.h +++ b/4ed_app_models.h @@ -65,6 +65,7 @@ struct Models{ Open_File_Hook_Function *hook_open_file; Open_File_Hook_Function *hook_new_file; + Open_File_Hook_Function *hook_save_file; Command_Caller_Hook_Function *command_caller; Input_Filter_Function *input_filter; Scroll_Rule_Function *scroll_rule; diff --git a/4ed_file_view.cpp b/4ed_file_view.cpp index 1b5358b2..8c36d667 100644 --- a/4ed_file_view.cpp +++ b/4ed_file_view.cpp @@ -744,7 +744,7 @@ file_synchronize_times(System_Functions *system, Editing_File *file){ } internal b32 -save_file_to_name(System_Functions *system, Mem_Options *mem, Editing_File *file, char *filename){ +save_file_to_name(System_Functions *system, Models *models, Editing_File *file, char *filename){ b32 result = 0; b32 using_actual_filename = 0; @@ -755,6 +755,11 @@ save_file_to_name(System_Functions *system, Mem_Options *mem, Editing_File *file } if (filename){ + Mem_Options *mem = &models->mem; + if (models->hook_save_file){ + models->hook_save_file(&models->app_links, file->id.id); + } + i32 max = 0, size = 0; b32 dos_write_mode = file->settings.dos_write_mode; char *data = 0; @@ -810,8 +815,8 @@ save_file_to_name(System_Functions *system, Mem_Options *mem, Editing_File *file } inline b32 -save_file(System_Functions *system, Mem_Options *mem, Editing_File *file){ - b32 result = save_file_to_name(system, mem, file, 0); +save_file(System_Functions *system, Models *models, Editing_File *file){ + b32 result = save_file_to_name(system, models, file, 0); return(result); } @@ -835,12 +840,10 @@ buffer_link_to_new_file(System_Functions *system, General_Memory *general, Worki } inline b32 -file_save_and_set_names(System_Functions *system, Mem_Options *mem, - Working_Set *working_set, Editing_File *file, - String filename){ - b32 result = buffer_link_to_new_file(system, &mem->general, working_set, file, filename); +file_save_and_set_names(System_Functions *system, Models *models, Editing_File *file, String filename){ + b32 result = buffer_link_to_new_file(system, &models->mem.general, &models->working_set, file, filename); if (result){ - result = save_file(system, mem, file); + result = save_file(system, models, file); } return(result); } @@ -1960,9 +1963,8 @@ file_create_from_string(System_Functions *system, Models *models, file->state.undo.current_block_normal = 1; } - Open_File_Hook_Function *open_hook = models->hook_open_file; - if (open_hook){ - open_hook(&models->app_links, file->id.id); + if (models->hook_open_file){ + models->hook_open_file(&models->app_links, file->id.id); } file->settings.is_initialized = 1; } @@ -3833,7 +3835,7 @@ view_open_file(System_Functions *system, Models *models, View *view, String file internal void view_interactive_save_as(System_Functions *system, Models *models, Editing_File *file, String filename){ if (terminate_with_null(&filename)){ - file_save_and_set_names(system, &models->mem, &models->working_set, file, filename); + file_save_and_set_names(system, models, file, filename); } } @@ -3913,7 +3915,7 @@ internal void save_file_by_name(System_Functions *system, Models *models, String name){ Editing_File *file = working_set_name_contains(&models->working_set, name); if (file){ - save_file(system, &models->mem, file); + save_file(system, models, file); } } diff --git a/TODO.txt b/TODO.txt index 9e52d6d9..58bc2524 100644 --- a/TODO.txt +++ b/TODO.txt @@ -81,8 +81,6 @@ ; ; [] indication on failure to save ; [] history is broken, revist the entire system -; [] 8.0\Include\um\dsound.h (not reproduced, get more info) -; [] paste external text from bug report (in email) (not reproduced, get more info) ; ; [] view fails to follow cursor when the view is shrunk ; [] view fails to follow cursor after deleting long line @@ -91,10 +89,6 @@ ; BEFORE I SHIP ; -; [] decent options for indentation rules for text & presentation -; [] code file outlining -; [] alternate code completion plan -; ; TODOS ; [X] success message when compiler works @@ -181,12 +175,16 @@ ; [X] handle comments ; [X] additional width for nesting? ; [X] fix issues when relexing happens in parallel +; [X] improve code display mode so that it can auto-indent the text on save ; [] command for setting wrap positions in views directly ; [] ability to see the wrap position as a number/line and adjust graphically ; [] code level wrapping level 2 ; [] comment lead whitespace problem ; [] remeasure version of measure_wraps +; [] code file outlining +; [] alternate code completion plan + ; Buffer behavior cleanup ; [X] show all characters as \# if they can't be rendered ; [X] get the navigation working correctly around multi-glyph characters diff --git a/power/4coder_experiments.cpp b/power/4coder_experiments.cpp index 61cbccf9..c3cb21bf 100644 --- a/power/4coder_experiments.cpp +++ b/power/4coder_experiments.cpp @@ -620,6 +620,7 @@ get_bindings(void *data, int size){ set_hook(context, hook_view_size_change, my_view_adjust); set_open_file_hook(context, my_file_settings); + set_save_file_hook(context, my_file_save); set_input_filter(context, my_suppress_mouse_filter); set_command_caller(context, default_command_caller); diff --git a/project.4coder b/project.4coder index abac8e42..3bce181c 100644 --- a/project.4coder +++ b/project.4coder @@ -1,34 +1,35 @@ -extensions=".c.cpp.h.hpp"; +extensions=".c.cpp.h.hpp.bat.sh"; -fkey_command_wnd[1] = {"build.bat", "*compilation*"}; -fkey_command_wnd[2] = {"..\\misc\\run.bat", 0}; -fkey_command_wnd[3] = {"site\\build.bat", "*compilation*"}; -fkey_command_wnd[4] = {0, 0}; -fkey_command_wnd[5] = {0, 0}; -fkey_command_wnd[6] = {0, 0}; -fkey_command_wnd[7] = {0, 0}; -fkey_command_wnd[8] = {0, 0}; -fkey_command_wnd[9] = {0, 0}; -fkey_command_wnd[10] = {0, 0}; -fkey_command_wnd[11] = {0, 0}; -fkey_command_wnd[12] = {"package.bat", "*compilation*"}; -fkey_command_wnd[13] = {0, 0}; -fkey_command_wnd[14] = {0, 0}; -fkey_command_wnd[15] = {0, 0}; -fkey_command_wnd[16] = {0, 0}; +// NOTE(allen): There is some kind of bug where hitting f12 causes a crash. It looks like there is a special meaning placed on some fkeys by windows, which I have not found how to disable yet. +fkey_command_win[1] = {"build.bat", "*compilation*", true}; +fkey_command_win[2] = {"..\\misc\\run.bat", "*run*"}; +fkey_command_win[3] = {"site\\build.bat", "*compilation*", true}; +fkey_command_win[4] = {0, 0}; +fkey_command_win[5] = {0, 0}; +fkey_command_win[6] = {0, 0}; +fkey_command_win[7] = {0, 0}; +fkey_command_win[8] = {"package.bat", "*compilation*", true}; +fkey_command_win[9] = {0, 0}; +fkey_command_win[10] = {0, 0}; +fkey_command_win[11] = {0, 0}; +fkey_command_win[12] = {0, 0}; +fkey_command_win[13] = {0, 0}; +fkey_command_win[14] = {0, 0}; +fkey_command_win[15] = {0, 0}; +fkey_command_win[16] = {0, 0}; -fkey_command_linux[1] = {"make", "*compilation*"}; -fkey_command_linux[2] = {"../build/4ed", 0}; -fkey_command_linux[3] = {"site/build.sh", "*compilation*"}; +fkey_command_linux[1] = {"make", "*compilation*", true}; +fkey_command_linux[2] = {"../build/4ed", "*run*"}; +fkey_command_linux[3] = {"site/build.sh", "*compilation*", true}; fkey_command_linux[4] = {0, 0}; fkey_command_linux[5] = {0, 0}; fkey_command_linux[6] = {0, 0}; fkey_command_linux[7] = {0, 0}; -fkey_command_linux[8] = {0, 0}; +fkey_command_linux[8] = {"package.sh", "*compilation*", true}; fkey_command_linux[9] = {0, 0}; fkey_command_linux[10] = {0, 0}; fkey_command_linux[11] = {0, 0}; -fkey_command_linux[12] = {"package.sh", "*compilation*"}; +fkey_command_linux[12] = {0, 0}; fkey_command_linux[13] = {0, 0}; fkey_command_linux[14] = {0, 0}; fkey_command_linux[15] = {0, 0};