From abec84f7b2ee0f75c4622a6d4469dbf0c7eaf5ad Mon Sep 17 00:00:00 2001 From: Leo Tenenbaum Date: Mon, 8 Feb 2021 11:33:43 -0500 Subject: find show number of matches --- buffer.c | 11 ++++++++++- find.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++------------ math.c | 2 ++ ted.h | 2 ++ 4 files changed, 66 insertions(+), 13 deletions(-) diff --git a/buffer.c b/buffer.c index 2b23fe7..b00f122 100644 --- a/buffer.c +++ b/buffer.c @@ -1937,6 +1937,15 @@ void buffer_check_valid(TextBuffer *buffer) { } #endif +u32 buffer_first_rendered_line(TextBuffer *buffer) { + return (u32)buffer->scroll_y; +} + +u32 buffer_last_rendered_line(TextBuffer *buffer) { + u32 line = buffer_first_rendered_line(buffer) + (u32)buffer_display_lines(buffer) + 1; + return clamp_u32(line, 0, buffer->nlines); +} + // Render the text buffer in the given rectangle void buffer_render(TextBuffer *buffer, Rect r) { float x1, y1, x2, y2; @@ -1956,7 +1965,7 @@ void buffer_render(TextBuffer *buffer, Rect r) { float const padding = settings->padding; float const border_thickness = settings->border_thickness; - u32 start_line = (u32)buffer->scroll_y; // line to start rendering from + u32 start_line = buffer_first_rendered_line(buffer); // line to start rendering from Rect bounding_box = rect4(x1, y1, x2, y2); float render_start_y = y1 - (float)(buffer->scroll_y - start_line) * char_height; // where the 1st line is rendered diff --git a/find.c b/find.c index 5bddd0e..0d7b2c4 100644 --- a/find.c +++ b/find.c @@ -3,6 +3,8 @@ static void find_open(Ted *ted) { ted->prev_active_buffer = ted->active_buffer; ted->active_buffer = &ted->find_buffer; ted->find = true; + ted->find_match_count = 0; + buffer_clear(&ted->find_buffer); } } @@ -35,10 +37,12 @@ static void find_menu_frame(Ted *ted) { u32 const *colors = settings->colors; bool invalid_search_term = false; - TextBuffer *buffer = ted->prev_active_buffer; + TextBuffer *buffer = ted->prev_active_buffer, *find_buffer = &ted->find_buffer; assert(buffer); + + - String32 term = buffer_get_line(&ted->find_buffer, 0); + String32 term = buffer_get_line(find_buffer, 0); if (term.len) { pcre2_match_data *match_data = pcre2_match_data_create(FIND_MAX_GROUPS, NULL); if (match_data) { @@ -46,10 +50,31 @@ static void find_menu_frame(Ted *ted) { // compile search term int error = 0; PCRE2_SIZE error_pos = 0; pcre2_code *code = pcre2_compile(term.str, term.len, PCRE2_LITERAL, &error, &error_pos, NULL); - - // @TODO: scroll to first match if (code) { - for (u32 line_idx = 0, nlines = buffer->nlines; line_idx < nlines; ++line_idx) { + if (find_buffer->modified) { // if search term has been changed, + // recompute match count + u32 match_count = 0; + for (u32 line_idx = 0, end = buffer->nlines; line_idx < end; ++line_idx) { + Line *line = &buffer->lines[line_idx]; + char32_t *str = line->str; + u32 len = line->len; + u32 start_index = 0; + while (start_index < len) { + int ret = pcre2_match(code, str, len, start_index, 0, match_data, NULL); + if (ret > 0) { + u32 match_end = (u32)groups[1]; + ++match_count; + start_index = match_end; + } else break; + } + } + ted->find_match_count = match_count; + find_buffer->modified = false; + } + + // highlight matches + for (u32 line_idx = buffer_first_rendered_line(buffer), end = buffer_last_rendered_line(buffer); + line_idx < end; ++line_idx) { Line *line = &buffer->lines[line_idx]; char32_t *str = line->str; u32 len = line->len; @@ -75,33 +100,48 @@ static void find_menu_frame(Ted *ted) { } pcre2_match_data_free(match_data); } + } else { + ted->find_match_count = 0; } - + + float x1 = padding, y1 = window_height - menu_height, x2 = window_width - padding, y2 = window_height - padding; + Rect menu_bounds = rect4(x1, y1, x2, y2); + Rect find_buffer_bounds = rect4(x1 + padding, y1 + char_height_bold, x2 - padding, y1 + char_height_bold + char_height); + gl_geometry_rect(menu_bounds, colors[COLOR_MENU_BG]); gl_geometry_rect_border(menu_bounds, 1, colors[COLOR_BORDER]); - + { + float w = 0, h = 0; + char str[32]; + strbuf_printf(str, U32_FMT " matches", ted->find_match_count); + text_get_size(font, str, &w, &h); + text_utf8(font, str, x2 - (w + padding), rect_ymid(find_buffer_bounds) - h * 0.5f, colors[COLOR_TEXT]); + x2 -= w + padding; + find_buffer_bounds.size.x -= w + padding; + } text_utf8(font_bold, "Find...", x1 + padding, y1, colors[COLOR_TEXT]); - text_render(font_bold); y1 += char_height_bold; - - Rect find_buffer_bounds = rect4(x1 + padding, y1, x2 - padding, y1 + char_height); - buffer_render(&ted->find_buffer, find_buffer_bounds); - + if (invalid_search_term) { gl_geometry_rect(find_buffer_bounds, colors[COLOR_ERROR_BG] & 0xFFFFFF7F); } gl_geometry_draw(); + text_render(font_bold); + + buffer_render(&ted->find_buffer, find_buffer_bounds); + } // go to next find result static void find_next(Ted *ted) { TextBuffer *buffer = ted->prev_active_buffer; + // create match data pcre2_match_data *match_data = pcre2_match_data_create(FIND_MAX_GROUPS, NULL); if (match_data) { diff --git a/math.c b/math.c index b833872..e6e90f3 100644 --- a/math.c +++ b/math.c @@ -657,6 +657,8 @@ static float rect_x1(Rect r) { return r.pos.x; } static float rect_y1(Rect r) { return r.pos.y; } static float rect_x2(Rect r) { return r.pos.x + r.size.x; } static float rect_y2(Rect r) { return r.pos.y + r.size.y; } +static float rect_xmid(Rect r) { return r.pos.x + r.size.x * 0.5f; } +static float rect_ymid(Rect r) { return r.pos.y + r.size.y * 0.5f; } static void rect_coords(Rect r, float *x1, float *y1, float *x2, float *y2) { *x1 = r.pos.x; diff --git a/ted.h b/ted.h index 9b2c18b..3694858 100644 --- a/ted.h +++ b/ted.h @@ -190,6 +190,7 @@ typedef struct Node { #define TED_MAX_NODES 256 // max tabs per node #define TED_MAX_TABS 100 + typedef struct Ted { SDL_Window *window; Font *font_bold; @@ -218,6 +219,7 @@ typedef struct Ted { bool search_cwd; // should the working directory be searched for files? set to true if the executable isn't "installed" bool quit; // if set to true, the window will close next frame. NOTE: this doesn't check for unsaved changes!! bool find; // is the find menu open? + u32 find_match_count; // how many matches of the search term were there? Command warn_unsaved; // if non-zero, the user is trying to execute this command, but there are unsaved changes char warn_unsaved_names[TED_PATH_MAX]; // comma-separated list of files with unsaved changes (only applicable if warn_unsaved != 0) char warn_overwrite[TED_PATH_MAX]; // file name user is trying to overwrite -- cgit v1.2.3