URI: 
       tImplement Y and r - ledit - Text editor (WIP)
  HTML git clone git://lumidify.org/ledit.git (fast, but not encrypted)
  HTML git clone https://lumidify.org/git/ledit.git (encrypted, but very slow)
   DIR Log
   DIR Files
   DIR Refs
   DIR README
   DIR LICENSE
       ---
   DIR commit b113e1224270deb100329dec7cc42e05d8aead63
   DIR parent 2e039562f045bbcb2a7b29bd39031f0597953460
  HTML Author: lumidify <nobody@lumidify.org>
       Date:   Wed, 10 Nov 2021 20:01:13 +0100
       
       Implement Y and r
       
       Diffstat:
         M keys_basic.c                        |     111 +++++++++++++++++++++++++------
         M keys_basic_config.h                 |       4 ++++
       
       2 files changed, 94 insertions(+), 21 deletions(-)
       ---
   DIR diff --git a/keys_basic.c b/keys_basic.c
       t@@ -179,6 +179,14 @@ clear_key_stack(void) {
                key_stack.len = 0;
        }
        
       +static struct action
       +err_invalid_key(ledit_buffer *buffer) {
       +        ledit_window_show_message(buffer->window, "Invalid key", -1);
       +        clear_key_stack();
       +        discard_repetition_stack();
       +        return (struct action){ACTION_NONE, NULL};
       +}
       +
        /* FIXME: error if no motion cb and not number key */
        static int
        get_key_repeat_and_motion_cb(motion_callback *cb_ret) {
       t@@ -326,10 +334,11 @@ delete_range(
            ledit_buffer *buffer,
            int line_based, int selected,
            int line_index1, int byte_index1,
       -    int line_index2, int byte_index2) {
       +    int line_index2, int byte_index2, int copy_to_buffer) {
                (void)selected; /* FIXME */
       -        if (!paste_buffer)
       +        if (copy_to_buffer && !paste_buffer)
                        paste_buffer = txtbuf_new();
       +        txtbuf *buf = copy_to_buffer ? paste_buffer : txtbuf_new();
                ledit_range cur_range, del_range;
                cur_range.line1 = buffer->cur_line;
                cur_range.byte1 = buffer->cur_index;
       t@@ -338,13 +347,15 @@ delete_range(
                    line_index1, byte_index1,
                    line_index2, byte_index2,
                    &buffer->cur_line, &buffer->cur_index,
       -            &del_range, paste_buffer
       +            &del_range, buf
                );
                cur_range.line2 = buffer->cur_line;
                cur_range.byte2 = buffer->cur_index;
                ledit_push_undo_delete(
       -            buffer->undo, paste_buffer, del_range, cur_range, 1, buffer->common->mode
       +            buffer->undo, buf, del_range, cur_range, 1, buffer->common->mode
                );
       +        if (!copy_to_buffer)
       +                txtbuf_destroy(buf);
        }
        
        static void
       t@@ -395,7 +406,7 @@ delete_selection(ledit_buffer *buffer) {
                        delete_range(
                            buffer, 0, 0,
                            buffer->sel.line1, buffer->sel.byte1,
       -                    buffer->sel.line2, buffer->sel.byte2
       +                    buffer->sel.line2, buffer->sel.byte2, 1
                        );
                        paste_buffer_line_based = 0;
                        /* FIXME: maybe just set this to the current cursor pos? */
       t@@ -453,7 +464,7 @@ delete_chars_forwards(ledit_buffer *buffer, char *text, int len) {
                delete_range(
                    buffer, 0, 0,
                    buffer->cur_line, buffer->cur_index,
       -            buffer->cur_line, end_index
       +            buffer->cur_line, end_index, 1
                );
                paste_buffer_line_based = 0;
                buffer->cur_index = ledit_buffer_get_legal_normal_pos(
       t@@ -480,7 +491,7 @@ delete_chars_backwards(ledit_buffer *buffer, char *text, int len) {
                delete_range(
                    buffer, 0, 0,
                    buffer->cur_line, start_index,
       -            buffer->cur_line, buffer->cur_index
       +            buffer->cur_line, buffer->cur_index, 1
                );
                paste_buffer_line_based = 0;
                /* I guess this is technically unnecessary since only
       t@@ -777,7 +788,7 @@ delete_to_eol(ledit_buffer *buffer, char *text, int len) {
                delete_range(
                    buffer, 0, 0,
                    buffer->cur_line, buffer->cur_index,
       -            buffer->cur_line, sl->start_index + sl->length
       +            buffer->cur_line, sl->start_index + sl->length, 1
                );
                paste_buffer_line_based = 0;
                buffer->cur_index = ledit_buffer_get_legal_normal_pos(
       t@@ -804,7 +815,7 @@ change_to_eol(ledit_buffer *buffer, char *text, int len) {
                delete_range(
                    buffer, 0, 0,
                    buffer->cur_line, buffer->cur_index,
       -            buffer->cur_line, sl->start_index + sl->length
       +            buffer->cur_line, sl->start_index + sl->length, 1
                );
                paste_buffer_line_based = 0;
                ledit_buffer_wipe_line_cursor_attrs(buffer, buffer->cur_line);
       t@@ -880,7 +891,7 @@ change_cb(ledit_buffer *buffer, int line, int char_pos, enum key_type type) {
                delete_range(
                    buffer, 0, 0,
                    buffer->cur_line, pos1,
       -            line, pos2
       +            line, pos2, 1
                );
                paste_buffer_line_based = line_based;
                ledit_buffer_wipe_line_cursor_attrs(buffer, buffer->cur_line);
       t@@ -936,6 +947,27 @@ yank(ledit_buffer *buffer, char *text, int len) {
                return (struct action){ACTION_NONE, NULL};
        }
        
       +static struct action
       +yank_lines(ledit_buffer *buffer, char *text, int len) {
       +        (void)text;
       +        (void)len;
       +        int num = get_key_repeat();
       +        if (num == -1)
       +                return err_invalid_key(buffer);
       +        else if (num == 0)
       +                num = 1;
       +        int new_line, new_softline;
       +        get_new_line_softline(
       +            buffer, buffer->cur_line, buffer->cur_index,
       +            num - 1, &new_line, &new_softline
       +        );
       +        ledit_line *ll = ledit_buffer_get_line(buffer, new_line);
       +        PangoLayoutLine *pl = pango_layout_get_line_readonly(ll->layout, new_softline);
       +        yank_cb(buffer, new_line, pl->start_index, KEY_MOTION_LINE);
       +        clear_key_stack();
       +        return (struct action){ACTION_NONE, NULL};
       +}
       +
        static void
        yank_cb(ledit_buffer *buffer, int line, int char_pos, enum key_type type) {
                int line_based = type == KEY_MOTION_LINE ? 1 : 0;
       t@@ -1019,7 +1051,7 @@ key_d_cb(ledit_buffer *buffer, int line, int char_pos, enum key_type type) {
                delete_range(
                    buffer, line_based, 0,
                    buffer->cur_line, buffer->cur_index,
       -            line, char_pos
       +            line, char_pos, 1
                );
                paste_buffer_line_based = line_based;
                ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
       t@@ -1242,17 +1274,18 @@ static struct action
        backspace(ledit_buffer *buffer, char *text, int len) {
                (void)text;
                (void)len;
       +        /* FIXME: don't copy to paste buffer on del_sel here; delete entire grapheme */
                if (delete_selection(buffer)) {
                        /* NOP */
                } else if (buffer->cur_index == 0) {
                        if (buffer->cur_line != 0) {
                                ledit_line *l1 = ledit_buffer_get_line(buffer, buffer->cur_line - 1);
       -                        delete_range(buffer, 0, 0, buffer->cur_line - 1, l1->len, buffer->cur_line, 0);
       +                        delete_range(buffer, 0, 0, buffer->cur_line - 1, l1->len, buffer->cur_line, 0, 0);
                        }
                } else {
                        ledit_line *l = ledit_buffer_get_line(buffer, buffer->cur_line);
                        int i = ledit_line_prev_utf8(l, buffer->cur_index);
       -                delete_range(buffer, 0, 0, buffer->cur_line, buffer->cur_index, buffer->cur_line, i);
       +                delete_range(buffer, 0, 0, buffer->cur_line, buffer->cur_index, buffer->cur_line, i, 0);
                }
                ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
                return (struct action){ACTION_NONE, NULL};
       t@@ -1267,11 +1300,11 @@ delete_key(ledit_buffer *buffer, char *text, int len) {
                        /* NOP */
                } else if (buffer->cur_index == cur_line->len) {
                        if (buffer->cur_line != buffer->lines_num - 1) {
       -                        delete_range(buffer, 0, 0, buffer->cur_line, cur_line->len, buffer->cur_line + 1, 0);
       +                        delete_range(buffer, 0, 0, buffer->cur_line, cur_line->len, buffer->cur_line + 1, 0, 0);
                        }
                } else {
                        int i = ledit_line_next_utf8(cur_line, buffer->cur_index);
       -                delete_range(buffer, 0, 0, buffer->cur_line, buffer->cur_index, buffer->cur_line, i);
       +                delete_range(buffer, 0, 0, buffer->cur_line, buffer->cur_index, buffer->cur_line, i, 0);
                }
                ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
                return (struct action){ACTION_NONE, NULL};
       t@@ -1923,16 +1956,52 @@ GEN_MOVE_TO_CHAR(
        )
        
        static struct action
       +replace_cb(ledit_buffer *buffer, char *text, int len) {
       +        int start_index = buffer->cur_index;
       +        /* FIXME: replace with (key repeat) * text instead of just text */
       +        int end_index = ledit_buffer_next_cursor_pos(
       +            buffer, buffer->cur_line, buffer->cur_index, 1
       +        );
       +        delete_range(
       +            buffer, 0, 0,
       +            buffer->cur_line, start_index, buffer->cur_line, end_index, 0
       +        );
       +        insert_text(
       +            buffer, buffer->cur_line, start_index, text, len,
       +            buffer->cur_line, start_index, buffer->cur_line, start_index, 0
       +        );
       +        /* this should not be necessary, but just in case */
       +        buffer->cur_index = ledit_buffer_get_legal_normal_pos(
       +            buffer, buffer->cur_line, buffer->cur_index
       +        );
       +        push_undo_empty_insert(buffer, buffer->cur_line, buffer->cur_index, 0);
       +        grab_char_cb = NULL;
       +        return (struct action){ACTION_NONE, NULL};
       +}
       +
       +static struct action
       +replace(ledit_buffer *buffer, char *text, int len) {
       +        (void)buffer;
       +        (void)text;
       +        (void)len;
       +        int num = get_key_repeat();
       +        if (num != 0)
       +                return err_invalid_key(buffer);
       +        grab_char_cb = &replace_cb;
       +        return (struct action){ACTION_NONE, NULL};
       +}
       +
       +static struct action
        handle_key(ledit_buffer *buffer, char *key_text, int len, KeySym sym, unsigned int key_state, int lang_index, int *found) {
                struct key *cur_keys = keys[lang_index].keys;
                int num_keys = keys[lang_index].num_keys;
                struct key_stack_elem *e = peek_key_stack();
       -        /* FIXME: allow to escape this grabbing somehow */
       -        /* -> I guess escape *does* actually work because it
       -           results in ascii 1b (escape) in this string, which
       -           will usually not be found in the text (but this is
       -           a bit of a hack) */
       -        if (len > 0 && grab_char_cb) {
       +        /* FIXME: check if control chars in text */
       +        /* FIXME: this is a bit of a hack because it's hardcoded */
       +        if (grab_char_cb && sym == XK_Escape) {
       +                grab_char_cb = NULL;
       +                return (struct action){ACTION_NONE, NULL};
       +        } else if (len > 0 && grab_char_cb) {
                        *found = 1;
                        return grab_char_cb(buffer, key_text, len);
                }
   DIR diff --git a/keys_basic_config.h b/keys_basic_config.h
       t@@ -88,6 +88,8 @@ static struct action delete_to_eol(ledit_buffer *buffer, char *text, int len);
        static struct action delete_chars_forwards(ledit_buffer *buffer, char *text, int len);
        static struct action delete_chars_backwards(ledit_buffer *buffer, char *text, int len);
        static struct action yank(ledit_buffer *buffer, char *text, int len);
       +static struct action yank_lines(ledit_buffer *buffer, char *text, int len);
       +static struct action replace(ledit_buffer *buffer, char *text, int len);
        
        /* FIXME: maybe sort these and use binary search
           -> but that would mess with the catch-all keys */
       t@@ -125,6 +127,7 @@ static struct key keys_en[] = {
                {"X",  0, 0, NORMAL, KEY_ANY, KEY_NUMBERALLOWED, &delete_chars_backwards},
                {"d",  0, 0, NORMAL|VISUAL, KEY_ANY, KEY_MOTION|KEY_NUMBERALLOWED, &key_d},
                {"y",  0, 0, NORMAL|VISUAL, KEY_ANY, KEY_MOTION|KEY_NUMBERALLOWED, &yank},
       +        {"Y",  0, 0, NORMAL, KEY_ANY, KEY_NUMBERALLOWED, &yank_lines},
                {"c",  0, 0, NORMAL|VISUAL, KEY_ANY, KEY_MOTION|KEY_NUMBERALLOWED, &change},
                {"v",  0, 0, NORMAL, KEY_ANY, KEY_ANY, &enter_visual},
                {"o",  0, 0, VISUAL, KEY_ANY, KEY_ANY, &switch_selection_end},
       t@@ -165,6 +168,7 @@ static struct key keys_en[] = {
                {"'",  0, 0, NORMAL, KEY_ANY, KEY_ANY, &jump_to_mark},
                {"C",  0, 0, NORMAL, KEY_ANY, KEY_ANY, &change_to_eol},
                {"D",  0, 0, NORMAL, KEY_ANY, KEY_ANY, &delete_to_eol},
       +        {"r",  0, 0, NORMAL, KEY_ANY, KEY_ANY, &replace},
                {"t",  0, 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBERALLOWED, &find_next_char_forwards},
                {"T",  0, 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBERALLOWED, &find_next_char_backwards},
                {"f",  0, 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBERALLOWED, &find_char_forwards},