From 42f6a359d1639ff5b8d21de90b23156063609071 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Juhani=20Krekel=C3=A4?= Date: Sun, 31 Oct 2021 02:55:26 +0300 Subject: [PATCH] Add search to editor(1). --- editor/command.c | 9 +++++ editor/cursor.c | 11 ++++-- editor/display.c | 3 ++ editor/editor.1 | 4 ++- editor/editor.h | 2 ++ editor/modal.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 118 insertions(+), 4 deletions(-) diff --git a/editor/command.c b/editor/command.c index f85e9018..4028a55e 100644 --- a/editor/command.c +++ b/editor/command.c @@ -649,6 +649,14 @@ void editor_type_edit(struct editor* editor) editor->mode = MODE_EDIT; } +void editor_type_search(struct editor* editor) +{ + editor->mode = MODE_SEARCH; + editor->modal_used = 0; + editor->modal_cursor = 0; + editor->modal_error = false; +} + void editor_type_goto_line(struct editor* editor) { editor->mode = MODE_GOTO_LINE; @@ -849,6 +857,7 @@ void editor_type_character(struct editor* editor, wchar_t c) switch ( towlower(c) ) { case L'c': editor_type_copy(editor); break; + case L'f': editor_type_search(editor); break; case L'g': editor_type_goto_line(editor); break; case L'k': editor_type_cut(editor); break; case L'o': editor->shift ? diff --git a/editor/cursor.c b/editor/cursor.c index c378ee33..43e9d4fa 100644 --- a/editor/cursor.c +++ b/editor/cursor.c @@ -30,12 +30,17 @@ void editor_select_set(struct editor* editor, size_t y, size_t x) { assert(y < editor->lines_used); assert(x <= editor->lines[y].used); - if ( editor->viewport_height ) + size_t viewport_height = editor->viewport_height; + // Account for the modal shrinking the viewport by one line. + if ( editor->mode != MODE_EDIT && viewport_height ) + viewport_height--; + + if ( viewport_height ) { if ( y < editor->page_y_offset ) editor->page_y_offset = y; - if ( editor->page_y_offset + editor->viewport_height <= y ) - editor->page_y_offset = y + 1 - editor->viewport_height; + if ( editor->page_y_offset + viewport_height <= y ) + editor->page_y_offset = y + 1 - viewport_height; } editor->select_row = y; diff --git a/editor/display.c b/editor/display.c index 8b19e231..13ca7440 100644 --- a/editor/display.c +++ b/editor/display.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2013, 2014 Jonas 'Sortie' Termansen. + * Copyright (c) 2021 Juhani 'nortti' Krekelä. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -267,6 +268,8 @@ void render_editor(struct editor* editor, struct terminal_state* state) msg = "Go to line: "; if ( editor->mode == MODE_COMMAND ) msg = "Enter miscellaneous command: "; + if ( editor->mode == MODE_SEARCH ) + msg = "Search: "; struct terminal_datum* data_line = state->data + (state->height - 1) * state->width; wchar_t* wcs_msg = convert_mbs_to_wcs(msg); diff --git a/editor/editor.1 b/editor/editor.1 index 3be14110..f02270bf 100644 --- a/editor/editor.1 +++ b/editor/editor.1 @@ -1,4 +1,4 @@ -.Dd January 8, 2016 +.Dd December 13, 2021 .Dt EDITOR 1 .Os .Sh NAME @@ -26,6 +26,8 @@ It supports these keyboard shortcuts: .Bl -tag -width "12345768" .It Sy Ctrl-C Copy. +.It Sy Ctrl-F +Search for a regular expression. .It Sy Ctrl-G Go to line. .It Sy Ctrl-K diff --git a/editor/editor.h b/editor/editor.h index 78114fb0..442990b0 100644 --- a/editor/editor.h +++ b/editor/editor.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2013, 2014 Jonas 'Sortie' Termansen. + * Copyright (c) 2021 Juhani 'nortti' Krekelä. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -51,6 +52,7 @@ enum editor_mode MODE_ASK_QUIT, MODE_GOTO_LINE, MODE_COMMAND, + MODE_SEARCH, }; struct editor diff --git a/editor/modal.c b/editor/modal.c index b2c734d6..a59c1caf 100644 --- a/editor/modal.c +++ b/editor/modal.c @@ -18,12 +18,16 @@ * Modal commands. */ +#include + #include +#include #include #include #include #include #include +#include #include #include "command.h" @@ -351,6 +355,94 @@ void editor_modal_command_config(struct editor* editor, const char* cmd) editor_modal_line_numbering(editor, cmd); } +bool match_line(regex_t* regex, const wchar_t* line, size_t used, + bool start_of_line, size_t* start, size_t *end) +{ + if ( !used ) + return false; + + char* buffer = calloc(used, MB_CUR_MAX); + if ( !buffer ) + return false; + size_t buffer_used = 0; + mbstate_t ps = {0}; + for ( size_t i = 0; i < used; i++ ) + buffer_used += wcrtomb(&buffer[buffer_used], line[i], &ps); + + regmatch_t start_end[] = {{.rm_so = 0, .rm_eo = buffer_used}}; + int flags = start_of_line ? REG_STARTEND : REG_STARTEND | REG_NOTBOL; + int no_match = regexec(regex, buffer, 1, start_end, flags); + free(buffer); + if ( no_match ) + return false; + + char mb[MB_CUR_MAX]; + memset(&ps, 0, sizeof(ps)); + size_t wc_offset = 0; + regoff_t mb_offset = 0; + for ( ; mb_offset < start_end[0].rm_so; wc_offset++ ) + mb_offset += wcrtomb(mb, line[wc_offset], &ps); + *start = wc_offset; + for ( ; mb_offset < start_end[0].rm_eo; wc_offset++ ) + mb_offset += wcrtomb(mb, line[wc_offset], &ps); + *end = wc_offset; + return true; +} + +void editor_modal_search(struct editor* editor, const char* search) +{ + if ( !search[0] ) + { + editor_type_edit(editor); + return; + } + + regex_t regex; + if ( regcomp(®ex, search, REG_EXTENDED) ) + { + editor->modal_error = true; + return; + } + + size_t column = editor->cursor_column + 1; + if ( column < editor->lines[editor->cursor_row].used ) + { + const wchar_t* line = &editor->lines[editor->cursor_row].data[column]; + size_t length = editor->lines[editor->cursor_row].used - column; + size_t match, match_end; + if ( match_line(®ex, line, length, false, &match, &match_end) ) + { + editor_cursor_set(editor, editor->cursor_row, match + column); + editor_select_set(editor, editor->cursor_row, match_end + column); + regfree(®ex); + return; + } + } + + size_t line = editor->cursor_row + 1; + size_t remaining = editor->lines_used; + while ( remaining-- ) + { + if ( editor->lines_used <= line ) + line = 0; + + size_t match, match_end; + if ( match_line(®ex, editor->lines[line].data, + editor->lines[line].used, true, &match, &match_end) ) + { + editor_cursor_set(editor, line, match); + editor_select_set(editor, line, match_end); + regfree(®ex); + return; + } + line++; + } + + regfree(®ex); + + editor->modal_error = true; +} + void editor_modal_character(struct editor* editor, wchar_t c) { @@ -382,6 +474,7 @@ void editor_modal_character(struct editor* editor, wchar_t c) case MODE_ASK_QUIT: editor_modal_ask_quit(editor, param); break; case MODE_GOTO_LINE: editor_modal_goto_line(editor, param); break; case MODE_COMMAND: editor_modal_command(editor, param); break; + case MODE_SEARCH: editor_modal_search(editor, param); break; default: break; } free(param);