tUse gap buffer for text - 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 8779bb819724ae7d05491112ae700063f49cde93 | |
parent 0e379b0cf1ba991e2024b2541a9d5d4f80068d5b | |
Author: lumidify <[email protected]> | |
Date: Sat, 12 Jun 2021 22:49:54 +0200 | |
Use gap buffer for text | |
This probably doesn't make anything more efficient and | |
really just makes everything more complicated, but whatever. | |
Diffstat: | |
M buffer.c | 540 ++++++++++++++++++++++++-----… | |
M buffer.h | 76 +++++++++++++++++++++++------… | |
M ledit.c | 4 ++++ | |
M search.c | 8 ++++++++ | |
4 files changed, 495 insertions(+), 133 deletions(-) | |
--- | |
diff --git a/buffer.c b/buffer.c | |
t@@ -1,4 +1,5 @@ | |
/* FIXME: shrink buffers when text length less than a fourth of the size */ | |
+/* FIXME: also cache PangoLayouts since keeping them around isn't really of mu… | |
#include <string.h> | |
#include <assert.h> | |
t@@ -13,11 +14,21 @@ | |
#include "buffer.h" | |
#include "cache.h" | |
+/* | |
+ * Important notes: | |
+ * - Lines must be null-terminated for some things to work (e.g. text search), | |
+ * but the '\0' is not included in 'len'. | |
+ * - When the line is not normalized, the '\0' is not always included! This | |
+ * should maybe be changed for consistency, but for now, the resizing just | |
+ * always makes sure to add one so there's enough space when the text is | |
+ * normalized. This is a bit ugly, but oh well. | |
+*/ | |
+ | |
static PangoAttrList *basic_attrs = NULL; | |
static void init_line(ledit_buffer *buffer, ledit_line *line); | |
-static void recalc_line_size_absolute(ledit_buffer *buffer); | |
-static void recalc_single_line_size(ledit_buffer *buffer, int line_index); | |
+/*static void delete_line_section(ledit_buffer *buffer, int line, int start, i… | |
+static void delete_line_section_base(ledit_buffer *buffer, int line, int start… | |
/* FIXME: destroy basic_attrs somewhere */ | |
ledit_buffer * | |
t@@ -35,6 +46,7 @@ ledit_create_buffer(ledit_common_state *state) { | |
buffer->lines_cap = 0; | |
buffer->cur_line = 0; | |
buffer->cur_index = 0; | |
+ /* FIXME: trailing currently not used */ | |
buffer->trailing = 0; | |
buffer->trailing_bytes = 0; | |
buffer->end_of_soft_line = 0; | |
t@@ -42,23 +54,53 @@ ledit_create_buffer(ledit_common_state *state) { | |
buffer->display_offset = 0; | |
buffer->sel.line1 = buffer->sel.byte1 = -1; | |
buffer->sel.line2 = buffer->sel.byte2 = -1; | |
- ledit_append_line(buffer, -1, -1); | |
+ ledit_append_line_base(buffer, -1, -1); | |
+ ledit_recalc_all_lines(buffer); | |
return buffer; | |
} | |
void | |
ledit_destroy_buffer(ledit_buffer *buffer) { | |
+ ledit_line *l; | |
for (int i = 0; i < buffer->lines_num; i++) { | |
- g_object_unref(buffer->lines[i].layout); | |
- free(buffer->lines[i].text); | |
+ l = ledit_get_line(buffer, i); | |
+ g_object_unref(l->layout); | |
+ free(l->text); | |
} | |
free(buffer->lines); | |
free(buffer); | |
} | |
void | |
+ledit_normalize_line(ledit_line *line) { | |
+ if (line->gap < line->len) { | |
+ memmove( | |
+ line->text + line->gap, | |
+ line->text + line->gap + line->cap - line->len, | |
+ line->len - line->gap | |
+ ); | |
+ line->gap = line->len; | |
+ } | |
+ line->text[line->len] = '\0'; | |
+} | |
+ | |
+static void | |
+err_text_dirty(ledit_buffer *buffer, int line) { | |
+ fprintf( | |
+ stderr, | |
+ "WARNING: Line had text_dirty or h_dirty attribute " | |
+ "set when rendering. Fix your code!\n" | |
+ ); | |
+ ledit_recalc_from_line(buffer, line); | |
+} | |
+ | |
+void | |
ledit_set_line_selection(ledit_buffer *buffer, int line, int start_byte, int e… | |
+ ledit_line *l = ledit_get_line(buffer, line); | |
+ if (l->text_dirty) | |
+ err_text_dirty(buffer, line); | |
+ /* FIXME: configure color */ | |
PangoAttribute *attr0 = pango_attr_background_new(0, 0, 0); | |
PangoAttribute *attr1 = pango_attr_foreground_new(65535, 65535, 65535); | |
attr0->start_index = start_byte; | |
t@@ -70,12 +112,15 @@ ledit_set_line_selection(ledit_buffer *buffer, int line, … | |
pango_attr_list_insert(list, attr0); | |
pango_attr_list_insert(list, attr1); | |
pango_attr_list_insert(list, attr2); | |
- pango_layout_set_attributes(buffer->lines[line].layout, list); | |
- buffer->lines[line].dirty = 1; | |
+ pango_layout_set_attributes(l->layout, list); | |
+ l->dirty = 1; | |
} | |
void | |
ledit_set_line_cursor_attrs(ledit_buffer *buffer, int line, int index) { | |
+ ledit_line *l = ledit_get_line(buffer, line); | |
+ if (l->text_dirty) | |
+ err_text_dirty(buffer, line); | |
if (buffer->state->mode == NORMAL) { | |
PangoAttribute *attr0 = pango_attr_background_new(0, 0, 0); | |
PangoAttribute *attr1 = pango_attr_foreground_new(65535, 65535… | |
t@@ -88,17 +133,18 @@ ledit_set_line_cursor_attrs(ledit_buffer *buffer, int lin… | |
pango_attr_list_insert(list, attr0); | |
pango_attr_list_insert(list, attr1); | |
pango_attr_list_insert(list, attr2); | |
- pango_layout_set_attributes(buffer->lines[line].layout, list); | |
+ pango_layout_set_attributes(l->layout, list); | |
} else { | |
- pango_layout_set_attributes(buffer->lines[line].layout, basic_… | |
+ pango_layout_set_attributes(l->layout, basic_attrs); | |
} | |
- buffer->lines[line].dirty = 1; | |
+ l->dirty = 1; | |
} | |
void | |
ledit_wipe_line_cursor_attrs(ledit_buffer *buffer, int line) { | |
- pango_layout_set_attributes(buffer->lines[line].layout, basic_attrs); | |
- buffer->lines[line].dirty = 1; | |
+ ledit_line *l = ledit_get_line(buffer, line); | |
+ pango_layout_set_attributes(l->layout, basic_attrs); | |
+ l->dirty = 1; | |
} | |
void | |
t@@ -106,34 +152,153 @@ ledit_insert_text_from_line( | |
ledit_buffer *buffer, | |
int dst_line, int dst_index, | |
int src_line, int src_index, int src_len) { | |
+ ledit_insert_text_from_line_base( | |
+ buffer, dst_line, dst_index, src_line, src_index, src_len | |
+ ); | |
+ ledit_recalc_line(buffer, dst_line); | |
+} | |
+ | |
+void | |
+ledit_insert_text_from_line_base( | |
+ ledit_buffer *buffer, | |
+ int dst_line, int dst_index, | |
+ int src_line, int src_index, int src_len) { | |
assert(dst_line != src_line); | |
ledit_line *ll = ledit_get_line(buffer, src_line); | |
if (src_len == -1) | |
src_len = ll->len - src_index; | |
- ledit_insert_text(buffer, dst_line, dst_index, ll->text, src_len); | |
+ if (src_index >= ll->gap) { | |
+ /* all text to insert is after gap */ | |
+ ledit_insert_text_base( | |
+ buffer, dst_line, dst_index, | |
+ ll->text + src_index + ll->cap - ll->len, src_len | |
+ ); | |
+ } else if (ll->gap - src_index >= src_len) { | |
+ /* all text to insert is before gap */ | |
+ ledit_insert_text_base( | |
+ buffer, dst_line, dst_index, | |
+ ll->text + src_index, src_len | |
+ ); | |
+ } else { | |
+ /* insert part of text before gap */ | |
+ ledit_insert_text_base( | |
+ buffer, dst_line, dst_index, | |
+ ll->text + src_index, ll->gap - src_index | |
+ ); | |
+ /* insert part of text after gap */ | |
+ ledit_insert_text_base( | |
+ buffer, dst_line, dst_index + ll->gap - src_index, | |
+ ll->text + ll->gap + ll->cap - ll->len, | |
+ src_len - ll->gap + src_index | |
+ ); | |
+ | |
+ } | |
+} | |
+ | |
+static void | |
+move_text_gap(ledit_line *line, int index) { | |
+ if (index > line->gap) { | |
+ /* move piece between end of original gap and | |
+ index to beginning of original gap */ | |
+ memmove( | |
+ line->text + line->gap, | |
+ line->text + line->gap + line->cap - line->len, | |
+ index - line->gap | |
+ ); | |
+ } else if (index < line->gap) { | |
+ /* move piece between index and original gap to | |
+ end of original gap */ | |
+ memmove( | |
+ line->text + index + line->cap - line->len, | |
+ line->text + index, | |
+ line->gap - index | |
+ ); | |
+ } | |
+ line->gap = index; | |
+} | |
+ | |
+/* This is almost certainly premature optimization and maybe | |
+ not optimization at all. */ | |
+/* FIXME: add "final" versions of the functions that include the | |
+ normalization, i.e. if they have to move the gap anyways, they | |
+ just move it to the end */ | |
+static void | |
+resize_and_move_text_gap(ledit_line *line, int min_size, int index) { | |
+ int gap_size = line->cap - line->len; | |
+ /* FIXME: read up on what the best values are here */ | |
+ line->cap = line->cap * 2 > min_size ? line->cap * 2 : min_size; | |
+ line->text = ledit_realloc(line->text, line->cap); | |
+ if (index > line->gap) { | |
+ /* move piece between end of original gap and index to | |
+ beginning of original gap */ | |
+ memmove( | |
+ line->text + line->gap, | |
+ line->text + line->gap + gap_size, | |
+ index - line->gap | |
+ ); | |
+ /* move piece after index to end of buffer */ | |
+ memmove( | |
+ line->text + line->cap - (line->len - index), | |
+ line->text + index + gap_size, | |
+ line->len - index | |
+ ); | |
+ } else if (index < line->gap) { | |
+ /* move piece after original gap to end of buffer */ | |
+ memmove( | |
+ line->text + line->cap - (line->len - line->gap), | |
+ line->text + line->gap + gap_size, | |
+ line->len - line->gap | |
+ ); | |
+ /* move piece between index and original gap to end */ | |
+ memmove( | |
+ line->text + line->cap - line->len + index, | |
+ line->text + index, | |
+ line->gap - index | |
+ ); | |
+ } else { | |
+ /* move piece after original gap to end of buffer */ | |
+ memmove( | |
+ line->text + line->cap - (line->len - line->gap), | |
+ line->text + line->gap + gap_size, | |
+ line->len - line->gap | |
+ ); | |
+ } | |
+ line->gap = index; | |
} | |
void | |
ledit_insert_text(ledit_buffer *buffer, int line_index, int index, char *text,… | |
+ ledit_insert_text_base(buffer, line_index, index, text, len); | |
+ ledit_recalc_line(buffer, line_index); | |
+} | |
+ | |
+void | |
+ledit_insert_text_base(ledit_buffer *buffer, int line_index, int index, char *… | |
ledit_line *line = &buffer->lines[line_index]; | |
if (len == -1) | |
len = strlen(text); | |
/* \0 is not included in line->len */ | |
- if (line->len + len + 1 > line->cap || line->text == NULL) { | |
- /* FIXME: read up on what the best values are here */ | |
- line->cap = line->cap * 2 > line->len + len + 1 ? line->cap * … | |
- line->text = ledit_realloc(line->text, line->cap); | |
- } | |
- memmove(line->text + index + len, line->text + index, line->len - inde… | |
+ if (line->len + len + 1 > line->cap || line->text == NULL) | |
+ resize_and_move_text_gap(line, line->len + len + 1, index); | |
+ else | |
+ move_text_gap(line, index); | |
+ /* the gap is now located at 'index' and least large enough to hold th… | |
memcpy(line->text + index, text, len); | |
+ line->gap += len; | |
line->len += len; | |
- line->text[line->len] = '\0'; | |
- pango_layout_set_text(line->layout, line->text, line->len); | |
- /*recalc_single_line_size(buffer, line_index);*/ | |
line->dirty = 1; | |
+ line->text_dirty = 1; | |
+ line->h_dirty = 1; | |
} | |
-static void append_line_impl(ledit_buffer *buffer, int line_index, int text_in… | |
+/* FIXME: implement this - if it is known that this is the last insertion, | |
+ move gap to end immediately if the gap needs to be enlarged anyways to | |
+ avoid another copy before rendering */ | |
+/* | |
+void | |
+ledit_insert_text_final(ledit_buffer *buffer, int line_index, int index, char … | |
+} | |
+*/ | |
/* FIXME: this isn't optimized like the standard version, but whatever */ | |
static char * | |
t@@ -145,12 +310,32 @@ strchr_len(char *text, char c, int len) { | |
return NULL; | |
} | |
+/* FIXME: make these functions that call recalc* also be final as described ab… | |
void | |
ledit_insert_text_with_newlines( | |
ledit_buffer *buffer, | |
int line_index, int index, | |
char *text, int len, | |
int *end_line_ret, int *end_char_ret) { | |
+ int end; | |
+ ledit_insert_text_with_newlines_base( | |
+ buffer, line_index, index, text, len, | |
+ &end, end_char_ret | |
+ ); | |
+ if (end_line_ret) | |
+ *end_line_ret = end; | |
+ if (line_index == end) | |
+ ledit_recalc_line(buffer, line_index); | |
+ else | |
+ ledit_recalc_from_line(buffer, line_index); | |
+} | |
+ | |
+void | |
+ledit_insert_text_with_newlines_base( | |
+ ledit_buffer *buffer, | |
+ int line_index, int index, | |
+ char *text, int len, | |
+ int *end_line_ret, int *end_char_ret) { | |
if (len == -1) | |
len = strlen(text); | |
int rem_len = len; | |
t@@ -158,27 +343,30 @@ ledit_insert_text_with_newlines( | |
int cur_line = line_index; | |
int cur_index = index; | |
while ((cur = strchr_len(last, '\n', rem_len)) != NULL) { | |
- ledit_insert_text(buffer, cur_line, cur_index, last, cur - las… | |
+ ledit_insert_text_base(buffer, cur_line, cur_index, last, cur … | |
/* FIXME: inefficient because there's no gap buffer yet */ | |
- append_line_impl(buffer, cur_line, -1); | |
+ ledit_append_line_base(buffer, cur_line, -1); | |
cur_index = 0; | |
cur_line++; | |
last = cur + 1; | |
rem_len -= cur - last + 1; | |
} | |
/* FIXME: check how legal this casting between pointers and ints is */ | |
- ledit_insert_text(buffer, cur_line, cur_index, last, text + len - last… | |
+ ledit_insert_text_base(buffer, cur_line, cur_index, last, text + len -… | |
if (end_line_ret) | |
*end_line_ret = cur_line; | |
if (end_char_ret) | |
*end_char_ret = cur_index + text + len - last; | |
- recalc_line_size_absolute(buffer); /* FIXME: make this more efficient … | |
} | |
+/* FIXME: standardize variable names (line/line_index, etc.) */ | |
void | |
ledit_render_line(ledit_buffer *buffer, int line_index) { | |
/* FIXME: check for <= 0 on size */ | |
ledit_line *line = &buffer->lines[line_index]; | |
+ /* this shouldn't happen if the functions here are used correctly */ | |
+ if (line->text_dirty || line->h_dirty) | |
+ err_text_dirty(buffer, line_index); | |
if (line->cache_index == -1) | |
ledit_assign_free_cache_index(buffer, line_index); | |
ledit_cache_pixmap *pix = ledit_get_cache_pixmap(line->cache_index); | |
t@@ -211,7 +399,6 @@ ledit_render_line(ledit_buffer *buffer, int line_index) { | |
line->dirty = 0; | |
} | |
-/* FIXME: use proper "viewport width" instead of just subtracting 10 */ | |
static void | |
init_line(ledit_buffer *buffer, ledit_line *line) { | |
line->parent_buffer = buffer; | |
t@@ -220,21 +407,32 @@ init_line(ledit_buffer *buffer, ledit_line *line) { | |
pango_layout_set_font_description(line->layout, buffer->state->font); | |
pango_layout_set_wrap(line->layout, PANGO_WRAP_WORD_CHAR); | |
pango_layout_set_attributes(line->layout, basic_attrs); | |
+ line->gap = 0; | |
line->cap = 2; /* arbitrary */ | |
line->text = ledit_malloc(line->cap); | |
line->text[0] = '\0'; | |
line->len = 0; | |
line->cache_index = -1; | |
line->dirty = 1; | |
+ line->text_dirty = 1; | |
+ line->h_dirty = 1; | |
+ /* FIXME: Is line->w needed? I don't think so. */ | |
+ line->w = line->h = 0; | |
/* FIXME: does this set line height reasonably when no text yet? */ | |
pango_layout_get_pixel_size(line->layout, &line->w, &line->h); | |
line->w = buffer->state->text_w; | |
line->y_offset = 0; | |
} | |
+void | |
+ledit_append_line(ledit_buffer *buffer, int line_index, int text_index) { | |
+ ledit_append_line_base(buffer, line_index, text_index); | |
+ ledit_recalc_from_line(buffer, line_index); | |
+} | |
+ | |
/* FIXME: error checking (index out of bounds, etc.) */ | |
-static void | |
-append_line_impl(ledit_buffer *buffer, int line_index, int text_index) { | |
+void | |
+ledit_append_line_base(ledit_buffer *buffer, int line_index, int text_index) { | |
if (buffer->lines_num >= buffer->lines_cap) { | |
buffer->lines_cap *= 2; | |
if (buffer->lines_cap == 0) | |
t@@ -248,38 +446,41 @@ append_line_impl(ledit_buffer *buffer, int line_index, i… | |
buffer->lines + line_index + 1, | |
(buffer->lines_num - (line_index + 1)) * sizeof(ledit_line) | |
); | |
- ledit_line *new_l = &buffer->lines[line_index + 1]; | |
+ ledit_line *new_l = ledit_get_line(buffer, line_index + 1); | |
init_line(buffer, new_l); | |
buffer->lines_num++; | |
if (text_index != -1) { | |
- /* FIXME: use ledit_insert... here */ | |
- ledit_line *l = &buffer->lines[line_index]; | |
- int len = l->len - text_index; | |
- new_l->len = len; | |
- new_l->cap = len + 10; | |
- new_l->text = ledit_malloc(new_l->cap); | |
- memcpy(new_l->text, l->text + text_index, len); | |
- new_l->text[new_l->len] = '\0'; | |
- l->len = text_index; | |
- l->text[l->len] = '\0'; | |
- pango_layout_set_text(new_l->layout, new_l->text, new_l->len); | |
- pango_layout_set_text(l->layout, l->text, l->len); | |
- /* FIXME: set height here */ | |
+ ledit_line *l = ledit_get_line(buffer, line_index); | |
+ ledit_insert_text_from_line_base( | |
+ buffer, line_index + 1, 0, | |
+ line_index, text_index, -1 | |
+ ); | |
+ delete_line_section_base( | |
+ buffer, line_index, | |
+ text_index, l->len - text_index | |
+ ); | |
} | |
} | |
+/* this is very weird and ugly with the recalc */ | |
void | |
-ledit_append_line(ledit_buffer *buffer, int line_index, int text_index) { | |
- append_line_impl(buffer, line_index, text_index); | |
- recalc_line_size_absolute(buffer); | |
+ledit_delete_line_entries(ledit_buffer *buffer, int index1, int index2) { | |
+ ledit_delete_line_entries_base(buffer, index1, index2); | |
+ ledit_recalc_from_line(buffer, index1 > 0 ? index1 - 1 : 0); | |
} | |
+/* IMPORTANT: ledit_recalc_from_line needs to be called sometime after this! */ | |
void | |
-ledit_delete_line_entries(ledit_buffer *buffer, int index1, int index2) { | |
+ledit_delete_line_entries_base(ledit_buffer *buffer, int index1, int index2) { | |
+ ledit_line *l; | |
+ /* FIXME: make sure this is always true */ | |
+ assert(index2 - index1 != buffer->lines_num); | |
for (int i = index1; i <= index2; i++) { | |
- g_object_unref(buffer->lines[i].layout); | |
- free(buffer->lines[i].text); | |
+ l = ledit_get_line(buffer, i); | |
+ g_object_unref(l->layout); | |
+ free(l->text); | |
} | |
+ /* FIXME: gap buffer */ | |
if (index2 < buffer->lines_num - 1) { | |
memmove( | |
buffer->lines + index1, buffer->lines + index2 + 1, | |
t@@ -287,8 +488,15 @@ ledit_delete_line_entries(ledit_buffer *buffer, int index… | |
); | |
} | |
buffer->lines_num -= index2 - index1 + 1; | |
- /* FIXME: avoid this by just subtracting the heights */ | |
- recalc_line_size_absolute(buffer); | |
+ /* force a recalc of the lines */ | |
+ if (index1 == 0) { | |
+ l = ledit_get_line(buffer, 0); | |
+ l->y_offset = 0; | |
+ l->h_dirty = 1; | |
+ } else { | |
+ l = ledit_get_line(buffer, index1 - 1); | |
+ l->h_dirty = 1; | |
+ } | |
} | |
void | |
t@@ -296,6 +504,11 @@ ledit_delete_line_entry(ledit_buffer *buffer, int index) { | |
ledit_delete_line_entries(buffer, index, index); | |
} | |
+void | |
+ledit_delete_line_entry_base(ledit_buffer *buffer, int index) { | |
+ ledit_delete_line_entries_base(buffer, index, index); | |
+} | |
+ | |
/* FIXME: use some sort of gap buffer (that would make this function | |
slightly more useful...) */ | |
ledit_line * | |
t@@ -303,47 +516,74 @@ ledit_get_line(ledit_buffer *buffer, int index) { | |
return &buffer->lines[index]; | |
} | |
-static void | |
-recalc_single_line_size(ledit_buffer *buffer, int line_index) { | |
+/* set text of pango layout if dirty and recalculate height of line | |
+ * - if height hasn't changed, nothing further is done | |
+ * - if height has changed, offset of all following lines is changed */ | |
+void | |
+ledit_recalc_line(ledit_buffer *buffer, int line) { | |
int w, h; | |
- ledit_line *line = &buffer->lines[line_index]; | |
- pango_layout_get_pixel_size(line->layout, &w, &h); | |
- /*line->w = w;*/ | |
+ ledit_line *l = ledit_get_line(buffer, line); | |
+ if (l->text_dirty) { | |
+ ledit_normalize_line(l); | |
+ pango_layout_set_text(l->layout, l->text, l->len); | |
+ l->text_dirty = 0; | |
+ } | |
+ l->h_dirty = 0; | |
+ pango_layout_get_pixel_size(l->layout, &w, &h); | |
/* if height changed, set height of current line | |
* and adjust offsets of all lines following it */ | |
- if (line->h != h) { | |
- int delta = h - line->h; | |
- line->h = h; | |
- buffer->total_height += delta; | |
- if (buffer->total_height < 0) | |
- buffer->total_height = 0; | |
- for (int i = line_index + 1; i < buffer->lines_num; i++) { | |
- buffer->lines[i].y_offset += delta; | |
- if (buffer->lines[i].y_offset < 0) | |
- buffer->lines[i].y_offset = 0; | |
+ if (l->h != h) { | |
+ long off = l->y_offset + h; | |
+ l->h = h; | |
+ for (int i = line + 1; i < buffer->lines_num; i++) { | |
+ l = ledit_get_line(buffer, i); | |
+ l->y_offset = off; | |
+ off += l->h; | |
} | |
+ buffer->total_height = off; | |
} | |
} | |
-static void | |
-recalc_line_size_absolute(ledit_buffer *buffer) { | |
+/* set text of pango layout and recalculate height | |
+ * and offset for all lines starting at 'line' */ | |
+void | |
+ledit_recalc_from_line(ledit_buffer *buffer, int line) { | |
int w, h; | |
- buffer->total_height = 0; | |
- /* completely recalculate line sizes and offsets from scratch */ | |
- for (int i = 0; i < buffer->lines_num; i++) { | |
- buffer->lines[i].y_offset = buffer->total_height; | |
- pango_layout_get_pixel_size(buffer->lines[i].layout, &w, &h); | |
- buffer->total_height += h; | |
- /*buffer->lines[i].w = w;*/ | |
- buffer->lines[i].h = h; | |
+ ledit_line *l = ledit_get_line(buffer, line); | |
+ long off = l->y_offset; | |
+ for (int i = line; i < buffer->lines_num; i++) { | |
+ l = ledit_get_line(buffer, i); | |
+ if (l->text_dirty) { | |
+ ledit_normalize_line(l); | |
+ pango_layout_set_text(l->layout, l->text, l->len); | |
+ l->text_dirty = 0; | |
+ pango_layout_get_pixel_size(l->layout, &w, &h); | |
+ l->h = h; | |
+ } else if (l->h_dirty) { | |
+ pango_layout_get_pixel_size(l->layout, &w, &h); | |
+ l->h = h; | |
+ } | |
+ l->h_dirty = 0; | |
+ l->y_offset = off; | |
+ off += l->h; | |
} | |
+ buffer->total_height = off; | |
+} | |
+ | |
+/* short for 'ledit_recalc_from_line' starting at line 0 */ | |
+void | |
+ledit_recalc_all_lines(ledit_buffer *buffer) { | |
+ /* force first line to offset 0, just in case */ | |
+ ledit_line *l = ledit_get_line(buffer, 0); | |
+ l->y_offset = 0; | |
+ ledit_recalc_from_line(buffer, 0); | |
} | |
int | |
ledit_line_visible(ledit_buffer *buffer, int index) { | |
- ledit_line *line = &buffer->lines[index]; | |
- return line->y_offset < buffer->display_offset + buffer->state->h && | |
- line->y_offset + line->h > buffer->display_offset; | |
+ ledit_line *l = ledit_get_line(buffer, index); | |
+ return l->y_offset < buffer->display_offset + buffer->state->text_h && | |
+ l->y_offset + l->h > buffer->display_offset; | |
} | |
/* get needed length of text range, including newlines | |
t@@ -368,6 +608,12 @@ ledit_textlen(ledit_buffer *buffer, int line1, int byte1,… | |
return len; | |
} | |
+/* FIXME: Only copy new text in selection when expanding it */ | |
+/* FIXME: Work directly with gap buffer instead of normalizing first | |
+ -> I guess it doesn't matter right now because copying text is | |
+ only done when it is re-rendered (and thus normalized because | |
+ of pango's requirements). If a more efficient rendering | |
+ backend is added, it would be good to optimize this, though. */ | |
/* copy text range into given buffer | |
* - dst is null-terminated | |
* - dst must be large enough to contain the text and NUL | |
t@@ -377,6 +623,7 @@ ledit_copy_text(ledit_buffer *buffer, char *dst, int line1… | |
assert(line1 < line2 || (line1 == line2 && byte1 <= byte2)); | |
ledit_line *ll1 = ledit_get_line(buffer, line1); | |
ledit_line *ll2 = ledit_get_line(buffer, line2); | |
+ ledit_normalize_line(ll1); | |
if (line1 == line2) { | |
memcpy(dst, ll1->text + byte1, byte2 - byte1); | |
dst[byte2 - byte1] = '\0'; | |
t@@ -388,11 +635,13 @@ ledit_copy_text(ledit_buffer *buffer, char *dst, int lin… | |
cur_pos++; | |
for (int i = line1 + 1; i < line2; i++) { | |
ledit_line *ll = ledit_get_line(buffer, i); | |
+ ledit_normalize_line(ll); | |
memcpy(dst + cur_pos, ll->text, ll->len); | |
cur_pos += ll->len; | |
dst[cur_pos] = '\n'; | |
cur_pos++; | |
} | |
+ ledit_normalize_line(ll2); | |
memcpy(dst + cur_pos, ll2->text, byte2); | |
cur_pos += byte2; | |
dst[cur_pos] = '\0'; | |
t@@ -421,11 +670,14 @@ ledit_copy_text_with_resize( | |
return len; | |
} | |
+/* get char with logical index i from line */ | |
+#define LINE_CHAR(line, i) ((i) < (line)->gap ? (line)->text[i] : (line)->text… | |
+ | |
int | |
ledit_prev_utf8(ledit_line *line, int index) { | |
int i = index - 1; | |
/* find valid utf8 char - this probably needs to be improved */ | |
- while (i > 0 && ((line->text[i] & 0xC0) == 0x80)) | |
+ while (i > 0 && ((LINE_CHAR(line, i) & 0xC0) == 0x80)) | |
i--; | |
return i; | |
} | |
t@@ -433,44 +685,80 @@ ledit_prev_utf8(ledit_line *line, int index) { | |
int | |
ledit_next_utf8(ledit_line *line, int index) { | |
int i = index + 1; | |
- while (i < line->len && ((line->text[i] & 0xC0) == 0x80)) | |
+ while (i < line->len && ((LINE_CHAR(line, i) & 0xC0) == 0x80)) | |
i++; | |
return i; | |
} | |
+/* FIXME: no idea why this exists */ | |
+/* | |
+static void | |
+delete_line_section(ledit_buffer *buffer, int line, int start, int length) { | |
+ delete_line_section_base(buffer, line, start, length); | |
+ ledit_recalc_line(buffer, line); | |
+} | |
+*/ | |
+ | |
+static void | |
+delete_line_section_base(ledit_buffer *buffer, int line, int start, int length… | |
+ ledit_line *l = ledit_get_line(buffer, line); | |
+ if (start <= l->gap && start + length >= l->gap) { | |
+ l->gap = start; | |
+ } else if (start < l->gap && start + length < l->gap) { | |
+ memmove( | |
+ l->text + l->cap - l->len + start + length, | |
+ l->text + start + length, | |
+ l->gap - start - length | |
+ ); | |
+ l->gap = start; | |
+ } else { | |
+ memmove( | |
+ l->text + l->gap, | |
+ l->text + l->gap + l->cap - l->len, | |
+ start - l->gap | |
+ ); | |
+ } | |
+ l->len -= length; | |
+ l->dirty = 1; | |
+ l->text_dirty = 1; | |
+ l->h_dirty = 1; | |
+} | |
+ | |
int | |
ledit_delete_unicode_char(ledit_buffer *buffer, int line_index, int byte_index… | |
+ int new_index = ledit_delete_unicode_char_base(buffer, line_index, byt… | |
+ ledit_recalc_line(buffer, line_index); | |
+ return new_index; | |
+} | |
+ | |
+int | |
+ledit_delete_unicode_char_base(ledit_buffer *buffer, int line_index, int byte_… | |
ledit_line *l = ledit_get_line(buffer, line_index); | |
int new_index = byte_index; | |
if (dir < 0) { | |
int i = ledit_prev_utf8(l, byte_index); | |
- memmove(l->text + i, l->text + byte_index, l->len - byte_index… | |
- l->len -= byte_index - i; | |
+ delete_line_section_base(buffer, line_index, i, byte_index - i… | |
new_index = i; | |
} else { | |
int i = ledit_next_utf8(l, byte_index); | |
- memmove(l->text + byte_index, l->text + i, l->len - i); | |
- l->len -= i - byte_index; | |
+ delete_line_section_base(buffer, line_index, byte_index, i - b… | |
} | |
- l->text[l->len] = '\0'; | |
- pango_layout_set_text(l->layout, l->text, l->len); | |
- recalc_single_line_size(buffer, line_index); | |
return new_index; | |
} | |
static void | |
-delete_line_section(ledit_buffer *buffer, int line, int start, int length) { | |
- ledit_line *l = &buffer->lines[line]; | |
- memmove(l->text + start, l->text + start + length, l->len - start - le… | |
- l->len -= length; | |
- l->text[l->len] = '\0'; | |
- pango_layout_set_text(l->layout, l->text, l->len); | |
- recalc_single_line_size(buffer, line); | |
- l->dirty = 1; | |
+normalize_and_set_pango_text(ledit_line *line) { | |
+ if (line->text_dirty) { | |
+ ledit_normalize_line(line); | |
+ pango_layout_set_text(line->layout, line->text, line->len); | |
+ line->text_dirty = 0; | |
+ line->h_dirty = 1; | |
+ } | |
} | |
void | |
ledit_pos_to_x_softline(ledit_line *line, int pos, int *x_ret, int *softline_r… | |
+ normalize_and_set_pango_text(line); | |
pango_layout_index_to_line_x(line->layout, pos, 0, softline_ret, x_ret… | |
/* FIXME: do these lines need to be unref'd? */ | |
PangoLayoutLine *pango_line = pango_layout_get_line_readonly(line->lay… | |
t@@ -495,6 +783,7 @@ void | |
ledit_x_softline_to_pos(ledit_line *line, int x, int softline, int *pos_ret) { | |
int trailing = 0; | |
int x_relative = x; | |
+ normalize_and_set_pango_text(line); | |
PangoLayoutLine *pango_line = | |
pango_layout_get_line_readonly(line->layout, softline); | |
/* x is absolute, so the margin at the left needs to be subtracted */ | |
t@@ -515,11 +804,13 @@ ledit_x_softline_to_pos(ledit_line *line, int x, int sof… | |
} | |
} | |
+/* FIXME: make sure PangoLayout has newest text already when these functions a… | |
int | |
ledit_get_legal_normal_pos(ledit_buffer *buffer, int line, int pos) { | |
/* move back one grapheme if at end of line */ | |
int ret = pos; | |
ledit_line *final_line = ledit_get_line(buffer, line); | |
+ normalize_and_set_pango_text(final_line); | |
if (pos == final_line->len && pos > 0) { | |
int nattrs; | |
const PangoLogAttr *attrs = | |
t@@ -534,16 +825,35 @@ ledit_get_legal_normal_pos(ledit_buffer *buffer, int lin… | |
return ret; | |
} | |
-/* FIXME: use at least somewhat sensible variable names */ | |
void | |
ledit_delete_range( | |
ledit_buffer *buffer, int line_based, | |
int line_index1, int byte_index1, | |
int line_index2, int byte_index2, | |
int *new_line_ret, int *new_byte_ret) { | |
+ ledit_delete_range_base( | |
+ buffer, line_based, | |
+ line_index1, byte_index1, | |
+ line_index2, byte_index2, | |
+ new_line_ret, new_byte_ret | |
+ ); | |
+ /* need to start recalculating one line before in case first | |
+ line was deleted and offset is now wrong */ | |
+ int min = line_index1 < line_index2 ? line_index1 : line_index2; | |
+ ledit_recalc_from_line(buffer, min > 0 ? min - 1 : min); | |
+} | |
+ | |
+/* FIXME: use at least somewhat sensible variable names */ | |
+void | |
+ledit_delete_range_base( | |
+ ledit_buffer *buffer, int line_based, | |
+ int line_index1, int byte_index1, | |
+ int line_index2, int byte_index2, | |
+ int *new_line_ret, int *new_byte_ret) { | |
if (line_based) { | |
int x, softline1, softline2; | |
ledit_line *line1 = ledit_get_line(buffer, line_index1); | |
+ normalize_and_set_pango_text(line1); | |
ledit_pos_to_x_softline(line1, byte_index1, &x, &softline1); | |
if (line_index1 == line_index2) { | |
int x_useless; | |
t@@ -553,9 +863,9 @@ ledit_delete_range( | |
int softlines = pango_layout_get_line_count(line1->lay… | |
PangoLayoutLine *pl1 = pango_layout_get_line_readonly(… | |
PangoLayoutLine *pl2 = pango_layout_get_line_readonly(… | |
- /* don't delete entire line of it is the last one rema… | |
+ /* don't delete entire line if it is the last one rema… | |
if (l1 == 0 && l2 == softlines - 1 && buffer->lines_nu… | |
- ledit_delete_line_entry(buffer, line_index1); | |
+ ledit_delete_line_entry_base(buffer, line_inde… | |
/* note: line_index1 is now the index of the n… | |
line since the current one was just deleted… | |
if (line_index1 < buffer->lines_num) { | |
t@@ -574,7 +884,7 @@ ledit_delete_range( | |
} | |
} else { | |
/* FIXME: sanity checks that the length is act… | |
- delete_line_section( | |
+ delete_line_section_base( | |
buffer, line_index1, pl1->start_index, | |
pl2->start_index + pl2->length - pl1->star… | |
); | |
t@@ -618,6 +928,8 @@ ledit_delete_range( | |
} | |
ledit_line *ll1 = ledit_get_line(buffer, l1); | |
ledit_line *ll2 = ledit_get_line(buffer, l2); | |
+ normalize_and_set_pango_text(ll1); | |
+ normalize_and_set_pango_text(ll2); | |
pango_layout_index_to_line_x(ll1->layout, b1, 0, &sl1,… | |
pango_layout_index_to_line_x(ll2->layout, b2, 0, &sl2,… | |
PangoLayoutLine *pl1 = pango_layout_get_line_readonly(… | |
t@@ -625,12 +937,12 @@ ledit_delete_range( | |
int softlines = pango_layout_get_line_count(ll2->layou… | |
if (sl1 == 0 && sl2 == softlines - 1) { | |
if (l1 == 0 && l2 == buffer->lines_num - 1) { | |
- delete_line_section(buffer, l1, 0, ll1… | |
- ledit_delete_line_entries(buffer, l1 +… | |
+ delete_line_section_base(buffer, l1, 0… | |
+ ledit_delete_line_entries_base(buffer,… | |
*new_line_ret = 0; | |
*new_byte_ret = 0; | |
} else { | |
- ledit_delete_line_entries(buffer, l1, … | |
+ ledit_delete_line_entries_base(buffer,… | |
if (l1 >= buffer->lines_num) { | |
*new_line_ret = buffer->lines_… | |
ledit_line *new_lline = ledit_… | |
t@@ -645,13 +957,13 @@ ledit_delete_range( | |
} | |
} | |
} else if (sl1 == 0) { | |
- delete_line_section(buffer, l2, 0, pl2->start_… | |
- ledit_delete_line_entries(buffer, l1, l2 - 1); | |
+ delete_line_section_base(buffer, l2, 0, pl2->s… | |
+ ledit_delete_line_entries_base(buffer, l1, l2 … | |
*new_line_ret = l1; | |
ledit_x_softline_to_pos(ledit_get_line(buffer,… | |
} else if (sl2 == softlines - 1) { | |
- delete_line_section(buffer, l1, pl1->start_ind… | |
- ledit_delete_line_entries(buffer, l1 + 1, l2); | |
+ delete_line_section_base(buffer, l1, pl1->star… | |
+ ledit_delete_line_entries_base(buffer, l1 + 1,… | |
if (l1 + 1 >= buffer->lines_num) { | |
*new_line_ret = buffer->lines_num - 1; | |
ledit_line *new_lline = ledit_get_line… | |
t@@ -666,10 +978,10 @@ ledit_delete_range( | |
} | |
} else { | |
/* FIXME: should this join the two lines? */ | |
- delete_line_section(buffer, l1, pl1->start_ind… | |
- delete_line_section(buffer, l2, 0, pl2->start_… | |
+ delete_line_section_base(buffer, l1, pl1->star… | |
+ delete_line_section_base(buffer, l2, 0, pl2->s… | |
if (l2 > l1 + 1) | |
- ledit_delete_line_entries(buffer, l1 +… | |
+ ledit_delete_line_entries_base(buffer,… | |
*new_line_ret = l1 + 1; | |
ledit_x_softline_to_pos(ledit_get_line(buffer,… | |
} | |
t@@ -684,7 +996,7 @@ ledit_delete_range( | |
b1 = byte_index2; | |
b2 = byte_index1; | |
} | |
- delete_line_section(buffer, line_index1, b1, b2 - b1); | |
+ delete_line_section_base(buffer, line_index1, b1, b2 -… | |
*new_line_ret = line_index1; | |
*new_byte_ret = b1; | |
} else { | |
t@@ -704,7 +1016,7 @@ ledit_delete_range( | |
ledit_line *line2 = ledit_get_line(buffer, l2); | |
line1->len = b1; | |
if (b2 > 0) { | |
- ledit_insert_text( | |
+ ledit_insert_text_base( | |
buffer, l1, b1, | |
line2->text + b2, | |
line2->len - b2 | |
t@@ -712,7 +1024,7 @@ ledit_delete_range( | |
} | |
*new_line_ret = l1; | |
*new_byte_ret = b1; | |
- ledit_delete_line_entries(buffer, l1 + 1, l2); | |
+ ledit_delete_line_entries_base(buffer, l1 + 1, l2); | |
} | |
if (buffer->state->mode == NORMAL) | |
*new_byte_ret = ledit_get_legal_normal_pos(buffer, *ne… | |
diff --git a/buffer.h b/buffer.h | |
t@@ -12,13 +12,17 @@ typedef struct { | |
PangoLayout *layout; | |
char *text; | |
ledit_buffer *parent_buffer; | |
+ int gap; /* position of gap for gap buffer */ | |
int cap; /* allocated space for text */ | |
int len; /* actual length of text */ | |
int w; | |
int h; | |
long y_offset; /* pixel offset starting at the top of the file */ | |
int cache_index; /* index of pixmap in cache, or -1 if not assigned */ | |
- char dirty; /* whether line needs to be rendered before being draw */ | |
+ char dirty; /* whether line needs to be rendered before being drawn */ | |
+ char text_dirty; /* whether the text in the PangoLayout needs to be | |
+ updated before the layout is rendered */ | |
+ char h_dirty; /* whether height needs to be recalculated still */ | |
} ledit_line; | |
struct ledit_buffer { | |
t@@ -40,9 +44,60 @@ struct ledit_buffer { | |
ledit_buffer *ledit_create_buffer(ledit_common_state *state); | |
void ledit_destroy_buffer(ledit_buffer *buffer); | |
+void ledit_normalize_line(ledit_line *line); | |
void ledit_set_line_selection(ledit_buffer *buffer, int line, int start_byte, … | |
void ledit_set_line_cursor_attrs(ledit_buffer *buffer, int line, int index); | |
void ledit_wipe_line_cursor_attrs(ledit_buffer *buffer, int line); | |
+void ledit_render_line(ledit_buffer *buffer, int line_index); | |
+ledit_line *ledit_get_line(ledit_buffer *buffer, int index); | |
+int ledit_line_visible(ledit_buffer *buffer, int index); | |
+int ledit_get_legal_normal_pos(ledit_buffer *buffer, int line, int pos); | |
+void ledit_pos_to_x_softline(ledit_line *line, int pos, int *x_ret, int *softl… | |
+void ledit_x_softline_to_pos(ledit_line *line, int x, int softline, int *pos_r… | |
+int ledit_next_utf8(ledit_line *line, int index); | |
+int ledit_prev_utf8(ledit_line *line, int index); | |
+size_t ledit_textlen(ledit_buffer *buffer, int line1, int byte1, int line2, in… | |
+void ledit_copy_text(ledit_buffer *buffer, char *dst, int line1, int byte1, in… | |
+size_t ledit_copy_text_with_resize( | |
+ ledit_buffer *buffer, | |
+ char **dst, size_t *alloc, | |
+ int line1, int byte1, | |
+ int line2, int byte2 | |
+); | |
+void ledit_recalc_line(ledit_buffer *buffer, int line); | |
+void ledit_recalc_from_line(ledit_buffer *buffer, int line); | |
+void ledit_recalc_all_lines(ledit_buffer *buffer); | |
+ | |
+/* The following functions all have two versions: | |
+ * - The _base version does not call any recalc functions - this can be used | |
+ * when multiple operations are performed before the next render in order to | |
+ * avoid recalculating everything every time. | |
+ * - The non-base versions call the appropriate recalc function in order to | |
+ * keep everything in a consistent state. */ | |
+ | |
+void ledit_insert_text_base(ledit_buffer *buffer, int line_index, int index, c… | |
+void ledit_insert_text_with_newlines_base( | |
+ ledit_buffer *buffer, | |
+ int line_index, int index, | |
+ char *text, int len, | |
+ int *end_line_ret, int *end_char_ret | |
+); | |
+void ledit_append_line_base(ledit_buffer *buffer, int line_index, int text_ind… | |
+void ledit_delete_line_entries_base(ledit_buffer *buffer, int index1, int inde… | |
+void ledit_delete_line_entry_base(ledit_buffer *buffer, int index); | |
+int ledit_delete_unicode_char_base(ledit_buffer *buffer, int line_index, int b… | |
+void ledit_delete_range_base( | |
+ ledit_buffer *buffer, int line_based, | |
+ int line_index1, int byte_index1, | |
+ int line_index2, int byte_index2, | |
+ int *new_line_ret, int *new_byte_ret | |
+); | |
+void ledit_insert_text_from_line_base( | |
+ ledit_buffer *buffer, | |
+ int dst_line, int dst_index, | |
+ int src_line, int src_index, int src_len | |
+); | |
+ | |
void ledit_insert_text(ledit_buffer *buffer, int line_index, int index, char *… | |
void ledit_insert_text_with_newlines( | |
ledit_buffer *buffer, | |
t@@ -50,34 +105,17 @@ void ledit_insert_text_with_newlines( | |
char *text, int len, | |
int *end_line_ret, int *end_char_ret | |
); | |
-void ledit_render_line(ledit_buffer *buffer, int line_index); | |
void ledit_append_line(ledit_buffer *buffer, int line_index, int text_index); | |
void ledit_delete_line_entries(ledit_buffer *buffer, int index1, int index2); | |
void ledit_delete_line_entry(ledit_buffer *buffer, int index); | |
-ledit_line *ledit_get_line(ledit_buffer *buffer, int index); | |
-int ledit_line_visible(ledit_buffer *buffer, int index); | |
int ledit_delete_unicode_char(ledit_buffer *buffer, int line_index, int byte_i… | |
-int ledit_get_legal_normal_pos(ledit_buffer *buffer, int line, int pos); | |
void ledit_delete_range( | |
ledit_buffer *buffer, int line_based, | |
int line_index1, int byte_index1, | |
int line_index2, int byte_index2, | |
int *new_line_ret, int *new_byte_ret | |
); | |
-void ledit_pos_to_x_softline(ledit_line *line, int pos, int *x_ret, int *softl… | |
-void ledit_x_softline_to_pos(ledit_line *line, int x, int softline, int *pos_r… | |
-int ledit_next_utf8(ledit_line *line, int index); | |
-int ledit_prev_utf8(ledit_line *line, int index); | |
-size_t ledit_textlen(ledit_buffer *buffer, int line1, int byte1, int line2, in… | |
-void ledit_copy_text(ledit_buffer *buffer, char *dst, int line1, int byte1, in… | |
-size_t ledit_copy_text_with_resize( | |
- ledit_buffer *buffer, | |
- char **dst, size_t *alloc, | |
- int line1, int byte1, | |
- int line2, int byte2 | |
-); | |
-void | |
-ledit_insert_text_from_line( | |
+void ledit_insert_text_from_line( | |
ledit_buffer *buffer, | |
int dst_line, int dst_index, | |
int src_line, int src_index, int src_len | |
diff --git a/ledit.c b/ledit.c | |
t@@ -1,3 +1,4 @@ | |
+/* FIXME: Only redraw part of screen if needed */ | |
/* FIXME: overflow in repeated commands */ | |
/* FIXME: Fix lag when scrolling */ | |
/* FIXME: Fix lag when selecting with mouse */ | |
t@@ -1367,6 +1368,7 @@ delete_key(void) { | |
} else if (buffer->cur_index == cur_line->len) { | |
if (buffer->cur_line != buffer->lines_num - 1) { | |
int old_len = cur_line->len; | |
+ /* FIXME: THIS CURRENTLY DOESN'T RECALC LINE SIZE! */ | |
ledit_insert_text_from_line( | |
buffer, buffer->cur_line, cur_line->len, | |
buffer->cur_line + 1, 0, -1 | |
t@@ -1839,12 +1841,14 @@ key_press(XEvent event) { | |
is needed to make keys that use shift match… | |
cur_keys->keys[i].func(); | |
found = 1; | |
+ break; | |
} | |
} else if ((cur_keys->keys[i].modes & state.mode) && | |
cur_keys->keys[i].keysym == sym && | |
match(cur_keys->keys[i].mods, key_state)) { | |
cur_keys->keys[i].func(); | |
found = 1; | |
+ break; | |
} | |
} | |
if (found) { | |
diff --git a/search.c b/search.c | |
t@@ -48,6 +48,7 @@ search_forward(ledit_buffer *buffer, int *line_ret, int *byt… | |
int byte = buffer->cur_index + 1; | |
char *res; | |
ledit_line *lline = ledit_get_line(buffer, line); | |
+ ledit_normalize_line(lline); | |
if ((res = strstr(lline->text + byte, last_search)) != NULL) { | |
*line_ret = line; | |
*byte_ret = (int)(res - lline->text); | |
t@@ -55,6 +56,7 @@ search_forward(ledit_buffer *buffer, int *line_ret, int *byt… | |
} | |
for (int i = line + 1; i < buffer->lines_num; i++) { | |
lline = ledit_get_line(buffer, i); | |
+ ledit_normalize_line(lline); | |
if ((res = strstr(lline->text, last_search)) != NULL) { | |
*line_ret = i; | |
*byte_ret = (int)(res - lline->text); | |
t@@ -63,6 +65,7 @@ search_forward(ledit_buffer *buffer, int *line_ret, int *byt… | |
} | |
for (int i = 0; i < line; i++) { | |
lline = ledit_get_line(buffer, i); | |
+ ledit_normalize_line(lline); | |
if ((res = strstr(lline->text, last_search)) != NULL) { | |
*line_ret = i; | |
*byte_ret = (int)(res - lline->text); | |
t@@ -70,6 +73,7 @@ search_forward(ledit_buffer *buffer, int *line_ret, int *byt… | |
} | |
} | |
lline = ledit_get_line(buffer, line); | |
+ ledit_normalize_line(lline); | |
if ((res = strstr(lline->text, last_search)) != NULL) { | |
*line_ret = line; | |
*byte_ret = (int)(res - lline->text); | |
t@@ -88,6 +92,7 @@ search_backward(ledit_buffer *buffer, int *line_ret, int *by… | |
int line = buffer->cur_line; | |
int byte = buffer->cur_index; | |
ledit_line *lline = ledit_get_line(buffer, line); | |
+ ledit_normalize_line(lline); | |
char *last = NULL, *res = lline->text; | |
while ((res = strstr(res, last_search)) != NULL && res < lline->text +… | |
last = res; | |
t@@ -101,6 +106,7 @@ search_backward(ledit_buffer *buffer, int *line_ret, int *… | |
} | |
for (int i = line - 1; i >= 0; i--) { | |
lline = ledit_get_line(buffer, i); | |
+ ledit_normalize_line(lline); | |
res = lline->text; | |
while ((res = strstr(res, last_search)) != NULL) { | |
last = res; | |
t@@ -114,6 +120,7 @@ search_backward(ledit_buffer *buffer, int *line_ret, int *… | |
} | |
for (int i = buffer->lines_num - 1; i > line; i--) { | |
lline = ledit_get_line(buffer, i); | |
+ ledit_normalize_line(lline); | |
res = lline->text; | |
while ((res = strstr(res, last_search)) != NULL) { | |
last = res; | |
t@@ -126,6 +133,7 @@ search_backward(ledit_buffer *buffer, int *line_ret, int *… | |
} | |
} | |
lline = ledit_get_line(buffer, line); | |
+ ledit_normalize_line(lline); | |
res = lline->text + byte; | |
while ((res = strstr(res, last_search)) != NULL) { | |
last = res; |