URI: 
       tAdd basic repetition and motion callback support to cursor movement keys - 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 3895a14f2fb3bcc35ba842e259074a67adddb8a1
   DIR parent 52cea73bf0e43483210cb94e5177989bc524a30c
  HTML Author: lumidify <nobody@lumidify.org>
       Date:   Thu, 13 May 2021 21:54:24 +0200
       
       Add basic repetition and motion callback support to cursor movement keys
       
       Diffstat:
         M buffer.c                            |      12 +++++++++---
         M ledit.c                             |     249 ++++++++++++++++++++-----------
       
       2 files changed, 171 insertions(+), 90 deletions(-)
       ---
   DIR diff --git a/buffer.c b/buffer.c
       t@@ -494,10 +494,16 @@ ledit_delete_range(
                                }
                                delete_line_section(buffer, line_index1, b1, b2 - b1);
                                *new_line_ret = line_index1;
       +                        ledit_line *ll = ledit_get_line(buffer, line_index1);
       +                        /* FIXME: where should this be done? */
       +                        /* FIXME: also do this below in the else statement */
       +                        if (buffer->state->mode == NORMAL && b1 == ll->len && b1 > 0) {
       +                                /* FIXME: use grapheme instead of character! */
       +                                b1 = ll->len - 1;
       +                                while (b1 > 0 && ((ll->text[b1] & 0xC0) == 0x80))
       +                                        b1--;
       +                        }
                                *new_byte_ret = b1;
       -                        /* FIXME: this needs to be checked by calling code to
       -                           move cursor one back if in normal mode and at end
       -                           of line */
                        } else {
                                int l1, l2, b1, b2;
                                if (line_index1 < line_index2) {
   DIR diff --git a/ledit.c b/ledit.c
       t@@ -91,9 +91,10 @@ static void resize_window(int w, int h);
        
        static void backspace(void);
        static void delete_key(void);
       -static void move_cursor(int dir);
       +static void move_cursor_left_right(int dir);
        static void cursor_left(void);
        static void cursor_right(void);
       +static void move_cursor_up_down(int dir);
        static void cursor_down(void);
        static void cursor_up(void);
        static void cursor_to_beginning(void);
       t@@ -117,6 +118,67 @@ static void key_d_cb(int line, int char_pos, enum key_type type);
        
        static void change_keyboard(char *lang);
        static void key_press(XEvent event);
       +static void get_new_line_softline(
       +    int cur_line, int cur_index, int movement,
       +    int *new_line_ret, int *new_softline_ret
       +);
       +
       +static void
       +get_new_line_softline(
       +    int cur_line, int cur_index, int movement,
       +    int *new_line_ret, int *new_softline_ret) {
       +        ledit_line *line = ledit_get_line(buffer, cur_line);
       +        int x, softline;
       +        pango_layout_index_to_line_x(line->layout, cur_index, 0, &softline, &x);
       +        if (movement > 0) {
       +                int softlines = pango_layout_get_line_count(line->layout);
       +                if (softlines - softline > movement) {
       +                        *new_line_ret = cur_line;
       +                        *new_softline_ret = softline + movement;
       +                } else {
       +                        movement -= (softlines - softline - 1);
       +                        int endline = cur_line + 1;
       +                        while (movement > 0 && endline < buffer->lines_num) {
       +                                line = ledit_get_line(buffer, endline);
       +                                softlines = pango_layout_get_line_count(line->layout);
       +                                movement -= softlines;
       +                                endline++;
       +                        }
       +                        endline--;
       +                        if (movement <= 0) {
       +                                *new_softline_ret = movement + softlines - 1;
       +                        } else {
       +                                *new_softline_ret = softlines - 1;
       +                        }
       +                        *new_line_ret = endline;
       +                }
       +        } else if (movement < 0) {
       +                int softlines = 0;
       +                if (softline + movement >= 0) {
       +                        *new_line_ret = cur_line;
       +                        *new_softline_ret = softline + movement;
       +                } else {
       +                        movement += softline;
       +                        int endline = cur_line - 1;
       +                        while (movement < 0 && endline >= 0) {
       +                                line = ledit_get_line(buffer, endline);
       +                                softlines = pango_layout_get_line_count(line->layout);
       +                                movement += softlines;
       +                                endline--;
       +                        }
       +                        endline++;
       +                        if (movement >= 0) {
       +                                *new_softline_ret = movement;
       +                        } else {
       +                                *new_softline_ret = 0;
       +                        }
       +                        *new_line_ret = endline;
       +                }
       +        } else {
       +                *new_line_ret = cur_line;
       +                *new_softline_ret = softline;
       +        }
       +}
        
        static void
        key_d(void) {
       t@@ -132,33 +194,14 @@ key_d(void) {
                                int prevnum = e->count > 0 ? e->count : 1;
                                num = num > 0 ? num : 1;
                                int lines = num * prevnum;
       -
       -                        ledit_line *line = ledit_get_line(buffer, buffer->cur_line);
       -                        int x, softline;
       -                        pango_layout_index_to_line_x(line->layout, buffer->cur_index, 0, &softline, &x);
       -                        int softlines = pango_layout_get_line_count(line->layout);
       -                        if (softlines - softline >= lines) {
       -                                PangoLayoutLine *l = pango_layout_get_line_readonly(line->layout, softline + lines - 1);
       -                                e->motion_cb(buffer->cur_line, l->start_index, KEY_MOTION_LINE);
       -                        } else {
       -                                lines -= (softlines - softline);
       -                                int endline = buffer->cur_line + 1;
       -                                while (lines > 0 && endline < buffer->lines_num) {
       -                                        line = ledit_get_line(buffer, endline);
       -                                        softlines = pango_layout_get_line_count(line->layout);
       -                                        lines -= softlines;
       -                                        endline++;
       -                                }
       -                                endline--;
       -                                int endsoftline = 0;
       -                                if (lines <= 0) {
       -                                        endsoftline = lines + softlines - 1;
       -                                } else {
       -                                        endsoftline = softlines - 1;
       -                                }
       -                                PangoLayoutLine *l = pango_layout_get_line_readonly(line->layout, endsoftline);
       -                                e->motion_cb(endline, l->start_index, KEY_MOTION_LINE);
       -                        }
       +                        int new_line, new_softline;
       +                        get_new_line_softline(
       +                            buffer->cur_line, buffer->cur_index, lines - 1,
       +                            &new_line, &new_softline
       +                        );
       +                        ledit_line *ll = ledit_get_line(buffer, new_line);
       +                        PangoLayoutLine *pl = pango_layout_get_line_readonly(ll->layout, new_softline);
       +                        e->motion_cb(new_line, pl->start_index, KEY_MOTION_LINE);
                                clear_key_stack();
                        } else if (e != NULL) {
                                clear_key_stack();
       t@@ -175,7 +218,6 @@ key_d(void) {
        /* FIXME: should this get number of lines to remove or actual end line? */
        static void
        key_d_cb(int line, int char_pos, enum key_type type) {
       -        printf("%d, %d\n", line, char_pos);
                int line_based = type == KEY_MOTION_LINE ? 1 : 0;
                ledit_delete_range(
                    buffer, line_based,
       t@@ -819,51 +861,79 @@ delete_key(void) {
                ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
        }
        
       -/* FIXME: this comment needs to be implemented still */
       -/* num is number of graphemes to move (see pango documentation) - negative
       - * is left, positive is right */
        static void
       -move_cursor(int dir) {
       +move_cursor_left_right(int dir) {
       +        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 : 0;
       +                        e = pop_key_stack();
       +                }
       +                if (e != NULL)
       +                        num *= (e->count > 0 ? e->count : 1);
       +        }
       +
                /* FIXME: trailing */
       -        int trailing, last_index = buffer->cur_index;
       +        int trailing = 0, tmp_index;
                ledit_line *cur_line = ledit_get_line(buffer, buffer->cur_line);
       -        pango_layout_move_cursor_visually(
       -            cur_line->layout, TRUE,
       -            buffer->cur_index, 0, dir,
       -            &buffer->cur_index, &trailing
       -        );
       +        int new_index = buffer->cur_index, last_index = buffer->cur_index;
       +        while (num > 0) {
       +                tmp_index = new_index;
       +                pango_layout_move_cursor_visually(
       +                    cur_line->layout, TRUE,
       +                    new_index, trailing, dir,
       +                    &new_index, &trailing
       +                );
       +                /* for some reason, this is necessary */
       +                if (new_index < 0)
       +                        new_index = 0;
       +                else if (new_index > cur_line->len)
       +                        new_index = cur_line->len;
       +                num--;
       +                if (tmp_index != new_index)
       +                        last_index = tmp_index;
       +        }
                /* FIXME: Allow cursor to be at end of soft line */
                /* we don't currently support a difference between the cursor being at
                   the end of a soft line and the beginning of the next line */
                /* FIXME: spaces at end of softlines are weird in normal mode */
                while (trailing > 0) {
                        trailing--;
       -                buffer->cur_index++;
       -                while (buffer->cur_index < cur_line->len &&
       -                       ((cur_line->text[buffer->cur_index] & 0xC0) == 0x80))
       -                        buffer->cur_index++;
       +                new_index++;
       +                while (new_index < cur_line->len &&
       +                       ((cur_line->text[new_index] & 0xC0) == 0x80))
       +                        new_index++;
                }
       -        if (buffer->cur_index < 0)
       -                buffer->cur_index = 0;
       +        if (new_index < 0)
       +                new_index = 0;
                /* when in normal mode, the cursor cannot be at the very end
                   of the line because it's always covering a character */
       -        if (buffer->cur_index >= cur_line->len) {
       -                if (state.mode == NORMAL)
       -                        buffer->cur_index = last_index;
       -                else
       -                        buffer->cur_index = cur_line->len;
       +        if (new_index >= cur_line->len) {
       +                if (state.mode == NORMAL && (e == NULL || e->motion_cb == NULL)) {
       +                        new_index = last_index;
       +                } else {
       +                        /* FIXME: I guess this is unnecessary */
       +                        new_index = cur_line->len;
       +                }
                }
       -        ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
       +        if (e != NULL & e->motion_cb != NULL) {
       +                e->motion_cb(buffer->cur_line, new_index, KEY_MOTION_CHAR);
       +        } else {
       +                buffer->cur_index = new_index;
       +                ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
       +        }
       +        clear_key_stack();
        }
        
        static void
        cursor_left(void) {
       -        move_cursor(-1);
       +        move_cursor_left_right(-1);
        }
        
        static void
        cursor_right(void) {
       -        move_cursor(1);
       +        move_cursor_left_right(1);
        }
        
        static void
       t@@ -901,48 +971,53 @@ enter_insert(void) {
                ledit_wipe_line_cursor_attrs(buffer, buffer->cur_line);
        }
        
       +/* FIXME: Check if previous key allows motion command - or is this checked automatically before? */
        static void
       -cursor_down(void) {
       -        int lineno, x;
       -        ledit_line *cur_line = ledit_get_line(buffer, buffer->cur_line);
       -        ledit_pos_to_x_softline(cur_line, buffer->cur_index, &x, &lineno);
       -
       -        int maxlines = pango_layout_get_line_count(cur_line->layout);
       -        if (lineno == maxlines - 1) {
       -                ledit_wipe_line_cursor_attrs(buffer, buffer->cur_line);
       -                /* move to the next hard line */
       -                if (buffer->cur_line < buffer->lines_num - 1) {
       -                        buffer->cur_line++;
       -                        cur_line = ledit_get_line(buffer, buffer->cur_line);
       -                        ledit_x_softline_to_pos(cur_line, x, 0, &buffer->cur_index);
       +move_cursor_up_down(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 : 0;
       +                        e = pop_key_stack();
                        }
       +                if (e != NULL)
       +                        num *= (e->count > 0 ? e->count : 1);
       +        }
       +        num *= dir;
       +
       +        get_new_line_softline(
       +            buffer->cur_line, buffer->cur_index, num,
       +            &new_line, &new_softline
       +        );
       +
       +        ledit_line *cur_lline = ledit_get_line(buffer, buffer->cur_line);
       +        ledit_line *new_lline = ledit_get_line(buffer, new_line);
       +        if (e != NULL && e->motion_cb != NULL) {
       +                PangoLayoutLine *pl = pango_layout_get_line_readonly(new_lline->layout, new_softline);
       +                e->motion_cb(new_line, pl->start_index, KEY_MOTION_LINE);
                } else {
       -                /* move to the next soft line */
       -                ledit_x_softline_to_pos(cur_line, x, lineno + 1, &buffer->cur_index);
       +                int lineno, x;
       +                ledit_pos_to_x_softline(cur_lline, buffer->cur_index, &x, &lineno);
       +                ledit_x_softline_to_pos(new_lline, x, new_softline, &buffer->cur_index);
       +                if (buffer->cur_line != new_line)
       +                        ledit_wipe_line_cursor_attrs(buffer, buffer->cur_line);
       +                buffer->cur_line = new_line;
       +                ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
                }
       -        ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
       +        clear_key_stack();
       +}
       +
       +static void
       +cursor_down(void) {
       +        move_cursor_up_down(1);
        }
        
        static void
        cursor_up(void) {
       -        int lineno, x;
       -        ledit_line *cur_line = ledit_get_line(buffer, buffer->cur_line);
       -        ledit_pos_to_x_softline(cur_line, buffer->cur_index, &x, &lineno);
       -        buffer->trailing = 0;
       -        if (lineno == 0) {
       -                ledit_wipe_line_cursor_attrs(buffer, buffer->cur_line);
       -                /* move to the previous hard line */
       -                if (buffer->cur_line > 0) {
       -                        buffer->cur_line--;
       -                        cur_line = ledit_get_line(buffer, buffer->cur_line);
       -                        int maxlines = pango_layout_get_line_count(cur_line->layout);
       -                        ledit_x_softline_to_pos(cur_line, x, maxlines - 1, &buffer->cur_index);
       -                }
       -        } else {
       -                /* move to the previous soft line */
       -                ledit_x_softline_to_pos(cur_line, x, lineno - 1, &buffer->cur_index);
       -        }
       -        ledit_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
       +        move_cursor_up_down(-1);
        }
        
        static void