Introduction
Introduction Statistics Contact Development Disclaimer Help
tDon't keep a PangoLayout for every line - 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 6c98800389dc02e20954a157675ac67fb02b7f8c
parent 60385928394de59d1104da2c69241745cbba4784
Author: lumidify <[email protected]>
Date: Sun, 21 Nov 2021 14:30:26 +0100
Don't keep a PangoLayout for every line
Diffstat:
M LICENSE | 3 ++-
M buffer.c | 632 ++++++++++++++++++-----------…
M buffer.h | 37 +++++++++++++++++------------…
M cache.c | 169 +++++++++++++++++++++++------…
M cache.h | 128 ++++++++++++++++++++++++++++-…
M keys_basic.c | 26 ++++++++++++--------------
M keys_command.c | 2 --
M memory.c | 23 +++++++++++++++++++++++
M memory.h | 1 +
9 files changed, 675 insertions(+), 346 deletions(-)
---
diff --git a/LICENSE b/LICENSE
t@@ -1,4 +1,5 @@
-Note: Some stuff is stolen from st (https://st.suckless.org)
+Note 1: Some stuff is stolen from st (https://st.suckless.org)
+Note 2: Some stuff is stolen from OpenBSD (https://openbsd.org)
ISC License
diff --git a/buffer.c b/buffer.c
t@@ -36,16 +36,44 @@
static PangoAttrList *basic_attrs = NULL;
-static void err_text_dirty(ledit_buffer *buffer, int line);
+/*static void err_text_dirty(ledit_buffer *buffer, int line);*/
static void move_text_gap(ledit_line *line, int index);
static void resize_and_move_text_gap(ledit_line *line, int min_size, int index…
static char *strchr_len(char *text, char c, long len);
static void init_line(ledit_buffer *buffer, ledit_line *line);
static void delete_line_section_base(ledit_buffer *buffer, int line, int start…
-static void normalize_and_set_pango_text(ledit_line *line);
static void swap(int *a, int *b);
static void copy_selection_to_x_primary(ledit_buffer *buffer, int line1, int b…
+/*
+ * Assign a cache index to line and set text and highlight of the pango layout.
+ */
+static void set_pango_text_and_highlight(ledit_buffer *buffer, int line);
+
+/*
+ * Get the pango layout for line.
+ * This first assigns a cache index (by calling set_pango_text_and_highlight).
+ */
+static PangoLayout *get_pango_layout(ledit_buffer *buffer, int line);
+
+/*
+ * Get an attribute list for a text highlight between the given range.
+ */
+static PangoAttrList *get_pango_attributes(ledit_buffer *buffer, int start_byt…
+
+/*
+ * Set the attributes for a PangoLayout belonging to the given line index.
+ * If the line is part of the buffer's selection, the selection is set.
+ * If that is not the case but cursor_index is set for the line, the character
+ * at that position is highlighted (this is used for the normal mode cursor).
+ * Otherwise, the default attributes (basic_attrs) are set.
+ */
+static void set_line_layout_attrs(ledit_buffer *buffer, int line, PangoLayout …
+
+static int line_visible_callback(void *data, int line);
+static void set_pixmap_line_helper(void *data, int line, int index);
+static void set_layout_line_helper(void *data, int line, int index);
+
void
ledit_buffer_set_mode(ledit_buffer *buffer, enum ledit_mode mode) {
buffer->common->mode = mode;
t@@ -105,6 +133,13 @@ ledit_buffer_create(ledit_common *common, ledit_theme *th…
}
ledit_buffer *buffer = ledit_malloc(sizeof(ledit_buffer));
+ buffer->cache = cache_create(common->dpy);
+ buffer->undo = ledit_undo_stack_create();
+ buffer->theme = theme;
+ buffer->marklist = marklist_create();
+ ledit_window_set_scroll_callback(window, &ledit_buffer_scroll_handler,…
+ ledit_window_set_button_callback(window, &ledit_buffer_button_handler,…
+
buffer->common = common;
buffer->window = window;
buffer->lines = NULL;
t@@ -122,14 +157,9 @@ ledit_buffer_create(ledit_common *common, ledit_theme *th…
buffer->display_offset = 0;
buffer->sel.line1 = buffer->sel.byte1 = -1;
buffer->sel.line2 = buffer->sel.byte2 = -1;
+
ledit_buffer_append_line_base(buffer, -1, -1);
ledit_buffer_recalc_all_lines(buffer);
- buffer->cache = ledit_cache_create(common);
- buffer->undo = ledit_undo_stack_create();
- buffer->theme = theme;
- buffer->marklist = marklist_create();
- ledit_window_set_scroll_callback(window, &ledit_buffer_scroll_handler,…
- ledit_window_set_button_callback(window, &ledit_buffer_button_handler,…
return buffer;
}
t@@ -217,10 +247,9 @@ ledit_buffer_destroy(ledit_buffer *buffer) {
ledit_line *l;
for (int i = 0; i < buffer->lines_num; i++) {
l = ledit_buffer_get_line(buffer, i);
- g_object_unref(l->layout);
free(l->text);
}
- ledit_cache_destroy(buffer->cache);
+ cache_destroy(buffer->cache);
ledit_undo_stack_destroy(buffer->undo);
free(buffer->lines);
if (buffer->filename)
t@@ -249,6 +278,7 @@ ledit_buffer_normalize_line(ledit_line *line) {
line->text[line->len] = '\0';
}
+#if 0
static void
err_text_dirty(ledit_buffer *buffer, int line) {
fprintf(
t@@ -258,9 +288,11 @@ err_text_dirty(ledit_buffer *buffer, int line) {
);
ledit_buffer_recalc_from_line(buffer, line);
}
+#endif
-void
-ledit_buffer_set_line_selection(ledit_buffer *buffer, int line, int start_byte…
+#if 0
+static void
+set_line_selection(ledit_buffer *buffer, int line, int start_byte, int end_byt…
ledit_line *l = ledit_buffer_get_line(buffer, line);
if (l->text_dirty)
err_text_dirty(buffer, line);
t@@ -284,41 +316,72 @@ ledit_buffer_set_line_selection(ledit_buffer *buffer, in…
pango_attr_list_unref(list);
l->dirty = 1;
}
+#endif
-void
-ledit_buffer_set_line_cursor_attrs(ledit_buffer *buffer, int line, int index) {
- ledit_line *l = ledit_buffer_get_line(buffer, line);
- if (l->text_dirty)
- err_text_dirty(buffer, line);
- if (buffer->common->mode == NORMAL) {
- XRenderColor fg = buffer->theme->text_fg.color;
- XRenderColor bg = buffer->theme->text_bg.color;
- PangoAttribute *attr0 = pango_attr_background_new(fg.red, fg.g…
- PangoAttribute *attr1 = pango_attr_foreground_new(bg.red, bg.g…
- attr0->start_index = index;
- attr0->end_index = index + 1;
- attr1->start_index = index;
- attr1->end_index = index + 1;
- PangoAttrList *list = pango_attr_list_new();
- pango_attr_list_insert(list, attr0);
- pango_attr_list_insert(list, attr1);
- #if PANGO_VERSION_CHECK(1, 44, 0)
- PangoAttribute *attr2 = pango_attr_insert_hyphens_new(FALSE);
- pango_attr_list_insert(list, attr2);
- #endif
- pango_layout_set_attributes(l->layout, list);
+static PangoAttrList *
+get_pango_attributes(ledit_buffer *buffer, int start_byte, int end_byte) {
+ XRenderColor fg = buffer->theme->text_fg.color;
+ XRenderColor bg = buffer->theme->text_bg.color;
+ PangoAttribute *attr0 = pango_attr_background_new(fg.red, fg.green, fg…
+ PangoAttribute *attr1 = pango_attr_foreground_new(bg.red, bg.green, bg…
+ attr0->start_index = start_byte;
+ attr0->end_index = end_byte;
+ attr1->start_index = start_byte;
+ attr1->end_index = end_byte;
+ PangoAttrList *list = pango_attr_list_new();
+ pango_attr_list_insert(list, attr0);
+ pango_attr_list_insert(list, attr1);
+ #if PANGO_VERSION_CHECK(1, 44, 0)
+ PangoAttribute *attr2 = pango_attr_insert_hyphens_new(FALSE);
+ pango_attr_list_insert(list, attr2);
+ #endif
+ return list;
+}
+
+/* this takes layout directly to possibly avoid infinite recursion */
+static void
+set_line_layout_attrs(ledit_buffer *buffer, int line, PangoLayout *layout) {
+ ledit_line *ll = ledit_buffer_get_line(buffer, line);
+ ledit_range sel = ll->parent_buffer->sel;
+ PangoAttrList *list = NULL;
+ if (sel.line1 < line && sel.line2 > line) {
+ list = get_pango_attributes(buffer, 0, ll->len);
+ } else if (sel.line1 == line && sel.line2 == line) {
+ int start = sel.byte1, end = sel.byte2;
+ if (start > end)
+ swap(&start, &end);
+ list = get_pango_attributes(buffer, start, end);
+ } else if (sel.line1 == line && sel.line2 > line) {
+ list = get_pango_attributes(buffer, sel.byte1, ll->len);
+ } else if (sel.line1 < line && sel.line2 == line) {
+ list = get_pango_attributes(buffer, 0, sel.byte2);
+ } else if (ll->cursor_index >= 0 && ll->cursor_index < ll->len) {
+ /* FIXME: does just adding one really do the right thing? */
+ list = get_pango_attributes(buffer, ll->cursor_index, ll->curs…
+ }
+ if (list != NULL) {
+ pango_layout_set_attributes(layout, list);
pango_attr_list_unref(list);
} else {
- pango_layout_set_attributes(l->layout, basic_attrs);
+ pango_layout_set_attributes(layout, basic_attrs);
}
- l->dirty = 1;
+ ll->highlight_dirty = 0;
+}
+
+void
+ledit_buffer_set_line_cursor_attrs(ledit_buffer *buffer, int line, int index) {
+ ledit_line *ll = ledit_buffer_get_line(buffer, line);
+ ll->cursor_index = index;
+ ll->highlight_dirty = 1;
+ ll->dirty = 1;
}
void
ledit_buffer_wipe_line_cursor_attrs(ledit_buffer *buffer, int line) {
- ledit_line *l = ledit_buffer_get_line(buffer, line);
- pango_layout_set_attributes(l->layout, basic_attrs);
- l->dirty = 1;
+ ledit_line *ll = ledit_buffer_get_line(buffer, line);
+ ll->cursor_index = -1;
+ ll->highlight_dirty = 1;
+ ll->dirty = 1;
}
/* FIXME: To simplify this a bit, maybe just copy text to txtbuf first and
t@@ -589,36 +652,31 @@ line_visible_callback(void *data, int line) {
void
ledit_buffer_render_line(ledit_buffer *buffer, int line_index) {
/* FIXME: check for <= 0 on size */
- ledit_line *line = ledit_buffer_get_line(buffer, 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) {
- int new_index = ledit_get_unneeded_cache_index(buffer->cache, …
- ledit_cache_pixmap *tmp_pix = ledit_get_cache_pixmap(buffer->c…
- if (tmp_pix->line >= 0) {
- ledit_line *old_line = ledit_buffer_get_line(buffer, t…
- old_line->cache_index = -1;
- }
- tmp_pix->line = line_index;
- line->cache_index = new_index;
+ ledit_line *ll = ledit_buffer_get_line(buffer, line_index);
+ assert(!ll->h_dirty); /* FIXME */
+ PangoLayout *layout = get_pango_layout(buffer, line_index);
+ if (ll->cache_pixmap_index == -1) {
+ cache_assign_pixmap_index(
+ buffer->cache, line_index, buffer,
+ &line_visible_callback, &set_pixmap_line_helper
+ );
}
- ledit_cache_pixmap *pix = ledit_get_cache_pixmap(buffer->cache, line->…
+ cache_pixmap *pix = cache_get_pixmap(buffer->cache, ll->cache_pixmap_i…
/* FIXME: sensible default pixmap sizes here */
if (pix->pixmap == None || pix->draw == NULL) {
pix->pixmap = XCreatePixmap(
buffer->common->dpy, buffer->window->drawable,
- line->w + 10, line->h + 10, buffer->common->depth
+ ll->w + 10, ll->h + 10, buffer->common->depth
);
- pix->w = line->w + 10;
- pix->h = line->h + 10;
+ pix->w = ll->w + 10;
+ pix->h = ll->h + 10;
pix->draw = XftDrawCreate(
buffer->common->dpy, pix->pixmap,
buffer->common->vis, buffer->common->cm
);
- } else if (pix->w < line->w || pix->h < line->h) {
- int new_w = line->w > pix->w ? line->w + 10 : pix->w + 10;
- int new_h = line->h > pix->h ? line->h + 10 : pix->h + 10;
+ } else if (pix->w < ll->w || pix->h < ll->h) {
+ int new_w = ll->w > pix->w ? ll->w + 10 : pix->w + 10;
+ int new_h = ll->h > pix->h ? ll->h + 10 : pix->h + 10;
XFreePixmap(buffer->common->dpy, pix->pixmap);
pix->pixmap = XCreatePixmap(
buffer->common->dpy, buffer->window->drawable,
t@@ -628,35 +686,32 @@ ledit_buffer_render_line(ledit_buffer *buffer, int line_…
pix->h = new_h;
XftDrawChange(pix->draw, pix->pixmap);
}
- XftDrawRect(pix->draw, &buffer->theme->text_bg, 0, 0, line->w, line->h…
- pango_xft_render_layout(pix->draw, &buffer->theme->text_fg, line->layo…
- line->dirty = 0;
+ XftDrawRect(pix->draw, &buffer->theme->text_bg, 0, 0, ll->w, ll->h);
+ pango_xft_render_layout(pix->draw, &buffer->theme->text_fg, layout, 0,…
+ ll->dirty = 0;
}
static void
init_line(ledit_buffer *buffer, ledit_line *line) {
int text_w, text_h;
line->parent_buffer = buffer;
- line->layout = pango_layout_new(buffer->window->context);
ledit_window_get_textview_size(buffer->window, &text_w, &text_h);
- pango_layout_set_width(line->layout, text_w * PANGO_SCALE);
- pango_layout_set_font_description(line->layout, buffer->window->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->cache_pixmap_index = -1;
+ line->cache_layout_index = -1;
line->dirty = 1;
line->text_dirty = 1;
+ line->highlight_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->cursor_index = -1;
+ line->softlines = 1;
line->w = text_w;
+ line->h = 0;
line->y_offset = 0;
}
t@@ -682,9 +737,9 @@ ledit_buffer_append_line_base(ledit_buffer *buffer, int li…
buffer->lines + line_index + 1,
(buffer->lines_num - (line_index + 1)) * sizeof(ledit_line)
);
+ buffer->lines_num++;
ledit_line *new_l = ledit_buffer_get_line(buffer, line_index + 1);
init_line(buffer, new_l);
- buffer->lines_num++;
if (text_index != -1) {
ledit_line *l = ledit_buffer_get_line(buffer, line_index);
ledit_buffer_insert_text_from_line_base(
t@@ -705,15 +760,31 @@ ledit_buffer_delete_line_entries(ledit_buffer *buffer, i…
ledit_buffer_recalc_from_line(buffer, index1 > 0 ? index1 - 1 : 0);
}
+static void
+set_pixmap_line_helper(void *data, int line, int index) {
+ ledit_line *ll = ledit_buffer_get_line((ledit_buffer *)data, line);
+ ll->cache_pixmap_index = index;
+}
+
+static void
+set_layout_line_helper(void *data, int line, int index) {
+ ledit_line *ll = ledit_buffer_get_line((ledit_buffer *)data, line);
+ ll->cache_layout_index = index;
+}
+
/* IMPORTANT: ledit_buffer_recalc_from_line needs to be called sometime after …
void
ledit_buffer_delete_line_entries_base(ledit_buffer *buffer, int index1, int in…
ledit_line *l;
/* FIXME: make sure this is always true */
+ /* FIXME: Ummm... what is that assert supposed to do? */
assert(index2 - index1 != buffer->lines_num);
+ cache_invalidate_from_line(
+ buffer->cache, index1, buffer,
+ &set_pixmap_line_helper, &set_layout_line_helper
+ );
for (int i = index1; i <= index2; i++) {
l = ledit_buffer_get_line(buffer, i);
- g_object_unref(l->layout);
free(l->text);
}
/* FIXME: gap buffer */
t@@ -747,9 +818,9 @@ ledit_buffer_delete_line_entry_base(ledit_buffer *buffer, …
/* FIXME: use some sort of gap buffer (that would make this function
slightly more useful...) */
-/* FIXME: error checking, assert? */
ledit_line *
ledit_buffer_get_line(ledit_buffer *buffer, int index) {
+ assert(index >= 0 && index < buffer->lines_num);
return &buffer->lines[index];
}
t@@ -758,20 +829,15 @@ ledit_buffer_get_line(ledit_buffer *buffer, int index) {
* - if height has changed, offset of all following lines is changed */
void
ledit_buffer_recalc_line(ledit_buffer *buffer, int line) {
- int w, h;
ledit_line *l = ledit_buffer_get_line(buffer, line);
- if (l->text_dirty) {
- ledit_buffer_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 (l->text_dirty)
+ set_pango_text_and_highlight(buffer, line);
+
/* if height changed, set height of current line
* and adjust offsets of all lines following it */
- if (l->h != h) {
- long off = l->y_offset + h;
- l->h = h;
+ if (l->h_dirty) {
+ l->h_dirty = 0;
+ long off = l->y_offset + l->h;
for (int i = line + 1; i < buffer->lines_num; i++) {
l = ledit_buffer_get_line(buffer, i);
l->y_offset = off;
t@@ -785,21 +851,12 @@ ledit_buffer_recalc_line(ledit_buffer *buffer, int line)…
* and offset for all lines starting at 'line' */
void
ledit_buffer_recalc_from_line(ledit_buffer *buffer, int line) {
- int w, h;
ledit_line *l = ledit_buffer_get_line(buffer, line);
long off = l->y_offset;
for (int i = line; i < buffer->lines_num; i++) {
l = ledit_buffer_get_line(buffer, i);
- if (l->text_dirty) {
- ledit_buffer_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;
- }
+ if (l->text_dirty)
+ set_pango_text_and_highlight(buffer, i);
l->h_dirty = 0;
l->y_offset = off;
off += l->h;
t@@ -951,8 +1008,9 @@ ledit_buffer_next_cursor_pos(ledit_buffer *buffer, int li…
ledit_line *ll = ledit_buffer_get_line(buffer, line);
int c = line_byte_to_char(ll, byte);
int cur_byte = byte;
+ PangoLayout *layout = get_pango_layout(buffer, line);
const PangoLogAttr *attrs =
- pango_layout_get_log_attrs_readonly(ll->layout, &nattrs);
+ pango_layout_get_log_attrs_readonly(layout, &nattrs);
for (int i = 0; i < num; i++) {
cur_byte = ledit_line_next_utf8(ll, byte);
for (c++; c < nattrs; c++) {
t@@ -972,8 +1030,9 @@ ledit_buffer_prev_cursor_pos(ledit_buffer *buffer, int li…
ledit_line *ll = ledit_buffer_get_line(buffer, line);
int c = line_byte_to_char(ll, byte);
int cur_byte = byte;
+ PangoLayout *layout = get_pango_layout(buffer, line);
const PangoLogAttr *attrs =
- pango_layout_get_log_attrs_readonly(ll->layout, &nattrs);
+ pango_layout_get_log_attrs_readonly(layout, &nattrs);
for (int i = 0; i < num; i++) {
cur_byte = ledit_line_prev_utf8(ll, cur_byte);
for (c--; c >= 0; c--) {
t@@ -988,15 +1047,17 @@ ledit_buffer_prev_cursor_pos(ledit_buffer *buffer, int …
}
static int
-line_next_word(ledit_line *line, int byte, int char_index, int wrapped_line, i…
+line_next_word(ledit_buffer *buffer, int line, int byte, int char_index, int w…
int c, nattrs;
+ ledit_line *ll = ledit_buffer_get_line(buffer, line);
if (char_index >= 0)
c = char_index;
else
- c = line_byte_to_char(line, byte);
- int cur_byte = wrapped_line ? byte : ledit_line_next_utf8(line, byte);
+ c = line_byte_to_char(ll, byte);
+ int cur_byte = wrapped_line ? byte : ledit_line_next_utf8(ll, byte);
+ PangoLayout *layout = get_pango_layout(buffer, line);
const PangoLogAttr *attrs =
- pango_layout_get_log_attrs_readonly(line->layout, &nattrs);
+ pango_layout_get_log_attrs_readonly(layout, &nattrs);
for (int i = wrapped_line ? c : c + 1; i < nattrs; i++) {
if (attrs[i].is_word_start) {
if (char_ret)
t@@ -1005,21 +1066,23 @@ line_next_word(ledit_line *line, int byte, int char_in…
*real_byte_ret = cur_byte;
return cur_byte;
}
- cur_byte = ledit_line_next_utf8(line, cur_byte);
+ cur_byte = ledit_line_next_utf8(ll, cur_byte);
}
return -1;
}
static int
-line_prev_word(ledit_line *line, int byte, int char_index, int *char_ret) {
+line_prev_word(ledit_buffer *buffer, int line, int byte, int char_index, int *…
int c, nattrs;
+ ledit_line *ll = ledit_buffer_get_line(buffer, line);
if (char_index >= 0)
c = char_index;
else
- c = line_byte_to_char(line, byte);
- int cur_byte = ledit_line_prev_utf8(line, byte);
+ c = line_byte_to_char(ll, byte);
+ int cur_byte = ledit_line_prev_utf8(ll, byte);
+ PangoLayout *layout = get_pango_layout(buffer, line);
const PangoLogAttr *attrs =
- pango_layout_get_log_attrs_readonly(line->layout, &nattrs);
+ pango_layout_get_log_attrs_readonly(layout, &nattrs);
if (c > nattrs)
return -1;
for (int i = c - 1; i >= 0; i--) {
t@@ -1028,21 +1091,23 @@ line_prev_word(ledit_line *line, int byte, int char_in…
*char_ret = i;
return cur_byte;
}
- cur_byte = ledit_line_prev_utf8(line, cur_byte);
+ cur_byte = ledit_line_prev_utf8(ll, cur_byte);
}
return -1;
}
static int
-line_prev_bigword(ledit_line *line, int byte, int char_index, int *char_ret) {
+line_prev_bigword(ledit_buffer *buffer, int line, int byte, int char_index, in…
int c, nattrs;
+ ledit_line *ll = ledit_buffer_get_line(buffer, line);
if (char_index >= 0)
c = char_index;
else
- c = line_byte_to_char(line, byte);
- int cur_byte = ledit_line_prev_utf8(line, byte);
+ c = line_byte_to_char(ll, byte);
+ int cur_byte = ledit_line_prev_utf8(ll, byte);
+ PangoLayout *layout = get_pango_layout(buffer, line);
const PangoLogAttr *attrs =
- pango_layout_get_log_attrs_readonly(line->layout, &nattrs);
+ pango_layout_get_log_attrs_readonly(layout, &nattrs);
int next_cursorb = byte;
int next_cursorc = c;
int found_word = 0;
t@@ -1063,20 +1128,22 @@ line_prev_bigword(ledit_line *line, int byte, int char…
next_cursorc = i;
next_cursorb = cur_byte;
}
- cur_byte = ledit_line_prev_utf8(line, cur_byte);
+ cur_byte = ledit_line_prev_utf8(ll, cur_byte);
}
return -1;
}
int
-line_next_bigword_end(ledit_line *line, int byte, int char_index, int wrapped_…
+line_next_bigword_end(ledit_buffer *buffer, int line, int byte, int char_index…
int c, nattrs;
+ ledit_line *ll = ledit_buffer_get_line(buffer, line);
if (char_index >= 0)
c = char_index;
else
- c = line_byte_to_char(line, byte);
+ c = line_byte_to_char(ll, byte);
+ PangoLayout *layout = get_pango_layout(buffer, line);
const PangoLogAttr *attrs =
- pango_layout_get_log_attrs_readonly(line->layout, &nattrs);
+ pango_layout_get_log_attrs_readonly(layout, &nattrs);
int last_cursorb, last_cursorc;
if (wrapped_line) {
last_cursorb = byte;
t@@ -1101,21 +1168,23 @@ line_next_bigword_end(ledit_line *line, int byte, int …
last_cursorc = i;
last_cursorb = cur_byte;
}
- cur_byte = ledit_line_next_utf8(line, cur_byte);
+ cur_byte = ledit_line_next_utf8(ll, cur_byte);
}
return -1;
}
static int
-line_next_word_end(ledit_line *line, int byte, int char_index, int wrapped_lin…
+line_next_word_end(ledit_buffer *buffer, int line, int byte, int char_index, i…
int c, nattrs;
+ ledit_line *ll = ledit_buffer_get_line(buffer, line);
if (char_index >= 0)
c = char_index;
else
- c = line_byte_to_char(line, byte);
- int cur_byte = ledit_line_next_utf8(line, byte);
+ c = line_byte_to_char(ll, byte);
+ int cur_byte = ledit_line_next_utf8(ll, byte);
+ PangoLayout *layout = get_pango_layout(buffer, line);
const PangoLogAttr *attrs =
- pango_layout_get_log_attrs_readonly(line->layout, &nattrs);
+ pango_layout_get_log_attrs_readonly(layout, &nattrs);
int last_cursorb, last_cursorc;
if (wrapped_line) {
last_cursorb = byte;
t@@ -1136,21 +1205,23 @@ line_next_word_end(ledit_line *line, int byte, int cha…
last_cursorc = i;
last_cursorb = cur_byte;
}
- cur_byte = ledit_line_next_utf8(line, cur_byte);
+ cur_byte = ledit_line_next_utf8(ll, cur_byte);
}
return -1;
}
static int
-line_next_bigword(ledit_line *line, int byte, int char_index, int wrapped_line…
+line_next_bigword(ledit_buffer *buffer, int line, int byte, int char_index, in…
int c, nattrs;
+ ledit_line *ll = ledit_buffer_get_line(buffer, line);
if (char_index >= 0)
c = char_index;
else
- c = line_byte_to_char(line, byte);
+ c = line_byte_to_char(ll, byte);
int cur_byte = byte;
+ PangoLayout *layout = get_pango_layout(buffer, line);
const PangoLogAttr *attrs =
- pango_layout_get_log_attrs_readonly(line->layout, &nattrs);
+ pango_layout_get_log_attrs_readonly(layout, &nattrs);
int found_ws = wrapped_line;
for (int i = c; i < nattrs; i++) {
if (!found_ws && attrs[i].is_white) {
t@@ -1162,24 +1233,26 @@ line_next_bigword(ledit_line *line, int byte, int char…
*real_byte_ret = cur_byte;
return cur_byte;
}
- cur_byte = ledit_line_next_utf8(line, cur_byte);
+ cur_byte = ledit_line_next_utf8(ll, cur_byte);
}
return -1;
}
int
-ledit_line_next_non_whitespace(ledit_line *line, int byte) {
+ledit_line_next_non_whitespace(ledit_buffer *buffer, int line, int byte) {
int c, nattrs;
- c = line_byte_to_char(line, byte);
+ ledit_line *ll = ledit_buffer_get_line(buffer, line);
+ c = line_byte_to_char(ll, byte);
int cur_byte = byte;
+ PangoLayout *layout = get_pango_layout(buffer, line);
const PangoLogAttr *attrs =
- pango_layout_get_log_attrs_readonly(line->layout, &nattrs);
+ pango_layout_get_log_attrs_readonly(layout, &nattrs);
for (; c < nattrs; c++) {
if (!attrs[c].is_white)
return cur_byte;
- cur_byte = ledit_line_next_utf8(line, cur_byte);
+ cur_byte = ledit_line_next_utf8(ll, cur_byte);
}
- return line->len;
+ return ll->len;
}
/* FIXME: document that word and bigword are a bit weird because word uses uni…
t@@ -1195,13 +1268,12 @@ ledit_buffer_next_##name( …
int cur_char = -1; …
int real_byte = -1; …
int wrapped_line; …
- ledit_line *ll = ledit_buffer_get_line(buffer, cur_line); …
for (int i = 0; i < num_repeat; i++) { …
wrapped_line = 0; …
- while ((cur_byte = func(ll, cur_byte, cur_char, wrapped_line, …
+ while ((cur_byte = func(buffer, cur_line, cur_byte, cur_char, …
+ wrapped_line, &cur_char, &real_byte)) == -1 && …
cur_line < buffer->lines_num - 1) { …
cur_line++; …
- ll = ledit_buffer_get_line(buffer, cur_line); …
cur_byte = 0; …
wrapped_line = 1; …
} …
t@@ -1210,6 +1282,7 @@ ledit_buffer_next_##name( …
} …
if (cur_byte == -1) { …
*line_ret = buffer->lines_num - 1; …
+ ledit_line *ll = ledit_buffer_get_line(buffer, buffer->lines_n…
*byte_ret = ledit_buffer_get_legal_normal_pos(buffer, buffer->…
*real_byte_ret = ll->len; …
} else { …
t@@ -1230,7 +1303,8 @@ ledit_buffer_prev_##name( …
int cur_char = -1; …
ledit_line *ll = ledit_buffer_get_line(buffer, cur_line); …
for (int i = 0; i < num_repeat; i++) { …
- while ((cur_byte = func(ll, cur_byte, cur_char, &cur_char)) ==…
+ while ((cur_byte = func(buffer, cur_line, cur_byte, cur_char, …
+ &cur_char)) == -1 && cur_line > 0) { …
cur_line--; …
ll = ledit_buffer_get_line(buffer, cur_line); …
cur_byte = ll->len; …
t@@ -1262,11 +1336,11 @@ ledit_buffer_get_pos_softline_bounds(
int *start_byte_ret, int *end_byte_ret) {
assert(line >= 0 && line < buffer->lines_num);
ledit_line *ll = ledit_buffer_get_line(buffer, line);
- normalize_and_set_pango_text(ll);
assert(pos >= 0 && pos <= ll->len);
+ PangoLayout *layout = get_pango_layout(buffer, line);
int x, sli;
- pango_layout_index_to_line_x(ll->layout, pos, 0, &sli, &x);
- PangoLayoutLine *pl = pango_layout_get_line_readonly(ll->layout, sli);
+ pango_layout_index_to_line_x(layout, pos, 0, &sli, &x);
+ PangoLayoutLine *pl = pango_layout_get_line_readonly(layout, sli);
*start_byte_ret = pl->start_index;
*end_byte_ret = pl->start_index + pl->length;
}
t@@ -1277,9 +1351,9 @@ ledit_buffer_get_softline_bounds(
int *start_byte_ret, int *end_byte_ret) {
assert(line >= 0 && line < buffer->lines_num);
ledit_line *ll = ledit_buffer_get_line(buffer, line);
- normalize_and_set_pango_text(ll);
- assert(softline < pango_layout_get_line_count(ll->layout));
- PangoLayoutLine *pl = pango_layout_get_line_readonly(ll->layout, softl…
+ PangoLayout *layout = get_pango_layout(buffer, line);
+ assert(softline < ll->softlines);
+ PangoLayoutLine *pl = pango_layout_get_line_readonly(layout, softline);
*start_byte_ret = pl->start_index;
*end_byte_ret = pl->start_index + pl->length;
}
t@@ -1288,7 +1362,9 @@ int
ledit_buffer_get_softline_count(ledit_buffer *buffer, int line) {
assert(line >= 0 && line < buffer->lines_num);
ledit_line *ll = ledit_buffer_get_line(buffer, line);
- return pango_layout_get_line_count(ll->layout);
+ if (ll->text_dirty)
+ set_pango_text_and_highlight(buffer, line);
+ return ll->softlines;
}
int
t@@ -1296,8 +1372,9 @@ ledit_buffer_pos_to_softline(ledit_buffer *buffer, int l…
assert(line >= 0 && line < buffer->lines_num);
ledit_line *ll = ledit_buffer_get_line(buffer, line);
assert(pos >= 0 && pos <= ll->len);
+ PangoLayout *layout = get_pango_layout(buffer, line);
int x, sli;
- pango_layout_index_to_line_x(ll->layout, pos, 0, &sli, &x);
+ pango_layout_index_to_line_x(layout, pos, 0, &sli, &x);
return sli;
}
t@@ -1306,8 +1383,9 @@ ledit_buffer_get_cursor_pixel_pos(ledit_buffer *buffer, …
assert(line >= 0 && line < buffer->lines_num);
ledit_line *ll = ledit_buffer_get_line(buffer, line);
assert(pos >= 0 && pos <= ll->len);
+ PangoLayout *layout = get_pango_layout(buffer, line);
PangoRectangle strong, weak;
- pango_layout_get_cursor_pos(ll->layout, 0, &strong, &weak);
+ pango_layout_get_cursor_pos(layout, 0, &strong, &weak);
*x_ret = strong.x / PANGO_SCALE;
*y_ret = strong.y / PANGO_SCALE;
*h_ret = strong.height / PANGO_SCALE;
t@@ -1323,6 +1401,7 @@ ledit_buffer_move_cursor_visually(ledit_buffer *buffer, …
/* FIXME: trailing */
int trailing = 0, tmp_index;
ledit_line *cur_line = ledit_buffer_get_line(buffer, line);
+ PangoLayout *layout = get_pango_layout(buffer, line);
int new_index = pos, last_index = pos;
int dir = 1;
int num = movement;
t@@ -1333,7 +1412,7 @@ ledit_buffer_move_cursor_visually(ledit_buffer *buffer, …
while (num > 0) {
tmp_index = new_index;
pango_layout_move_cursor_visually(
- cur_line->layout, TRUE,
+ layout, TRUE,
new_index, trailing, dir,
&new_index, &trailing
);
t@@ -1403,7 +1482,6 @@ delete_line_section_base(ledit_buffer *buffer, int line,…
l->len -= length;
l->dirty = 1;
l->text_dirty = 1;
- l->h_dirty = 1;
}
int
t@@ -1428,85 +1506,124 @@ ledit_buffer_delete_unicode_char_base(ledit_buffer *b…
return new_index;
}
-/* FIXME: normalize_line will generally be called whenever it is not normalized
- since text_dirty should be set then, but the naming of this function techni…
- isn't very good */
static void
-normalize_and_set_pango_text(ledit_line *line) {
- if (line->text_dirty) {
- ledit_buffer_normalize_line(line);
- pango_layout_set_text(line->layout, line->text, line->len);
- line->text_dirty = 0;
- line->h_dirty = 1;
+set_pango_text_and_highlight(ledit_buffer *buffer, int line) {
+ cache_layout *cl;
+ ledit_line *ll = ledit_buffer_get_line(buffer, line);
+ int old_index = ll->cache_layout_index;
+ if (ll->cache_layout_index < 0) {
+ cache_assign_layout_index(
+ ll->parent_buffer->cache, line,
+ ll->parent_buffer, &set_layout_line_helper
+ );
+ assert(ll->cache_layout_index >= 0);
+ cl = cache_get_layout(ll->parent_buffer->cache, ll->cache_layo…
+ if (cl->layout == NULL) {
+ cl->layout = pango_layout_new(ll->parent_buffer->windo…
+ pango_layout_set_font_description(cl->layout, buffer->…
+ pango_layout_set_wrap(cl->layout, PANGO_WRAP_WORD_CHAR…
+ }
+ } else {
+ cl = cache_get_layout(ll->parent_buffer->cache, ll->cache_layo…
+ }
+ if (ll->text_dirty || old_index < 0) {
+ ledit_buffer_normalize_line(ll);
+ pango_layout_set_text(cl->layout, ll->text, ll->len);
+ set_line_layout_attrs(buffer, line, cl->layout);
+ /* FIXME: is this guard necessary? */
+ ll->softlines = ll->len > 0 ? pango_layout_get_line_count(cl->…
+ pango_layout_set_width(cl->layout, ll->w * PANGO_SCALE);
+ int w, h;
+ pango_layout_get_pixel_size(cl->layout, &w, &h);
+ if (h != ll->h) {
+ ll->h = h;
+ ll->h_dirty = 1;
+ }
+ ll->text_dirty = 0;
+ ll->dirty = 1;
+ } else if (ll->highlight_dirty) {
+ set_line_layout_attrs(buffer, line, cl->layout);
}
+ ll->highlight_dirty = 0;
+}
+
+static PangoLayout *
+get_pango_layout(ledit_buffer *buffer, int line) {
+ set_pango_text_and_highlight(buffer, line);
+ ledit_line *ll = ledit_buffer_get_line(buffer, line);
+ cache_layout *cl = cache_get_layout(
+ ll->parent_buffer->cache, ll->cache_layout_index
+ );
+ return cl->layout;
}
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…
+ledit_pos_to_x_softline(ledit_buffer *buffer, int line, int pos, int *x_ret, i…
+ ledit_line *ll = ledit_buffer_get_line(buffer, line);
+ PangoLayout *layout = get_pango_layout(buffer, line);
+ pango_layout_index_to_line_x(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…
+ PangoLayoutLine *pango_line = pango_layout_get_line_readonly(layout, *…
/* add left margin to x position if line is aligned right */
if (pango_line->resolved_dir == PANGO_DIRECTION_RTL) {
PangoRectangle rect;
pango_layout_line_get_extents(pango_line, NULL, &rect);
- *x_ret += (line->w * PANGO_SCALE - rect.width);
+ *x_ret += (ll->w * PANGO_SCALE - rect.width);
}
/* if in normal mode, change position to the middle of the
current rectangle so that moving around won't jump weirdly */
/* FIXME: also in visual? */
/* FIXME: this is too much magic for my taste */
- if (line->parent_buffer->common->mode == NORMAL) {
+ if (ll->parent_buffer->common->mode == NORMAL) {
PangoRectangle rect;
- pango_layout_index_to_pos(line->layout, pos, &rect);
+ pango_layout_index_to_pos(layout, pos, &rect);
*x_ret += rect.width / 2;
}
}
/* FIXME: change this to return int */
void
-ledit_x_softline_to_pos(ledit_line *line, int x, int softline, int *pos_ret) {
+ledit_x_softline_to_pos(ledit_buffer *buffer, int line, int x, int softline, i…
int trailing = 0;
int x_relative = x;
- normalize_and_set_pango_text(line);
+ ledit_line *ll = ledit_buffer_get_line(buffer, line);
+ PangoLayout *layout = get_pango_layout(buffer, line);
PangoLayoutLine *pango_line =
- pango_layout_get_line_readonly(line->layout, softline);
+ pango_layout_get_line_readonly(layout, softline);
/* x is absolute, so the margin at the left needs to be subtracted */
if (pango_line->resolved_dir == PANGO_DIRECTION_RTL) {
PangoRectangle rect;
pango_layout_line_get_extents(pango_line, NULL, &rect);
- x_relative -= (line->w * PANGO_SCALE - rect.width);
+ x_relative -= (ll->w * PANGO_SCALE - rect.width);
}
pango_layout_line_x_to_index(
pango_line, x_relative, pos_ret, &trailing
);
/* if in insert mode, snap to the nearest border between graphemes */
/* FIXME: add parameter for this instead of checking mode */
- if (line->parent_buffer->common->mode == INSERT) {
+ if (ll->parent_buffer->common->mode == INSERT) {
while (trailing > 0) {
trailing--;
- *pos_ret = ledit_line_next_utf8(line, *pos_ret);
+ *pos_ret = ledit_line_next_utf8(ll, *pos_ret);
}
}
}
-/* FIXME: make sure PangoLayout has newest text already when these functions a…
int
ledit_buffer_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_buffer_get_line(buffer, line);
- normalize_and_set_pango_text(final_line);
- if (pos == final_line->len && pos > 0) {
+ ledit_line *ll = ledit_buffer_get_line(buffer, line);
+ if (pos == ll->len && pos > 0) {
int nattrs;
+ PangoLayout *layout = get_pango_layout(buffer, line);
const PangoLogAttr *attrs =
- pango_layout_get_log_attrs_readonly(final_line->layout, &n…
+ pango_layout_get_log_attrs_readonly(layout, &nattrs);
int cur = nattrs - 2;
- ret = ledit_line_prev_utf8(final_line, ret);
+ ret = ledit_line_prev_utf8(ll, ret);
while (ret > 0 && cur > 0 && !attrs[cur].is_cursor_position) {
cur--;
- ret = ledit_line_prev_utf8(final_line, ret);
+ ret = ledit_line_prev_utf8(ll, ret);
}
}
return ret;
t@@ -1565,7 +1682,7 @@ ledit_buffer_delete_range_base(
}
int dell1 = l1, dell2 = l2;
ledit_line *ll = ledit_buffer_get_line(buffer, line_index1);
- ledit_pos_to_x_softline(ll, byte_index1, &x, &sl_useless);
+ ledit_pos_to_x_softline(buffer, line_index1, byte_index1, &x, …
if (l1 > 0 && l2 < buffer->lines_num - 1) {
rgl1 = l1;
rgb1 = 0;
t@@ -1600,13 +1717,13 @@ ledit_buffer_delete_range_base(
if (l2 < buffer->lines_num - 1) {
new_line = l1;
ledit_x_softline_to_pos(
- ledit_buffer_get_line(buffer, l2 + 1),
+ buffer, l2 + 1,
x, 0, &new_byte
);
} else if (l1 > 0) {
new_line = l1 - 1;
ledit_x_softline_to_pos(
- ledit_buffer_get_line(buffer, l1 - 1),
+ buffer, l1 - 1,
x, 0, &new_byte
);
} else {
t@@ -1626,23 +1743,22 @@ ledit_buffer_delete_range_base(
} else if (delmode == DELETE_SOFTLINE) {
int x, softline1, softline2;
ledit_line *line1 = ledit_buffer_get_line(buffer, line_index1);
- normalize_and_set_pango_text(line1);
- ledit_pos_to_x_softline(line1, byte_index1, &x, &softline1);
+ ledit_pos_to_x_softline(buffer, line_index1, byte_index1, &x, …
if (line_index1 == line_index2) {
int x_useless;
- pango_layout_index_to_line_x(line1->layout, byte_index…
+ PangoLayout *layout = get_pango_layout(buffer, line_in…
+ pango_layout_index_to_line_x(layout, byte_index2, 0, &…
int l1 = softline1 < softline2 ? softline1 : softline2;
int l2 = softline1 < softline2 ? softline2 : softline1;
- int softlines = pango_layout_get_line_count(line1->lay…
- PangoLayoutLine *pl1 = pango_layout_get_line_readonly(…
- PangoLayoutLine *pl2 = pango_layout_get_line_readonly(…
+ 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…
- if (l1 == 0 && l2 == softlines - 1 && buffer->lines_nu…
+ if (l1 == 0 && l2 == line1->softlines - 1 && buffer->l…
if (line_index1 < buffer->lines_num - 1) {
/* cursor can be moved to next hard li…
new_line = line_index1;
ledit_x_softline_to_pos(
- ledit_buffer_get_line(buffer, line…
+ buffer, line_index1 + 1,
x, 0, &new_byte
);
rgl1 = line_index1;
t@@ -1655,9 +1771,10 @@ ledit_buffer_delete_range_base(
/* note: logically, line_index1 - 1 mu…
buffer->lines_num > 1 && line_index…
new_line = line_index1 - 1;
- ledit_line *prevline = ledit_buffer_ge…
- softlines = pango_layout_get_line_coun…
- ledit_x_softline_to_pos(prevline, x, s…
+ ledit_line *prevline = ledit_buffer_ge…
+ if (prevline->text_dirty)
+ set_pango_text_and_highlight(b…
+ ledit_x_softline_to_pos(buffer, new_li…
rgl1 = line_index1 - 1;
rgb1 = prevline->len;
rgl2 = line_index1;
t@@ -1686,22 +1803,22 @@ ledit_buffer_delete_range_base(
delete_line_section_base(
buffer, line_index1, rgb1, rgb2 - rgb1
);
- if (l2 == softlines - 1 && line_index1 < buffe…
+ if (l2 == line1->softlines - 1 && line_index1 …
new_line = line_index1 + 1;
ledit_x_softline_to_pos(
- ledit_buffer_get_line(buffer, line…
+ buffer, line_index1 + 1,
x, 0, &new_byte
);
- } else if (l2 < softlines - 1) {
+ } else if (l2 < line1->softlines - 1) {
new_line = line_index1;
ledit_x_softline_to_pos(
- ledit_buffer_get_line(buffer, line…
+ buffer, line_index1,
x, l1, &new_byte
);
} else if (l1 > 0) {
new_line = line_index1;
ledit_x_softline_to_pos(
- ledit_buffer_get_line(buffer, line…
+ buffer, line_index1,
x, l1 - 1, &new_byte
);
} else {
t@@ -1726,14 +1843,13 @@ ledit_buffer_delete_range_base(
}
ledit_line *ll1 = ledit_buffer_get_line(buffer, l1);
ledit_line *ll2 = ledit_buffer_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(…
- PangoLayoutLine *pl2 = pango_layout_get_line_readonly(…
- int softlines = pango_layout_get_line_count(ll2->layou…
- if (sl1 == 0 && sl2 == softlines - 1) {
+ PangoLayout *layout1 = get_pango_layout(buffer, l1);
+ PangoLayout *layout2 = get_pango_layout(buffer, l2);
+ pango_layout_index_to_line_x(layout1, b1, 0, &sl1, &x_…
+ pango_layout_index_to_line_x(layout2, b2, 0, &sl2, &x_…
+ PangoLayoutLine *pl1 = pango_layout_get_line_readonly(…
+ PangoLayoutLine *pl2 = pango_layout_get_line_readonly(…
+ if (sl1 == 0 && sl2 == ll2->softlines - 1) {
if (l1 == 0 && l2 == buffer->lines_num - 1) {
rgl1 = l1;
rgl2 = l2;
t@@ -1754,17 +1870,17 @@ ledit_buffer_delete_range_base(
if (l2 == buffer->lines_num - 1) {
new_line = l1 - 1;
ledit_line *new_lline = ledit_…
- int new_softlines = pango_layo…
- ledit_x_softline_to_pos(new_ll…
+ if (new_lline->text_dirty)
+ set_pango_text_and_hig…
+ ledit_x_softline_to_pos(buffer…
rgl1 = l1 - 1;
rgb1 = new_lline->len;
rgl2 = l2;
rgb2 = ll2->len;
} else {
new_line = l1;
- ledit_line *nextline = ledit_b…
ledit_x_softline_to_pos(
- nextline, x, 0, &new_byte
+ buffer, l2 + 1, x, 0, &new…
);
rgl1 = l1;
rgb1 = 0;
t@@ -1794,20 +1910,20 @@ ledit_buffer_delete_range_base(
}
delete_line_section_base(buffer, l2, 0, pl2->s…
new_line = l1;
- ledit_x_softline_to_pos(ll2, x, 0, &new_byte);
+ ledit_x_softline_to_pos(buffer, l2, x, 0, &new…
ledit_buffer_delete_line_entries_base(buffer, …
- } else if (sl2 == softlines - 1) {
+ } else if (sl2 == ll2->softlines - 1) {
rgl1 = l1;
rgb1 = pl1->start_index;
rgl2 = l2;
rgb2 = ll2->len;
if (l2 + 1 == buffer->lines_num) {
new_line = l1;
- ledit_x_softline_to_pos(ll1, x, sl1 - …
+ ledit_x_softline_to_pos(buffer, l1, x,…
} else {
new_line = l1 + 1;
ledit_x_softline_to_pos(
- ledit_buffer_get_line(buffer, l2 +…
+ buffer, l2 + 1,
x, 0, &new_byte
);
}
t@@ -1843,7 +1959,7 @@ ledit_buffer_delete_range_base(
);
ledit_buffer_delete_line_entries_base(buffer, …
new_line = l1;
- int new_softlines = pango_layout_get_line_coun…
+ set_pango_text_and_highlight(buffer, 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@@ -1851,7 +1967,7 @@ ledit_buffer_delete_range_base(
same line, but it now includes the rest of …
(FIXME: this is probably not the best thing…
ledit_x_softline_to_pos(
- ll1, x, sl1 + 1 < new_softlines ? sl1 + 1 …
+ buffer, l1, x, sl1 + 1 < ll1->softlines ? …
);
}
}
t@@ -1919,22 +2035,21 @@ ledit_buffer_delete_range_base(
*new_byte_ret = new_byte;
}
-/* FIXME: always normalize lines */
+/* FIXME: any way to make this more efficient? */
void
ledit_buffer_resize_textview(ledit_buffer *buffer) {
buffer->total_height = 0;
- int tmp_w, tmp_h;
int text_w, text_h;
ledit_window_get_textview_size(buffer->window, &text_w, &text_h);
for (int i = 0; i < buffer->lines_num; i++) {
ledit_line *line = ledit_buffer_get_line(buffer, i);
- pango_layout_set_width(line->layout, text_w * PANGO_SCALE);
- pango_layout_get_pixel_size(line->layout, &tmp_w, &tmp_h);
- line->h = tmp_h;
line->w = text_w;
+ line->text_dirty = 1;
+ set_pango_text_and_highlight(buffer, i);
line->y_offset = buffer->total_height;
line->dirty = 1;
- buffer->total_height += tmp_h;
+ line->h_dirty = 0;
+ buffer->total_height += line->h;
}
ledit_window_set_scroll_max(buffer->window, buffer->total_height);
if (buffer->display_offset > 0 &&
t@@ -1956,6 +2071,7 @@ ledit_buffer_scroll(ledit_buffer *buffer, long new_offse…
}
/* FIXME: there's gotta be a better/more efficient way to do this... */
+/* FIXME: make sure h_dirty is not set here */
void
ledit_buffer_get_nearest_legal_pos(
ledit_buffer *buffer,
t@@ -1967,10 +2083,9 @@ ledit_buffer_get_nearest_legal_pos(
int x, sl_useless;
ledit_window_get_textview_size(buffer->window, &text_w, &text_h);
ledit_line *lline = ledit_buffer_get_line(buffer, line);
- pango_layout_get_cursor_pos(
- lline->layout, byte, &strong, &weak
- );
- ledit_pos_to_x_softline(lline, byte, &x, &sl_useless);
+ PangoLayout *layout = get_pango_layout(buffer, line);
+ pango_layout_get_cursor_pos(layout, byte, &strong, &weak);
+ ledit_pos_to_x_softline(buffer, line, byte, &x, &sl_useless);
long cursor_y = strong.y / PANGO_SCALE + lline->y_offset;
PangoRectangle ink, log;
if (cursor_y < buffer->display_offset) {
t@@ -1980,13 +2095,14 @@ ledit_buffer_get_nearest_legal_pos(
lline = ledit_buffer_get_line(buffer, ++hline);
}
/* the current hard line is now the one at the very top of the…
- int num_sl = pango_layout_get_line_count(lline->layout);
+ layout = get_pango_layout(buffer, hline);
+ int num_sl = lline->softlines;
int cur_y_off = 0;
int sl_index = -1;
PangoLayoutLine *sl;
/* search for first soft line completely on-screen */
for (int i = 0; i < num_sl; i++) {
- sl = pango_layout_get_line_readonly(lline->layout, i);
+ sl = pango_layout_get_line_readonly(layout, i);
if (cur_y_off + lline->y_offset >= buffer->display_off…
sl_index = i;
break;
t@@ -1997,17 +2113,16 @@ ledit_buffer_get_nearest_legal_pos(
if (sl_index >= 0) {
/* we found the correct soft line */
*line_ret = hline;
- ledit_x_softline_to_pos(lline, x, sl_index, byte_ret);
+ ledit_x_softline_to_pos(buffer, hline, x, sl_index, by…
} else if (hline < buffer->lines_num - 1) {
/* need to move to next hard line */
*line_ret = hline + 1;
- lline = ledit_buffer_get_line(buffer, hline + 1);
- ledit_x_softline_to_pos(lline, x, 0, byte_ret);
+ ledit_x_softline_to_pos(buffer, hline + 1, x, 0, byte_…
} else {
/* no idea if this can happen, but just fail and use
the last soft line of the last hard line */
*line_ret = hline;
- ledit_x_softline_to_pos(lline, x, num_sl - 1, byte_ret…
+ ledit_x_softline_to_pos(buffer, hline, x, num_sl - 1, …
}
} else if (cursor_y + strong.height / PANGO_SCALE >
buffer->display_offset + text_h) {
t@@ -2017,13 +2132,14 @@ ledit_buffer_get_nearest_legal_pos(
lline = ledit_buffer_get_line(buffer, --hline);
}
/* the current hard line is now the one at the very bottom of …
- int num_sl = pango_layout_get_line_count(lline->layout);
+ layout = get_pango_layout(buffer, hline);
+ int num_sl = lline->softlines;
int cur_y_off = 0;
int sl_index = -1;
PangoLayoutLine *sl;
/* search for last soft line completely on-screen */
for (int i = num_sl - 1; i >= 0; i--) {
- sl = pango_layout_get_line_readonly(lline->layout, i);
+ sl = pango_layout_get_line_readonly(layout, i);
if (lline->y_offset + lline->h - cur_y_off < buffer->d…
sl_index = i;
break;
t@@ -2034,18 +2150,18 @@ ledit_buffer_get_nearest_legal_pos(
if (sl_index >= 0) {
/* we found the correct soft line */
*line_ret = hline;
- ledit_x_softline_to_pos(lline, x, sl_index, byte_ret);
+ ledit_x_softline_to_pos(buffer, hline, x, sl_index, by…
} else if (hline > 0) {
/* need to move to previous hard line */
*line_ret = hline - 1;
lline = ledit_buffer_get_line(buffer, hline - 1);
- num_sl = pango_layout_get_line_count(lline->layout);
- ledit_x_softline_to_pos(lline, x, num_sl - 1, byte_ret…
+ num_sl = lline->softlines;
+ ledit_x_softline_to_pos(buffer, hline - 1, x, num_sl -…
} else {
/* no idea if this can happen, but just fail and use
the first soft line of the first hard line */
*line_ret = hline;
- ledit_x_softline_to_pos(lline, x, 0, byte_ret);
+ ledit_x_softline_to_pos(buffer, hline, x, 0, byte_ret);
}
}
}
t@@ -2060,9 +2176,10 @@ ledit_xy_to_line_byte(ledit_buffer *buffer, int x, int …
ledit_line *line = ledit_buffer_get_line(buffer, i);
if ((h <= pos && h + line->h > pos) || i == buffer->lines_num …
int index, trailing;
+ PangoLayout *layout = get_pango_layout(buffer, i);
/* FIXME: what if i == buffer->lines_num - 1 but pos -…
pango_layout_xy_to_index(
- line->layout,
+ layout,
x * PANGO_SCALE, (int)(pos - h) * PANGO_SCALE,
&index, &trailing
);
t@@ -2086,7 +2203,8 @@ scroll_to_pos(ledit_buffer *buffer, int line, int byte, …
int text_w, text_h;
ledit_window_get_textview_size(buffer->window, &text_w, &text_h);
ledit_line *ll = ledit_buffer_get_line(buffer, line);
- pango_layout_get_cursor_pos(ll->layout, byte, &strong, &weak);
+ PangoLayout *layout = get_pango_layout(buffer, line);
+ pango_layout_get_cursor_pos(layout, byte, &strong, &weak);
long cursor_y = strong.y / PANGO_SCALE + ll->y_offset;
if (top) {
ledit_buffer_scroll(buffer, cursor_y);
t@@ -2111,8 +2229,9 @@ ledit_buffer_ensure_cursor_shown(ledit_buffer *buffer) {
int text_w, text_h;
ledit_window_get_textview_size(buffer->window, &text_w, &text_h);
ledit_line *line = ledit_buffer_get_line(buffer, buffer->cur_line);
+ PangoLayout *layout = get_pango_layout(buffer, buffer->cur_line);
pango_layout_get_cursor_pos(
- line->layout, buffer->cur_index, &strong, &weak
+ layout, buffer->cur_index, &strong, &weak
);
long cursor_y = strong.y / PANGO_SCALE + line->y_offset;
if (cursor_y < buffer->display_offset) {
t@@ -2184,18 +2303,11 @@ ledit_buffer_set_selection(ledit_buffer *buffer, int l…
}
}
if (l1_new >= 0 && l2_new >= 0) {
- if (l1_new == l2_new) {
- ledit_buffer_set_line_selection(buffer, l1_new…
- } else {
- ledit_line *ll1 = ledit_buffer_get_line(buffer…
- ledit_buffer_set_line_selection(buffer, l1_new…
- ledit_buffer_set_line_selection(buffer, l2_new…
- /* FIXME: optimize this */
- for (int i = l1_new + 1; i < l2_new; i++) {
- if (i <= buffer->sel.line1 || i >= buf…
- ledit_line *llx = ledit_buffer…
- ledit_buffer_set_line_selectio…
- }
+ for (int i = l1_new; i <= l2_new; i++) {
+ /* only change the ones that were not already …
+ if (i <= buffer->sel.line1 || i >= buffer->sel…
+ ledit_line *ll = ledit_buffer_get_line…
+ ll->highlight_dirty = 1;
}
}
if (l1_new != l2_new || b1_new != b2_new)
t@@ -2281,7 +2393,10 @@ ledit_buffer_redraw(ledit_buffer *buffer) {
for (int i = 0; i < buffer->lines_num; i++) {
ledit_line *line = ledit_buffer_get_line(buffer, i);
if (h + line->h > buffer->display_offset) {
- if (line->dirty || line->cache_index == -1) {
+ /* FIXME: line->text_dirty should not happen here */
+ if (line->text_dirty || line->highlight_dirty)
+ set_pango_text_and_highlight(buffer, i);
+ if (line->dirty || line->cache_pixmap_index == -1) {
ledit_buffer_render_line(buffer, i);
}
int final_y = 0;
t@@ -2296,8 +2411,8 @@ ledit_buffer_redraw(ledit_buffer *buffer) {
final_h -= final_y + final_h -
buffer->display_offset - text_h;
}
- ledit_cache_pixmap *pix = ledit_get_cache_pixmap(
- buffer->cache, line->cache_index
+ cache_pixmap *pix = cache_get_pixmap(
+ buffer->cache, line->cache_pixmap_index
);
XCopyArea(
buffer->common->dpy, pix->pixmap,
t@@ -2308,17 +2423,18 @@ ledit_buffer_redraw(ledit_buffer *buffer) {
cur_line_y = h - buffer->display_offset;
cursor_displayed = 1;
}
+ if (h + line->h >= buffer->display_offset + text_h)
+ break;
}
- if (h + line->h >= buffer->display_offset + text_h)
- break;
h += line->h;
}
XSetForeground(buffer->common->dpy, buffer->window->gc, buffer->theme-…
PangoRectangle strong, weak;
ledit_line *cur_line = ledit_buffer_get_line(buffer, buffer->cur_line);
+ PangoLayout *layout = get_pango_layout(buffer, buffer->cur_line);
pango_layout_get_cursor_pos(
- cur_line->layout, buffer->cur_index, &strong, &weak
+ layout, buffer->cur_index, &strong, &weak
);
/* FIXME: long, int, etc. */
int cursor_y = strong.y / PANGO_SCALE + cur_line_y;
t@@ -2338,11 +2454,11 @@ ledit_buffer_redraw(ledit_buffer *buffer) {
if (buffer->cur_index >= cur_line->len)
tmp_index = cur_line->len - 1;
if (tmp_index >= 0)
- dir = pango_layout_get_direction(cur_line->lay…
+ dir = pango_layout_get_direction(layout, tmp_i…
int x, sli;
- pango_layout_index_to_line_x(cur_line->layout, buffer-…
- PangoLayoutLine *sl = pango_layout_get_line_readonly(c…
+ pango_layout_index_to_line_x(layout, buffer->cur_index…
+ PangoLayoutLine *sl = pango_layout_get_line_readonly(l…
if (dir != sl->resolved_dir) {
box_w = 3;
}
diff --git a/buffer.h b/buffer.h
t@@ -2,20 +2,24 @@ typedef struct ledit_buffer ledit_buffer;
/* FIXME: size_t for len, etc. */
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 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 */
+ char *text; /* text, stored as gap buffer */
+ int gap; /* position of gap for gap buffer */
+ int cap; /* allocated space for text */
+ int len; /* actual length of text */
+ int w; /* width in pixels */
+ int h; /* height in pixels */
+ long y_offset; /* pixel offset starting at the top of the fil…
+ int cache_pixmap_index; /* index of pixmap in cache, or -1 if not assi…
+ int cache_layout_index; /* index of pango layout in cache, or -1 if no…
+ int cursor_index; /* cursor index if it should be highlighted, -…
+ int softlines; /* number of softlines - cached from PangoLayo…
+ char dirty; /* whether line needs to be rendered before be…
+ char text_dirty; /* whether the text in the PangoLayout needs t…
+ * updated before the layout is rendered */
+ char highlight_dirty; /* whether highlight (cursor or selection) nee…
+ * updated still in the PangoLayout before ren…
+ char h_dirty; /* whether height needs to be recalculated */
} ledit_line;
typedef struct {
t@@ -64,15 +68,14 @@ int ledit_buffer_load_file(ledit_buffer *buffer, char *fil…
int ledit_buffer_write_to_file(ledit_buffer *buffer, char *filename, char **er…
void ledit_buffer_destroy(ledit_buffer *buffer);
void ledit_buffer_normalize_line(ledit_line *line);
-void ledit_buffer_set_line_selection(ledit_buffer *buffer, int line, int start…
void ledit_buffer_set_line_cursor_attrs(ledit_buffer *buffer, int line, int in…
void ledit_buffer_wipe_line_cursor_attrs(ledit_buffer *buffer, int line);
void ledit_buffer_render_line(ledit_buffer *buffer, int line_index);
ledit_line *ledit_buffer_get_line(ledit_buffer *buffer, int index);
int ledit_buffer_line_visible(ledit_buffer *buffer, int index);
int ledit_buffer_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…
+void ledit_pos_to_x_softline(ledit_buffer *buffer, int line, int pos, int *x_r…
+void ledit_x_softline_to_pos(ledit_buffer *buffer, int line, int x, int softli…
int ledit_line_next_utf8(ledit_line *line, int index);
int ledit_line_prev_utf8(ledit_line *line, int index);
int ledit_buffer_next_cursor_pos(ledit_buffer *buffer, int line, int byte, int…
t@@ -182,5 +185,5 @@ void ledit_buffer_scroll_to_pos_top(ledit_buffer *buffer, …
void ledit_buffer_scroll_to_pos_bottom(ledit_buffer *buffer, int line, int byt…
/* FIXME: just make generic sort range */
void ledit_buffer_sort_selection(int *line1, int *byte1, int *line2, int *byte…
-int ledit_line_next_non_whitespace(ledit_line *line, int byte);
+int ledit_line_next_non_whitespace(ledit_buffer *buffer, int line, int byte);
void ledit_buffer_insert_mark(ledit_buffer *buffer, char *mark, int len, int l…
diff --git a/cache.c b/cache.c
t@@ -1,3 +1,6 @@
+#include <stdio.h>
+#include <assert.h>
+#include <limits.h>
#include <stdlib.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
t@@ -9,73 +12,153 @@
#include "cache.h"
ledit_cache *
-ledit_cache_create(ledit_common *common) {
- /* FIXME: prevent overflow */
+cache_create(Display *dpy) {
ledit_cache *cache = ledit_malloc(sizeof(ledit_cache));
- cache->dpy = common->dpy;
- cache->entries = ledit_malloc(20 * sizeof(ledit_cache_pixmap));
- for (int i = 0; i < 20; i++) {
- cache->entries[i].pixmap = None;
- cache->entries[i].draw = NULL;
- cache->entries[i].line = -1;
+ cache->dpy = dpy;
+ cache->pixmaps = ledit_reallocarray(NULL, PIXMAP_CACHE_INITIAL_SIZE, s…
+ cache->layouts = ledit_reallocarray(NULL, LAYOUT_CACHE_SIZE, sizeof(ca…
+ for (size_t i = 0; i < PIXMAP_CACHE_INITIAL_SIZE; i++) {
+ cache->pixmaps[i].pixmap = None;
+ cache->pixmaps[i].draw = NULL;
+ cache->pixmaps[i].line = -1;
}
- cache->entries_num = 20;
- cache->cur_replace_index = -1;
+ for (size_t i = 0; i < LAYOUT_CACHE_SIZE; i++) {
+ cache->layouts[i].layout = NULL;
+ cache->layouts[i].line = -1;
+ }
+ cache->num_pixmaps = PIXMAP_CACHE_INITIAL_SIZE;
+ cache->num_layouts = LAYOUT_CACHE_SIZE;
+ cache->cur_pixmap_index = cache->cur_layout_index = 0;
return cache;
}
void
-ledit_cache_flush(ledit_cache *cache) {
- for (int i = 0; i < cache->entries_num; i++) {
- cache->entries[i].line = -1;
+cache_flush(
+ ledit_cache *cache, void *callback_data,
+ void (*set_pixmap_line)(void *, int, int),
+ void (*set_layout_line)(void *, int, int)) {
+ cache_invalidate_from_line(
+ cache, 0, callback_data, set_pixmap_line, set_layout_line
+ );
+}
+
+void
+cache_invalidate_from_line(
+ ledit_cache *cache, int start, void *callback_data,
+ void (*set_pixmap_line)(void *, int, int),
+ void (*set_layout_line)(void *, int, int)) {
+ for (size_t i = 0; i < cache->num_pixmaps; i++) {
+ if (cache->pixmaps[i].line >= start) {
+ set_pixmap_line(callback_data, cache->pixmaps[i].line,…
+ cache->pixmaps[i].line = -1;
+ }
+ }
+ for (size_t i = 0; i < cache->num_layouts; i++) {
+ if (cache->layouts[i].line >= start) {
+ set_layout_line(callback_data, cache->layouts[i].line,…
+ cache->layouts[i].line = -1;
+ }
}
}
void
-ledit_cache_destroy(ledit_cache *cache) {
- for (int i = 0; i < cache->entries_num; i++) {
- if (cache->entries[i].pixmap != None)
- XFreePixmap(cache->dpy, cache->entries[i].pixmap);
- if (cache->entries[i].draw != NULL)
- XftDrawDestroy(cache->entries[i].draw);
+cache_destroy(ledit_cache *cache) {
+ for (size_t i = 0; i < cache->num_pixmaps; i++) {
+ if (cache->pixmaps[i].pixmap != None)
+ XFreePixmap(cache->dpy, cache->pixmaps[i].pixmap);
+ if (cache->pixmaps[i].draw != NULL)
+ XftDrawDestroy(cache->pixmaps[i].draw);
+ }
+ for (size_t i = 0; i < cache->num_layouts; i++) {
+ if (cache->layouts[i].layout != NULL)
+ g_object_unref(cache->layouts[i].layout);
}
- free(cache->entries);
+ free(cache->pixmaps);
+ free(cache->layouts);
free(cache);
}
-/* returns a cache index that is currently not needed (if needed, the cache si…
- whether it is needed or not is checked with line_needed, to which
- callback_data is always passed as the first argument */
-int
-ledit_get_unneeded_cache_index(ledit_cache *cache, void *callback_data, int (*…
- int entry_index;
+cache_pixmap *
+cache_get_pixmap(ledit_cache *cache, int index) {
+ assert(index >= 0 && (size_t)index < cache->num_pixmaps);
+ return &cache->pixmaps[index];
+}
+
+cache_layout *
+cache_get_layout(ledit_cache *cache, int index) {
+ assert(index >= 0 && (size_t)index < cache->num_layouts);
+ return &cache->layouts[index];
+}
+
+/* FIXME: standardize overflow checking */
+static void
+err_overflow(void) {
+ fprintf(stderr, "ERROR: Integer overflow in cache handling.\n");
+ exit(1);
+}
+
+/* FIXME: decide on int or size_t, but not both */
+/* or maybe ssize_t */
+void
+cache_assign_pixmap_index(
+ ledit_cache *cache, int line,
+ void *callback_data,
+ int (*line_needed)(void *, int),
+ void (*set_pixmap_line)(void *, int, int)) {
int line_index;
- /* start at 1 because the cache->cur_replace_index is actually the las…
- for (int i = 1; i <= cache->entries_num; i++) {
- entry_index = (i + cache->cur_replace_index) % cache->entries_…
- line_index = cache->entries[entry_index].line;
+ size_t entry_index;
+ for (size_t i = 0; i <= cache->num_pixmaps; i++) {
+ entry_index = (i + cache->cur_pixmap_index) % cache->num_pixma…
+ line_index = cache->pixmaps[entry_index].line;
/* replace line when entry isn't assigned or currently assigne…
if (line_index == -1 ||
(line_index >= 0 &&
!line_needed(callback_data, line_index))) {
- cache->cur_replace_index = entry_index;
- return entry_index;
+ cache->cur_pixmap_index = (entry_index + 1) % cache->n…
+ if (entry_index > INT_MAX)
+ err_overflow();
+ cache_pixmap *pix = &cache->pixmaps[entry_index];
+ if (pix->line >= 0)
+ set_pixmap_line(callback_data, pix->line, -1);
+ pix->line = line;
+ set_pixmap_line(callback_data, line, (int)entry_index);
+ return;
}
}
/* no free entry found, increase cache size */
- cache->entries = ledit_realloc(cache->entries, cache->entries_num * 2 …
- entry_index = cache->entries_num;
- for (int i = cache->entries_num; i < cache->entries_num * 2; i++) {
- cache->entries[i].line = -1;
- cache->entries[i].pixmap = None;
- cache->entries[i].draw = NULL;
+ /* FIXME: what is the ideal size to resize to? */
+ /* FIXME: overflow */
+ /* FIXME: maybe have maximum cache size */
+ cache->pixmaps = ledit_reallocarray(cache->pixmaps, cache->num_pixmaps…
+ entry_index = cache->num_pixmaps;
+ for (size_t i = cache->num_pixmaps; i < cache->num_pixmaps * 2; i++) {
+ cache->pixmaps[i].line = -1;
+ cache->pixmaps[i].pixmap = None;
+ cache->pixmaps[i].draw = NULL;
}
- cache->entries_num *= 2;
- return entry_index;
+ cache->num_pixmaps *= 2;
+ if (entry_index > INT_MAX)
+ err_overflow();
+ cache_pixmap *pix = &cache->pixmaps[entry_index];
+ pix->line = line;
+ set_pixmap_line(callback_data, line, (int)entry_index);
}
-ledit_cache_pixmap *
-ledit_get_cache_pixmap(ledit_cache *cache, int index) {
- return &cache->entries[index];
+/* FIXME: perhaps use "real" clock cache management, i.e. set a bit on a cache…
+ when it is used so it isn't invalidated yet. */
+void
+cache_assign_layout_index(
+ ledit_cache *cache, int line,
+ void *callback_data,
+ void (*set_layout_line)(void *, int, int)) {
+ size_t old = cache->cur_layout_index;
+ cache->cur_layout_index = (cache->cur_layout_index + 1) % cache->num_l…
+ if (old > INT_MAX)
+ err_overflow();
+ cache_layout *layout = &cache->layouts[old];
+ if (layout->line >= 0)
+ set_layout_line(callback_data, layout->line, -1);
+ layout->line = line;
+ set_layout_line(callback_data, line, (int)old);
}
diff --git a/cache.h b/cache.h
t@@ -1,19 +1,125 @@
+/*
+ *The maximum number of layouts in the cache.
+ */
+#define LAYOUT_CACHE_SIZE 40
+
+/*
+ * The initial number of pixmas in the cache.
+ * The size is increased when more pixmaps are visible
+ * at the same time than there are entries in the cache.
+ */
+#define PIXMAP_CACHE_INITIAL_SIZE 20
+
typedef struct {
Pixmap pixmap;
XftDraw *draw;
- int w, h;
- int line;
-} ledit_cache_pixmap;
+ int w, h; /* width and height of the pixmap */
+ int line;/* the line associated with this entry, or -1 if unassigned */
+} cache_pixmap;
+
+typedef struct {
+ PangoLayout *layout;
+ int line; /* the line associated with this entry, or -1 if unassigned …
+} cache_layout;
typedef struct {
Display *dpy;
- ledit_cache_pixmap *entries;
- int entries_num;
- int cur_replace_index;
+ cache_pixmap *pixmaps;
+ cache_layout *layouts;
+ size_t num_pixmaps;
+ size_t num_layouts;
+ size_t cur_pixmap_index; /* current replacement index for pixmaps */
+ size_t cur_layout_index; /* current replacement index for layouts */
} ledit_cache;
-ledit_cache *ledit_cache_create(ledit_common *common);
-void ledit_cache_flush(ledit_cache *cache);
-void ledit_cache_destroy(ledit_cache *cache);
-ledit_cache_pixmap *ledit_get_cache_pixmap(ledit_cache *cache, int index);
-int ledit_get_unneeded_cache_index(ledit_cache *cache, void *callback_data, in…
+/* FIXME: maybe handle pixmap creation and resizing here */
+
+/*
+ * Create cache using X Display dpy (this is
+ * needed to destroy the pixmaps in the end).
+ */
+ledit_cache *cache_create(Display *dpy);
+
+/*
+ * Reset line index of every cache entry (pixmaps and layouts).
+ * set_pixmap_line is called with callback_data as its first argument,
+ * a line index which has been removed from the pixmap cache as the
+ * second argument, and '-1' as the third argument.
+ * set_layout_line is the same, but for the layout cache.
+ */
+void cache_flush(
+ ledit_cache *cache,
+ void *callback_data,
+ void (*set_pixmap_line)(void *, int, int),
+ void (*set_layout_line)(void *, int, int)
+);
+
+/*
+ * Like cache_flush, but only line numbers >= start are invalidated.
+ */
+void cache_invalidate_from_line(
+ ledit_cache *cache, int start,
+ void *callback_data,
+ void (*set_pixmap_line)(void *, int, int),
+ void (*set_layout_line)(void *, int, int)
+);
+
+/*
+ * Destroy cache.
+ */
+void cache_destroy(ledit_cache *cache);
+
+/*
+ * Get the cache_pixmap at index.
+ */
+cache_pixmap *cache_get_pixmap(ledit_cache *cache, int index);
+
+/*
+ * Get the cache_layout at index.
+ */
+cache_layout *cache_get_layout(ledit_cache *cache, int index);
+
+/*
+ * The following two functions have a somewhat cumbersome interface
+ * because set_pixmap_line and set_layout_line are required as helper
+ * functions instead of just returning the cache index and letting the
+ * caller handle the updating. However, this has led to horrible bugs
+ * in the past, when one of the updating steps was forgotten, so this
+ * interface was designed instead to avoid some of those more basic bugs.
+ */
+
+/*
+ * Assign an unneeded pixmap cache index to the line with index 'line'.
+ * line_needed is used to check if a line is needed.
+ * It is called with callback_data as the first argument and the
+ * line to be checked as the second argument.
+ * The line of the cache entry is set to 'line' and if a line was
+ * set before, it is reset by calling 'reset_pixmap_line' with
+ * 'callback_data' as the first argument, the old line as the second
+ * argument, and '-1' as the third argument.
+ * Similarly, the cache index of the new line is changed by calling
+ * 'set_pixmap_line' with the new cache index as the third argument.
+ */
+void cache_assign_pixmap_index(
+ ledit_cache *cache, int line,
+ void *callback_data,
+ int (*line_needed)(void *, int),
+ void (*set_pixmap_line)(void *, int, int)
+);
+
+/*
+ * Assign a layout cache index to the line with index 'line'.
+ * Since it is not clear which layouts are needed more, this just
+ * uses the next index in a clock fashion.
+ * The line of the cache entry is set to 'line' and if a line was
+ * set before, it is reset by calling 'set_layout_line' with
+ * 'callback_data' as the first argument, the old line as the second
+ * argument, and '-1' as the third argument.
+ * Similarly, the cache index of the new line is changed by calling
+ * 'set_layout_line' with the new cache index as the third argument.
+ */
+void cache_assign_layout_index(
+ ledit_cache *cache, int line,
+ void *callback_data,
+ void (*set_layout_line)(void *, int, int)
+);
diff --git a/keys_basic.c b/keys_basic.c
t@@ -654,7 +654,7 @@ scroll_lines(ledit_buffer *buffer, int lines, int dir) {
ledit_line *ll = ledit_buffer_get_line(buffer, buffer->cur_lin…
ledit_buffer_get_cursor_pixel_pos(buffer, buffer->cur_line, bu…
/* get the middle position of char */
- ledit_pos_to_x_softline(ll, buffer->cur_index, &x, &sli);
+ ledit_pos_to_x_softline(buffer, buffer->cur_line, buffer->cur_…
long abs_pos = ll->y_offset + y;
ledit_window_get_textview_size(buffer->window, &text_w, &text_…
if (lines > 0)
t@@ -670,7 +670,7 @@ scroll_lines(ledit_buffer *buffer, int lines, int dir) {
int start, end;
ledit_buffer_get_softline_bounds(buffer, buffer->cur_line, sli…
ll = ledit_buffer_get_line(buffer, buffer->cur_line);
- ledit_x_softline_to_pos(ll, x, sli, &buffer->cur_index);
+ ledit_x_softline_to_pos(buffer, buffer->cur_line, x, sli, &buf…
ledit_buffer_get_cursor_pixel_pos(buffer, buffer->cur_line, bu…
long new_abs_pos = ll->y_offset + y;
ledit_buffer_scroll(buffer, buffer->display_offset + (new_abs_…
t@@ -806,7 +806,7 @@ move_half_screen(ledit_buffer *buffer, int movement) {
/* try to keep current x position of cursor */
int x, softline;
/* FIXME: properly document what uses PANGO_SCALE and what not */
- ledit_pos_to_x_softline(ll, buffer->cur_index, &x, &softline);
+ ledit_pos_to_x_softline(buffer, buffer->cur_line, buffer->cur_index, &…
ledit_xy_to_line_byte(
buffer, x / PANGO_SCALE, y, 0,
&buffer->cur_line, &buffer->cur_index
t@@ -1355,7 +1355,8 @@ backspace(ledit_buffer *buffer, char *text, int len) {
int i = ledit_line_prev_utf8(l, buffer->cur_index);
delete_range(buffer, 0, 0, buffer->cur_line, buffer->cur_index…
}
- ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->c…
+ /* FIXME: This was probably a mistake earlier, right?
+ ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->c…
return (struct action){ACTION_NONE, NULL};
}
t@@ -1374,7 +1375,8 @@ delete_key(ledit_buffer *buffer, char *text, int len) {
int i = ledit_line_next_utf8(cur_line, buffer->cur_index);
delete_range(buffer, 0, 0, buffer->cur_line, buffer->cur_index…
}
- ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->c…
+ /* FIXME: This was probably a mistake earlier, right?
+ ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->c…
return (struct action){ACTION_NONE, NULL};
}
t@@ -1419,8 +1421,8 @@ move_to_eol(ledit_buffer *buffer, char *text, int len) {
buffer, buffer->cur_line, buffer->cur_index
);
}
+ ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, b…
}
- ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->c…
return (struct action){ACTION_NONE, NULL};
}
t@@ -1594,7 +1596,6 @@ escape_key(ledit_buffer *buffer, char *text, int len) {
}
buffer->sel.line1 = buffer->sel.line2 = -1;
buffer->sel.byte1 = buffer->sel.byte2 = -1;
- /* FIXME: optimize this to avoid first wiping and then setting…
ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, b…
}
return (struct action){ACTION_NONE, NULL};
t@@ -1629,16 +1630,14 @@ move_cursor_up_down(ledit_buffer *buffer, int dir) {
num, &new_line, &new_softline
);
- ledit_line *cur_lline = ledit_buffer_get_line(buffer, buffer->cur_line…
- ledit_line *new_lline = ledit_buffer_get_line(buffer, new_line);
if (cb != NULL) {
int start, end;
ledit_buffer_get_softline_bounds(buffer, new_line, new_softlin…
cb(buffer, new_line, start, KEY_MOTION_LINE);
} else {
int lineno, x;
- ledit_pos_to_x_softline(cur_lline, buffer->cur_index, &x, &lin…
- ledit_x_softline_to_pos(new_lline, x, new_softline, &buffer->c…
+ ledit_pos_to_x_softline(buffer, buffer->cur_line, buffer->cur_…
+ ledit_x_softline_to_pos(buffer, new_line, x, new_softline, &bu…
if (buffer->cur_line != new_line)
ledit_buffer_wipe_line_cursor_attrs(buffer, buffer->cu…
buffer->cur_line = new_line;
t@@ -1746,13 +1745,12 @@ cursor_to_first_non_ws(ledit_buffer *buffer, char *tex…
if (num != 0)
return err_invalid_key(buffer);
int new_index = 0;
- ledit_line *ll = ledit_buffer_get_line(buffer, buffer->cur_line);
if (hard_line_based) {
- new_index = ledit_line_next_non_whitespace(ll, 0);
+ new_index = ledit_line_next_non_whitespace(buffer, buffer->cur…
} else {
int start, end;
ledit_buffer_get_pos_softline_bounds(buffer, buffer->cur_line,…
- new_index = ledit_line_next_non_whitespace(ll, start);
+ new_index = ledit_line_next_non_whitespace(buffer, buffer->cur…
/* next non-whitespace might be on next softline */
if (new_index >= end) {
new_index = ledit_buffer_prev_cursor_pos(
diff --git a/keys_command.c b/keys_command.c
t@@ -305,7 +305,6 @@ edit_submit(ledit_buffer *buffer, char *key_text, int len)…
/* FIXME: support visual mode, i.e. change selection to new place? */
void
search_next(ledit_buffer *buffer) {
- /* FIXME: avoid this when line doesn't change */
ledit_buffer_wipe_line_cursor_attrs(buffer, buffer->cur_line);
enum ledit_search_state ret = ledit_search_next(buffer, &buffer->cur_l…
ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->c…
t@@ -315,7 +314,6 @@ search_next(ledit_buffer *buffer) {
void
search_prev(ledit_buffer *buffer) {
- /* FIXME: avoid this when line doesn't change */
ledit_buffer_wipe_line_cursor_attrs(buffer, buffer->cur_line);
enum ledit_search_state ret = ledit_search_prev(buffer, &buffer->cur_l…
ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->c…
diff --git a/memory.c b/memory.c
t@@ -1,7 +1,9 @@
#include <stdio.h>
+#include <stdint.h>
#include <stdlib.h>
#include <string.h>
+/* FIXME: clean up on exit */
static void
fatal_err(const char *msg) {
fprintf(stderr, "%s", msg);
t@@ -64,3 +66,24 @@ ledit_strcat(const char *str1, const char *str2) {
return ret;
}
+
+/*
+ * This is from OpenBSD (adapted to exit on error):
+ * Copyright (c) 2008 Otto Moerbeek <[email protected]>
+ */
+
+/*
+ * This is sqrt(SIZE_MAX+1), as s1*s2 <= SIZE_MAX
+ * if both s1 < MUL_NO_OVERFLOW and s2 < MUL_NO_OVERFLOW
+ */
+#define MUL_NO_OVERFLOW ((size_t)1 << (sizeof(size_t) * 4))
+
+void *
+ledit_reallocarray(void *optr, size_t nmemb, size_t size)
+{
+ if ((nmemb >= MUL_NO_OVERFLOW || size >= MUL_NO_OVERFLOW) &&
+ nmemb > 0 && SIZE_MAX / nmemb < size) {
+ fatal_err("Out of memory.\n");
+ }
+ return realloc(optr, size * nmemb);
+}
diff --git a/memory.h b/memory.h
t@@ -4,3 +4,4 @@ void *ledit_malloc(size_t size);
void *ledit_calloc(size_t nmemb, size_t size);
void *ledit_realloc(void *ptr, size_t size);
char *ledit_strcat(const char *str1, const char *str2);
+void *ledit_reallocarray(void *optr, size_t nmemb, size_t size);
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.