Introduction
Introduction Statistics Contact Development Disclaimer Help
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;
You are viewing proxied material from lumidify.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.