ledit

Text editor (WIP)
git clone git://lumidify.org/ledit.git (fast, but not encrypted)
git clone https://lumidify.org/git/ledit.git (encrypted, but very slow)
Log | Files | Refs | README | LICENSE

commit 920ff77cf2e64f74df86d0a415cd6e3180a36c8d
parent 250e61a764d867385a09548558b0df6c28af8036
Author: lumidify <nobody@lumidify.org>
Date:   Sun,  7 Nov 2021 21:21:36 +0100

Implement x, X, D, C; fix issue with undo

Diffstat:
Mbuffer.c | 36+++++++++++++++++++++++-------------
Mbuffer.h | 4++--
Mkeys_basic.c | 218++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------
Mkeys_basic_config.h | 10++++++++--
Mundo.c | 18++++++++++++------
5 files changed, 218 insertions(+), 68 deletions(-)

diff --git a/buffer.c b/buffer.c @@ -902,35 +902,45 @@ line_byte_to_char(ledit_line *line, int byte) { } int -ledit_buffer_next_cursor_pos(ledit_buffer *buffer, int line, int byte) { +ledit_buffer_next_cursor_pos(ledit_buffer *buffer, int line, int byte, int num) { int nattrs; ledit_line *ll = ledit_buffer_get_line(buffer, line); int c = line_byte_to_char(ll, byte); - int cur_byte = ledit_line_next_utf8(ll, byte); + int cur_byte = byte; const PangoLogAttr *attrs = pango_layout_get_log_attrs_readonly(ll->layout, &nattrs); - for (int i = c + 1; i < nattrs; i++) { - if (attrs[i].is_cursor_position) - return cur_byte; - cur_byte = ledit_line_next_utf8(ll, cur_byte); + for (int i = 0; i < num; i++) { + cur_byte = ledit_line_next_utf8(ll, byte); + for (c++; c < nattrs; c++) { + if (attrs[c].is_cursor_position) + break; + cur_byte = ledit_line_next_utf8(ll, cur_byte); + } + if (cur_byte >= ll->len) + break; } - return ll->len; + return cur_byte <= ll->len ? cur_byte : ll->len; } int -ledit_buffer_prev_cursor_pos(ledit_buffer *buffer, int line, int byte) { +ledit_buffer_prev_cursor_pos(ledit_buffer *buffer, int line, int byte, int num) { int nattrs; ledit_line *ll = ledit_buffer_get_line(buffer, line); int c = line_byte_to_char(ll, byte); - int cur_byte = ledit_line_prev_utf8(ll, byte); + int cur_byte = byte; const PangoLogAttr *attrs = pango_layout_get_log_attrs_readonly(ll->layout, &nattrs); - for (int i = c - 1; i >= 0; i--) { - if (attrs[i].is_cursor_position) - return cur_byte; + for (int i = 0; i < num; i++) { cur_byte = ledit_line_prev_utf8(ll, cur_byte); + for (c--; c >= 0; c--) { + if (attrs[c].is_cursor_position) + break; + cur_byte = ledit_line_prev_utf8(ll, cur_byte); + } + if (cur_byte <= 0) + break; } - return 0; + return cur_byte > 0 ? cur_byte : 0; } static int diff --git a/buffer.h b/buffer.h @@ -57,8 +57,8 @@ void ledit_pos_to_x_softline(ledit_line *line, int pos, int *x_ret, int *softlin void ledit_x_softline_to_pos(ledit_line *line, int x, int softline, int *pos_ret); int ledit_line_next_utf8(ledit_line *line, int index); int ledit_line_prev_utf8(ledit_line *line, int index); -int ledit_buffer_next_cursor_pos(ledit_buffer *buffer, int line, int byte); -int ledit_buffer_prev_cursor_pos(ledit_buffer *buffer, int line, int byte); +int ledit_buffer_next_cursor_pos(ledit_buffer *buffer, int line, int byte, int num); +int ledit_buffer_prev_cursor_pos(ledit_buffer *buffer, int line, int byte, int num); void ledit_buffer_next_word(ledit_buffer *buffer, int line, int byte, int num_repeat, int *line_ret, int *byte_ret, int *real_byte_ret); void ledit_buffer_next_word_end(ledit_buffer *buffer, int line, int byte, int num_repeat, int *line_ret, int *byte_ret, int *real_byte_ret); diff --git a/keys_basic.c b/keys_basic.c @@ -101,6 +101,7 @@ static void unwind_repetition_stack(void); static void advance_repetition_stack(void); static void discard_repetition_stack(void); +static int key_stack_empty(void); static struct key_stack_elem *push_key_stack(void); static struct key_stack_elem *peek_key_stack(void); static struct key_stack_elem *pop_key_stack(void); @@ -117,6 +118,7 @@ static void get_new_line_softline( static void move_cursor_in_line_dir(ledit_buffer *buffer, int dir, int allow_illegal_index); 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); +static void push_undo_empty_insert(ledit_buffer *buffer, int line, int index, int start_group); /* FIXME: move to common */ static void @@ -126,6 +128,11 @@ swap(int *a, int *b) { *b = tmp; } +static int +key_stack_empty(void) { + return key_stack.len == 0; +} + static struct key_stack_elem * push_key_stack(void) { struct key_stack_elem *e; @@ -322,14 +329,24 @@ delete_range( } static void -insert_text(ledit_buffer *buffer, int line, int index, char *text, int len, int new_line, int new_index, int start_group) { +insert_text( + ledit_buffer *buffer, + int line, int index, + char *text, int len, + int cur_line1, int cur_index1, + int cur_line2, int cur_index2, int start_group) { if (len < 0) len = strlen(text); /* FIXME: this is kind of hacky... */ txtbuf ins_buf = {.text = text, .len = len, .cap = len}; ledit_range cur_range, del_range; - cur_range.line1 = buffer->cur_line; - cur_range.byte1 = buffer->cur_index; + if (cur_line1 >= 0 && cur_index1 >= 0) { + cur_range.line1 = cur_line1; + cur_range.byte1 = cur_index1; + } else { + cur_range.line1 = buffer->cur_line; + cur_range.byte1 = buffer->cur_index; + } del_range.line1 = line; del_range.byte1 = index; int cur_line, cur_index; @@ -339,9 +356,9 @@ insert_text(ledit_buffer *buffer, int line, int index, char *text, int len, int ); /* this is mainly for pasting, where the new line and index should not be at the end of the pasted text */ - if (new_line >= 0 && new_index >= 0) { - cur_range.line2 = buffer->cur_line = new_line; - cur_range.byte2 = buffer->cur_index = new_index; + if (cur_line2 >= 0 && cur_index2 >= 0) { + cur_range.line2 = buffer->cur_line = cur_line2; + cur_range.byte2 = buffer->cur_index = cur_index2; } else { cur_range.line2 = buffer->cur_line = cur_line; cur_range.byte2 = buffer->cur_index = cur_index; @@ -401,6 +418,73 @@ get_key_repeat(void) { } static struct action +delete_chars_forwards(ledit_buffer *buffer, char *text, int len) { + (void)text; + (void)len; + int num = get_key_repeat(); + if (num == -1) { + ledit_window_show_message(buffer->window, "Invalid key", -1); + return (struct action){ACTION_NONE, NULL}; + } else if (num == 0) { + num = 1; + } + int end_index = ledit_buffer_next_cursor_pos( + buffer, buffer->cur_line, buffer->cur_index, num + ); + delete_range( + buffer, 0, 0, + buffer->cur_line, buffer->cur_index, + buffer->cur_line, end_index + ); + paste_buffer_line_based = 0; + 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); + return (struct action){ACTION_NONE, NULL}; +} + +static struct action +delete_chars_backwards(ledit_buffer *buffer, char *text, int len) { + (void)text; + (void)len; + int num = get_key_repeat(); + if (num == -1) { + ledit_window_show_message(buffer->window, "Invalid key", -1); + return (struct action){ACTION_NONE, NULL}; + } else if (num == 0) { + num = 1; + } + int start_index = ledit_buffer_prev_cursor_pos( + buffer, buffer->cur_line, buffer->cur_index, num + ); + delete_range( + buffer, 0, 0, + buffer->cur_line, start_index, + buffer->cur_line, buffer->cur_index + ); + paste_buffer_line_based = 0; + /* I guess this is technically unnecessary since only + text before the current position is deleted */ + buffer->cur_index = ledit_buffer_get_legal_normal_pos( + buffer, buffer->cur_line, start_index + ); + ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index); + return (struct action){ACTION_NONE, NULL}; +} + +/* used to set cursor - I guess this is sort of a hack */ +static void +push_undo_empty_insert(ledit_buffer *buffer, int line, int index, int start_group) { + txtbuf ins_buf = {.text = "", .len = 0, .cap = 0}; + ledit_range ins_range = {.line1 = line, .byte1 = index, .line2 = line, .byte2 = index}; + ledit_range cur_range = {.line1 = line, .byte1 = index, .line2 = line, .byte2 = index}; + ledit_push_undo_insert( + buffer->undo, &ins_buf, ins_range, cur_range, start_group, buffer->common->mode + ); +} + +static struct action append_line_above(ledit_buffer *buffer, char *text, int len) { int sli, x; /* do this here already so the mode group is the same for the newline insertion */ @@ -408,10 +492,10 @@ append_line_above(ledit_buffer *buffer, char *text, int len) { 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); if (sli == 0) { - insert_text(buffer, buffer->cur_line, 0, "\n", -1, buffer->cur_line, 0, 1); + insert_text(buffer, buffer->cur_line, 0, "\n", -1, -1, -1, buffer->cur_line, 0, 1); } else { PangoLayoutLine *sl = pango_layout_get_line_readonly(ll->layout, sli); - insert_text(buffer, buffer->cur_line, sl->start_index, "\n\n", -1, buffer->cur_line + 1, 0, 1); + insert_text(buffer, buffer->cur_line, sl->start_index, "\n\n", -1, -1, -1, buffer->cur_line + 1, 0, 1); } return (struct action){ACTION_NONE, NULL}; } @@ -424,29 +508,35 @@ append_line_below(ledit_buffer *buffer, char *text, int len) { pango_layout_index_to_line_x(ll->layout, buffer->cur_index, 0, &sli, &x); PangoLayoutLine *sl = pango_layout_get_line_readonly(ll->layout, sli); if (sl->start_index + sl->length == ll->len) { - insert_text(buffer, buffer->cur_line, ll->len, "\n", -1, buffer->cur_line + 1, 0, 1); + insert_text(buffer, buffer->cur_line, ll->len, "\n", -1, -1, -1, buffer->cur_line + 1, 0, 1); } else { - insert_text(buffer, buffer->cur_line, sl->start_index + sl->length, "\n\n", -1, buffer->cur_line + 1, 0, 1); + insert_text(buffer, buffer->cur_line, sl->start_index + sl->length, "\n\n", -1, -1, -1, buffer->cur_line + 1, 0, 1); } return (struct action){ACTION_NONE, NULL}; } static struct action append_after_cursor(ledit_buffer *buffer, char *text, int len) { + enter_insert(buffer, text, len); + /* make cursor jump back to original position on undo */ + push_undo_empty_insert(buffer, buffer->cur_line, buffer->cur_index, 1); buffer->cur_index = ledit_buffer_next_cursor_pos( - buffer, buffer->cur_line, buffer->cur_index + buffer, buffer->cur_line, buffer->cur_index, 1 ); - return enter_insert(buffer, text, len); + return (struct action){ACTION_NONE, NULL}; } static struct action append_after_eol(ledit_buffer *buffer, char *text, int len) { int sli, x; + enter_insert(buffer, text, len); + /* make cursor jump back to original position on undo */ + push_undo_empty_insert(buffer, buffer->cur_line, buffer->cur_index, 1); 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); buffer->cur_index = sl->start_index + sl->length; - return enter_insert(buffer, text, len); + return (struct action){ACTION_NONE, NULL}; } /* FIXME: allow motion callback! */ @@ -651,6 +741,57 @@ screen_down(ledit_buffer *buffer, char *text, int len) { return (struct action){ACTION_NONE, NULL}; } +static struct action +delete_to_eol(ledit_buffer *buffer, char *text, int len) { + (void)text; + (void)len; + /* FIXME: move to separate function */ + if (!key_stack_empty()) { + clear_key_stack(); + ledit_window_show_message(buffer->window, "Invalid key", -1); + return (struct action){ACTION_NONE, NULL}; + } + int x, sli; + 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); + delete_range( + buffer, 0, 0, + buffer->cur_line, buffer->cur_index, + buffer->cur_line, sl->start_index + sl->length + ); + paste_buffer_line_based = 0; + 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); + return (struct action){ACTION_NONE, NULL}; +} + +static struct action +change_to_eol(ledit_buffer *buffer, char *text, int len) { + (void)text; + (void)len; + if (!key_stack_empty()) { + clear_key_stack(); + ledit_window_show_message(buffer->window, "Invalid key", -1); + return (struct action){ACTION_NONE, NULL}; + } + ledit_buffer_set_mode(buffer, INSERT); + int x, sli; + 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); + delete_range( + buffer, 0, 0, + buffer->cur_line, buffer->cur_index, + buffer->cur_line, sl->start_index + sl->length + ); + paste_buffer_line_based = 0; + ledit_buffer_wipe_line_cursor_attrs(buffer, buffer->cur_line); + return (struct action){ACTION_NONE, NULL}; +} + /* FIXME: clear selection on most commands */ /* FIXME: don't include escape when repeating change with '.'? */ static struct action @@ -808,7 +949,7 @@ paste_normal(ledit_buffer *buffer, char *text, int len) { PangoLayoutLine *sl = pango_layout_get_line_readonly(ll->layout, softline); insert_text( buffer, buffer->cur_line, sl->start_index + sl->length, - "\n", -1, buffer->cur_line, buffer->cur_index, 1 + "\n", -1, -1, -1, buffer->cur_line, buffer->cur_index, 1 ); int text_len = paste_buffer->len; ll = ledit_buffer_get_line(buffer, buffer->cur_line + 1); @@ -820,19 +961,22 @@ paste_normal(ledit_buffer *buffer, char *text, int len) { /* ensure pasted text is on its own hard line */ insert_text( buffer, buffer->cur_line + 1, 0, - "\n", -1, buffer->cur_line, buffer->cur_index, 0 + "\n", -1, -1, -1, buffer->cur_line, buffer->cur_index, 0 ); } insert_text( buffer, buffer->cur_line + 1, 0, - paste_buffer->text, text_len, buffer->cur_line + 1, 0, 0 + paste_buffer->text, text_len, -1, -1, buffer->cur_line + 1, 0, 0 ); } else { + int old_line = buffer->cur_line; + int old_index = buffer->cur_index; /* must allow illegal index so text can be pasted at end of line */ move_cursor_logically(buffer, 1, 1); insert_text( buffer, buffer->cur_line, buffer->cur_index, - paste_buffer->text, paste_buffer->len, buffer->cur_line, buffer->cur_index, 1 + paste_buffer->text, paste_buffer->len, + old_line, old_index, buffer->cur_line, buffer->cur_index, 1 ); } ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index); @@ -857,7 +1001,7 @@ paste_normal_backwards(ledit_buffer *buffer, char *text, int len) { PangoLayoutLine *sl = pango_layout_get_line_readonly(ll->layout, softline); insert_text( buffer, buffer->cur_line, sl->start_index, - "\n", -1, buffer->cur_line, buffer->cur_index, 1 + "\n", -1, -1, -1, buffer->cur_line, buffer->cur_index, 1 ); int text_len = paste_buffer->len; ll = ledit_buffer_get_line(buffer, buffer->cur_line); @@ -871,18 +1015,19 @@ paste_normal_backwards(ledit_buffer *buffer, char *text, int len) { /* ensure pasted text is on its own hard line */ insert_text( buffer, buffer->cur_line, ll->len, - "\n", -1, buffer->cur_line, buffer->cur_index, 0 + "\n", -1, -1, -1, buffer->cur_line, buffer->cur_index, 0 ); new_line = buffer->cur_line + 1; } insert_text( buffer, new_line, 0, - paste_buffer->text, text_len, new_line, 0, 0 + paste_buffer->text, text_len, -1, -1, new_line, 0, 0 ); } else { insert_text( buffer, buffer->cur_line, buffer->cur_index, - paste_buffer->text, paste_buffer->len, buffer->cur_line, buffer->cur_index, 1 + paste_buffer->text, paste_buffer->len, + -1, -1, buffer->cur_line, buffer->cur_index, 1 ); } ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index); @@ -890,24 +1035,6 @@ paste_normal_backwards(ledit_buffer *buffer, char *text, int len) { return (struct action){ACTION_NONE, NULL}; } -static struct action -key_x(ledit_buffer *buffer, char *text, int len) { - (void)buffer; - (void)text; - (void)len; - struct key_stack_elem *e = pop_key_stack(); - int num = 1; - clear_key_stack(); - if (e && !(e->key & KEY_NUMBER)) - return (struct action){ACTION_NONE, NULL}; - if (e) - num = e->count; - if (num <= 0) - num = 1; - /* FIXME: actually do something */ - return (struct action){ACTION_NONE, NULL}; -} - static void push_num(int num) { struct key_stack_elem *e = peek_key_stack(); @@ -1245,7 +1372,7 @@ return_key(ledit_buffer *buffer, char *text, int len) { int start_group = 1; if (delete_selection(buffer)) start_group = 0; - insert_text(buffer, buffer->cur_line, buffer->cur_index, "\n", -1, -1, -1, start_group); + insert_text(buffer, buffer->cur_line, buffer->cur_index, "\n", -1, -1, -1, -1, -1, start_group); /* FIXME: these aren't needed, right? This only works in insert mode * anyways, so there's nothing to wipe */ /* ledit_buffer_wipe_line_cursor_attrs(buffer, buffer->cur_line); @@ -1571,7 +1698,7 @@ redo(ledit_buffer *buffer, char *text, int len) { static struct action insert_mode_insert_text(ledit_buffer *buffer, char *text, int len) { delete_selection(buffer); - insert_text(buffer, buffer->cur_line, buffer->cur_index, text, len, -1, -1, 1); + insert_text(buffer, buffer->cur_line, buffer->cur_index, text, len, -1, -1, -1, -1, 1); return (struct action){ACTION_NONE, NULL}; } @@ -1642,9 +1769,10 @@ search_str_forwards(char *haystack, int hlen, char *needle, int nlen, int start_ /* just to make the macro below works for all cases */ /* FIXME: is there a more elegant way to do this? */ static int -dummy_cursor_helper(ledit_buffer *buffer, int line, int index) { +dummy_cursor_helper(ledit_buffer *buffer, int line, int index, int num) { (void)buffer; (void)line; + (void)num; return index; } @@ -1674,13 +1802,13 @@ name##_cb(ledit_buffer *buffer, char *text, int len) { if (new_index >= 0) { \ if (cb != NULL) { \ new_index = cur_funcm( \ - buffer, buffer->cur_line, new_index \ + buffer, buffer->cur_line, new_index, 1 \ ); \ cb(buffer, buffer->cur_line, new_index, KEY_MOTION_CHAR); \ } else { \ if (buffer->common->mode == VISUAL) { \ buffer->cur_index = cur_funcv( \ - buffer, buffer->cur_line, new_index \ + buffer, buffer->cur_line, new_index, 1 \ ); \ ledit_buffer_set_selection( \ buffer, \ @@ -1689,7 +1817,7 @@ name##_cb(ledit_buffer *buffer, char *text, int len) { ); \ } else { \ buffer->cur_index = cur_funcn( \ - buffer, buffer->cur_line, new_index \ + buffer, buffer->cur_line, new_index, 1 \ ); \ ledit_buffer_set_line_cursor_attrs( \ buffer, buffer->cur_line, buffer->cur_index \ diff --git a/keys_basic_config.h b/keys_basic_config.h @@ -41,7 +41,6 @@ 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_x(ledit_buffer *buffer, char *text, int len); static struct action key_d(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); @@ -84,6 +83,10 @@ static struct action find_next_char_forwards(ledit_buffer *buffer, char *text, i static struct action find_next_char_backwards(ledit_buffer *buffer, char *text, int len); static struct action find_char_forwards(ledit_buffer *buffer, char *text, int len); static struct action find_char_backwards(ledit_buffer *buffer, char *text, int len); +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); /* FIXME: maybe sort these and use binary search -> but that would mess with the catch-all keys */ @@ -117,7 +120,8 @@ static struct key keys_en[] = { {"7", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBER, &push_7}, {"8", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBER, &push_8}, {"9", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_NUMBER, &push_9}, - {"x", 0, 0, NORMAL, KEY_ANY, KEY_NUMBERALLOWED, &key_x}, + {"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}, {"c", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_MOTION|KEY_NUMBERALLOWED, &change}, {"v", 0, 0, NORMAL, KEY_ANY, KEY_ANY, &enter_visual}, @@ -157,6 +161,8 @@ static struct key keys_en[] = { {"o", 0, 0, NORMAL, KEY_ANY, KEY_ANY, &append_line_below}, {"m", 0, 0, NORMAL, KEY_ANY, KEY_ANY, &mark_line}, {"'", 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}, {"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}, diff --git a/undo.c b/undo.c @@ -84,8 +84,8 @@ push_undo( ledit_undo_stack *undo, txtbuf *text, ledit_range insert_range, ledit_range cursor_range, - int start_group, enum operation type, - enum ledit_mode mode) { + int start_group, + enum operation type, enum ledit_mode mode) { undo_elem *old = peek_undo_elem(undo); int last_group = old == NULL ? 0 : old->group; int last_mode_group = old == NULL ? 0 : old->mode_group; @@ -106,8 +106,7 @@ push_undo( void ledit_push_undo_insert( ledit_undo_stack *undo, txtbuf *text, - ledit_range insert_range, - ledit_range cursor_range, + ledit_range insert_range, ledit_range cursor_range, int start_group, enum ledit_mode mode) { push_undo( undo, text, insert_range, cursor_range, @@ -118,8 +117,7 @@ ledit_push_undo_insert( void ledit_push_undo_delete( ledit_undo_stack *undo, txtbuf *text, - ledit_range insert_range, - ledit_range cursor_range, + ledit_range insert_range, ledit_range cursor_range, int start_group, enum ledit_mode mode) { push_undo( undo, text, insert_range, cursor_range, @@ -132,6 +130,10 @@ ledit_undo(ledit_undo_stack *undo, enum ledit_mode mode, void *callback_data, undo_insert_callback insert_cb, undo_delete_callback delete_cb, int *cur_line_ret, int *cur_index_ret, int *min_line_ret) { undo_elem *e; + /* skip empty elements */ + while (undo->cur >= 0 && undo->stack[undo->cur].text->len == 0) { + undo->cur--; + } if (undo->cur < 0) return UNDO_OLDEST_CHANGE; int group = undo->stack[undo->cur].group; @@ -188,6 +190,10 @@ ledit_redo(ledit_undo_stack *undo, enum ledit_mode mode, void *callback_data, undo_insert_callback insert_cb, undo_delete_callback delete_cb, int *cur_line_ret, int *cur_index_ret, int *min_line_ret) { undo_elem *e; + /* skip elements where no text is changed */ + while (undo->cur < undo->len - 1 && undo->stack[undo->cur + 1].text->len == 0) { + undo->cur++; + } if (undo->cur >= undo->len - 1) return UNDO_NEWEST_CHANGE; undo->cur++;