added query helpers, replace functions
parent
16108d0f57
commit
c8b901e299
|
@ -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;
|
||||
input = make_fixed_width_string(string_space);
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (in.abort) return;
|
||||
|
||||
line_number = str_to_int(bar.string);
|
||||
if (query_user_number(app, make_lit_string("Goto Line: "), &input)){
|
||||
line_number = str_to_int(input);
|
||||
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,24 +390,35 @@ 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);
|
||||
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, 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) 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);
|
||||
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, 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) new_pos = start_pos;
|
||||
}
|
||||
match.start = new_pos;
|
||||
match.end = match.start + bar.string.size;
|
||||
}
|
||||
}
|
||||
}
|
||||
else{
|
||||
match.end = match.start + bar.string.size;
|
||||
}
|
||||
|
||||
app->view_set_highlight(app, &view, match.start, match.end, 1);
|
||||
}
|
||||
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
25
4ed.cpp
25
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;
|
||||
|
||||
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);
|
||||
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue