URI: 
       tImplement yanking - 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 2e039562f045bbcb2a7b29bd39031f0597953460
   DIR parent 42118fa8413ad5bd8be9d6d9b54b3b233a68df5f
  HTML Author: lumidify <nobody@lumidify.org>
       Date:   Wed, 10 Nov 2021 11:27:29 +0100
       
       Implement yanking
       
       Diffstat:
         M buffer.c                            |      10 +++++-----
         M buffer.h                            |       2 ++
         M keys_basic.c                        |     118 ++++++++++++++++++++++++++-----
         M keys_basic_config.h                 |       2 ++
       
       4 files changed, 110 insertions(+), 22 deletions(-)
       ---
   DIR diff --git a/buffer.c b/buffer.c
       t@@ -43,7 +43,6 @@ static void init_line(ledit_buffer *buffer, ledit_line *line);
        static void delete_line_section_base(ledit_buffer *buffer, int line, int start, int length);
        static void normalize_and_set_pango_text(ledit_line *line);
        static void swap(int *a, int *b);
       -static void sort_selection(int *line1, int *byte1, int *line2, int *byte2);
        static void copy_selection_to_x_primary(ledit_buffer *buffer, int line1, int byte1, int line2, int byte2);
        
        void
       t@@ -704,6 +703,7 @@ ledit_buffer_delete_line_entry_base(ledit_buffer *buffer, int index) {
        
        /* FIXME: use some sort of gap buffer (that would make this function
           slightly more useful...) */
       +/* FIXME: error checking, assert? */
        ledit_line *
        ledit_buffer_get_line(ledit_buffer *buffer, int index) {
                return &buffer->lines[index];
       t@@ -1886,8 +1886,8 @@ swap(int *a, int *b) {
                *b = tmp;
        }
        
       -static void
       -sort_selection(int *line1, int *byte1, int *line2, int *byte2) {
       +void
       +ledit_buffer_sort_selection(int *line1, int *byte1, int *line2, int *byte2) {
                if (*line1 > *line2) {
                        swap(line1, line2);
                        swap(byte1, byte2);
       t@@ -1921,8 +1921,8 @@ ledit_buffer_set_selection(ledit_buffer *buffer, int line1, int byte1, int line2
                if (buffer->sel.line1 >= 0) {
                        int l1_new = line1, l2_new = line2;
                        int b1_new = byte1, b2_new = byte2;
       -                sort_selection(&buffer->sel.line1, &buffer->sel.byte1, &buffer->sel.line2, &buffer->sel.byte2);
       -                sort_selection(&l1_new, &b1_new, &l2_new, &b2_new);
       +                ledit_buffer_sort_selection(&buffer->sel.line1, &buffer->sel.byte1, &buffer->sel.line2, &buffer->sel.byte2);
       +                ledit_buffer_sort_selection(&l1_new, &b1_new, &l2_new, &b2_new);
                        if (buffer->sel.line1 > l2_new || buffer->sel.line2 < l1_new) {
                                for (int i = buffer->sel.line1; i <= buffer->sel.line2; i++) {
                                        ledit_buffer_wipe_line_cursor_attrs(buffer, i);
   DIR diff --git a/buffer.h b/buffer.h
       t@@ -156,3 +156,5 @@ void ledit_buffer_resize_textview(ledit_buffer *buffer);
        void ledit_buffer_scroll(ledit_buffer *buffer, long new_offset);
        void ledit_buffer_scroll_to_pos_top(ledit_buffer *buffer, int line, int byte);
        void ledit_buffer_scroll_to_pos_bottom(ledit_buffer *buffer, int line, int byte);
       +/* FIXME: just make generic sort range */
       +void ledit_buffer_sort_selection(int *line1, int *byte1, int *line2, int *byte2);
   DIR diff --git a/keys_basic.c b/keys_basic.c
       t@@ -9,6 +9,7 @@
           them reliably yet */
        #include <stdio.h>
        #include <stdlib.h>
       +#include <assert.h>
        
        #include <X11/Xlib.h>
        #include <X11/Xutil.h>
       t@@ -111,6 +112,7 @@ static void move_cursor_left_right(ledit_buffer *buffer, int dir, int allow_ille
        static void move_cursor_up_down(ledit_buffer *buffer, int dir);
        static void push_num(int num);
        static void key_d_cb(ledit_buffer *buffer, int line, int char_pos, enum key_type type);
       +static void yank_cb(ledit_buffer *buffer, int line, int char_pos, enum key_type type);
        static void get_new_line_softline(
            ledit_buffer *buffer, int cur_line, int cur_index,
            int movement, int *new_line_ret, int *new_softline_ret
       t@@ -177,6 +179,24 @@ clear_key_stack(void) {
                key_stack.len = 0;
        }
        
       +/* FIXME: error if no motion cb and not number key */
       +static int
       +get_key_repeat_and_motion_cb(motion_callback *cb_ret) {
       +        int num = 1;
       +        struct key_stack_elem *e = pop_key_stack();
       +        if (e != NULL) {
       +                if (e->key & KEY_NUMBER) {
       +                        num = e->count > 0 ? e->count : 1;
       +                        e = pop_key_stack();
       +                }
       +                if (e != NULL)
       +                        num *= (e->count > 0 ? e->count : 1);
       +        }
       +        if (cb_ret != NULL && e != NULL && e->motion_cb != NULL)
       +                *cb_ret = e->motion_cb;
       +        return num;
       +}
       +
        static struct repetition_stack_elem *
        push_repetition_stack(void) {
                struct repetition_stack_elem *e;
       t@@ -867,6 +887,87 @@ change_cb(ledit_buffer *buffer, int line, int char_pos, enum key_type type) {
        }
        
        static struct action
       +yank(ledit_buffer *buffer, char *text, int len) {
       +        (void)text;
       +        (void)len;
       +        if (!paste_buffer)
       +                paste_buffer = txtbuf_new();
       +        if (buffer->common->mode == VISUAL) {
       +                ledit_buffer_sort_selection(
       +                    &buffer->sel.line1, &buffer->sel.byte1, &buffer->sel.line2, &buffer->sel.byte2
       +                );
       +                ledit_buffer_copy_text_to_txtbuf(
       +                    buffer, paste_buffer,
       +                    buffer->sel.line1, buffer->sel.byte1, buffer->sel.line2, buffer->sel.byte2
       +                );
       +                paste_buffer_line_based = 0;
       +                buffer->cur_line = buffer->sel.line1;
       +                buffer->cur_index = buffer->sel.byte1;
       +                ledit_buffer_set_selection(buffer, -1, -1, -1, -1);
       +                ledit_buffer_set_mode(buffer, NORMAL);
       +                buffer->cur_index = ledit_buffer_get_legal_normal_pos(
       +                    buffer, buffer->cur_line, buffer->cur_index
       +                );
       +                ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
       +                clear_key_stack();
       +        } else {
       +                motion_callback cb = NULL;
       +                int num = get_key_repeat_and_motion_cb(&cb);
       +                if (cb == &yank_cb) {
       +                        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);
       +                        cb(buffer, new_line, pl->start_index, KEY_MOTION_LINE);
       +                        clear_key_stack();
       +                } else if (cb == NULL) {
       +                        struct key_stack_elem *e = push_key_stack();
       +                        e->key = KEY_MOTION;
       +                        e->count = num;
       +                        e->motion_cb = &yank_cb;
       +                } else {
       +                        clear_key_stack();
       +                }
       +        }
       +        discard_repetition_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;
       +        int l1 = buffer->cur_line, l2 = line, b1 = buffer->cur_index, b2 = char_pos;
       +        if (!paste_buffer)
       +                paste_buffer = txtbuf_new();
       +        if (l2 < l1 || (l1 == l2 && b2 < b1)) {
       +                swap(&l1, &l2);
       +                swap(&b1, &b2);
       +        }
       +        if (line_based) {
       +                int x, sl1, sl2;
       +                ledit_line *ll1 = ledit_buffer_get_line(buffer, l1);
       +                pango_layout_index_to_line_x(ll1->layout, b1, 0, &sl1, &x);
       +                PangoLayoutLine *pl1 = pango_layout_get_line_readonly(ll1->layout, sl1);
       +                ledit_line *ll2 = ledit_buffer_get_line(buffer, l2);
       +                pango_layout_index_to_line_x(ll2->layout, b2, 0, &sl2, &x);
       +                PangoLayoutLine *pl2 = pango_layout_get_line_readonly(ll2->layout, sl2);
       +                assert(pl1 != NULL && pl2 != NULL);
       +                ledit_buffer_copy_text_to_txtbuf(
       +                    buffer, paste_buffer, l1, pl1->start_index, l2, pl2->start_index + pl2->length
       +                );
       +        } else {
       +                ledit_buffer_copy_text_to_txtbuf(
       +                    buffer, paste_buffer, l1, b1, l2, b2
       +                );
       +        }
       +        paste_buffer_line_based = line_based;
       +        discard_repetition_stack();
       +}
       +
       +static struct action
        key_d(ledit_buffer *buffer, char *text, int len) {
                (void)text;
                (void)len;
       t@@ -1700,23 +1801,6 @@ clippaste(ledit_buffer *buffer, char *text, int len) {
                return (struct action){ACTION_NONE, NULL};
        }
        
       -static int
       -get_key_repeat_and_motion_cb(motion_callback *cb_ret) {
       -        int num = 1;
       -        struct key_stack_elem *e = pop_key_stack();
       -        if (e != NULL) {
       -                if (e->key & KEY_NUMBER) {
       -                        num = e->count > 0 ? e->count : 1;
       -                        e = pop_key_stack();
       -                }
       -                if (e != NULL)
       -                        num *= (e->count > 0 ? e->count : 1);
       -        }
       -        if (cb_ret != NULL && e != NULL && e->motion_cb != NULL)
       -                *cb_ret = e->motion_cb;
       -        return num;
       -}
       -
        /* FIXME: make sure the found position is valid cursor position? */
        static int
        search_str_backwards(char *haystack, int hlen, char *needle, int nlen, int start_index) {
   DIR diff --git a/keys_basic_config.h b/keys_basic_config.h
       t@@ -87,6 +87,7 @@ static struct action change_to_eol(ledit_buffer *buffer, char *text, int len);
        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);
        
        /* FIXME: maybe sort these and use binary search
           -> but that would mess with the catch-all keys */
       t@@ -123,6 +124,7 @@ static struct key keys_en[] = {
                {"x",  0, 0, NORMAL, KEY_ANY, KEY_NUMBERALLOWED, &delete_chars_forwards},
                {"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},
                {"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},