From b0506f2a667d0c1f4bddf7429068a4ee2042c039 Mon Sep 17 00:00:00 2001 From: Leo Tenenbaum Date: Sun, 7 Feb 2021 19:49:53 -0500 Subject: simple find menu --- command.c | 14 +++++++---- find.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- main.c | 6 ++++- menu.c | 1 + ted.h | 1 + 5 files changed, 96 insertions(+), 8 deletions(-) diff --git a/command.c b/command.c index 5c6ae29..0dd2b34 100644 --- a/command.c +++ b/command.c @@ -104,9 +104,15 @@ void command_execute(Ted *ted, Command c, i64 argument) { if (!buffer) { } else if (buffer->is_line_buffer) { switch (ted->menu) { + case MENU_NONE: + if (ted->find) { + if (buffer == &ted->find_buffer) { + find_next(ted); + } + } + break; case MENU_ASK_RELOAD: case MENU_WARN_UNSAVED: - case MENU_NONE: break; case MENU_OPEN: case MENU_SAVE_AS: { @@ -252,9 +258,7 @@ void command_execute(Ted *ted, Command c, i64 argument) { break; case CMD_FIND: - ted->find = true; - buffer_clear(&ted->find_buffer); - ted->active_buffer = &ted->find_buffer; + find_open(ted); break; case CMD_ESCAPE: @@ -263,6 +267,8 @@ void command_execute(Ted *ted, Command c, i64 argument) { *ted->error_shown = '\0'; } else if (ted->menu) { menu_escape(ted); + } else if (ted->find) { + find_close(ted); } else if (buffer) { buffer_disable_selection(buffer); } diff --git a/find.c b/find.c index 85816ba..309923d 100644 --- a/find.c +++ b/find.c @@ -1,3 +1,14 @@ +static void find_open(Ted *ted) { + ted->prev_active_buffer = ted->active_buffer; + ted->active_buffer = &ted->find_buffer; + ted->find = true; +} + +static void find_close(Ted *ted) { + ted->find = false; + ted->active_buffer = ted->prev_active_buffer; +} + static float find_menu_height(Ted *ted) { Font *font = ted->font, *font_bold = ted->font_bold; float char_height = text_font_char_height(font), @@ -5,7 +16,7 @@ static float find_menu_height(Ted *ted) { Settings const *settings = &ted->settings; float padding = settings->padding; - return char_height_bold + char_height + 4 * padding; + return char_height_bold + char_height + 2 * padding; } static void find_menu_frame(Ted *ted) { @@ -19,10 +30,75 @@ static void find_menu_frame(Ted *ted) { float const window_width = ted->window_width, window_height = ted->window_height; u32 const *colors = settings->colors; - Rect menu_bounds = rect(V2(padding, window_height - menu_height), V2(window_width - 2*padding, menu_height - padding)); + float x1 = padding, y1 = window_height - menu_height, x2 = window_width - padding, y2 = window_height - padding; + Rect menu_bounds = rect4(x1, y1, x2, y2); gl_geometry_rect_border(menu_bounds, 1, colors[COLOR_BORDER]); gl_geometry_draw(); - (void)char_height; (void)char_height_bold; + + text_utf8(font_bold, "Find...", x1 + padding, y1, colors[COLOR_TEXT]); + text_render(font_bold); + + y1 += char_height_bold; + + buffer_render(&ted->find_buffer, rect4(x1 + padding, y1, x2 - padding, y1 + char_height)); +} + +#define FIND_MAX_GROUPS 100 + +// go to next find result +static void find_next(Ted *ted) { + TextBuffer *buffer = ted->prev_active_buffer; + if (buffer) { + // create match data + pcre2_match_data *match_data = pcre2_match_data_create(FIND_MAX_GROUPS, NULL); + if (match_data) { + String32 term = buffer_get_line(&ted->find_buffer, 0); + int error = 0; + size_t error_pos = 0; + // compile the search term + pcre2_code *code = pcre2_compile(term.str, term.len, PCRE2_LITERAL, &error, &error_pos, NULL); + if (code) { + // do the searching + BufferPos pos = buffer->cursor_pos; + size_t nsearches = 0; + u32 nlines = buffer->nlines; + + // we need to search the starting line twice, because we might start at a non-zero index + while (nsearches < nlines + 1) { + Line *line = &buffer->lines[pos.line]; + char32_t *str = line->str; + size_t len = line->len; + int ret = pcre2_match(code, str, len, pos.index, 0, match_data, NULL); + if (ret > 0) { + PCRE2_SIZE *groups = pcre2_get_ovector_pointer(match_data); + u32 match_start = (u32)groups[0]; + u32 match_end = (u32)groups[1]; + BufferPos pos_start = {.line = pos.line, .index = match_start}; + BufferPos pos_end = {.line = pos.line, .index = match_end}; + buffer_cursor_move_to_pos(buffer, pos_start); + buffer_select_to_pos(buffer, pos_end); + break; + } + + ++nsearches; + pos.index = 0; + pos.line += 1; + pos.line %= nlines; + } + } else { + char32_t buf[256] = {0}; + size_t len = (size_t)pcre2_get_error_message(error, buf, sizeof buf - 1); + char *error_cstr = str32_to_utf8_cstr(str32(buf, len)); + if (error_cstr) { + ted_seterr(ted, "Invalid search term: (at position %zu) %s.", (size_t)error_pos, error_cstr); + free(error_cstr); + } + } + pcre2_match_data_free(match_data); + } else { + ted_seterr(ted, "Out of memory."); + } + } } diff --git a/main.c b/main.c index e6deb96..0090071 100644 --- a/main.c +++ b/main.c @@ -46,12 +46,12 @@ no_warn_end #include "syntax.c" #include "buffer.c" #include "ted.c" +#include "find.c" #include "ui.c" #include "node.c" #include "menu.c" #include "command.c" #include "config.c" -#include "find.c" #if PROFILE #define PROFILE_TIME(var) double var = time_get_seconds(); @@ -305,6 +305,8 @@ int main(int argc, char **argv) { if (buffer_haserr(lbuffer)) die("Error creating line buffer: %s", buffer_geterr(lbuffer)); } + line_buffer_create(&ted->find_buffer, ted); + line_buffer_create(&ted->replace_buffer, ted); { @@ -693,6 +695,8 @@ int main(int argc, char **argv) { if (ted->nodes_used[i]) node_free(&ted->nodes[i]); buffer_free(&ted->line_buffer); + buffer_free(&ted->find_buffer); + buffer_free(&ted->replace_buffer); text_font_free(ted->font); text_font_free(ted->font_bold); settings_free(&ted->settings); diff --git a/menu.c b/menu.c index 60bdd7a..c4502e0 100644 --- a/menu.c +++ b/menu.c @@ -1,4 +1,5 @@ static void menu_open(Ted *ted, Menu menu) { + if (ted->find) find_close(ted); ted->menu = menu; ted->prev_active_buffer = ted->active_buffer; ted->active_buffer = NULL; diff --git a/ted.h b/ted.h index f1570e5..9b2c18b 100644 --- a/ted.h +++ b/ted.h @@ -212,6 +212,7 @@ typedef struct Ted { FileSelector file_selector; TextBuffer line_buffer; // general-purpose line buffer for inputs -- used for menus TextBuffer find_buffer; // use for "find" term in find/find+replace + TextBuffer replace_buffer; // "replace" for find+replace double error_time; // time error box was opened (in seconds -- see time_get_seconds) KeyAction key_actions[KEY_COMBO_COUNT]; bool search_cwd; // should the working directory be searched for files? set to true if the executable isn't "installed" -- cgit v1.2.3