URI: 
       tAdd change command ('c') - 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 67de119de536bb598da9aba39ecfdde09562a8d3
   DIR parent 5c1f64919a8a2974c107614f144fd932aa6db1bf
  HTML Author: lumidify <nobody@lumidify.org>
       Date:   Mon,  1 Nov 2021 19:04:48 +0100
       
       Add change command ('c')
       
       Diffstat:
         M buffer.c                            |       3 +++
         M keys_basic.c                        |      75 +++++++++++++++++++++++++++++++
         M keys_basic_config.h                 |       2 ++
       
       3 files changed, 80 insertions(+), 0 deletions(-)
       ---
   DIR diff --git a/buffer.c b/buffer.c
       t@@ -1037,6 +1037,7 @@ ledit_buffer_delete_range(
                ledit_buffer_recalc_from_line(buffer, min > 0 ? min - 1 : min);
        }
        
       +/* Note: line_index* and byte_index* don't need to be sorted */
        /* FIXME: use at least somewhat sensible variable names */
        void
        ledit_buffer_delete_range_base(
       t@@ -1049,6 +1050,8 @@ ledit_buffer_delete_range_base(
                /* range line x, range byte x */
                int rgl1 = 0, rgb1 = 0, rgl2 = 0, rgb2 = 0;
                int new_line = 0, new_byte = 0;
       +        /* FIXME: could this be simplified by just calculating the range and then using
       +           the non-line-based version? */
                if (line_based) {
                        int x, softline1, softline2;
                        ledit_line *line1 = ledit_buffer_get_line(buffer, line_index1);
   DIR diff --git a/keys_basic.c b/keys_basic.c
       t@@ -102,6 +102,7 @@ static void get_new_line_softline(
            int movement, int *new_line_ret, int *new_softline_ret
        );
        static void move_cursor_logically(ledit_buffer *buffer, int movement_dir, int allow_illegal_index);
       +static void change_cb(ledit_buffer *buffer, int line, int char_pos, enum key_type type);
        
        /* FIXME: move to common */
        static void
       t@@ -587,6 +588,80 @@ screen_down(ledit_buffer *buffer, char *text, int len) {
                return (struct action){ACTION_NONE, NULL};
        }
        
       +/* FIXME: clear selection on most commands */
       +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) {
       +                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();
       +                        e->key = KEY_MOTION; /* ? */
       +                        e->count = num;
       +                        e->motion_cb = &change_cb;
       +                }
       +        }
       +        return (struct action){ACTION_NONE, NULL};
       +}
       +
       +static void
       +change_cb(ledit_buffer *buffer, int line, int char_pos, enum key_type type) {
       +        /* set mode first so the deletion is included in the undo group */
       +        ledit_buffer_set_mode(buffer, INSERT);
       +        int line_based = type == KEY_MOTION_LINE ? 1 : 0;
       +        /* this hackery is needed to avoid deleting the entire last line and
       +           instead leave an empty line - this should be made nicer (FIXME) */
       +        int pos1 = buffer->cur_index, pos2 = char_pos, x, sli;
       +        if (line_based) {
       +                ledit_line *ll = ledit_buffer_get_line(buffer, buffer->cur_line);
       +                pango_layout_index_to_line_x(ll->layout, buffer->cur_index, 0, &sli, &x);
       +                PangoLayoutLine *sl = pango_layout_get_line_readonly(ll->layout, sli);
       +                pos1 = sl->start_index;
       +                ll = ledit_buffer_get_line(buffer, line);
       +                pango_layout_index_to_line_x(ll->layout, char_pos, 0, &sli, &x);
       +                sl = pango_layout_get_line_readonly(ll->layout, sli);
       +                pos2 = sl->start_index + sl->length;
       +        }
       +        /* force line_based to 0 (see comment about hackery above) */
       +        delete_range(
       +            buffer, 0, 0,
       +            buffer->cur_line, pos1,
       +            line, pos2
       +        );
       +        paste_buffer_line_based = line_based;
       +        ledit_buffer_wipe_line_cursor_attrs(buffer, buffer->cur_line);
       +}
       +
        static struct action
        key_d(ledit_buffer *buffer, char *text, int len) {
                (void)text;
   DIR diff --git a/keys_basic_config.h b/keys_basic_config.h
       t@@ -66,6 +66,7 @@ static struct action scroll_lines_down(ledit_buffer *buffer, char *text, int len
        static struct action move_to_line(ledit_buffer *buffer, char *text, int len);
        static struct action paste_normal(ledit_buffer *buffer, char *text, int len);
        static struct action paste_normal_backwards(ledit_buffer *buffer, char *text, int len);
       +static struct action change(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@@ -101,6 +102,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, &key_x},
                {"d",  0, 0, NORMAL|VISUAL, KEY_ANY, KEY_MOTION|KEY_NUMBERALLOWED, &key_d},
       +        {"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},
                {"c",  ControlMask, 0, INSERT|VISUAL, KEY_ANY, KEY_ANY, &clipcopy},