From c8b901e299d35fad2c9ac4d86e2f5b16007b3818 Mon Sep 17 00:00:00 2001 From: Allen Webster Date: Sat, 27 Feb 2016 17:23:40 -0500 Subject: [PATCH] added query helpers, replace functions --- 4coder_custom.cpp | 162 ++++++++++++++++++++++++++++++++-------------- 4coder_custom.h | 4 +- 4coder_helper.h | 88 +++++++++++++++++++++++++ 4ed.cpp | 29 +++++---- 4 files changed, 221 insertions(+), 62 deletions(-) diff --git a/4coder_custom.cpp b/4coder_custom.cpp index 13ff8a8f..2d15d326 100644 --- a/4coder_custom.cpp +++ b/4coder_custom.cpp @@ -223,7 +223,7 @@ CUSTOM_COMMAND_SIG(switch_to_compilation){ // to change the specific type of view and set files even when the view didn't // contain a file. view = app->get_active_file_view(app); - buffer = app->get_buffer_by_name(app, make_string(name, name_size)); + buffer = app->get_buffer_by_name(app, name, name_size); app->view_set_buffer(app, &view, buffer.buffer_id); } @@ -302,44 +302,16 @@ CUSTOM_COMMAND_SIG(switch_to_file_in_quotes){ } CUSTOM_COMMAND_SIG(goto_line){ - User_Input in; - Query_Bar bar; - char string_space[256]; int line_number; + String input; + char string_space[256]; - // NOTE(allen|a3.4.4): It will not cause an *error* if we continue on after failing to. - // start a query bar, but it will be unusual behavior from the point of view of the - // user, if this command starts intercepting input even though no prompt is shown. - // This will only happen if you have a lot of bars open already or if the current view - // doesn't support query bars. - if (app->start_query_bar(app, &bar, 0) == 0) return; - - // NOTE(allen|a3.4.4): The application side is storing a pointer straight to your Query_Bar - // any change you make to it will be reflected in what the application renders. The application - // also makes sure that it destroys all query bars whenever a command exists or an abort - // mesasge is sent to it. - bar.prompt = make_lit_string("Goto Line: "); - bar.string = make_fixed_width_string(string_space); - - while (1){ - in = app->get_user_input(app, EventOnAnyKey, EventOnEsc | EventOnButton); - if (in.abort) break; - if (in.type == UserInputKey){ - if (in.key.character >= '0' && in.key.character <= '9'){ - append(&bar.string, in.key.character); - } - else if (in.key.keycode == key_back){ - --bar.string.size; - } - else if (in.key.keycode == '\n' || in.key.keycode == '\t'){ - break; - } - } + input = make_fixed_width_string(string_space); + + if (query_user_number(app, make_lit_string("Goto Line: "), &input)){ + line_number = str_to_int(input); + active_view_to_line(app, line_number); } - if (in.abort) return; - - line_number = str_to_int(bar.string); - active_view_to_line(app, line_number); } CUSTOM_COMMAND_SIG(search); @@ -371,12 +343,17 @@ isearch(Application_Links *app, int start_reversed){ String rsearch = make_lit_string("Reverse-I-Search: "); while (1){ + // NOTE(allen): Change the bar's prompt to match the current direction. if (reverse) bar.prompt = rsearch; else bar.prompt = isearch; in = app->get_user_input(app, EventOnAnyKey, EventOnEsc | EventOnButton); if (in.abort) break; + // NOTE(allen): If we're getting mouse events here it's a 4coder bug, because we + // only asked to intercept key events. + assert(in.type == UserInputKey); + int made_change = 0; if (in.key.keycode == '\n' || in.key.keycode == '\t'){ break; @@ -413,22 +390,33 @@ isearch(Application_Links *app, int start_reversed){ if (in.key.keycode != key_back){ int new_pos; if (reverse){ - app->buffer_seek_string(app, &buffer, start_pos - 1, bar.string, 0, &new_pos); - if (step_backward){ - pos = new_pos; - start_pos = new_pos; - app->buffer_seek_string(app, &buffer, start_pos - 1, bar.string, 0, &new_pos); + app->buffer_seek_string(app, &buffer, start_pos - 1, bar.string.str, bar.string.size, 0, &new_pos); + if (new_pos >= 0){ + if (step_backward){ + pos = new_pos; + start_pos = new_pos; + app->buffer_seek_string(app, &buffer, start_pos - 1, bar.string.str, bar.string.size, 0, &new_pos); + if (new_pos < 0) new_pos = start_pos; + } + match.start = new_pos; + match.end = match.start + bar.string.size; } } else{ - app->buffer_seek_string(app, &buffer, start_pos + 1, bar.string, 1, &new_pos); - if (step_forward){ - pos = new_pos; - start_pos = new_pos; - app->buffer_seek_string(app, &buffer, start_pos + 1, bar.string, 1, &new_pos); + app->buffer_seek_string(app, &buffer, start_pos + 1, bar.string.str, bar.string.size, 1, &new_pos); + if (new_pos < buffer.size){ + if (step_forward){ + pos = new_pos; + start_pos = new_pos; + app->buffer_seek_string(app, &buffer, start_pos + 1, bar.string.str, bar.string.size, 1, &new_pos); + if (new_pos >= buffer.size) new_pos = start_pos; + } + match.start = new_pos; + match.end = match.start + bar.string.size; } } - match.start = new_pos; + } + else{ match.end = match.start + bar.string.size; } @@ -448,6 +436,84 @@ CUSTOM_COMMAND_SIG(reverse_search){ isearch(app, 1); } +CUSTOM_COMMAND_SIG(replace_in_range){ + char replace_space[1024]; + String replace = make_fixed_width_string(replace_space); + + char with_space[1024]; + String with = make_fixed_width_string(with_space);; + + if (!query_user_string(app, make_lit_string("Replace: "), &replace)) return; + if (!query_user_string(app, make_lit_string("With: "), &with)) return; + + Buffer_Summary buffer; + File_View_Summary view; + + view = app->get_active_file_view(app); + buffer = app->get_buffer(app, view.buffer_id); + + Range range = get_range(&view); + + int pos, new_pos; + pos = range.min; + app->buffer_seek_string(app, &buffer, pos, replace.str, replace.size, 1, &new_pos); + + while (new_pos < range.end){ + app->buffer_replace_range(app, &buffer, new_pos, new_pos + replace.size, with.str, with.size); + pos = new_pos + with.size; + app->buffer_seek_string(app, &buffer, pos, replace.str, replace.size, 1, &new_pos); + } +} + +CUSTOM_COMMAND_SIG(query_replace){ + char replace_space[1024]; + String replace = make_fixed_width_string(replace_space); + + char with_space[1024]; + String with = make_fixed_width_string(with_space);; + + if (!query_user_string(app, make_lit_string("Replace: "), &replace)) return; + if (!query_user_string(app, make_lit_string("With: "), &with)) return; + + Query_Bar bar; + Buffer_Summary buffer; + File_View_Summary view; + int pos, new_pos; + + bar.prompt = make_lit_string("Replace? (y)es, (n)ext, (esc)\n"); + bar.string = {}; + + view = app->get_active_file_view(app); + buffer = app->get_buffer(app, view.buffer_id); + + pos = view.cursor.pos; + app->buffer_seek_string(app, &buffer, pos, replace.str, replace.size, 1, &new_pos); + + User_Input in = {}; + while (new_pos < buffer.size){ + Range match = make_range(new_pos, new_pos + replace.size); + app->view_set_highlight(app, &view, match.min, match.max, 1); + + in = app->get_user_input(app, EventOnAnyKey, EventOnButton); + if (in.abort || in.key.keycode == key_esc) break; + + if (in.key.character == 'y' || in.key.character == 'Y' || in.key.character == '\n' || in.key.character == '\t'){ + app->buffer_replace_range(app, &buffer, match.min, match.max, with.str, with.size); + pos = match.start + with.size; + } + else{ + pos = match.max; + } + + app->buffer_seek_string(app, &buffer, pos, replace.str, replace.size, 1, &new_pos); + } + + app->view_set_highlight(app, &view, 0, 0, 0); + if (in.abort) return; + + app->view_set_cursor(app, &view, seek_pos(pos), 1); +} + CUSTOM_COMMAND_SIG(open_in_other){ exec_command(app, cmdid_change_active_panel); exec_command(app, cmdid_interactive_open); @@ -678,6 +744,8 @@ extern "C" GET_BINDING_DATA(get_bindings){ bind(context, 'f', MDFR_CTRL, search); bind(context, 'r', MDFR_CTRL, reverse_search); bind(context, 'g', MDFR_CTRL, goto_line); + bind(context, 'q', MDFR_CTRL, query_replace); + bind(context, 'a', MDFR_CTRL, replace_in_range); bind(context, 'K', MDFR_CTRL, cmdid_kill_buffer); bind(context, 'O', MDFR_CTRL, cmdid_reopen); diff --git a/4coder_custom.h b/4coder_custom.h index 7c9d21ff..68283a13 100644 --- a/4coder_custom.h +++ b/4coder_custom.h @@ -183,11 +183,11 @@ struct Application_Links; #define GET_BUFFER_MAX_INDEX_SIG(name) int name(Application_Links *context) #define GET_BUFFER_SIG(name) Buffer_Summary name(Application_Links *context, int index) #define GET_ACTIVE_BUFFER_SIG(name) Buffer_Summary name(Application_Links *context) -#define GET_BUFFER_BY_NAME(name) Buffer_Summary name(Application_Links *context, String filename) +#define GET_BUFFER_BY_NAME(name) Buffer_Summary name(Application_Links *context, char *filename, int len) #define REFRESH_BUFFER_SIG(name) int name(Application_Links *context, Buffer_Summary *buffer) #define BUFFER_SEEK_DELIMITER_SIG(name) int name(Application_Links *context, Buffer_Summary *buffer, int start, char delim, int seek_forward, int *out) -#define BUFFER_SEEK_STRING_SIG(name) int name(Application_Links *context, Buffer_Summary *buffer, int start, String string, int seek_forward, int *out) +#define BUFFER_SEEK_STRING_SIG(name) int name(Application_Links *context, Buffer_Summary *buffer, int start, char *str, int len, int seek_forward, int *out) #define BUFFER_READ_RANGE_SIG(name) int name(Application_Links *context, Buffer_Summary *buffer, int start, int end, char *out) #define BUFFER_REPLACE_RANGE_SIG(name) int name(Application_Links *context, Buffer_Summary *buffer, int start, int end, char *str, int len) #define BUFFER_SAVE_SIG(name) int name(Application_Links *context, Buffer_Summary *buffer, char *filename, int len) diff --git a/4coder_helper.h b/4coder_helper.h index 78ae50cc..a567677f 100644 --- a/4coder_helper.h +++ b/4coder_helper.h @@ -250,3 +250,91 @@ key_is_unmodified(Key_Event_Data *key){ return(unmodified); } +static int +query_user_general(Application_Links *app, String prompt, String *string, int force_number){ + User_Input in; + Query_Bar bar; + int success = 1; + int good_character = 0; + + // NOTE(allen|a3.4.4): It will not cause an *error* if we continue on after failing to. + // start a query bar, but it will be unusual behavior from the point of view of the + // user, if this command starts intercepting input even though no prompt is shown. + // This will only happen if you have a lot of bars open already or if the current view + // doesn't support query bars. + if (app->start_query_bar(app, &bar, 0) == 0) return 0; + + // NOTE(allen|a3.4.4): The application side is storing a pointer straight to your Query_Bar + // any change you make to it will be reflected in what the application renders. The application + // also makes sure that it destroys all query bars whenever a command exits. + bar.prompt = prompt; + bar.string = *string; + + while (1){ + // NOTE(allen|a3.4.4): This call will block until the user does one of the input + // types specified in the flags. The first set of flags are inputs you'd like to intercept + // that you don't want to abort on. The second set are inputs that you'd like to cause + // the command to abort. If an event satisfies both flags, it is treated as an abort. + in = app->get_user_input(app, EventOnAnyKey, EventOnEsc | EventOnButton); + + // NOTE(allen|a3.4.4): The responsible thing to do on abort is to end the command + // without waiting on get_user_input again. + if (in.abort){ + success = 0; + break; + } + + good_character = 0; + if (key_is_unmodified(&in.key)){ + if (force_number){ + if (in.key.character >= '0' && in.key.character <= '9'){ + good_character = 1; + } + } + else{ + if (in.key.character != 0){ + good_character = 1; + } + } + } + + // NOTE(allen|a3.4.4): All we have to do to update what is shown on the query bar + // is to edit or local Query_Bar struct! This is handy because it means our Query_Bar + // can double as storing the state of the input AND as the source for the UI. + if (in.type == UserInputKey){ + if (in.key.keycode == '\n' || in.key.keycode == '\t'){ + break; + } + else if (in.key.keycode == key_back){ + --bar.string.size; + } + else if (good_character){ + append(&bar.string, in.key.character); + } + } + } + + // NOTE(allen|a3.4.4): It is not always necessary to end your query bars, because + // 4coder will clean them up when the command exits anyway, but because this is + // a local scope we should clean up the bar manually because the pointer the application + // stores to our Query_Bar is about to go bad. + app->end_query_bar(app, &bar, 0); + + if (success){ + *string = bar.string; + } + + return(success); +} + +inline int +query_user_string(Application_Links *app, String prompt, String *string){ + int success = query_user_general(app, prompt, string, 0); + return(success); +} + +inline int +query_user_number(Application_Links *app, String prompt, String *string){ + int success = query_user_general(app, prompt, string, 1); + return(success); +} diff --git a/4ed.cpp b/4ed.cpp index 5c27870c..4c651814 100644 --- a/4ed.cpp +++ b/4ed.cpp @@ -2065,7 +2065,7 @@ extern "C"{ Buffer_Summary buffer = {}; working_set = cmd->working_set; - if (table_find(&working_set->table, filename, &index)){ + if (table_find(&working_set->table, make_string(filename, len), &index)){ file = working_set->files + index; if (!file->state.is_dummy && file_is_ready(file)){ fill_buffer_summary(&buffer, file, working_set); @@ -2094,17 +2094,19 @@ extern "C"{ file = working_set->files + buffer->buffer_id; if (!file->state.is_dummy && file_is_ready(file)){ size = buffer_size(&file->state.buffer); - if (start >= 0 && start < size){ - result = 1; + result = 1; + + if (start < 0 && !seek_forward) *out = start; + else if (start >= size && seek_forward) *out = start; + else{ if (seek_forward){ *out = buffer_seek_delimiter(&file->state.buffer, start, delim); } else{ *out = buffer_reverse_seek_delimiter(&file->state.buffer, start, delim); } - if (*out < 0) *out = 0; - if (*out > size) *out = size; } + fill_buffer_summary(buffer, file, working_set); } } @@ -2127,19 +2129,20 @@ extern "C"{ file = working_set->files + buffer->buffer_id; if (!file->state.is_dummy && file_is_ready(file)){ size = buffer_size(&file->state.buffer); - if (start >= 0 && start < size){ + + if (start < 0 && !seek_forward) *out = start; + else if (start >= size && seek_forward) *out = start; + else{ part = &cmd->mem->part; temp = begin_temp_memory(part); - spare = push_array(part, char, string.size); + spare = push_array(part, char, len); result = 1; if (seek_forward){ - *out = buffer_find_string(&file->state.buffer, start, size, string.str, string.size, spare); + *out = buffer_find_string(&file->state.buffer, start, size, str, len, spare); } else{ - *out = buffer_rfind_string(&file->state.buffer, start, string.str, string.size, spare); + *out = buffer_rfind_string(&file->state.buffer, start, str, len, spare); } - if (*out < 0) *out = 0; - if (*out > size) *out = size; end_temp_memory(temp); } fill_buffer_summary(buffer, file, working_set); @@ -2647,7 +2650,7 @@ app_hardcode_styles(App_Vars *vars){ style->main.file_info_style = file_info_style; style->font_changed = 1; ++style; - + ///////////////// *style = *(style-1); style_set_name(style, make_lit_string("4coder-mono")); @@ -2689,7 +2692,7 @@ app_hardcode_styles(App_Vars *vars){ file_info_style.bar_color = 0xFFCACACA; file_info_style.bar_active_color = 0xFFA8A8A8; file_info_style.base_color = 0xFF000000; - file_info_style.pop1_color = 0xFF1504CF; + file_info_style.pop1_color = 0xFF03CF0C; file_info_style.pop2_color = 0xFFFF0000; style->main.file_info_style = file_info_style; style->font_changed = 1;