tMove undo handling to buffer - 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 3572b3d7dd300642a94067f72f30fc83c8651e39 | |
parent 123a3087ad0bc4f772f0cd0a17caf95304092f87 | |
Author: lumidify <[email protected]> | |
Date: Thu, 9 Dec 2021 11:01:05 +0100 | |
Move undo handling to buffer | |
Diffstat: | |
M buffer.c | 336 +++++++++++++++++++++++++----… | |
M buffer.h | 197 +++++++++++------------------… | |
M keys_basic.c | 109 ++++++++++++-----------------… | |
M keys_command.c | 47 +++++++++++++----------------… | |
M undo.c | 6 ++++++ | |
M undo.h | 9 +++++++++ | |
M view.c | 361 +++++++++++------------------… | |
M view.h | 23 ++++++++++++++++------- | |
8 files changed, 553 insertions(+), 535 deletions(-) | |
--- | |
diff --git a/buffer.c b/buffer.c | |
t@@ -73,10 +73,103 @@ static char *strchr_len(char *text, char c, size_t len); | |
*/ | |
static void init_line(ledit_buffer *buffer, ledit_line *line); | |
+/* | |
+ * Insert 'src_len' bytes from 'src_line' starting at byte position 'src_index' | |
+ * into line 'dst_line' at byte position 'dst_index'. | |
+ * 'dst_line' must not be the same as 'src_line'. | |
+ * If 'text_ret' is not NULL, the copied text is additionally copied into 'tex… | |
+ * This function does not update the views or normalize the lines, so it should | |
+ * only be used for efficiency purposes when performing multiple operations. | |
+ */ | |
+static void buffer_insert_text_from_line_base( | |
+ ledit_buffer *buffer, | |
+ size_t dst_line, size_t dst_index, | |
+ size_t src_line, size_t src_index, size_t src_len, | |
+ txtbuf *text_ret | |
+); | |
+ | |
+/* | |
+ * Insert text 'text' with length 'len' at line 'line_index' and byte position… | |
+ * The text must not contain newlines. | |
+ * This function does not update the views or normalize the lines, so it should | |
+ * only be used for efficiency purposes when performing multiple operations. | |
+ */ | |
+static void buffer_insert_text_base( | |
+ ledit_buffer *buffer, | |
+ size_t line_index, size_t index, | |
+ char *text, size_t len | |
+); | |
+ | |
+/* Insert text 'text' with length 'len' at line 'line_index' and byte position… | |
+ * The text may contain newlines. | |
+ * If end_line_ret is not NULL, the line index at the end of the insertion is | |
+ * written into it. | |
+ * If end_byte_ret is not NULL, the byte position at the end of the insertion … | |
+ * written into it. | |
+ * This function does not update the views or normalize the lines, so it should | |
+ * only be used for efficiency purposes when performing multiple operations. | |
+ */ | |
+static void buffer_insert_text_with_newlines_base( | |
+ ledit_buffer *buffer, | |
+ size_t line_index, size_t index, | |
+ char *text, size_t len, | |
+ size_t *end_line_ret, size_t *end_char_ret | |
+); | |
+ | |
+/* | |
+ * Same as buffer_insert_text_with_newlines_base, but the views are updated af… | |
+ */ | |
+static void buffer_insert_text_with_newlines( | |
+ ledit_buffer *buffer, | |
+ size_t line_index, size_t index, | |
+ char *text, size_t len, | |
+ size_t *end_line_ret, size_t *end_char_ret | |
+); | |
+ | |
+/* | |
+ * Append line after line at 'line_index'. | |
+ * if 'break_text' is not 0, the text on line 'line_index' starting at | |
+ * byte index 'text_index' is moved to the newly appended line. | |
+ * The views are notified that a line has been appended, but not told to update | |
+ * their line heights and offsets. | |
+ */ | |
+static void buffer_append_line_base(ledit_buffer *buffer, size_t line_index, s… | |
+ | |
+/* | |
+ * Delete lines between 'index1' and 'index2' (inclusive). | |
+ * The views are notified of the deletion but not told to | |
+ * update their line heights and offsets. | |
+ */ | |
+static void buffer_delete_line_entries_base(ledit_buffer *buffer, size_t index… | |
+ | |
+/* | |
+ * Delete the section of line 'line' starting at byte 'start' with length 'len… | |
+ * and notify the views. | |
+ * Note that this does not tell the views to recalculate their line heights an… | |
+ */ | |
+static void buffer_delete_line_section_base(ledit_buffer *buffer, size_t line,… | |
+ | |
static void marklist_destroy(ledit_buffer_marklist *marklist); | |
static ledit_buffer_marklist *marklist_create(void); | |
static void | |
+swap_sz(size_t *a, size_t *b) { | |
+ size_t tmp = *a; | |
+ *a = *b; | |
+ *b = tmp; | |
+} | |
+ | |
+static void | |
+sort_range(size_t *l1, size_t *b1, size_t *l2, size_t *b2) { | |
+ if (*l1 == *l2 && *b1 > *b2) { | |
+ swap_sz(b1, b2); | |
+ } else if (*l1 > *l2) { | |
+ swap_sz(l1, l2); | |
+ swap_sz(b1, b2); | |
+ } | |
+} | |
+ | |
+static void | |
marklist_destroy(ledit_buffer_marklist *marklist) { | |
for (size_t i = 0; i < marklist->len; i++) { | |
free(marklist->marks[i].text); | |
t@@ -324,22 +417,10 @@ buffer_normalize_line(ledit_line *line) { | |
/* FIXME: To simplify this a bit, maybe just copy text to txtbuf first and | |
then insert it in one go instead of having this complex logic */ | |
-void | |
-buffer_insert_text_from_line( | |
- ledit_buffer *buffer, | |
- size_t dst_line, size_t dst_index, | |
- size_t src_line, size_t src_index, size_t src_len, | |
- txtbuf *text_ret) { | |
- buffer_insert_text_from_line_base( | |
- buffer, dst_line, dst_index, src_line, src_index, src_len, text_ret | |
- ); | |
- buffer_recalc_line(buffer, dst_line); | |
-} | |
- | |
/* FIXME: check if there can be bugs when a newline is inserted in some way | |
other than pasting or pressing enter */ | |
-void | |
+static void | |
buffer_insert_text_from_line_base( | |
ledit_buffer *buffer, | |
size_t dst_line, size_t dst_index, | |
t@@ -452,13 +533,7 @@ resize_and_move_line_gap(ledit_buffer *buffer, size_t min… | |
); | |
} | |
-void | |
-buffer_insert_text(ledit_buffer *buffer, size_t line_index, size_t index, char… | |
- buffer_insert_text_base(buffer, line_index, index, text, len); | |
- buffer_recalc_line(buffer, line_index); | |
-} | |
- | |
-void | |
+static void | |
buffer_insert_text_base(ledit_buffer *buffer, size_t line_index, size_t index,… | |
ledit_line *line = buffer_get_line(buffer, line_index); | |
/* \0 is not included in line->len */ | |
t@@ -487,7 +562,7 @@ strchr_len(char *text, char c, size_t len) { | |
} | |
/* FIXME: make these functions that call recalc* also be final as described ab… | |
-void | |
+static void | |
buffer_insert_text_with_newlines( | |
ledit_buffer *buffer, | |
size_t line_index, size_t index, | |
t@@ -507,7 +582,7 @@ buffer_insert_text_with_newlines( | |
} | |
/* FIXME: also look for \r */ | |
-void | |
+static void | |
buffer_insert_text_with_newlines_base( | |
ledit_buffer *buffer, | |
size_t line_index, size_t index, | |
t@@ -546,14 +621,8 @@ init_line(ledit_buffer *buffer, ledit_line *line) { | |
line->len = 0; | |
} | |
-void | |
-buffer_append_line(ledit_buffer *buffer, size_t line_index, size_t text_index,… | |
- buffer_append_line_base(buffer, line_index, text_index, break_text); | |
- buffer_recalc_from_line(buffer, line_index); | |
-} | |
- | |
/* FIXME: error checking (index out of bounds, etc.) */ | |
-void | |
+static void | |
buffer_append_line_base(ledit_buffer *buffer, size_t line_index, size_t text_i… | |
size_t new_len = buffer->lines_num + 1; | |
if (new_len <= buffer->lines_num) | |
t@@ -582,15 +651,8 @@ buffer_append_line_base(ledit_buffer *buffer, size_t line… | |
} | |
} | |
-/* FIXME: set offset to 0 when recalculating first line? */ | |
-void | |
-buffer_delete_line_entries(ledit_buffer *buffer, size_t index1, size_t index2)… | |
- buffer_delete_line_entries_base(buffer, index1, index2); | |
- buffer_recalc_from_line(buffer, index1 > 0 ? index1 - 1 : 0); | |
-} | |
- | |
/* IMPORTANT: buffer_recalc_from_line needs to be called sometime after this! … | |
-void | |
+static void | |
buffer_delete_line_entries_base(ledit_buffer *buffer, size_t index1, size_t in… | |
ledit_line *l; | |
assert (index2 >= index1); | |
t@@ -607,16 +669,6 @@ buffer_delete_line_entries_base(ledit_buffer *buffer, siz… | |
} | |
} | |
-void | |
-buffer_delete_line_entry(ledit_buffer *buffer, size_t index) { | |
- buffer_delete_line_entries(buffer, index, index); | |
-} | |
- | |
-void | |
-buffer_delete_line_entry_base(ledit_buffer *buffer, size_t index) { | |
- buffer_delete_line_entries_base(buffer, index, index); | |
-} | |
- | |
ledit_line * | |
buffer_get_line(ledit_buffer *buffer, size_t index) { | |
assert(index < buffer->lines_num); | |
t@@ -753,7 +805,7 @@ line_byte_to_char(ledit_line *line, size_t byte) { | |
return c; | |
} | |
-void | |
+static void | |
buffer_delete_line_section_base(ledit_buffer *buffer, size_t line, size_t star… | |
ledit_line *l = buffer_get_line(buffer, line); | |
if (start <= l->gap && start + length >= l->gap) { | |
t@@ -778,24 +830,178 @@ buffer_delete_line_section_base(ledit_buffer *buffer, s… | |
} | |
} | |
-size_t | |
-buffer_delete_unicode_char(ledit_buffer *buffer, size_t line_index, size_t byt… | |
- size_t new_index = buffer_delete_unicode_char_base(buffer, line_index,… | |
- buffer_recalc_line(buffer, line_index); | |
- return new_index; | |
+static void | |
+delete_range_base( | |
+ ledit_buffer *buffer, | |
+ size_t line_index1, size_t byte_index1, | |
+ size_t line_index2, size_t byte_index2, | |
+ txtbuf *text_ret) { | |
+ sort_range(&line_index1, &byte_index1, &line_index2, &byte_index2); | |
+ if (line_index1 == line_index2) { | |
+ if (text_ret) { | |
+ buffer_copy_text_to_txtbuf( | |
+ buffer, text_ret, | |
+ line_index1, byte_index1, | |
+ line_index2, byte_index2 | |
+ ); | |
+ } | |
+ buffer_delete_line_section_base( | |
+ buffer, line_index1, byte_index1, byte_index2 - byte_index1 | |
+ ); | |
+ } else { | |
+ if (text_ret) { | |
+ buffer_copy_text_to_txtbuf( | |
+ buffer, text_ret, | |
+ line_index1, byte_index1, | |
+ line_index2, byte_index2 | |
+ ); | |
+ } | |
+ ledit_line *line1 = buffer_get_line(buffer, line_index1); | |
+ ledit_line *line2 = buffer_get_line(buffer, line_index2); | |
+ buffer_delete_line_section_base( | |
+ buffer, line_index1, byte_index1, line1->len - byte_index1 | |
+ ); | |
+ buffer_insert_text_from_line_base( | |
+ buffer, | |
+ line_index1, byte_index1, | |
+ line_index2, byte_index2, | |
+ line2->len - byte_index2, NULL | |
+ ); | |
+ buffer_delete_line_entries_base( | |
+ buffer, line_index1 + 1, line_index2 | |
+ ); | |
+ } | |
} | |
-size_t | |
-buffer_delete_unicode_char_base(ledit_buffer *buffer, size_t line_index, size_… | |
- ledit_line *l = buffer_get_line(buffer, line_index); | |
- size_t new_index = byte_index; | |
- if (dir < 0) { | |
- size_t i = line_prev_utf8(l, byte_index); | |
- buffer_delete_line_section_base(buffer, line_index, i, byte_in… | |
- new_index = i; | |
- } else { | |
- size_t i = line_next_utf8(l, byte_index); | |
- buffer_delete_line_section_base(buffer, line_index, byte_index… | |
+static void | |
+undo_insert_helper(void *data, size_t line, size_t byte, char *text, size_t te… | |
+ ledit_buffer *buffer = (ledit_buffer *)data; | |
+ buffer_insert_text_with_newlines_base(buffer, line, byte, text, text_l… | |
+} | |
+ | |
+static void | |
+undo_delete_helper(void *data, size_t line1, size_t byte1, size_t line2, size_… | |
+ ledit_buffer *buffer = (ledit_buffer *)data; | |
+ delete_range_base(buffer, line1, byte1, line2, byte2, NULL); | |
+} | |
+ | |
+void | |
+buffer_undo(ledit_buffer *buffer, enum ledit_mode mode, size_t *cur_line, size… | |
+ size_t min_line; | |
+ ledit_undo( | |
+ buffer->undo, mode, buffer, &undo_insert_helper, | |
+ &undo_delete_helper, cur_line, cur_byte, &min_line | |
+ ); | |
+ /* FIXME: why is this check here? */ | |
+ if (min_line < buffer->lines_num) { | |
+ buffer_recalc_all_views_from_line( | |
+ buffer, min_line > 0 ? min_line - 1 : min_line | |
+ ); | |
} | |
- return new_index; | |
+} | |
+ | |
+void | |
+buffer_redo(ledit_buffer *buffer, enum ledit_mode mode, size_t *cur_line, size… | |
+ size_t min_line; | |
+ ledit_redo( | |
+ buffer->undo, mode, buffer, &undo_insert_helper, | |
+ &undo_delete_helper, cur_line, cur_byte, &min_line | |
+ ); | |
+ if (min_line < buffer->lines_num) { | |
+ buffer_recalc_all_views_from_line( | |
+ buffer, min_line > 0 ? min_line - 1 : min_line | |
+ ); | |
+ } | |
+} | |
+ | |
+void | |
+buffer_delete_with_undo_base( | |
+ ledit_buffer *buffer, ledit_range cur_range, | |
+ int start_undo_group, enum ledit_mode mode, /* for undo */ | |
+ size_t line_index1, size_t byte_index1, | |
+ size_t line_index2, size_t byte_index2, | |
+ txtbuf *text_ret) { | |
+ /* FIXME: global txtbuf to avoid allocating each time */ | |
+ txtbuf *buf = text_ret != NULL ? text_ret : txtbuf_new(); | |
+ sort_range(&line_index1, &byte_index1, &line_index2, &byte_index2); | |
+ delete_range_base( | |
+ buffer, line_index1, byte_index1, line_index2, byte_index2, buf | |
+ ); | |
+ ledit_range del_range = { | |
+ .line1 = line_index1, .byte1 = byte_index1, | |
+ .line2 = line_index2, .byte2 = byte_index2 | |
+ }; | |
+ undo_push_delete( | |
+ buffer->undo, buf, del_range, cur_range, start_undo_group, mode | |
+ ); | |
+ if (text_ret == NULL) | |
+ txtbuf_destroy(buf); | |
+} | |
+ | |
+void | |
+buffer_delete_with_undo( | |
+ ledit_buffer *buffer, ledit_range cur_range, | |
+ int start_undo_group, enum ledit_mode mode, /* for undo */ | |
+ size_t line_index1, size_t byte_index1, | |
+ size_t line_index2, size_t byte_index2, | |
+ txtbuf *text_ret) { | |
+ buffer_delete_with_undo_base( | |
+ buffer, cur_range, | |
+ start_undo_group, mode, | |
+ line_index1, byte_index1, | |
+ line_index2, byte_index2, | |
+ text_ret | |
+ ); | |
+ size_t min = line_index1 < line_index2 ? line_index1 : line_index2; | |
+ buffer_recalc_all_views_from_line(buffer, min); | |
+} | |
+ | |
+void | |
+buffer_insert_with_undo_base( | |
+ ledit_buffer *buffer, | |
+ ledit_range cur_range, int set_range_end, | |
+ int start_undo_group, enum ledit_mode mode, | |
+ size_t line, size_t byte, | |
+ char *text, size_t len, | |
+ size_t *line_ret, size_t *byte_ret) { | |
+ txtbuf ins_buf = {.text = text, .len = len, .cap = len}; | |
+ ledit_range ins_range; | |
+ ins_range.line1 = line; | |
+ ins_range.byte1 = byte; | |
+ size_t new_line, new_byte; | |
+ buffer_insert_text_with_newlines_base( | |
+ buffer, line, byte, text, len, | |
+ &new_line, &new_byte | |
+ ); | |
+ if (set_range_end) { | |
+ cur_range.line2 = new_line; | |
+ cur_range.byte2 = new_byte; | |
+ } | |
+ ins_range.line2 = new_line; | |
+ ins_range.byte2 = new_byte; | |
+ undo_push_insert( | |
+ buffer->undo, &ins_buf, ins_range, cur_range, start_undo_group, mo… | |
+ ); | |
+ if (line_ret != NULL) | |
+ *line_ret = new_line; | |
+ if (byte_ret != NULL) | |
+ *byte_ret = new_byte; | |
+} | |
+ | |
+void | |
+buffer_insert_with_undo( | |
+ ledit_buffer *buffer, | |
+ ledit_range cur_range, int set_range_end, | |
+ int start_undo_group, enum ledit_mode mode, | |
+ size_t line, size_t byte, | |
+ char *text, size_t len, | |
+ size_t *line_ret, size_t *byte_ret) { | |
+ buffer_insert_with_undo_base( | |
+ buffer, | |
+ cur_range, set_range_end, | |
+ start_undo_group, mode, | |
+ line, byte, text, len, | |
+ line_ret, byte_ret | |
+ ); | |
+ buffer_recalc_all_views_from_line(buffer, line); | |
} | |
diff --git a/buffer.h b/buffer.h | |
t@@ -5,7 +5,6 @@ typedef struct ledit_buffer ledit_buffer; | |
#include "view.h" | |
-/* FIXME: size_t for len, etc. */ | |
typedef struct { | |
ledit_buffer *parent_buffer; | |
char *text; /* text, stored as gap buffer */ | |
t@@ -29,7 +28,7 @@ typedef struct { | |
struct ledit_buffer { | |
ledit_common *common; /* common stuff, e.g. display, etc. */ | |
char *filename; /* last opened filename */ | |
- undo_stack *undo; /* undo manager */ | |
+ undo_stack *undo; /* undo manager */ | |
ledit_buffer_marklist *marklist; /* list of mark positions set */ | |
ledit_line *lines; /* array of lines */ | |
ledit_view **views; /* array of registered views */ | |
t@@ -115,118 +114,6 @@ void buffer_destroy(ledit_buffer *buffer); | |
void buffer_normalize_line(ledit_line *line); | |
/* | |
- * Insert 'src_len' bytes from 'src_line' starting at byte position 'src_index' | |
- * into line 'dst_line' at byte position 'dst_index'. | |
- * 'dst_line' must not be the same as 'src_line'. | |
- * If 'text_ret' is not NULL, the copied text is additionally copied into 'tex… | |
- * This function does not update the views or normalize the lines, so it should | |
- * only be used for efficiency purposes when performing multiple operations. | |
- */ | |
-void buffer_insert_text_from_line_base( | |
- ledit_buffer *buffer, | |
- size_t dst_line, size_t dst_index, | |
- size_t src_line, size_t src_index, size_t src_len, | |
- txtbuf *text_ret | |
-); | |
- | |
-/* | |
- * Same as buffer_insert_text_from_line_base, but the views are updated afterw… | |
- */ | |
-void buffer_insert_text_from_line( | |
- ledit_buffer *buffer, | |
- size_t dst_line, size_t dst_index, | |
- size_t src_line, size_t src_index, size_t src_len, | |
- txtbuf *text_ret | |
-); | |
- | |
-/* | |
- * Insert text 'text' with length 'len' at line 'line_index' and byte position… | |
- * The text must not contain newlines. | |
- * This function does not update the views or normalize the lines, so it should | |
- * only be used for efficiency purposes when performing multiple operations. | |
- */ | |
-void buffer_insert_text_base( | |
- ledit_buffer *buffer, | |
- size_t line_index, size_t index, | |
- char *text, size_t len | |
-); | |
- | |
-/* | |
- * Same as buffer_insert_text_base, but the views are updated afterwards. | |
- */ | |
-void buffer_insert_text( | |
- ledit_buffer *buffer, | |
- size_t line_index, size_t index, | |
- char *text, size_t len | |
-); | |
- | |
-/* Insert text 'text' with length 'len' at line 'line_index' and byte position… | |
- * The text may contain newlines. | |
- * If end_line_ret is not NULL, the line index at the end of the insertion is | |
- * written into it. | |
- * If end_byte_ret is not NULL, the byte position at the end of the insertion … | |
- * written into it. | |
- * This function does not update the views or normalize the lines, so it should | |
- * only be used for efficiency purposes when performing multiple operations. | |
- */ | |
-void buffer_insert_text_with_newlines_base( | |
- ledit_buffer *buffer, | |
- size_t line_index, size_t index, | |
- char *text, size_t len, | |
- size_t *end_line_ret, size_t *end_char_ret | |
-); | |
- | |
-/* | |
- * Same as buffer_insert_text_with_newlines_base, but the views are updated af… | |
- */ | |
-void buffer_insert_text_with_newlines( | |
- ledit_buffer *buffer, | |
- size_t line_index, size_t index, | |
- char *text, size_t len, | |
- size_t *end_line_ret, size_t *end_char_ret | |
-); | |
- | |
-/* | |
- * Append line after line at 'line_index'. | |
- * if 'break_text' is not 0, the text on line 'line_index' starting at | |
- * byte index 'text_index' is moved to the newly appended line. | |
- * The views are notified that a line has been appended, but not told to update | |
- * their line heights and offsets. | |
- */ | |
-void buffer_append_line_base(ledit_buffer *buffer, size_t line_index, size_t t… | |
- | |
-/* | |
- * Same as buffer_append_line_base, but the views are told to update | |
- * their line heights and offsets afterwards. | |
- */ | |
-void buffer_append_line(ledit_buffer *buffer, size_t line_index, size_t text_i… | |
- | |
-/* | |
- * Delete lines between 'index1' and 'index2' (inclusive). | |
- * The views are notified of the deletion but not told to | |
- * update their line heights and offsets. | |
- */ | |
-void buffer_delete_line_entries_base(ledit_buffer *buffer, size_t index1, size… | |
- | |
-/* | |
- * Same as buffer_delete_line_entries_base, but the views are told to | |
- * update their line heights and offsets. | |
- */ | |
-void buffer_delete_line_entries(ledit_buffer *buffer, size_t index1, size_t in… | |
- | |
-/* | |
- * Convenience function to call buffer_delete_line_entries_base | |
- * with two times the same line index. | |
- */ | |
-void buffer_delete_line_entry_base(ledit_buffer *buffer, size_t index); | |
- | |
-/* | |
- * Convenience function to call buffer_delete_line_entries | |
- * with two times the same line index. | |
- */ | |
-void buffer_delete_line_entry(ledit_buffer *buffer, size_t index); | |
- | |
-/* | |
* Get the line at logical index 'index'. | |
* The returned line is only valid until the next | |
* action that appends or deletes line entries. | |
t@@ -292,33 +179,87 @@ size_t line_prev_utf8(ledit_line *line, size_t index); | |
/* | |
* Get the unicode character index of a byte position. | |
+ * Note that the time complexity of this is linear. | |
*/ | |
size_t line_byte_to_char(ledit_line *line, size_t byte); | |
/* | |
- * Delete the section of line 'line' starting at byte 'start' with length 'len… | |
- * and notify the views. | |
- * Note that this does not tell the views to recalculate their line heights an… | |
+ * Insert a mark with key 'mark' at line 'line' and byte 'byte'. | |
*/ | |
-void buffer_delete_line_section_base(ledit_buffer *buffer, size_t line, size_t… | |
+void buffer_insert_mark(ledit_buffer *buffer, char *mark, size_t len, size_t l… | |
/* | |
- * Delete the unicode char at 'line_index' and 'byte_index' if 'dir' is >= 0, | |
- * or the previous char if 'dir' is < 0. | |
- * Returns the byte index where the deleted text was located. | |
- * This function only notifies the views of the deletion, but does not tell | |
- * them to recalculate their line heights and offsets. | |
+ * 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 | |
+ * position after the undo. | |
*/ | |
-size_t buffer_delete_unicode_char_base(ledit_buffer *buffer, size_t line_index… | |
+void buffer_undo(ledit_buffer *buffer, enum ledit_mode mode, size_t *cur_line,… | |
/* | |
- * Same as buffer_delete_unicode_char_base, but the views are updated. | |
+ * Same as 'buffer_undo', but for redo. | |
*/ | |
-size_t buffer_delete_unicode_char(ledit_buffer *buffer, size_t line_index, siz… | |
+void buffer_redo(ledit_buffer *buffer, enum ledit_mode mode, size_t *cur_line,… | |
/* | |
- * Insert a mark with key 'mark' at line 'line' and byte 'byte'. | |
+ * Delete the given range (which does not need to be sorted yet) and | |
+ * add the operation to the undo stack. | |
+ * 'cur_range' is the cursor range to be added to the undo stack. | |
+ * 'start_undo_group' and 'mode' are also used for the undo stack. | |
+ * If 'text_ret' is not NULL, the deleted text is written to it. | |
+ * This function does not tell the views to update their line heights | |
+ * and offsets. | |
*/ | |
-void buffer_insert_mark(ledit_buffer *buffer, char *mark, size_t len, size_t l… | |
+void buffer_delete_with_undo_base( | |
+ ledit_buffer *buffer, ledit_range cur_range, | |
+ int start_undo_group, enum ledit_mode mode, /* for undo */ | |
+ size_t line_index1, size_t byte_index1, | |
+ size_t line_index2, size_t byte_index2, | |
+ txtbuf *text_ret | |
+); | |
+ | |
+/* | |
+ * Same as 'buffer_delete_with_undo_base', but the views are told to | |
+ * update their line heights and offsets afterwards. | |
+ */ | |
+void buffer_delete_with_undo( | |
+ ledit_buffer *buffer, ledit_range cur_range, | |
+ int start_undo_group, enum ledit_mode mode, /* for undo */ | |
+ size_t line_index1, size_t byte_index1, | |
+ size_t line_index2, size_t byte_index2, | |
+ txtbuf *text_ret | |
+); | |
+ | |
+/* | |
+ * Insert the given 'text' with length 'len' at line 'line' and | |
+ * byte index 'byte and add the operation to the undo stack. | |
+ * 'cur_range', 'start_undo_group', and 'mode' are used for the | |
+ * undo stack. If 'set_range_end' is set, the end position of | |
+ * 'cur_range' is set to the end position of the insertion before | |
+ * adding the operation to the undo stack. | |
+ * If 'line_ret' and 'byte_ret' are not NULL, they are filled with | |
+ * the end position of the insertion. | |
+ */ | |
+void buffer_insert_with_undo_base( | |
+ ledit_buffer *buffer, | |
+ ledit_range cur_range, int set_range_end, | |
+ int start_undo_group, enum ledit_mode mode, | |
+ size_t line, size_t byte, | |
+ char *text, size_t len, | |
+ size_t *line_ret, size_t *byte_ret | |
+); | |
+ | |
+/* | |
+ * Same as 'buffer_insert_with_undo_base', but the views are told to | |
+ * update their line heights and offsets afterwards. | |
+ */ | |
+void buffer_insert_with_undo( | |
+ ledit_buffer *buffer, | |
+ ledit_range cur_range, int set_range_end, | |
+ int start_undo_group, enum ledit_mode mode, | |
+ size_t line, size_t byte, | |
+ char *text, size_t len, | |
+ size_t *line_ret, size_t *byte_ret | |
+); | |
#endif | |
diff --git a/keys_basic.c b/keys_basic.c | |
t@@ -37,7 +37,7 @@ | |
#include "keys_command.h" | |
#include "keys_basic_config.h" | |
-/* note: this is supposed to be global for all buffers */ | |
+/* note: this is supposed to be global for all views/buffers */ | |
int paste_buffer_line_based = 0; | |
static txtbuf *paste_buffer = NULL; | |
static int last_lines_scrolled = -1; | |
t@@ -77,7 +77,6 @@ static struct { | |
} key_stack = {0, 0, NULL}; | |
static struct action (*grab_char_cb)(ledit_view *view, char *text, size_t len)… | |
-static int hard_line_based = 1; | |
void | |
basic_key_cleanup(void) { | |
t@@ -334,7 +333,7 @@ static void | |
get_new_line_softline( | |
ledit_view *view, size_t cur_line, size_t cur_index, int movement, | |
size_t *new_line_ret, int *new_softline_ret) { | |
- if (hard_line_based) { | |
+ if (view->buffer->hard_line_based) { | |
if (movement < 0 && (size_t)-movement > cur_line) | |
*new_line_ret = 0; | |
else | |
t@@ -403,31 +402,21 @@ delete_range( | |
(void)selected; /* FIXME */ | |
if (copy_to_buffer && !paste_buffer) | |
paste_buffer = txtbuf_new(); | |
- txtbuf *buf = copy_to_buffer ? paste_buffer : txtbuf_new(); | |
- ledit_range cur_range, del_range; | |
- cur_range.line1 = view->cur_line; | |
- cur_range.byte1 = view->cur_index; | |
+ txtbuf *buf = copy_to_buffer ? paste_buffer : NULL; | |
enum delete_mode delmode = DELETE_CHAR; | |
if (line_based) { | |
- if (hard_line_based) | |
+ if (view->buffer->hard_line_based) | |
delmode = DELETE_HARDLINE; | |
else | |
delmode = DELETE_SOFTLINE; | |
} | |
view_delete_range( | |
- view, delmode, | |
+ view, delmode, 1, | |
line_index1, byte_index1, | |
line_index2, byte_index2, | |
&view->cur_line, &view->cur_index, | |
- &del_range, buf | |
+ buf | |
); | |
- cur_range.line2 = view->cur_line; | |
- cur_range.byte2 = view->cur_index; | |
- undo_push_delete( | |
- view->buffer->undo, buf, del_range, cur_range, 1, view->mode | |
- ); | |
- if (!copy_to_buffer) | |
- txtbuf_destroy(buf); | |
} | |
/* FIXME: better interface for this; documentation */ | |
t@@ -439,8 +428,7 @@ insert_text( | |
size_t cur_line1, size_t cur_index1, | |
size_t cur_line2, size_t cur_index2, | |
int set_range_start, int set_range_end, int start_group) { | |
- txtbuf ins_buf = {.text = text, .len = len, .cap = len}; | |
- ledit_range cur_range, del_range; | |
+ ledit_range cur_range; | |
if (set_range_start) { | |
cur_range.line1 = cur_line1; | |
cur_range.byte1 = cur_index1; | |
t@@ -448,27 +436,24 @@ insert_text( | |
cur_range.line1 = view->cur_line; | |
cur_range.byte1 = view->cur_index; | |
} | |
- del_range.line1 = line; | |
- del_range.byte1 = index; | |
- size_t cur_line, cur_index; | |
- buffer_insert_text_with_newlines( | |
- view->buffer, line, index, text, len, | |
- &cur_line, &cur_index | |
- ); | |
/* this is mainly for pasting, where the new line and index | |
should not be at the end of the pasted text */ | |
if (set_range_end) { | |
- cur_range.line2 = view->cur_line = cur_line2; | |
- cur_range.byte2 = view->cur_index = cur_index2; | |
- } else { | |
- cur_range.line2 = view->cur_line = cur_line; | |
- cur_range.byte2 = view->cur_index = cur_index; | |
+ cur_range.line2 = cur_line2; | |
+ cur_range.byte2 = cur_index2; | |
} | |
- del_range.line2 = cur_line; | |
- del_range.byte2 = cur_index; | |
- undo_push_insert( | |
- view->buffer->undo, &ins_buf, del_range, cur_range, start_group, v… | |
+ /* FIXME: why did I ever decide to make set_range_end | |
+ mean exactly the opposite for the two functions? */ | |
+ buffer_insert_with_undo( | |
+ view->buffer, cur_range, !set_range_end, | |
+ start_group, view->mode, | |
+ line, index, text, len, | |
+ &view->cur_line, &view->cur_index | |
); | |
+ if (set_range_end) { | |
+ view->cur_line = cur_line2; | |
+ view->cur_index = cur_index2; | |
+ } | |
} | |
static int | |
t@@ -562,7 +547,7 @@ append_line_above(ledit_view *view, char *text, size_t len… | |
/* do this here already so the mode group is the same for the newline … | |
enter_insert(view, text, len); | |
view_get_pos_softline_bounds(view, view->cur_line, view->cur_index, &s… | |
- if (hard_line_based || start == 0) { | |
+ if (view->buffer->hard_line_based || start == 0) { | |
insert_text(view, view->cur_line, 0, "\n", 1, 0, 0, view->cur_… | |
} else { | |
/* FIXME: this interface really is horrible */ | |
t@@ -577,7 +562,7 @@ append_line_below(ledit_view *view, char *text, size_t len… | |
enter_insert(view, text, len); | |
view_get_pos_softline_bounds(view, view->cur_line, view->cur_index, &s… | |
ledit_line *ll = buffer_get_line(view->buffer, view->cur_line); | |
- if (hard_line_based || end == ll->len) { | |
+ if (view->buffer->hard_line_based || end == ll->len) { | |
insert_text(view, view->cur_line, ll->len, "\n", 1, 0, 0, view… | |
} else { | |
insert_text(view, view->cur_line, end, "\n\n", 2, 0, 0, view->… | |
t@@ -604,7 +589,7 @@ append_after_eol(ledit_view *view, char *text, size_t len)… | |
/* make cursor jump back to original position on undo */ | |
push_undo_empty_insert(view, view->cur_line, view->cur_index, 1); | |
ledit_line *ll = buffer_get_line(view->buffer, view->cur_line); | |
- if (hard_line_based) | |
+ if (view->buffer->hard_line_based) | |
view->cur_index = ll->len; | |
else | |
view->cur_index = end; | |
t@@ -852,7 +837,7 @@ delete_to_eol(ledit_view *view, char *text, size_t len) { | |
return err_invalid_key(view); | |
size_t start, end; | |
ledit_line *ll = buffer_get_line(view->buffer, view->cur_line); | |
- if (hard_line_based) { | |
+ if (view->buffer->hard_line_based) { | |
end = ll->len; | |
} else { | |
view_get_pos_softline_bounds(view, view->cur_line, view->cur_i… | |
t@@ -879,7 +864,7 @@ change_to_eol(ledit_view *view, char *text, size_t len) { | |
view_set_mode(view, INSERT); | |
size_t start, end; | |
ledit_line *ll = buffer_get_line(view->buffer, view->cur_line); | |
- if (hard_line_based) { | |
+ if (view->buffer->hard_line_based) { | |
end = ll->len; | |
} else { | |
view_get_pos_softline_bounds(view, view->cur_line, view->cur_i… | |
t@@ -942,11 +927,11 @@ change_cb(ledit_view *view, size_t line, size_t char_pos… | |
/* this hackery is needed to avoid deleting the entire last line and | |
instead leave an empty line - this should be made nicer (FIXME) */ | |
size_t pos1 = view->cur_index, pos2 = char_pos; | |
- if (line_based && !hard_line_based) { | |
+ if (line_based && !view->buffer->hard_line_based) { | |
size_t pos1, pos2, tmp; | |
view_get_pos_softline_bounds(view, view->cur_line, view->cur_i… | |
view_get_pos_softline_bounds(view, line, char_pos, &tmp, &pos2… | |
- } else if (line_based && hard_line_based) { | |
+ } else if (line_based && view->buffer->hard_line_based) { | |
pos1 = 0; | |
ledit_line *ll = buffer_get_line(view->buffer, line); | |
pos2 = ll->len; | |
t@@ -1046,7 +1031,7 @@ yank_cb(ledit_view *view, size_t line, size_t char_pos, … | |
swap_sz(&l1, &l2); | |
swap_sz(&b1, &b2); | |
} | |
- if (line_based && !hard_line_based) { | |
+ if (line_based && !view->buffer->hard_line_based) { | |
size_t start1, end2, tmp; | |
view_get_pos_softline_bounds(view, l1, b1, &start1, &tmp); | |
view_get_pos_softline_bounds(view, l2, b2, &tmp, &end2); | |
t@@ -1058,7 +1043,7 @@ yank_cb(ledit_view *view, size_t line, size_t char_pos, … | |
buffer_copy_text_to_txtbuf( | |
view->buffer, paste_buffer, l1, start1, l2, end2 | |
); | |
- } else if (line_based && hard_line_based) { | |
+ } else if (line_based && view->buffer->hard_line_based) { | |
ledit_line *ll = buffer_get_line(view->buffer, l2); | |
size_t end = ll->len; | |
if (l2 < view->lines_num - 1) { | |
t@@ -1119,6 +1104,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) { | |
+ view_wipe_line_cursor_attrs(view, view->cur_line); | |
int line_based = type == KEY_MOTION_LINE ? 1 : 0; | |
delete_range( | |
view, line_based, 0, | |
t@@ -1147,12 +1133,14 @@ paste_normal(ledit_view *view, char *text, size_t len)… | |
view_wipe_line_cursor_attrs(view, view->cur_line); | |
ledit_line *ll = buffer_get_line(view->buffer, view->cur_line); | |
size_t brk = 0; | |
- if (hard_line_based) { | |
+ if (view->buffer->hard_line_based) { | |
brk = ll->len; | |
} else { | |
size_t tmp; | |
view_get_pos_softline_bounds(view, view->cur_line, vie… | |
} | |
+ /* FIXME: this is a bit inefficient because insert_text does n… | |
+ use the *_base functions, but maybe this way is a bit safer… | |
insert_text( | |
view, view->cur_line, brk, | |
"\n", 1, 0, 0, view->cur_line, view->cur_index, 0, 1, 1 | |
t@@ -1203,7 +1191,7 @@ paste_normal_backwards(ledit_view *view, char *text, siz… | |
view_wipe_line_cursor_attrs(view, view->cur_line); | |
ledit_line *ll = buffer_get_line(view->buffer, view->cur_line); | |
size_t brk = 0; | |
- if (!hard_line_based) { | |
+ if (!view->buffer->hard_line_based) { | |
size_t tmp; | |
view_get_pos_softline_bounds(view, view->cur_line, vie… | |
} | |
t@@ -1414,7 +1402,7 @@ move_to_eol(ledit_view *view, char *text, size_t len) { | |
); | |
ledit_line *ll = buffer_get_line(view->buffer, new_line); | |
size_t end_index = ll->len; | |
- if (!hard_line_based) { | |
+ if (!view->buffer->hard_line_based) { | |
size_t tmp; | |
view_get_softline_bounds(view, new_line, new_softline, &tmp, &… | |
} | |
t@@ -1683,11 +1671,6 @@ join_lines(ledit_view *view, char *text, size_t len) { | |
int start_group = 1; | |
ledit_line *ll1; | |
size_t cur_line = view->cur_line; | |
- /* FIXME: have a general tmp buf for everyone to use */ | |
- txtbuf *buf = txtbuf_new(); | |
- size_t oldlen; | |
- ledit_range cur_range, del_range; | |
- cur_range.line1 = cur_range.line2 = cur_line; | |
for (int i = 0; i < num; i++) { | |
if (cur_line == view->lines_num - 1) | |
break; | |
t@@ -1695,26 +1678,18 @@ join_lines(ledit_view *view, char *text, size_t len) { | |
I'll just leave it in case I change the way lines | |
are stored later */ | |
ll1 = buffer_get_line(view->buffer, cur_line); | |
- oldlen = ll1->len; | |
/* FIXME: truncate whitespace to one space */ | |
- view_delete_range( | |
- view, DELETE_CHAR, | |
+ view_delete_range_base( | |
+ view, DELETE_CHAR, start_group, | |
cur_line, ll1->len, cur_line + 1, 0, | |
- NULL, NULL, &del_range, buf | |
- ); | |
- cur_range.byte1 = view->cur_index; | |
- cur_range.byte2 = view->cur_index = | |
- view_get_legal_normal_pos(view, view->cur_line, oldlen); | |
- undo_push_delete( | |
- view->buffer->undo, buf, del_range, cur_range, | |
- start_group, view->mode | |
+ &view->cur_line, &view->cur_index, NULL | |
); | |
start_group = 0; | |
} | |
+ buffer_recalc_all_views_from_line(view->buffer, cur_line); | |
view_set_line_cursor_attrs( | |
view, view->cur_line, view->cur_index | |
); | |
- txtbuf_destroy(buf); | |
finalize_repetition_stack(); | |
return (struct action){ACTION_NONE, NULL}; | |
} | |
t@@ -1725,7 +1700,7 @@ insert_at_beginning(ledit_view *view, char *text, size_t… | |
return err_invalid_key(view); | |
enter_insert(view, text, len); | |
size_t new_index = 0; | |
- if (!hard_line_based) { | |
+ if (!view->buffer->hard_line_based) { | |
size_t tmp; | |
view_get_pos_softline_bounds(view, view->cur_line, view->cur_i… | |
} | |
t@@ -1744,7 +1719,7 @@ cursor_to_first_non_ws(ledit_view *view, char *text, siz… | |
if (num != 0) | |
return err_invalid_key(view); | |
size_t new_index = 0; | |
- if (hard_line_based) { | |
+ if (view->buffer->hard_line_based) { | |
new_index = view_line_next_non_whitespace(view, view->cur_line… | |
} else { | |
size_t start, end; | |
t@@ -1779,7 +1754,7 @@ cursor_to_beginning(ledit_view *view, char *text, size_t… | |
return err_invalid_key(view); | |
/* FIXME: should anything be done with num? */ | |
size_t start_index = 0; | |
- if (!hard_line_based) { | |
+ if (!view->buffer->hard_line_based) { | |
size_t tmp; | |
view_get_pos_softline_bounds(view, view->cur_line, view->cur_i… | |
} | |
diff --git a/keys_command.c b/keys_command.c | |
t@@ -162,44 +162,34 @@ handle_substitute(ledit_view *view, char *cmd, size_t l1… | |
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, del_range; | |
+ ledit_range cur_range; | |
cur_range.line1 = view->cur_line; | |
cur_range.byte1 = view->cur_line; | |
- view_delete_range( | |
- view, DELETE_CHAR, | |
- i, index, | |
- i, index + slen, | |
- &view->cur_line, &view->cur_index, | |
- &del_range, buf | |
- ); | |
- cur_range.line2 = view->cur_line; | |
- cur_range.byte2 = view->cur_index; | |
- undo_push_delete( | |
- view->buffer->undo, buf, del_range, cur_ra… | |
+ 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; | |
- txtbuf ins_buf = {.text = last_replacement, .l… | |
- cur_range.line1 = view->cur_line; | |
- cur_range.byte1 = view->cur_index; | |
- del_range.line1 = i; | |
- del_range.byte1 = index; | |
- size_t cur_line, cur_index; | |
- buffer_insert_text_with_newlines( | |
- view->buffer, i, index, last_replacement, … | |
- &cur_line, &cur_index | |
- ); | |
- cur_range.line2 = view->cur_line; | |
- cur_range.byte2 = view->cur_index; | |
- del_range.line2 = cur_line; | |
- del_range.byte2 = cur_index; | |
- undo_push_insert( | |
- view->buffer->undo, &ins_buf, del_range, c… | |
+ 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; | |
t@@ -207,6 +197,7 @@ handle_substitute(ledit_view *view, char *cmd, size_t l1, … | |
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… | |
diff --git a/undo.c b/undo.c | |
t@@ -267,3 +267,9 @@ ledit_redo(undo_stack *undo, enum ledit_mode mode, void *c… | |
*min_line_ret = min_line; | |
return UNDO_NORMAL; | |
} | |
+ | |
+void | |
+undo_change_last_cur_range(undo_stack *undo, ledit_range cur_range) { | |
+ undo_elem *e = peek_undo_elem(undo); | |
+ e->cursor_range = cur_range; | |
+} | |
diff --git a/undo.h b/undo.h | |
t@@ -106,3 +106,12 @@ undo_status ledit_redo( | |
void *callback_data, undo_insert_callback insert_cb, undo_delete_callback … | |
size_t *cur_line_ret, size_t *cur_index_ret, size_t *min_line_ret | |
); | |
+ | |
+/* | |
+ * Change the cursor range stored for the last operation. | |
+ * This is sort of a hack used by view_delete_range and some other | |
+ * functions to set the cursor range later if it isn't known yet before | |
+ * inserting/deleting text. | |
+ * Fails silently if the stack is empty. | |
+ */ | |
+void undo_change_last_cur_range(undo_stack *undo, ledit_range cur_range); | |
diff --git a/view.c b/view.c | |
t@@ -188,14 +188,23 @@ resize_and_move_line_gap(ledit_view *view, size_t min_si… | |
); | |
} | |
+/* Checking vl->cursor_index_valid in these notify functions is needed | |
+ to avoid re-setting the cursor index for lines that were wiped but | |
+ where the line/index of the view hasn't been updated yet (e.g. when | |
+ the current line is wiped, then view_delete_range is called to delete | |
+ a part, which may cause these notification functions to be called) */ | |
void | |
view_notify_insert_text(ledit_view *view, size_t line, size_t index, size_t le… | |
ledit_view_line *vl = view_get_line(view, line); | |
vl->text_dirty = 1; | |
if (line == view->cur_line && index < view->cur_index) { | |
view->cur_index += len; | |
- view_set_line_cursor_attrs(view, line, view->cur_index); | |
+ ledit_view_line *vl = view_get_line(view, line); | |
+ if (vl->cursor_index_valid) | |
+ view_set_line_cursor_attrs(view, line, view->cur_index… | |
} | |
+ /* FIXME: maybe just wipe selection completely, or at least | |
+ when in insert mode? */ | |
if (view->sel_valid) | |
view_set_selection(view, view->cur_line, view->cur_index, view… | |
} | |
t@@ -216,7 +225,9 @@ view_notify_delete_text(ledit_view *view, size_t line, siz… | |
view, view->cur_line, view->cur_index | |
); | |
} | |
- view_set_line_cursor_attrs(view, line, view->cur_index); | |
+ ledit_view_line *vl = view_get_line(view, line); | |
+ if (vl->cursor_index_valid) | |
+ view_set_line_cursor_attrs(view, line, view->cur_index… | |
} | |
if (view->sel_valid) | |
view_set_selection(view, view->cur_line, view->cur_index, view… | |
t@@ -255,6 +266,9 @@ view_notify_delete_lines(ledit_view *view, size_t index1, … | |
view->cur_line = 0; | |
view->cur_index = 0; | |
} | |
+ ledit_view_line *vl = view_get_line(view, view->cur_line); | |
+ if (vl->cursor_index_valid) | |
+ view_set_line_cursor_attrs(view, view->cur_line, view-… | |
} | |
if (view->sel_valid) | |
view_set_selection(view, view->cur_line, view->cur_index, view… | |
t@@ -1132,22 +1146,23 @@ view_get_legal_normal_pos(ledit_view *view, size_t lin… | |
void | |
view_delete_range( | |
- ledit_view *view, enum delete_mode delmode, | |
+ ledit_view *view, | |
+ enum delete_mode delmode, int start_undo_group, | |
size_t line_index1, size_t byte_index1, | |
size_t line_index2, size_t byte_index2, | |
size_t *new_line_ret, size_t *new_byte_ret, | |
- ledit_range *final_range_ret, txtbuf *text_ret) { | |
+ txtbuf *text_ret) { | |
view_delete_range_base( | |
- view, delmode, | |
+ view, | |
+ delmode, start_undo_group, | |
line_index1, byte_index1, | |
line_index2, byte_index2, | |
new_line_ret, new_byte_ret, | |
- final_range_ret, text_ret | |
+ text_ret | |
); | |
/* need to start recalculating one line before in case first | |
line was deleted and offset is now wrong */ | |
size_t min = line_index1 < line_index2 ? line_index1 : line_index2; | |
- /* FIXME: a bit ugly to do this here */ | |
buffer_recalc_all_views_from_line( | |
view->buffer, min > 0 ? min - 1 : min | |
); | |
t@@ -1162,29 +1177,31 @@ view_delete_range( | |
given, but I couldn't reproduce this bug */ | |
void | |
view_delete_range_base( | |
- ledit_view *view, enum delete_mode delmode, | |
+ ledit_view *view, | |
+ enum delete_mode delmode, int start_undo_group, | |
size_t line_index1, size_t byte_index1, | |
size_t line_index2, size_t byte_index2, | |
size_t *new_line_ret, size_t *new_byte_ret, | |
- ledit_range *final_range_ret, txtbuf *text_ret) { | |
+ txtbuf *text_ret) { | |
/* FIXME: Oh boy, this is nasty */ | |
/* range line x, range byte x */ | |
size_t rgl1 = 0, rgb1 = 0, rgl2 = 0, rgb2 = 0; | |
+ /* line_index1 and byte_index1 are used as cursor start position | |
+ -> FIXME: why not just use view->cur_line, view->cur_index here? */ | |
+ size_t cur_line = line_index1; | |
+ size_t cur_byte = byte_index1; | |
+ view_sort_selection(&line_index1, &byte_index1, &line_index2, &byte_in… | |
size_t new_line = 0, new_byte = 0; | |
assert(line_index1 < view->lines_num); | |
assert(line_index2 < view->lines_num); | |
+ ledit_range cur_range = {0, 0, 0, 0}; | |
/* FIXME: could this be simplified by just calculating the range and t… | |
the non-line-based version? */ | |
if (delmode == DELETE_HARDLINE) { | |
int x, sl_useless; | |
size_t l1 = line_index1, l2 = line_index2; | |
- if (line_index1 > line_index2) { | |
- l1 = line_index2; | |
- l2 = line_index1; | |
- } | |
- size_t dell1 = l1, dell2 = l2; | |
- ledit_line *ll = buffer_get_line(view->buffer, line_index1); | |
- view_pos_to_x_softline(view, line_index1, byte_index1, &x, &sl… | |
+ ledit_line *ll; | |
+ view_pos_to_x_softline(view, cur_line, cur_byte, &x, &sl_usele… | |
if (l1 > 0 && l2 < view->lines_num - 1) { | |
rgl1 = l1; | |
rgb1 = 0; | |
t@@ -1209,13 +1226,6 @@ view_delete_range_base( | |
ll = buffer_get_line(view->buffer, rgl2); | |
rgb2 = ll->len; | |
} | |
- if (text_ret) { | |
- buffer_copy_text_to_txtbuf( | |
- view->buffer, text_ret, | |
- rgl1, rgb1, rgl2, rgb2 | |
- ); | |
- } | |
- /* default is dell1 = l1, dell2 = l2 */ | |
if (l2 < view->lines_num - 1) { | |
new_line = l1; | |
new_byte = view_x_softline_to_pos( | |
t@@ -1227,30 +1237,24 @@ view_delete_range_base( | |
view, l1 - 1, x, 0 | |
); | |
} else { | |
- dell1 = l1 + 1; | |
- dell2 = l2; | |
- new_line = l1; | |
+ new_line = 0; | |
new_byte = 0; | |
- /* happens when all lines are deleted, so one line has… | |
- ll = buffer_get_line(view->buffer, l1); | |
- buffer_delete_line_section_base( | |
- view->buffer, l1, 0, ll->len | |
- ); | |
- } | |
- if (dell1 <= dell2) { | |
- buffer_delete_line_entries_base(view->buffer, dell1, d… | |
} | |
+ buffer_delete_with_undo_base( | |
+ view->buffer, cur_range, | |
+ start_undo_group, view->mode, | |
+ rgl1, rgb1, rgl2, rgb2, text_ret | |
+ ); | |
} else if (delmode == DELETE_SOFTLINE) { | |
- int x, softline1, softline2; | |
- ledit_line *line1 = buffer_get_line(view->buffer, line_index1); | |
- ledit_view_line *vline1 = view_get_line(view, line_index1); | |
- view_pos_to_x_softline(view, line_index1, byte_index1, &x, &so… | |
+ int x, sl_useless; | |
+ view_pos_to_x_softline(view, cur_line, cur_byte, &x, &sl_usele… | |
if (line_index1 == line_index2) { | |
- int x_useless; | |
+ int x_useless, l1, l2; | |
+ ledit_line *line1 = buffer_get_line(view->buffer, line… | |
+ ledit_view_line *vline1 = view_get_line(view, line_ind… | |
+ view_pos_to_x_softline(view, line_index1, byte_index1,… | |
+ view_pos_to_x_softline(view, line_index2, byte_index2,… | |
PangoLayout *layout = get_pango_layout(view, line_inde… | |
- pango_layout_index_to_line_x(layout, byte_index2, 0, &… | |
- int l1 = softline1 < softline2 ? softline1 : softline2; | |
- int l2 = softline1 < softline2 ? softline2 : softline1; | |
PangoLayoutLine *pl1 = pango_layout_get_line_readonly(… | |
PangoLayoutLine *pl2 = pango_layout_get_line_readonly(… | |
/* don't delete entire line if it is the last one rema… | |
t@@ -1258,11 +1262,9 @@ view_delete_range_base( | |
if (line_index1 < view->lines_num - 1) { | |
/* cursor can be moved to next hard li… | |
new_line = line_index1; | |
- size_t tmp_byte; | |
- tmp_byte = view_x_softline_to_pos( | |
+ new_byte = view_x_softline_to_pos( | |
view, line_index1 + 1, x, 0 | |
); | |
- new_byte = (size_t)tmp_byte; | |
rgl1 = line_index1; | |
rgb1 = 0; | |
rgl2 = line_index1 + 1; | |
t@@ -1283,50 +1285,41 @@ view_delete_range_base( | |
rgl2 = line_index1; | |
rgb2 = line1->len; | |
} | |
- if (text_ret) { | |
- buffer_copy_text_to_txtbuf( | |
- view->buffer, text_ret, | |
- rgl1, rgb1, | |
- rgl2, rgb2 | |
- ); | |
- } | |
- buffer_delete_line_entry_base(view->buffer, li… | |
+ buffer_delete_with_undo_base( | |
+ view->buffer, cur_range, | |
+ start_undo_group, view->mode, | |
+ rgl1, rgb1, rgl2, rgb2, text_ret | |
+ ); | |
} else { | |
assert(pl2->start_index + pl2->length >= pl1->… | |
rgl1 = rgl2 = line_index1; | |
rgb1 = (size_t)pl1->start_index; | |
rgb2 = (size_t)(pl2->start_index + pl2->length… | |
- if (text_ret) { | |
- buffer_copy_text_to_txtbuf( | |
- view->buffer, text_ret, | |
- rgl1, rgb1, | |
- rgl2, rgb2 | |
- ); | |
- } | |
- buffer_delete_line_section_base( | |
- view->buffer, line_index1, rgb1, rgb2 - rg… | |
+ /* the deletion has to happen before calculati… | |
+ position because deleting softlines could c… | |
+ wrapping as well */ | |
+ buffer_delete_with_undo_base( | |
+ view->buffer, cur_range, | |
+ start_undo_group, view->mode, | |
+ rgl1, rgb1, rgl2, rgb2, text_ret | |
); | |
- if (l2 == vline1->softlines - 1 && line_index1… | |
+ set_pango_text_and_highlight(view, line_index1… | |
+ vline1 = view_get_line(view, line_index1); | |
+ if (l1 == vline1->softlines && line_index1 < v… | |
new_line = line_index1 + 1; | |
- size_t tmp_byte; | |
- tmp_byte = view_x_softline_to_pos( | |
+ new_byte = view_x_softline_to_pos( | |
view, line_index1 + 1, x, 0 | |
); | |
- new_byte = (size_t)tmp_byte; | |
- } else if (l2 < vline1->softlines - 1) { | |
+ } else if (l1 <= vline1->softlines - 1) { | |
new_line = line_index1; | |
- size_t tmp_byte; | |
- tmp_byte = view_x_softline_to_pos( | |
+ new_byte = view_x_softline_to_pos( | |
view, line_index1, x, l1 | |
); | |
- new_byte = (size_t)tmp_byte; | |
} else if (l1 > 0) { | |
new_line = line_index1; | |
- size_t tmp_byte; | |
- tmp_byte = view_x_softline_to_pos( | |
+ new_byte = view_x_softline_to_pos( | |
view, line_index1, x, l1 - 1 | |
); | |
- new_byte = (size_t)tmp_byte; | |
} else { | |
/* the line has been emptied and is th… | |
new_line = 0; | |
t@@ -1335,19 +1328,8 @@ view_delete_range_base( | |
} | |
} else { | |
int x_useless, sl1, sl2; | |
- size_t l1, l2, b1, b2; | |
- if (line_index1 < line_index2) { | |
- l1 = line_index1; | |
- b1 = byte_index1; | |
- l2 = line_index2; | |
- b2 = byte_index2; | |
- } else { | |
- l1 = line_index2; | |
- b1 = byte_index2; | |
- l2 = line_index1; | |
- b2 = byte_index1; | |
- } | |
- ledit_line *ll1 = buffer_get_line(view->buffer, l1); | |
+ size_t l1 = line_index1, b1 = byte_index1; | |
+ size_t l2 = line_index2, b2 = byte_index2; | |
ledit_line *ll2 = buffer_get_line(view->buffer, l2); | |
ledit_view_line *vl1 = view_get_line(view, l1); | |
ledit_view_line *vl2 = view_get_line(view, l2); | |
t@@ -1363,15 +1345,6 @@ view_delete_range_base( | |
rgl2 = l2; | |
rgb1 = 0; | |
rgb2 = ll2->len; | |
- if (text_ret) { | |
- buffer_copy_text_to_txtbuf( | |
- view->buffer, text_ret, | |
- rgl1, rgb1, | |
- rgl2, rgb2 | |
- ); | |
- } | |
- buffer_delete_line_section_base(view->… | |
- buffer_delete_line_entries_base(view->… | |
new_line = 0; | |
new_byte = 0; | |
} else { | |
t@@ -1396,31 +1369,24 @@ view_delete_range_base( | |
rgl2 = l2 + 1; | |
rgb2 = 0; | |
} | |
- if (text_ret) { | |
- buffer_copy_text_to_txtbuf( | |
- view->buffer, text_ret, | |
- rgl1, rgb1, | |
- rgl2, rgb2 | |
- ); | |
- } | |
- buffer_delete_line_entries_base(view->… | |
} | |
+ buffer_delete_with_undo_base( | |
+ view->buffer, cur_range, | |
+ start_undo_group, view->mode, | |
+ rgl1, rgb1, rgl2, rgb2, text_ret | |
+ ); | |
} else if (sl1 == 0) { | |
rgl1 = l1; | |
rgb1 = 0; | |
rgl2 = l2; | |
rgb2 = (size_t)(pl2->start_index + pl2->length… | |
- if (text_ret) { | |
- buffer_copy_text_to_txtbuf( | |
- view->buffer, text_ret, | |
- rgl1, rgb1, | |
- rgl2, rgb2 | |
- ); | |
- } | |
- buffer_delete_line_section_base(view->buffer, … | |
+ buffer_delete_with_undo_base( | |
+ view->buffer, cur_range, | |
+ start_undo_group, view->mode, | |
+ rgl1, rgb1, rgl2, rgb2, text_ret | |
+ ); | |
new_line = l1; | |
- new_byte = view_x_softline_to_pos(view, l2, x,… | |
- buffer_delete_line_entries_base(view->buffer, … | |
+ new_byte = view_x_softline_to_pos(view, l1, x,… | |
} else if (sl2 == vl2->softlines - 1) { | |
rgl1 = l1; | |
rgb1 = (size_t)pl1->start_index; | |
t@@ -1435,38 +1401,27 @@ view_delete_range_base( | |
view, l2 + 1, x, 0 | |
); | |
} | |
- if (text_ret) { | |
- buffer_copy_text_to_txtbuf( | |
- view->buffer, text_ret, | |
- rgl1, rgb1, | |
- rgl2, rgb2 | |
- ); | |
- } | |
- buffer_delete_line_section_base(view->buffer, … | |
- buffer_delete_line_entries_base(view->buffer, … | |
+ buffer_delete_with_undo_base( | |
+ view->buffer, cur_range, | |
+ start_undo_group, view->mode, | |
+ rgl1, rgb1, rgl2, rgb2, text_ret | |
+ ); | |
} else { | |
- /* FIXME: this could be made nicer by just usi… | |
- delete all in one go at the end */ | |
rgl1 = l1; | |
rgb1 = (size_t)pl1->start_index; | |
rgl2 = l2; | |
rgb2 = (size_t)(pl2->start_index + pl2->length… | |
- if (text_ret) { | |
- buffer_copy_text_to_txtbuf( | |
- view->buffer, text_ret, | |
- rgl1, rgb1, | |
- rgl2, rgb2 | |
- ); | |
- } | |
- buffer_delete_line_section_base(view->buffer, … | |
- buffer_insert_text_from_line_base( | |
- view->buffer, | |
- l1, rgb1, l2, rgb2, | |
- ll2->len - rgb2, NULL | |
+ buffer_delete_with_undo_base( | |
+ view->buffer, cur_range, | |
+ start_undo_group, view->mode, | |
+ rgl1, rgb1, rgl2, rgb2, text_ret | |
); | |
- buffer_delete_line_entries_base(view->buffer, … | |
new_line = l1; | |
+ /* important so vl1->softlines is updated */ | |
set_pango_text_and_highlight(view, l1); | |
+ /* important because line pointers may only st… | |
+ valid until something is deleted or inserte… | |
+ vl1 = view_get_line(view, l1); | |
/* it's technically possible that the remainin… | |
second line is so small that it doesn't gen… | |
softline, so there needs to be a special ca… | |
t@@ -1479,64 +1434,26 @@ view_delete_range_base( | |
} | |
} | |
} else { | |
- if (line_index1 == line_index2) { | |
- rgl1 = rgl2 = line_index1; | |
- if (byte_index1 < byte_index2) { | |
- rgb1 = byte_index1; | |
- rgb2 = byte_index2; | |
- } else { | |
- rgb1 = byte_index2; | |
- rgb2 = byte_index1; | |
- } | |
- if (text_ret) { | |
- buffer_copy_text_to_txtbuf( | |
- view->buffer, text_ret, | |
- rgl1, rgb1, | |
- rgl2, rgb2 | |
- ); | |
- } | |
- buffer_delete_line_section_base(view->buffer, line_ind… | |
- new_line = line_index1; | |
- new_byte = rgb1; | |
- } else { | |
- if (line_index1 < line_index2) { | |
- rgl1 = line_index1; | |
- rgb1 = byte_index1; | |
- rgl2 = line_index2; | |
- rgb2 = byte_index2; | |
- } else { | |
- rgl1 = line_index2; | |
- rgb1 = byte_index2; | |
- rgl2 = line_index1; | |
- rgb2 = byte_index1; | |
- } | |
- if (text_ret) { | |
- buffer_copy_text_to_txtbuf( | |
- view->buffer, text_ret, | |
- rgl1, rgb1, | |
- rgl2, rgb2 | |
- ); | |
- } | |
- ledit_line *line1 = buffer_get_line(view->buffer, rgl1… | |
- ledit_line *line2 = buffer_get_line(view->buffer, rgl2… | |
- buffer_delete_line_section_base(view->buffer, rgl1, rg… | |
- buffer_insert_text_from_line_base( | |
- view->buffer, rgl1, rgb1, rgl2, rgb2, line2->len -… | |
- ); | |
- new_line = rgl1; | |
- new_byte = rgb1; | |
- buffer_delete_line_entries_base(view->buffer, rgl1 + 1… | |
- } | |
- /* FIXME: too much magic - maybe don't include this here */ | |
- if (view->mode == NORMAL) | |
- new_byte = view_get_legal_normal_pos(view, new_line, n… | |
- } | |
- if (final_range_ret) { | |
- final_range_ret->line1 = rgl1; | |
- final_range_ret->byte1 = rgb1; | |
- final_range_ret->line2 = rgl2; | |
- final_range_ret->byte2 = rgb2; | |
+ rgl1 = line_index1; | |
+ rgb1 = byte_index1; | |
+ rgl2 = line_index2; | |
+ rgb2 = byte_index2; | |
+ new_line = rgl1; | |
+ new_byte = rgb1; | |
+ buffer_delete_with_undo_base( | |
+ view->buffer, cur_range, | |
+ start_undo_group, view->mode, | |
+ rgl1, rgb1, rgl2, rgb2, text_ret | |
+ ); | |
} | |
+ cur_range.line1 = view->cur_line; | |
+ cur_range.byte1 = view->cur_index; | |
+ cur_range.line2 = new_line; | |
+ cur_range.byte2 = new_byte; | |
+ undo_change_last_cur_range(view->buffer->undo, cur_range); | |
+ /* FIXME: too much magic - maybe don't include this here */ | |
+ if (view->mode == NORMAL) | |
+ new_byte = view_get_legal_normal_pos(view, new_line, new_byte); | |
if (new_line_ret) | |
*new_line_ret = new_line; | |
if (new_byte_ret) | |
t@@ -2040,27 +1957,11 @@ view_redraw(ledit_view *view) { | |
} | |
} | |
-static void | |
-undo_insert_helper(void *data, size_t line, size_t byte, char *text, size_t te… | |
- ledit_view *view = (ledit_view *)data; | |
- buffer_insert_text_with_newlines_base(view->buffer, line, byte, text, … | |
-} | |
- | |
-static void | |
-undo_delete_helper(void *data, size_t line1, size_t byte1, size_t line2, size_… | |
- ledit_view *view = (ledit_view *)data; | |
- view_delete_range_base(view, DELETE_CHAR, line1, byte1, line2, byte2, … | |
-} | |
- | |
void | |
view_undo(ledit_view *view) { | |
/* FIXME: maybe wipe selection */ | |
- size_t min_line; | |
size_t old_line = view->cur_line; | |
- ledit_undo( | |
- view->buffer->undo, view->mode, view, &undo_insert_helper, | |
- &undo_delete_helper, &view->cur_line, &view->cur_index, &min_line | |
- ); | |
+ buffer_undo(view->buffer, view->mode, &view->cur_line, &view->cur_inde… | |
if (view->mode == NORMAL) { | |
view->cur_index = view_get_legal_normal_pos( | |
view, view->cur_line, view->cur_index | |
t@@ -2068,23 +1969,13 @@ view_undo(ledit_view *view) { | |
} | |
view_wipe_line_cursor_attrs(view, old_line); | |
view_set_line_cursor_attrs(view, view->cur_line, view->cur_index); | |
- /* FIXME: why is this check here? */ | |
- if (min_line < view->lines_num) { | |
- buffer_recalc_all_views_from_line( | |
- view->buffer, min_line > 0 ? min_line - 1 : min_line | |
- ); | |
- } | |
/* FIXME: show undo message */ | |
} | |
void | |
view_redo(ledit_view *view) { | |
- size_t min_line; | |
size_t old_line = view->cur_line; | |
- ledit_redo( | |
- view->buffer->undo, view->mode, view, &undo_insert_helper, | |
- &undo_delete_helper, &view->cur_line, &view->cur_index, &min_line | |
- ); | |
+ buffer_redo(view->buffer, view->mode, &view->cur_line, &view->cur_inde… | |
if (view->mode == NORMAL) { | |
view->cur_index = view_get_legal_normal_pos( | |
view, view->cur_line, view->cur_index | |
t@@ -2092,33 +1983,23 @@ view_redo(ledit_view *view) { | |
} | |
view_wipe_line_cursor_attrs(view, old_line); | |
view_set_line_cursor_attrs(view, view->cur_line, view->cur_index); | |
- if (min_line < view->lines_num) { | |
- buffer_recalc_all_views_from_line( | |
- view->buffer, min_line > 0 ? min_line - 1 : min_line | |
- ); | |
- } | |
/* FIXME: show undo message */ | |
} | |
static void | |
paste_callback(void *data, char *text, size_t len) { | |
ledit_view *view = (ledit_view *)data; | |
- txtbuf ins_buf = {.text = text, .len = len, .cap = len}; | |
- ledit_range cur_range, ins_range; | |
- cur_range.line1 = ins_range.line1 = view->cur_line; | |
- cur_range.byte1 = ins_range.byte1 = view->cur_index; | |
- buffer_insert_text_with_newlines( | |
- view->buffer, view->cur_line, view->cur_index, | |
- text, len, &view->cur_line, &view->cur_index | |
- ); | |
- cur_range.line2 = ins_range.line2 = view->cur_line; | |
- cur_range.byte2 = ins_range.byte2 = view->cur_index; | |
- undo_push_insert( | |
- view->buffer->undo, &ins_buf, ins_range, cur_range, 1, view->mode | |
+ ledit_range cur_range; | |
+ cur_range.line1 = view->cur_line; | |
+ cur_range.byte1 = view->cur_index; | |
+ buffer_insert_with_undo( | |
+ view->buffer, cur_range, 1, 1, view->mode, | |
+ view->cur_line, view->cur_index, text, len, | |
+ &view->cur_line, &view->cur_index | |
); | |
} | |
-/* FIXME: guard against buffer being destroyed before paste callback is nulled… | |
+/* FIXME: guard against view being destroyed before paste callback is nulled */ | |
void | |
view_paste_clipboard(ledit_view *view) { | |
diff --git a/view.h b/view.h | |
t@@ -339,7 +339,7 @@ size_t view_x_softline_to_pos(ledit_view *view, size_t lin… | |
size_t view_get_legal_normal_pos(ledit_view *view, size_t line, size_t pos); | |
/* | |
- * Delete a range accorxing to a delete_mode. | |
+ * Delete a range according to a delete_mode. | |
* The line and byte indeces do not need to be sorted (in fact, they often | |
* shouldn't be, as shown in the next sentence). | |
* If 'delmode' is DELETE_HARDLINE or DELETE_SOFTLINE, 'line_index1' and | |
t@@ -352,19 +352,27 @@ size_t view_get_legal_normal_pos(ledit_view *view, size_… | |
* in the buffer afterwards, although it may have its text deleted. | |
* If 'delmode' is DELETE_CHAR, the exact specified range is deleted, and | |
* the new line and byte are simply the beginning of the range. | |
- * In all cases, the final deletion range is written to 'final_range_ret', | |
- * and the deleted text is written to 'text_ret'. | |
+ * The deleted text is written to 'text_ret'. | |
* In normal mode, the new cursor index is always at a valid normal mode | |
* position. | |
* All return arguments may be NULL. | |
+ * The deletion is added to the undo stack with 'start_undo_group' | |
+ * specifying whether a new undo group should be started. | |
+ * The start cursor position for the undo stack is taken directly from | |
+ * the view's current position. The end cursor position is the same as | |
+ * what is returned in 'new_line_ret' and 'new_byte_ret', except that it | |
+ * is set before calling 'view_get_legal_normal_pos'. | |
+ * If different cursor positions are needed, call | |
+ * 'undo_change_last_cur_range' afterwards. | |
* This function does not recalculate the line heights or offsets. | |
*/ | |
void view_delete_range_base( | |
- ledit_view *view, enum delete_mode delmode, | |
+ ledit_view *view, | |
+ enum delete_mode delmode, int start_undo_group, | |
size_t line_index1, size_t byte_index1, | |
size_t line_index2, size_t byte_index2, | |
size_t *new_line_ret, size_t *new_byte_ret, | |
- ledit_range *final_range_ret, txtbuf *text_ret | |
+ txtbuf *text_ret | |
); | |
/* | |
t@@ -372,11 +380,12 @@ void view_delete_range_base( | |
* all views are recalculated afterwards. | |
*/ | |
void view_delete_range( | |
- ledit_view *view, enum delete_mode delmode, | |
+ ledit_view *view, | |
+ enum delete_mode delmode, int start_undo_group, | |
size_t line_index1, size_t byte_index1, | |
size_t line_index2, size_t byte_index2, | |
size_t *new_line_ret, size_t *new_byte_ret, | |
- ledit_range *final_range_ret, txtbuf *text_ret | |
+ txtbuf *text_ret | |
); | |
/* |