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 0498ed82f507017d8a5c6d83da08c66b2520bf95
parent 3572b3d7dd300642a94067f72f30fc83c8651e39
Author: lumidify <nobody@lumidify.org>
Date:   Thu,  9 Dec 2021 17:34:35 +0100

Somewhat properly implement substitution

Diffstat:
Mbuffer.c | 29+++++++++++++++++++++++++++++
Mbuffer.h | 11++++++++++-
Mkeys_basic.c | 61++++++++++++++++++++++++++++++++++++-------------------------
Mkeys_command.c | 242+++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------
Mview.c | 8++++++--
5 files changed, 251 insertions(+), 100 deletions(-)

diff --git a/buffer.c b/buffer.c @@ -202,6 +202,35 @@ buffer_insert_mark(ledit_buffer *buffer, char *mark, size_t len, size_t line, si marklist->len++; } +/* FIXME: check that byte is actually at grapheme boundary + (difficult because that can't currently be done by the buffer) */ +int +buffer_get_mark(ledit_buffer *buffer, char *mark, size_t len, size_t *line_ret, size_t *byte_ret) { + ledit_buffer_marklist *marklist = buffer->marklist; + int ret = 1; + for (size_t i = 0; i < marklist->len; i++) { + if (!strncmp(mark, marklist->marks[i].text, len)) { + ledit_line *ll; + ledit_buffer_mark *m = &marklist->marks[i]; + if (m->line >= buffer->lines_num) { + *line_ret = buffer->lines_num - 1; + ll = buffer_get_line(buffer, *line_ret); + *byte_ret = ll->len; + } else { + *line_ret = m->line; + ll = buffer_get_line(buffer, m->line); + if (m->byte >= ll->len) + *byte_ret = ll->len; + else + *byte_ret = m->byte; + } + ret = 0; + break; + } + } + return ret; +} + static ledit_buffer_marklist * marklist_create(void) { ledit_buffer_marklist *marklist = ledit_malloc(sizeof(ledit_buffer_marklist)); diff --git a/buffer.h b/buffer.h @@ -184,11 +184,20 @@ size_t line_prev_utf8(ledit_line *line, size_t index); size_t line_byte_to_char(ledit_line *line, size_t byte); /* - * Insert a mark with key 'mark' at line 'line' and byte 'byte'. + * Insert a mark with key 'mark' (which has byte length 'len') at line 'line' and byte 'byte'. */ void buffer_insert_mark(ledit_buffer *buffer, char *mark, size_t len, size_t line, size_t byte); /* + * Retrieve mark 'mark' (with byte length 'len'). + * The line and byte of the mark are written to 'line_ret' and 'byte_ret'. + * These returned positions are always valid positions in the buffer, even + * if the buffer has been modified and the mark points to an invalid location. + * Returns 0 if the mark was found, 1 otherwise. + */ +int buffer_get_mark(ledit_buffer *buffer, char *mark, size_t len, size_t *line_ret, size_t *byte_ret); + +/* * Perform one undo step. * 'mode' should be the current mode of the calling view. * 'cur_line' and 'cur_byte' are filled with the new line and cursor diff --git a/keys_basic.c b/keys_basic.c @@ -121,6 +121,15 @@ static void change_cb(ledit_view *view, size_t line, size_t char_pos, enum key_t static void push_undo_empty_insert(ledit_view *view, size_t line, size_t index, int start_group); static void move_half_screen(ledit_view *view, int movement); +static struct action +view_locked_error(ledit_view *view) { + window_show_message(view->window, view->lock_text, -1); + return (struct action){ACTION_NONE, NULL}; +} + +#define CHECK_VIEW_LOCKED(view) if (view->lock_text) return view_locked_error(view) +#define CHECK_VIEW_LOCKED_NORETURN(view) if (view->lock_text) (void)view_locked_error(view) + /* FIXME: move to common */ static void swap_sz(size_t *a, size_t *b) { @@ -478,6 +487,7 @@ static struct action delete_chars_forwards(ledit_view *view, char *text, size_t len) { (void)text; (void)len; + CHECK_VIEW_LOCKED(view); int num = get_key_repeat(); if (num == -1) { window_show_message(view->window, "Invalid key", -1); @@ -505,6 +515,7 @@ static struct action delete_chars_backwards(ledit_view *view, char *text, size_t len) { (void)text; (void)len; + CHECK_VIEW_LOCKED(view); int num = get_key_repeat(); if (num == -1) { window_show_message(view->window, "Invalid key", -1); @@ -543,6 +554,7 @@ push_undo_empty_insert(ledit_view *view, size_t line, size_t index, int start_gr static struct action append_line_above(ledit_view *view, char *text, size_t len) { + CHECK_VIEW_LOCKED(view); size_t start, end; /* do this here already so the mode group is the same for the newline insertion */ enter_insert(view, text, len); @@ -558,6 +570,7 @@ append_line_above(ledit_view *view, char *text, size_t len) { static struct action append_line_below(ledit_view *view, char *text, size_t len) { + CHECK_VIEW_LOCKED(view); size_t start, end; enter_insert(view, text, len); view_get_pos_softline_bounds(view, view->cur_line, view->cur_index, &start, &end); @@ -572,6 +585,7 @@ append_line_below(ledit_view *view, char *text, size_t len) { static struct action append_after_cursor(ledit_view *view, char *text, size_t len) { + CHECK_VIEW_LOCKED(view); enter_insert(view, text, len); /* make cursor jump back to original position on undo */ push_undo_empty_insert(view, view->cur_line, view->cur_index, 1); @@ -583,6 +597,7 @@ append_after_cursor(ledit_view *view, char *text, size_t len) { static struct action append_after_eol(ledit_view *view, char *text, size_t len) { + CHECK_VIEW_LOCKED(view); size_t start, end; enter_insert(view, text, len); view_get_pos_softline_bounds(view, view->cur_line, view->cur_index, &start, &end); @@ -833,6 +848,7 @@ static struct action delete_to_eol(ledit_view *view, char *text, size_t len) { (void)text; (void)len; + CHECK_VIEW_LOCKED(view); if (!key_stack_empty()) return err_invalid_key(view); size_t start, end; @@ -859,6 +875,7 @@ static struct action change_to_eol(ledit_view *view, char *text, size_t len) { (void)text; (void)len; + CHECK_VIEW_LOCKED(view); if (!key_stack_empty()) return err_invalid_key(view); view_set_mode(view, INSERT); @@ -885,6 +902,7 @@ static struct action change(ledit_view *view, char *text, size_t len) { (void)text; (void)len; + CHECK_VIEW_LOCKED(view); motion_callback cb = NULL; int num = get_key_repeat_and_motion_cb(&cb); if (num == -1) @@ -921,6 +939,7 @@ change(ledit_view *view, char *text, size_t len) { static void change_cb(ledit_view *view, size_t line, size_t char_pos, enum key_type type) { + CHECK_VIEW_LOCKED_NORETURN(view); /* set mode first so the deletion is included in the undo group */ view_set_mode(view, INSERT); int line_based = type == KEY_MOTION_LINE ? 1 : 0; @@ -1066,6 +1085,7 @@ static struct action delete(ledit_view *view, char *text, size_t len) { (void)text; (void)len; + CHECK_VIEW_LOCKED(view); motion_callback cb = NULL; int num = get_key_repeat_and_motion_cb(&cb); if (num == -1) @@ -1104,6 +1124,7 @@ delete(ledit_view *view, char *text, size_t len) { /* FIXME: should this get number of lines to remove or actual end line? */ static void delete_cb(ledit_view *view, size_t line, size_t char_pos, enum key_type type) { + CHECK_VIEW_LOCKED_NORETURN(view); view_wipe_line_cursor_attrs(view, view->cur_line); int line_based = type == KEY_MOTION_LINE ? 1 : 0; delete_range( @@ -1124,6 +1145,7 @@ static struct action paste_normal(ledit_view *view, char *text, size_t len) { (void)text; (void)len; + CHECK_VIEW_LOCKED(view); if (!paste_buffer) { window_show_message(view->window, "Nothing to paste", -1); discard_repetition_stack(); @@ -1182,6 +1204,7 @@ static struct action paste_normal_backwards(ledit_view *view, char *text, size_t len) { (void)text; (void)len; + CHECK_VIEW_LOCKED(view); if (!paste_buffer) { window_show_message(view->window, "Nothing to paste", -1); discard_repetition_stack(); @@ -1347,6 +1370,7 @@ static struct action backspace(ledit_view *view, char *text, size_t len) { (void)text; (void)len; + CHECK_VIEW_LOCKED(view); /* FIXME: don't copy to paste buffer on del_sel here; delete entire grapheme */ if (delete_selection(view)) { /* NOP */ @@ -1367,6 +1391,7 @@ static struct action delete_key(ledit_view *view, char *text, size_t len) { (void)text; (void)len; + CHECK_VIEW_LOCKED(view); ledit_line *cur_line = buffer_get_line(view->buffer, view->cur_line); if (delete_selection(view)) { /* NOP */ @@ -1387,6 +1412,7 @@ static struct action move_to_eol(ledit_view *view, char *text, size_t len) { (void)text; (void)len; + CHECK_VIEW_LOCKED(view); motion_callback cb; int num = get_key_repeat_and_motion_cb(&cb); if (num == -1) @@ -1544,6 +1570,7 @@ static struct action return_key(ledit_view *view, char *text, size_t len) { (void)text; (void)len; + CHECK_VIEW_LOCKED(view); int start_group = 1; if (delete_selection(view)) start_group = 0; @@ -1591,6 +1618,7 @@ static struct action enter_insert(ledit_view *view, char *text, size_t len) { (void)text; (void)len; + CHECK_VIEW_LOCKED(view); if (view->mode == NORMAL) view_wipe_line_cursor_attrs(view, view->cur_line); view_set_mode(view, INSERT); @@ -1663,6 +1691,7 @@ static struct action join_lines(ledit_view *view, char *text, size_t len) { (void)text; (void)len; + CHECK_VIEW_LOCKED(view); int num = get_key_repeat(); if (num == -1) return err_invalid_key(view); @@ -1696,6 +1725,7 @@ join_lines(ledit_view *view, char *text, size_t len) { static struct action insert_at_beginning(ledit_view *view, char *text, size_t len) { + CHECK_VIEW_LOCKED(view); if (!key_stack_empty()) return err_invalid_key(view); enter_insert(view, text, len); @@ -1854,39 +1884,15 @@ mark_line_cb(ledit_view *view, char *text, size_t len) { return (struct action){ACTION_NONE, NULL}; } -/* FIXME: move part of this to buffer.c */ -/* FIXME: check that byte is actually at grapheme boundary */ static struct action jump_to_mark_cb(ledit_view *view, char *text, size_t len) { grab_char_cb = NULL; - ledit_buffer_marklist *marklist = view->buffer->marklist; motion_callback cb; int num = get_key_repeat_and_motion_cb(&cb); if (num > 0) return err_invalid_key(view); size_t line = 0, index = 0; - int mark_found = 0; - for (size_t i = 0; i < marklist->len; i++) { - if (!strncmp(text, marklist->marks[i].text, len)) { - ledit_line *ll; - ledit_buffer_mark *m = &marklist->marks[i]; - if (m->line >= view->lines_num) { - line = view->lines_num - 1; - ll = buffer_get_line(view->buffer, line); - index = ll->len; - } else { - line = m->line; - ll = buffer_get_line(view->buffer, m->line); - if (m->byte >= ll->len) - index = ll->len; - else - index = m->byte; - } - mark_found = 1; - break; - } - } - if (!mark_found) + if (buffer_get_mark(view->buffer, text, len, &line, &index)) return err_invalid_key(view); if (view->mode == VISUAL) { view_set_selection( @@ -1969,6 +1975,7 @@ static struct action undo(ledit_view *view, char *text, size_t len) { (void)text; (void)len; + CHECK_VIEW_LOCKED(view); view_wipe_selection(view); view_wipe_line_cursor_attrs(view, view->cur_line); view_undo(view); @@ -1981,6 +1988,7 @@ static struct action redo(ledit_view *view, char *text, size_t len) { (void)text; (void)len; + CHECK_VIEW_LOCKED(view); view_wipe_selection(view); view_wipe_line_cursor_attrs(view, view->cur_line); view_redo(view); @@ -1991,6 +1999,7 @@ redo(ledit_view *view, char *text, size_t len) { static struct action insert_mode_insert_text(ledit_view *view, char *text, size_t len) { + CHECK_VIEW_LOCKED(view); /* FIXME: set cur_index when deleting selection */ delete_selection(view); insert_text(view, view->cur_line, view->cur_index, text, len, 0, 0, 0, 0, 0, 0, 1); @@ -2149,6 +2158,7 @@ GEN_MOVE_TO_CHAR( static struct action replace_cb(ledit_view *view, char *text, size_t len) { + CHECK_VIEW_LOCKED(view); size_t start_index = view->cur_index; /* FIXME: replace with (key repeat) * text instead of just text */ size_t end_index = view_next_cursor_pos( @@ -2176,6 +2186,7 @@ replace(ledit_view *view, char *text, size_t len) { (void)view; (void)text; (void)len; + CHECK_VIEW_LOCKED(view); int num = get_key_repeat(); if (num != 0) return err_invalid_key(view); diff --git a/keys_command.c b/keys_command.c @@ -27,9 +27,20 @@ #include "keys_command.h" #include "keys_command_config.h" -static char *last_search = NULL; -static char *last_replacement = NULL; -static int last_replacement_global = 0; +static struct { + char *search; + char *replace; + size_t slen; + size_t rlen; + size_t line; + size_t byte; + size_t old_line; + size_t old_byte; + size_t max_line; + int global; + int num; + int start_group; /* only set for the first replacement */ +} sub_state = {NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}; static int view_locked_error(ledit_view *view) { @@ -37,7 +48,7 @@ view_locked_error(ledit_view *view) { return 0; } -#define CHECK_VIEW_LOCKED if (view->lock_text) return view_locked_error(view) +#define CHECK_VIEW_LOCKED(view) if (view->lock_text) return view_locked_error(view) /* FIXME: history for search and commands */ @@ -110,13 +121,114 @@ handle_write_quit(ledit_view *view, char *cmd, size_t l1, size_t l2) { return 0; } +static void +show_num_substituted(ledit_view *view) { + char buf[30]; + if (snprintf(buf, sizeof(buf), "%d substitution(s)", sub_state.num) < 0) { + window_show_message(view->window, "This message is a bug, tell lumidify about it", -1); + } else { + window_show_message(view->window, buf, -1); + } +} + +/* returns 1 when match was found, 0 otherwise */ +static int +next_replace_pos( + ledit_view *view, + size_t line, size_t byte, size_t max_line, + size_t *line_ret, size_t *byte_ret) { + size_t start_index = byte; + for (size_t i = line; i <= max_line; i++) { + ledit_line *ll = buffer_get_line(view->buffer, i); + buffer_normalize_line(ll); + char *pos = strstr(ll->text + start_index, sub_state.search); + if (pos != NULL) { + *line_ret = i; + *byte_ret = (size_t)(pos - ll->text); + return 1; + } + start_index = 0; + } + return 0; +} + +/* returns whether keys should continue being captured */ +static int +move_to_next_substitution(ledit_view *view) { + view_wipe_line_cursor_attrs(view, view->cur_line); + if (!next_replace_pos(view, sub_state.line, sub_state.byte, sub_state.max_line, &sub_state.line, &sub_state.byte)) { + view->cur_line = sub_state.line; + view->cur_index = sub_state.byte; + view_set_line_cursor_attrs(view, view->cur_line, view->cur_index); + window_show_message(view->window, "No more matches", -1); + buffer_unlock_all_views(view->buffer); + return 0; + } + view->cur_line = sub_state.line; + view->cur_index = sub_state.byte; + view_set_line_cursor_attrs(view, view->cur_line, view->cur_index); + window_show_message(view->window, "Replace? (y/Y/n/N)", -1); + view_ensure_cursor_shown(view); + return 1; +} + +/* WARNING: sub_state must be set properly! */ +static void +substitute_single(ledit_view *view) { + ledit_range cur_range; + cur_range.line1 = sub_state.old_line; + cur_range.byte1 = sub_state.old_byte; + cur_range.line2 = sub_state.line; + cur_range.byte2 = sub_state.byte; + buffer_delete_with_undo_base( + view->buffer, cur_range, + sub_state.start_group, view->mode, + sub_state.line, sub_state.byte, + sub_state.line, sub_state.byte + sub_state.slen, NULL + ); + sub_state.start_group = 0; + cur_range.line1 = sub_state.line; + cur_range.byte1 = sub_state.byte; + buffer_insert_with_undo_base( + view->buffer, cur_range, 0, 0, view->mode, + sub_state.line, sub_state.byte, + sub_state.replace, sub_state.rlen, + NULL, NULL + ); + sub_state.num++; +} + +static void +substitute_all_remaining(ledit_view *view) { + view_wipe_line_cursor_attrs(view, view->cur_line); + size_t min_line = SIZE_MAX; + while (next_replace_pos(view, sub_state.line, sub_state.byte, sub_state.max_line, &sub_state.line, &sub_state.byte)) { + if (sub_state.line < min_line) + min_line = sub_state.line; + substitute_single(view); + view->cur_line = sub_state.old_line = sub_state.line; + view->cur_index = sub_state.old_byte = sub_state.byte; + if (!sub_state.global) { + sub_state.line++; + sub_state.byte = 0; + } else { + sub_state.byte += sub_state.rlen; + } + } + if (min_line < view->lines_num) + buffer_recalc_all_views_from_line(view->buffer, min_line); + /* FIXME: show number replaced */ + /* this doesn't need to be added to the undo stack since it's called on undo/redo anyways */ + view->cur_index = view_get_legal_normal_pos(view, view->cur_line, view->cur_index); + view_set_line_cursor_attrs(view, view->cur_line, view->cur_index); + view_ensure_cursor_shown(view); + show_num_substituted(view); + buffer_unlock_all_views(view->buffer); +} + static int handle_substitute(ledit_view *view, char *cmd, size_t l1, size_t l2) { - (void)view; - (void)cmd; - (void)l1; - (void)l2; - CHECK_VIEW_LOCKED; + CHECK_VIEW_LOCKED(view); cmd++; /* remove 's' at beginning */ size_t len = strlen(cmd); if (len == 0) goto error; @@ -146,64 +258,27 @@ handle_substitute(ledit_view *view, char *cmd, size_t l1, size_t l2) { } c++; } - free(last_search); - free(last_replacement); - last_search = ledit_strdup(cmd); - last_replacement = ledit_strdup(next); - last_replacement_global = global; + free(sub_state.search); + free(sub_state.replace); + sub_state.search = ledit_strdup(cmd); + sub_state.replace = ledit_strdup(next); + sub_state.slen = strlen(sub_state.search); + sub_state.rlen = strlen(sub_state.replace); + sub_state.global = global; + sub_state.line = l1; + sub_state.byte = 0; + sub_state.old_line = view->cur_line; + sub_state.old_byte = view->cur_index; + sub_state.max_line = l2; + sub_state.num = 0; + sub_state.start_group = 1; if (confirm) { buffer_lock_all_views_except(view->buffer, view, "Ongoing substitution in other view."); - buffer_unlock_all_views(view->buffer); + view->cur_command_type = CMD_SUBSTITUTE; + return move_to_next_substitution(view); } else { - int num = 0; - int start_undo_group = 1; - size_t slen = strlen(last_search); - size_t rlen = strlen(last_replacement); - txtbuf *buf = txtbuf_new(); /* FIXME: don't allocate new every time */ - view_wipe_line_cursor_attrs(view, view->cur_line); - size_t min_line = SIZE_MAX; - for (size_t i = l1 - 1; i < l2; i++) { - ledit_line *ll = buffer_get_line(view->buffer, i); - buffer_normalize_line(ll); - char *pos = strstr(ll->text, last_search); - if (pos != NULL && i < min_line) - min_line = i; - while (pos != NULL) { - size_t index = (size_t)(pos - ll->text); - ledit_range cur_range; - cur_range.line1 = view->cur_line; - cur_range.byte1 = view->cur_line; - cur_range.line2 = i; - cur_range.byte2 = index; - buffer_delete_with_undo_base( - view->buffer, cur_range, - start_undo_group, view->mode, - i, index, i, index + slen, NULL - ); - view->cur_line = i; - view->cur_index = index; - start_undo_group = 0; - cur_range.line1 = i; - cur_range.byte1 = index; - buffer_insert_with_undo_base( - view->buffer, cur_range, 0, 0, view->mode, - i, index, last_replacement, rlen, - NULL, NULL - ); - num++; - if (!global) break; - buffer_normalize_line(ll); /* just in case */ - pos = strstr(ll->text + index + rlen, last_search); - } - } - buffer_recalc_all_views_from_line(view->buffer, min_line); - /* FIXME: show number replaced */ - /* this doesn't need to be added to the undo stack since it's called on undo/redo anyways */ - view->cur_index = view_get_legal_normal_pos(view, view->cur_line, view->cur_index); - view_set_line_cursor_attrs(view, view->cur_line, view->cur_index); - view_ensure_cursor_shown(view); - txtbuf_destroy(buf); + substitute_all_remaining(view); } return 0; error: @@ -312,8 +387,9 @@ parse_range(ledit_view *view, char *cmd, size_t len, char **cmd_ret, size_t *lin if ((*l1_valid || *l2_valid) && (l1 == 0 || l2 == 0 || l1 > view->lines_num || l2 > view->lines_num)) return 1; /* FIXME: better error messages */ *cmd_ret = c; - *line1_ret = l1; - *line2_ret = l2; + /* ranges are given 1-indexed by user */ + *line1_ret = l1 - 1; + *line2_ret = l2 - 1; return 0; } @@ -328,7 +404,9 @@ handle_cmd(ledit_view *view, char *cmd, size_t len) { if (parse_range(view, cmd, len, &c, &l1, &l2, &l1_valid, &l2_valid)) return 0; int range_given = l1_valid && l2_valid; - /* FIXME: mandatory range */ + if (!range_given) { + l1 = l2 = view->cur_line; + } for (size_t i = 0; i < LENGTH(cmds); i++) { if (!strncmp(cmds[i].cmd, c, strlen(cmds[i].cmd)) && (!range_given || cmds[i].type == CMD_OPTIONAL_RANGE)) { @@ -340,33 +418,53 @@ handle_cmd(ledit_view *view, char *cmd, size_t len) { static int substitute_yes(ledit_view *view, char *key_text, size_t len) { - (void)view; (void)key_text; (void)len; - return 1; + substitute_single(view); + buffer_recalc_line(view->buffer, sub_state.line); + if (!sub_state.global) { + sub_state.line++; + sub_state.byte = 0; + } else { + sub_state.byte += sub_state.rlen; + } + int ret = move_to_next_substitution(view); + if (ret) + show_num_substituted(view); + return ret; } static int substitute_yes_all(ledit_view *view, char *key_text, size_t len) { - (void)view; (void)key_text; (void)len; + substitute_all_remaining(view); + show_num_substituted(view); return 0; } static int substitute_no(ledit_view *view, char *key_text, size_t len) { - (void)view; (void)key_text; (void)len; - return 1; + if (!sub_state.global) { + sub_state.line++; + sub_state.byte = 0; + } else { + sub_state.byte += sub_state.slen; + } + int ret = move_to_next_substitution(view); + if (ret) + show_num_substituted(view); + return ret; } static int substitute_no_all(ledit_view *view, char *key_text, size_t len) { - (void)view; (void)key_text; (void)len; + buffer_unlock_all_views(view->buffer); + show_num_substituted(view); return 0; } diff --git a/view.c b/view.c @@ -627,8 +627,8 @@ line_prev_word( PangoLayout *layout = get_pango_layout(view, line); const PangoLogAttr *attrs = pango_layout_get_log_attrs_readonly(layout, &nattrs); - if (char_index > (size_t)nattrs) - return -1; + if (char_index > (size_t)nattrs - 1) + char_index = (size_t)nattrs - 1; /* this is a bit weird because size_t can't be negative */ for (size_t i = char_index; i > 0; i--) { if (attrs[i-1].is_word_start) { @@ -655,6 +655,8 @@ line_prev_bigword( size_t next_cursorb = byte; size_t next_cursorc = char_index; int found_word = 0; + if (char_index > (size_t)nattrs - 1) + char_index = (size_t)nattrs - 1; /* this is a bit weird because size_t can't be negative */ for (size_t i = char_index; i > 0; i--) { if (!found_word && !attrs[i-1].is_white) { @@ -820,6 +822,7 @@ view_next_##name( cur_line < view->lines_num - 1) { \ cur_line++; \ cur_byte = 0; \ + cur_char = 0; \ wrapped_line = 1; \ } \ if (last_ret == -1 && cur_line == view->lines_num - 1) \ @@ -854,6 +857,7 @@ view_prev_##name( cur_line--; \ ll = buffer_get_line(view->buffer, cur_line); \ cur_byte = ll->len; \ + cur_char = ll->len; \ } \ if (last_ret == -1 && cur_line == 0) \ break; \