From 0b524a94a5e34148716832f1b6cada02e35369b0 Mon Sep 17 00:00:00 2001 From: Albert Cervin Date: Mon, 12 Feb 2024 16:28:37 +0100 Subject: Improve word deletion Now it only deletes the word under dot. --- src/dged/buffer.c | 19 +++++++++++++++++++ src/dged/buffer.h | 11 +++++++++++ src/dged/buffer_view.c | 17 +++++------------ src/dged/buffer_view.h | 3 +-- src/main/bindings.c | 1 - src/main/cmds.c | 6 ++---- test/buffer.c | 33 ++++++++++++++++++++++++++++++++- 7 files changed, 70 insertions(+), 20 deletions(-) diff --git a/src/dged/buffer.c b/src/dged/buffer.c index a512c60..749dba7 100644 --- a/src/dged/buffer.c +++ b/src/dged/buffer.c @@ -328,6 +328,11 @@ static struct match_result find_prev_in_line(struct buffer *buffer, --bytei; } + // first byte on line can also be a match + if (predicate(line.text[bytei])) { + found = true; + } + uint32_t target_col = text_byteindex_to_col(buffer->text, start.line, bytei); return (struct match_result){ .at = (struct location){.line = start.line, .col = target_col}, @@ -595,6 +600,20 @@ struct location buffer_next_char(struct buffer *buffer, struct location dot) { return dot; } +struct region buffer_word_at(struct buffer *buffer, struct location at) { + struct match_result prev_word_break = + find_prev_in_line(buffer, at, is_word_break); + struct match_result next_word_break = + find_next_in_line(buffer, at, is_word_break); + + if (prev_word_break.at.col != next_word_break.at.col && + prev_word_break.found) { + moveh(buffer, 1, &prev_word_break.at); + } + + return region_new(prev_word_break.at, next_word_break.at); +} + struct location buffer_next_word(struct buffer *buffer, struct location dot) { struct match_result res = find_next_in_line(buffer, dot, is_word_break); if (!res.found) { diff --git a/src/dged/buffer.h b/src/dged/buffer.h index 2e71fb3..3cb8d03 100644 --- a/src/dged/buffer.h +++ b/src/dged/buffer.h @@ -244,6 +244,17 @@ struct location buffer_next_word(struct buffer *buffer, struct location dot); */ struct location buffer_next_line(struct buffer *buffer, struct location dot); +/** + * Get the extents of the word located at @ref at. + * + * @param [in] buffer The buffer to look in. + * @param [in] at The location to start from. + * + * @returns The extent of the closest word as a region. If + * there is no word, the region will be zero-sized. + */ +struct region buffer_word_at(struct buffer *buffer, struct location at); + /** * Clamp a buffer position to the boundaries of the buffer. * diff --git a/src/dged/buffer_view.c b/src/dged/buffer_view.c index aeee2f6..15aa812 100644 --- a/src/dged/buffer_view.c +++ b/src/dged/buffer_view.c @@ -186,24 +186,17 @@ void buffer_view_backward_delete_char(struct buffer_view *view) { region_new(buffer_previous_char(view->buffer, view->dot), view->dot)); } -void buffer_view_forward_delete_word(struct buffer_view *view) { +void buffer_view_delete_word(struct buffer_view *view) { if (maybe_delete_region(view)) { return; } - view->dot = buffer_delete( - view->buffer, - region_new(view->dot, buffer_next_word(view->buffer, view->dot))); -} + struct region word = buffer_word_at(view->buffer, view->dot); -void buffer_view_backward_delete_word(struct buffer_view *view) { - if (maybe_delete_region(view)) { - return; + if (region_has_size(word)) { + buffer_delete(view->buffer, word); + view->dot = word.begin; } - - view->dot = buffer_delete( - view->buffer, - region_new(buffer_previous_word(view->buffer, view->dot), view->dot)); } void buffer_view_kill_line(struct buffer_view *view) { diff --git a/src/dged/buffer_view.h b/src/dged/buffer_view.h index 1c8fa6a..620c261 100644 --- a/src/dged/buffer_view.h +++ b/src/dged/buffer_view.h @@ -62,8 +62,7 @@ void buffer_view_backward_nlines(struct buffer_view *view, uint32_t nlines); void buffer_view_forward_delete_char(struct buffer_view *view); void buffer_view_backward_delete_char(struct buffer_view *view); -void buffer_view_forward_delete_word(struct buffer_view *view); -void buffer_view_backward_delete_word(struct buffer_view *view); +void buffer_view_delete_word(struct buffer_view *view); void buffer_view_kill_line(struct buffer_view *view); diff --git a/src/main/bindings.c b/src/main/bindings.c index ad63974..9038f37 100644 --- a/src/main/bindings.c +++ b/src/main/bindings.c @@ -55,7 +55,6 @@ void set_default_buffer_bindings(struct keymap *keymap) { BINDING(DELETE, "delete-char"), BINDING(Ctrl, 'D', "delete-char"), BINDING(Meta, 'd', "delete-word"), - BINDING(Meta, 'D', "backward-delete-word"), BINDING(BACKSPACE, "backward-delete-char"), BINDING(Ctrl, '@', "set-mark"), diff --git a/src/main/cmds.c b/src/main/cmds.c index 838f2ea..e24643e 100644 --- a/src/main/cmds.c +++ b/src/main/cmds.c @@ -404,8 +404,7 @@ BUFFER_WRAPCMD(reload); BUFFER_VIEW_WRAPCMD(kill_line); BUFFER_VIEW_WRAPCMD(forward_delete_char); BUFFER_VIEW_WRAPCMD(backward_delete_char); -BUFFER_VIEW_WRAPCMD(forward_delete_word); -BUFFER_VIEW_WRAPCMD(backward_delete_word); +BUFFER_VIEW_WRAPCMD(delete_word); BUFFER_VIEW_WRAPCMD(backward_char); BUFFER_VIEW_WRAPCMD(backward_word); BUFFER_VIEW_WRAPCMD(forward_char); @@ -475,8 +474,7 @@ static int32_t insert_tab(struct command_ctx ctx, int argc, void register_buffer_commands(struct commands *commands) { static struct command buffer_commands[] = { {.name = "kill-line", .fn = kill_line_cmd}, - {.name = "delete-word", .fn = forward_delete_word_cmd}, - {.name = "backward-delete-word", .fn = backward_delete_word_cmd}, + {.name = "delete-word", .fn = delete_word_cmd}, {.name = "delete-char", .fn = forward_delete_char_cmd}, {.name = "backward-delete-char", .fn = backward_delete_char_cmd}, {.name = "backward-char", .fn = backward_char_cmd}, diff --git a/test/buffer.c b/test/buffer.c index 19fca8c..f4aefc5 100644 --- a/test/buffer.c +++ b/test/buffer.c @@ -17,4 +17,35 @@ void test_add() { "Expected buffer to have one line with characters"); } -void run_buffer_tests() { run_test(test_add); } +void test_word_at() { + struct buffer b = buffer_create("test-word-at-buffer"); + const char *txt = "word1 (word2). Another"; + buffer_add(&b, (struct location){.line = 0, .col = 0}, (uint8_t *)txt, + strlen(txt)); + + struct region word1 = + buffer_word_at(&b, (struct location){.line = 0, .col = 0}); + ASSERT(region_has_size(word1), "expected 0,0 to be a word"); + ASSERT(word1.begin.col == 0 && word1.end.col == 5, + "Expected word to end at col 5"); + + // test that dot can be in the middle of a word + // and that '(' and ')' works as a delimiter + struct region word2 = + buffer_word_at(&b, (struct location){.line = 0, .col = 8}); + ASSERT(region_has_size(word2), "expected 0,8 to be in a word"); + ASSERT(word2.begin.col == 7 && word2.end.col == 12, + "Expected word to span cols 7..12"); + + // test that clamping works correctly + struct region word3 = + buffer_word_at(&b, (struct location){.line = 0, .col = 100}); + ASSERT(region_has_size(word3), "expected 0,100 to be in the last word"); + ASSERT(word3.begin.col == 15 && word3.end.col == 22, + "Expected word to span cols 15..22"); +} + +void run_buffer_tests() { + run_test(test_add); + run_test(test_word_at); +} -- cgit v1.2.3