URI: 
       tClean up commands with motion callback - 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 c5fa1b6794a0e0dc9346571a34c85d40fdbb01b5
   DIR parent b113e1224270deb100329dec7cc42e05d8aead63
  HTML Author: lumidify <nobody@lumidify.org>
       Date:   Wed, 10 Nov 2021 21:43:56 +0100
       
       Clean up commands with motion callback
       
       Diffstat:
         M keys_basic.c                        |     308 ++++++++++++++++---------------
         M keys_basic_config.h                 |       4 ++--
       
       2 files changed, 160 insertions(+), 152 deletions(-)
       ---
   DIR diff --git a/keys_basic.c b/keys_basic.c
       t@@ -111,7 +111,7 @@ void clear_key_stack(void);
        static void move_cursor_left_right(ledit_buffer *buffer, int dir, int allow_illegal_index);
        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 delete_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,
       t@@ -187,21 +187,74 @@ err_invalid_key(ledit_buffer *buffer) {
                return (struct action){ACTION_NONE, NULL};
        }
        
       -/* FIXME: error if no motion cb and not number key */
       +/*
       + * Get the number of times a command should be repeated and the motion
       + * callback, if there was one on the stack.
       + * Note that *cb_ret is set to NULL if a non-null address is given and
       + * there is no motion callback on the stack.
       + * An empty stack or a stack where the top element has a count of 0
       + * leads to 0 being returned.
       + * In case of error, -1 is returned:
       + * - When the top or second element is not a number key but also does
       + *   not comtain a motion callback.
       + * - When the stack is not empty after removing the top element and
       + *   possibly a second one if the top one was a 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;
       +                        num = e->count;
                                e = pop_key_stack();
       +                } else if (e->count == 0) {
       +                        num = 0;
                        }
                        if (e != NULL)
                                num *= (e->count > 0 ? e->count : 1);
       +        } else {
       +                num = 0;
                }
       +        if (e != NULL && !(e->key & KEY_NUMBER) && e->motion_cb == NULL)
       +                num = -1;
       +        else if (!key_stack_empty())
       +                num = -1;
                if (cb_ret != NULL && e != NULL && e->motion_cb != NULL)
                        *cb_ret = e->motion_cb;
       +        else if (cb_ret != NULL)
       +                *cb_ret = NULL;
       +        return num;
       +}
       +
       +/*
       + * Get the number of times a command should be repeated, or -1 if anything
       + * invalid was on the stack - this is for commands that just take a repeat
       + * count and nothing else (cursor movement keys are different because they
       + * can use other elements on the key stack too, for instance call a callback
       + * as is done for deletion.
       + * Note that an empty stack leads to 0 being returned even though most commands
       + * use 1 as repeat then so the caller can distinguish between empty stack and
       + * a repetition count of 1.
       + */
       +static int
       +get_key_repeat(void) {
       +        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) {
       +                        /* the key was not a number, or there was another
       +                           element under it on the stack -> error */
       +                        num = -1;
       +                }
       +        } else {
       +                num = 0;
       +        }
       +        clear_key_stack();
                return num;
        }
        
       t@@ -418,35 +471,6 @@ delete_selection(ledit_buffer *buffer) {
                return 0;
        }
        
       -/* get the number of times a command should be repeated, or -1 if anything
       -   invalid was on the stack - this is for commands that just take a repeat
       -   count and nothing else (cursor movement keys are different because they
       -   can use other elements on the key stack too, for instance call a callback
       -   as is done for deletion
       -   Note that an empty stack leads to 0 being returned even though most commands
       -   use 1 as repeat then so the caller can distinguish between empty stack and
       -   a repetition count of 1 */
       -static int
       -get_key_repeat(void) {
       -        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) {
       -                        /* the key was not a number, or there was another
       -                           element under it on the stack -> error */
       -                        num = -1;
       -                }
       -        } else {
       -                num = 0;
       -        }
       -        clear_key_stack();
       -        return num;
       -}
       -
        static struct action
        delete_chars_forwards(ledit_buffer *buffer, char *text, int len) {
                (void)text;
       t@@ -574,15 +598,18 @@ static struct action
        move_to_line(ledit_buffer *buffer, char *text, int len) {
                (void)text;
                (void)len;
       -        int repeat = get_key_repeat();
       +        motion_callback cb = NULL;
       +        int repeat = get_key_repeat_and_motion_cb(&cb);
                int line;
                if (repeat > 0)
                        line = repeat > buffer->lines_num ? buffer->lines_num : repeat;
                else if (repeat == 0)
                        line = buffer->lines_num;
                else
       -                ledit_window_show_message(buffer->window, "Invalid key", -1);
       -        if (repeat >= 0) {
       +                return err_invalid_key(buffer);
       +        if (cb != NULL) {
       +                cb(buffer, line - 1, 0, KEY_MOTION_LINE);
       +        } else {
                        ledit_buffer_wipe_line_cursor_attrs(buffer, buffer->cur_line);
                        buffer->cur_line = line - 1;
                        buffer->cur_index = 0;
       t@@ -597,8 +624,8 @@ move_to_line(ledit_buffer *buffer, char *text, int len) {
                                ledit_buffer_scroll(buffer, ll->y_offset - text_h / 2);
                        }
                        ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
       +                discard_repetition_stack();
                }
       -        discard_repetition_stack();
                return (struct action){ACTION_NONE, NULL};
        }
        
       t@@ -828,39 +855,31 @@ static struct action
        change(ledit_buffer *buffer, char *text, int len) {
                (void)text;
                (void)len;
       -        int num = 0;
       -        struct key_stack_elem *e = pop_key_stack();
       -        if (buffer->sel.line1 != buffer->sel.line2 || buffer->sel.byte1 != buffer->sel.byte2) {
       +        motion_callback cb = NULL;
       +        int num = get_key_repeat_and_motion_cb(&cb);
       +        if (num == -1)
       +                return err_invalid_key(buffer);
       +        if (buffer->common->mode == VISUAL) {
                        ledit_buffer_set_mode(buffer, INSERT);
                        delete_selection(buffer);
                        ledit_buffer_wipe_line_cursor_attrs(buffer, buffer->cur_line);
                        clear_key_stack();
                } else {
       -                if (e != NULL) {
       -                        if (e->key & KEY_NUMBER) {
       -                                num = e->count;
       -                                e = pop_key_stack();
       -                        }
       -                        if (e != NULL && e->motion_cb == &change_cb) {
       -                                int prevnum = e->count > 0 ? e->count : 1;
       -                                num = num > 0 ? num : 1;
       -                                int lines = num * prevnum;
       -                                int new_line, new_softline;
       -                                get_new_line_softline(
       -                                    buffer, buffer->cur_line, buffer->cur_index,
       -                                    lines - 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);
       -                                e->motion_cb(buffer, new_line, pl->start_index, KEY_MOTION_LINE);
       -                                clear_key_stack();
       -                        } else if (e != NULL) {
       -                                /* FIXME: show message? */
       -                                clear_key_stack();
       -                        }
       -                }
       -                if (e == NULL) {
       -                        e = push_key_stack();
       +                if (cb == &change_cb) {
       +                        int lines = num > 0 ? num : 1;
       +                        int new_line, new_softline;
       +                        get_new_line_softline(
       +                            buffer, buffer->cur_line, buffer->cur_index,
       +                            lines - 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) {
       +                        return err_invalid_key(buffer);
       +                } else {
       +                        struct key_stack_elem *e = push_key_stack();
                                e->key = KEY_MOTION; /* ? */
                                e->count = num;
                                e->motion_cb = &change_cb;
       t@@ -924,6 +943,8 @@ yank(ledit_buffer *buffer, char *text, int len) {
                } else {
                        motion_callback cb = NULL;
                        int num = get_key_repeat_and_motion_cb(&cb);
       +                if (num == 0)
       +                        num = 1;
                        if (cb == &yank_cb) {
                                int new_line, new_softline;
                                get_new_line_softline(
       t@@ -1000,45 +1021,38 @@ yank_cb(ledit_buffer *buffer, int line, int char_pos, enum key_type type) {
        }
        
        static struct action
       -key_d(ledit_buffer *buffer, char *text, int len) {
       +delete(ledit_buffer *buffer, char *text, int len) {
                (void)text;
                (void)len;
       -        int num = 0;
       +        motion_callback cb = NULL;
       +        int num = get_key_repeat_and_motion_cb(&cb);
       +        if (num == -1)
       +                return err_invalid_key(buffer);
                if (delete_selection(buffer)) {
                        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 {
       -                struct key_stack_elem *e = pop_key_stack();
       -                if (e != NULL) {
       -                        if (e->key & KEY_NUMBER) {
       -                                num = e->count;
       -                                e = pop_key_stack();
       -                        }
       -                        /* FIXME: checking equality of the function pointer may be a bit risky */
       -                        if (e != NULL && e->motion_cb == &key_d_cb) {
       -                                int prevnum = e->count > 0 ? e->count : 1;
       -                                num = num > 0 ? num : 1;
       -                                int lines = num * prevnum;
       -                                int new_line, new_softline;
       -                                get_new_line_softline(
       -                                    buffer, buffer->cur_line, buffer->cur_index,
       -                                    lines - 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);
       -                                e->motion_cb(buffer, new_line, pl->start_index, KEY_MOTION_LINE);
       -                                clear_key_stack();
       -                        } else if (e != NULL) {
       -                                clear_key_stack();
       -                        }
       -                }
       -                if (e == NULL) {
       -                        e = push_key_stack();
       +                /* FIXME: checking equality of the function pointer may be a bit risky */
       +                if (cb == &delete_cb) {
       +                        int lines = num > 0 ? num : 1;
       +                        int new_line, new_softline;
       +                        get_new_line_softline(
       +                            buffer, buffer->cur_line, buffer->cur_index,
       +                            lines - 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) {
       +                        return err_invalid_key(buffer);
       +                } else {
       +                        struct key_stack_elem *e = push_key_stack();
                                e->key = KEY_MOTION; /* ? */
                                e->count = num;
       -                        e->motion_cb = &key_d_cb;
       +                        e->motion_cb = &delete_cb;
                        }
                }
                return (struct action){ACTION_NONE, NULL};
       t@@ -1046,7 +1060,7 @@ key_d(ledit_buffer *buffer, char *text, int len) {
        
        /* FIXME: should this get number of lines to remove or actual end line? */
        static void
       -key_d_cb(ledit_buffer *buffer, int line, int char_pos, enum key_type type) {
       +delete_cb(ledit_buffer *buffer, int line, int char_pos, enum key_type type) {
                int line_based = type == KEY_MOTION_LINE ? 1 : 0;
                delete_range(
                    buffer, line_based, 0,
       t@@ -1314,16 +1328,12 @@ static struct action
        move_to_eol(ledit_buffer *buffer, char *text, int len) {
                (void)text;
                (void)len;
       -        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);
       -        }
       +        motion_callback cb;
       +        int num = get_key_repeat_and_motion_cb(&cb);
       +        if (num == -1)
       +                return err_invalid_key(buffer);
       +        if (num == 0)
       +                num = 1;
                ledit_buffer_wipe_line_cursor_attrs(buffer, buffer->cur_line);
                int new_line, new_softline;
                get_new_line_softline(
       t@@ -1333,13 +1343,17 @@ move_to_eol(ledit_buffer *buffer, char *text, int len) {
                ledit_line *ll = ledit_buffer_get_line(buffer, new_line);
                PangoLayoutLine *sl = pango_layout_get_line_readonly(ll->layout, new_softline);
                int end_index = sl->start_index + sl->length;
       -        if (e != NULL && e->motion_cb != NULL) {
       -                e->motion_cb(buffer, new_line, end_index, KEY_MOTION_CHAR);
       +        if (cb != NULL) {
       +                cb(buffer, new_line, end_index, KEY_MOTION_CHAR);
                } else {
                        buffer->cur_line = new_line;
                        buffer->cur_index = end_index;
                        if (buffer->common->mode == VISUAL) {
       -                        ledit_buffer_set_selection(buffer, buffer->sel.line1, buffer->sel.byte1, new_line, end_index);
       +                        ledit_buffer_set_selection(
       +                            buffer,
       +                            buffer->sel.line1, buffer->sel.byte1,
       +                            new_line, end_index
       +                        );
                        } else {
                                /* FIXME: this is weird because the cursor is actually on the
                                   next soft line, but the alternative has too many weird effects
       t@@ -1358,24 +1372,20 @@ static struct action                                                            
        name(ledit_buffer *buffer, char *text, int len) {                                \
                (void)text;                                                              \
                (void)len;                                                               \
       -        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);                    \
       -        }                                                                        \
       +        motion_callback cb;                                                      \
       +        int num = get_key_repeat_and_motion_cb(&cb);                             \
       +        if (num == -1)                                                           \
       +                return err_invalid_key(buffer);                                  \
       +        if (num == 0)                                                            \
       +                num = 1;                                                         \
                int new_line, new_index, new_real_index;                                 \
                func(                                                                    \
                    buffer,                                                              \
                    buffer->cur_line, buffer->cur_index, num,                            \
                    &new_line, &new_index, &new_real_index                               \
                );                                                                       \
       -        if (e != NULL && e->motion_cb != NULL) {                                 \
       -                e->motion_cb(buffer, new_line, new_real_index, KEY_MOTION_CHAR); \
       +        if (cb != NULL) {                                                        \
       +                cb(buffer, new_line, new_real_index, KEY_MOTION_CHAR);           \
                } else {                                                                 \
                        if (buffer->common->mode == VISUAL) {                            \
                                ledit_buffer_set_selection(                              \
       t@@ -1411,16 +1421,12 @@ GEN_WORD_MOVEMENT(prev_bigword, ledit_buffer_prev_bigword)
        
        static void
        move_cursor_left_right(ledit_buffer *buffer, int dir, int allow_illegal_index) {
       -        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);
       -        }
       +        motion_callback cb;
       +        int num = get_key_repeat_and_motion_cb(&cb);
       +        if (num == -1)
       +                (void)err_invalid_key(buffer);
       +        if (num == 0)
       +                num = 1;
        
                /* FIXME: trailing */
                int trailing = 0, tmp_index;
       t@@ -1456,16 +1462,15 @@ move_cursor_left_right(ledit_buffer *buffer, int dir, int allow_illegal_index) {
                   of the line because it's always covering a character */
                if (new_index >= cur_line->len) {
                        if (!allow_illegal_index &&
       -                    buffer->common->mode == NORMAL &&
       -                    (e == NULL || e->motion_cb == NULL)) {
       +                    buffer->common->mode == NORMAL && cb == NULL) {
                                new_index = last_index;
                        } else {
                                /* FIXME: I guess this is unnecessary */
                                new_index = cur_line->len;
                        }
                }
       -        if (e != NULL && e->motion_cb != NULL) {
       -                e->motion_cb(buffer, buffer->cur_line, new_index, KEY_MOTION_CHAR);
       +        if (cb != NULL) {
       +                cb(buffer, buffer->cur_line, new_index, KEY_MOTION_CHAR);
                } else {
                        buffer->cur_index = new_index;
                        if (buffer->common->mode == VISUAL) {
       t@@ -1578,21 +1583,17 @@ enter_insert(ledit_buffer *buffer, char *text, int len) {
                return (struct action){ACTION_NONE, NULL};
        }
        
       -/* FIXME: Check if previous key allows motion command - or is this checked automatically before? */
       +/* FIXME: Check if previous key allows motion command - or should this be checked automatically before? */
        static void
        move_cursor_up_down(ledit_buffer *buffer, int dir) {
                int new_line, new_softline;
        
       -        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);
       -        }
       +        motion_callback cb;
       +        int num = get_key_repeat_and_motion_cb(&cb);
       +        if (num == -1)
       +                (void)err_invalid_key(buffer);
       +        if (num == 0)
       +                num = 1;
                num *= dir;
        
                get_new_line_softline(
       t@@ -1602,9 +1603,9 @@ move_cursor_up_down(ledit_buffer *buffer, int dir) {
        
                ledit_line *cur_lline = ledit_buffer_get_line(buffer, buffer->cur_line);
                ledit_line *new_lline = ledit_buffer_get_line(buffer, new_line);
       -        if (e != NULL && e->motion_cb != NULL) {
       +        if (cb != NULL) {
                        PangoLayoutLine *pl = pango_layout_get_line_readonly(new_lline->layout, new_softline);
       -                e->motion_cb(buffer, new_line, pl->start_index, KEY_MOTION_LINE);
       +                cb(buffer, new_line, pl->start_index, KEY_MOTION_LINE);
                } else {
                        int lineno, x;
                        ledit_pos_to_x_softline(cur_lline, buffer->cur_index, &x, &lineno);
       t@@ -1647,10 +1648,13 @@ static struct action
        cursor_to_beginning(ledit_buffer *buffer, char *text, int len) {
                (void)text;
                (void)len;
       -        struct key_stack_elem *e = pop_key_stack();
       -        /* FIXME: error when no callback? */
       -        if (e != NULL && e->motion_cb != NULL) {
       -                e->motion_cb(buffer, buffer->cur_line, 0, KEY_MOTION_CHAR);
       +        motion_callback cb;
       +        int num = get_key_repeat_and_motion_cb(&cb);
       +        if (num == -1)
       +                return err_invalid_key(buffer);
       +        /* FIXME: should anything be done with num? */
       +        if (cb != NULL) {
       +                cb(buffer, buffer->cur_line, 0, KEY_MOTION_CHAR);
                } else {
                        buffer->cur_index = 0;
                        if (buffer->common->mode == VISUAL) {
       t@@ -1888,6 +1892,10 @@ static struct action                                                            
        name##_cb(ledit_buffer *buffer, char *text, int len) {                             \
                motion_callback cb = NULL;                                                 \
                int num = get_key_repeat_and_motion_cb(&cb);                               \
       +        if (num == -1)                                                             \
       +                return err_invalid_key(buffer);                                    \
       +        if (num == 0)                                                              \
       +                num = 1;                                                           \
                ledit_line *ll = ledit_buffer_get_line(buffer, buffer->cur_line);          \
                int new_index = buffer->cur_index;                                         \
                for (int i = 0; i < num; i++) {                                            \
   DIR diff --git a/keys_basic_config.h b/keys_basic_config.h
       t@@ -41,7 +41,7 @@ static struct action push_6(ledit_buffer *buffer, char *text, int len);
        static struct action push_7(ledit_buffer *buffer, char *text, int len);
        static struct action push_8(ledit_buffer *buffer, char *text, int len);
        static struct action push_9(ledit_buffer *buffer, char *text, int len);
       -static struct action key_d(ledit_buffer *buffer, char *text, int len);
       +static struct action delete(ledit_buffer *buffer, char *text, int len);
        static struct action enter_visual(ledit_buffer *buffer, char *text, int len);
        static struct action switch_selection_end(ledit_buffer *buffer, char *text, int len);
        static struct action clipcopy(ledit_buffer *buffer, char *text, int len);
       t@@ -125,7 +125,7 @@ static struct key keys_en[] = {
                {"9",  0, 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBER, &push_9},
                {"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},
       +        {"d",  0, 0, NORMAL|VISUAL, KEY_ANY, KEY_MOTION|KEY_NUMBERALLOWED, &delete},
                {"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},