tSomewhat properly implement substitution - 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 <[email protected]> | |
Date: Thu, 9 Dec 2021 17:34:35 +0100 | |
Somewhat properly implement substitution | |
Diffstat: | |
M buffer.c | 29 +++++++++++++++++++++++++++++ | |
M buffer.h | 11 ++++++++++- | |
M keys_basic.c | 61 ++++++++++++++++++-----------… | |
M keys_command.c | 242 ++++++++++++++++++++++-------… | |
M view.c | 8 ++++++-- | |
5 files changed, 251 insertions(+), 100 deletions(-) | |
--- | |
diff --git a/buffer.c b/buffer.c | |
t@@ -202,6 +202,35 @@ buffer_insert_mark(ledit_buffer *buffer, char *mark, size… | |
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… | |
+ 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_mar… | |
diff --git a/buffer.h b/buffer.h | |
t@@ -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' … | |
*/ | |
void buffer_insert_mark(ledit_buffer *buffer, char *mark, size_t len, size_t l… | |
/* | |
+ * 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… | |
+ | |
+/* | |
* 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 | |
t@@ -121,6 +121,15 @@ static void change_cb(ledit_view *view, size_t line, size… | |
static void push_undo_empty_insert(ledit_view *view, size_t line, size_t index… | |
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(… | |
+#define CHECK_VIEW_LOCKED_NORETURN(view) if (view->lock_text) (void)view_locke… | |
+ | |
/* FIXME: move to common */ | |
static void | |
swap_sz(size_t *a, size_t *b) { | |
t@@ -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); | |
t@@ -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); | |
t@@ -543,6 +554,7 @@ push_undo_empty_insert(ledit_view *view, size_t line, size… | |
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 … | |
enter_insert(view, text, len); | |
t@@ -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, &s… | |
t@@ -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); | |
t@@ -583,6 +597,7 @@ append_after_cursor(ledit_view *view, char *text, size_t l… | |
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, &s… | |
t@@ -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; | |
t@@ -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); | |
t@@ -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) | |
t@@ -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; | |
t@@ -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) | |
t@@ -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( | |
t@@ -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(); | |
t@@ -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(); | |
t@@ -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 gr… | |
if (delete_selection(view)) { | |
/* NOP */ | |
t@@ -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 */ | |
t@@ -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) | |
t@@ -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; | |
t@@ -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); | |
t@@ -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); | |
t@@ -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); | |
t@@ -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( | |
t@@ -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); | |
t@@ -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); | |
t@@ -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,… | |
t@@ -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( | |
t@@ -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 | |
t@@ -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) { | |
t@@ -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(… | |
/* FIXME: history for search and commands */ | |
t@@ -110,13 +121,114 @@ handle_write_quit(ledit_view *view, char *cmd, size_t l… | |
return 0; | |
} | |
+static void | |
+show_num_substituted(ledit_view *view) { | |
+ char buf[30]; | |
+ if (snprintf(buf, sizeof(buf), "%d substitution(s)", sub_state.num) < … | |
+ window_show_message(view->window, "This message is a bug, tell… | |
+ } 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.… | |
+ view->cur_line = sub_state.line; | |
+ view->cur_index = sub_state.byte; | |
+ view_set_line_cursor_attrs(view, view->cur_line, view->cur_ind… | |
+ 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_stat… | |
+ 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 o… | |
+ view->cur_index = view_get_legal_normal_pos(view, view->cur_line, view… | |
+ 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; | |
t@@ -146,64 +258,27 @@ handle_substitute(ledit_view *view, char *cmd, size_t l1… | |
} | |
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 subs… | |
- 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… | |
- 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_sea… | |
- } | |
- } | |
- 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 … | |
- view->cur_index = view_get_legal_normal_pos(view, view->cur_li… | |
- view_set_line_cursor_attrs(view, view->cur_line, view->cur_ind… | |
- view_ensure_cursor_shown(view); | |
- txtbuf_destroy(buf); | |
+ substitute_all_remaining(view); | |
} | |
return 0; | |
error: | |
t@@ -312,8 +387,9 @@ parse_range(ledit_view *view, char *cmd, size_t len, char … | |
if ((*l1_valid || *l2_valid) && (l1 == 0 || l2 == 0 || l1 > view->line… | |
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; | |
} | |
t@@ -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)) { | |
t@@ -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 | |
t@@ -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) { | |
t@@ -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) { | |
t@@ -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) … | |
t@@ -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; … |