commit 6c98800389dc02e20954a157675ac67fb02b7f8c
parent 60385928394de59d1104da2c69241745cbba4784
Author: lumidify <nobody@lumidify.org>
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
@@ -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
@@ -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, int length);
-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 byte1, int line2, int byte2);
+/*
+ * 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_byte, int end_byte);
+
+/*
+ * 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 *layout);
+
+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;
@@ -105,6 +133,13 @@ ledit_buffer_create(ledit_common *common, ledit_theme *theme, ledit_window *wind
}
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, buffer);
+ ledit_window_set_button_callback(window, &ledit_buffer_button_handler, buffer);
+
buffer->common = common;
buffer->window = window;
buffer->lines = NULL;
@@ -122,14 +157,9 @@ ledit_buffer_create(ledit_common *common, ledit_theme *theme, ledit_window *wind
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, buffer);
- ledit_window_set_button_callback(window, &ledit_buffer_button_handler, buffer);
return buffer;
}
@@ -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)
@@ -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(
@@ -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, int end_byte) {
+#if 0
+static void
+set_line_selection(ledit_buffer *buffer, int line, int start_byte, int end_byte) {
ledit_line *l = ledit_buffer_get_line(buffer, line);
if (l->text_dirty)
err_text_dirty(buffer, line);
@@ -284,41 +316,72 @@ ledit_buffer_set_line_selection(ledit_buffer *buffer, int line, int start_byte,
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.green, fg.blue);
- PangoAttribute *attr1 = pango_attr_foreground_new(bg.red, bg.green, bg.blue);
- 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.blue);
+ PangoAttribute *attr1 = pango_attr_foreground_new(bg.red, bg.green, bg.blue);
+ 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->cursor_index + 1);
+ }
+ 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
@@ -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, buffer, &line_visible_callback);
- ledit_cache_pixmap *tmp_pix = ledit_get_cache_pixmap(buffer->cache, new_index);
- if (tmp_pix->line >= 0) {
- ledit_line *old_line = ledit_buffer_get_line(buffer, tmp_pix->line);
- 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_index);
+ cache_pixmap *pix = cache_get_pixmap(buffer->cache, ll->cache_pixmap_index);
/* 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,
@@ -628,35 +686,32 @@ ledit_buffer_render_line(ledit_buffer *buffer, int line_index) {
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->layout, 0, 0);
- 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, 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;
}
@@ -682,9 +737,9 @@ ledit_buffer_append_line_base(ledit_buffer *buffer, int line_index, int text_ind
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(
@@ -705,15 +760,31 @@ ledit_buffer_delete_line_entries(ledit_buffer *buffer, int index1, int index2) {
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 this! */
void
ledit_buffer_delete_line_entries_base(ledit_buffer *buffer, int index1, int index2) {
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 */
@@ -747,9 +818,9 @@ ledit_buffer_delete_line_entry_base(ledit_buffer *buffer, int index) {
/* 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];
}
@@ -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;
@@ -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;
@@ -951,8 +1008,9 @@ ledit_buffer_next_cursor_pos(ledit_buffer *buffer, int line, int byte, int num)
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++) {
@@ -972,8 +1030,9 @@ ledit_buffer_prev_cursor_pos(ledit_buffer *buffer, int line, int byte, int num)
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--) {
@@ -988,15 +1047,17 @@ ledit_buffer_prev_cursor_pos(ledit_buffer *buffer, int line, int byte, int num)
}
static int
-line_next_word(ledit_line *line, int byte, int char_index, int wrapped_line, int *char_ret, int *real_byte_ret) {
+line_next_word(ledit_buffer *buffer, int line, int byte, int char_index, int wrapped_line, int *char_ret, int *real_byte_ret) {
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)
@@ -1005,21 +1066,23 @@ line_next_word(ledit_line *line, int byte, int char_index, int wrapped_line, int
*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 *char_ret) {
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--) {
@@ -1028,21 +1091,23 @@ line_prev_word(ledit_line *line, int byte, int char_index, int *char_ret) {
*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, int *char_ret) {
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;
@@ -1063,20 +1128,22 @@ line_prev_bigword(ledit_line *line, int byte, int char_index, int *char_ret) {
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, int *char_ret, int *real_byte_ret) {
+line_next_bigword_end(ledit_buffer *buffer, int line, int byte, int char_index, int wrapped_line, int *char_ret, int *real_byte_ret) {
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;
@@ -1101,21 +1168,23 @@ line_next_bigword_end(ledit_line *line, int byte, int char_index, int wrapped_li
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_line, int *char_ret, int *real_byte_ret) {
+line_next_word_end(ledit_buffer *buffer, int line, int byte, int char_index, int wrapped_line, int *char_ret, int *real_byte_ret) {
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;
@@ -1136,21 +1205,23 @@ line_next_word_end(ledit_line *line, int byte, int char_index, int wrapped_line,
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, int *char_ret, int *real_byte_ret) {
+line_next_bigword(ledit_buffer *buffer, int line, int byte, int char_index, int wrapped_line, int *char_ret, int *real_byte_ret) {
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) {
@@ -1162,24 +1233,26 @@ line_next_bigword(ledit_line *line, int byte, int char_index, int wrapped_line,
*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 unicode semantics */
@@ -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, &cur_char, &real_byte)) == -1 && \
+ 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; \
} \
@@ -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_num - 1); \
*byte_ret = ledit_buffer_get_legal_normal_pos(buffer, buffer->lines_num - 1, ll->len); \
*real_byte_ret = ll->len; \
} else { \
@@ -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)) == -1 && cur_line > 0) { \
+ 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; \
@@ -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;
}
@@ -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, softline);
+ 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;
}
@@ -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
@@ -1296,8 +1372,9 @@ ledit_buffer_pos_to_softline(ledit_buffer *buffer, int line, int pos) {
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;
}
@@ -1306,8 +1383,9 @@ ledit_buffer_get_cursor_pixel_pos(ledit_buffer *buffer, int line, int pos, int *
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;
@@ -1323,6 +1401,7 @@ ledit_buffer_move_cursor_visually(ledit_buffer *buffer, int line, int pos, int m
/* 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;
@@ -1333,7 +1412,7 @@ ledit_buffer_move_cursor_visually(ledit_buffer *buffer, int line, int pos, int m
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
);
@@ -1403,7 +1482,6 @@ delete_line_section_base(ledit_buffer *buffer, int line, int start, int length)
l->len -= length;
l->dirty = 1;
l->text_dirty = 1;
- l->h_dirty = 1;
}
int
@@ -1428,85 +1506,124 @@ ledit_buffer_delete_unicode_char_base(ledit_buffer *buffer, int line_index, int
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 technically
- 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_layout_index);
+ if (cl->layout == NULL) {
+ cl->layout = pango_layout_new(ll->parent_buffer->window->context);
+ pango_layout_set_font_description(cl->layout, buffer->window->font);
+ pango_layout_set_wrap(cl->layout, PANGO_WRAP_WORD_CHAR);
+ }
+ } else {
+ cl = cache_get_layout(ll->parent_buffer->cache, ll->cache_layout_index);
+ }
+ 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->layout) : 1;
+ 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_ret) {
- 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, int *softline_ret) {
+ 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->layout, *softline_ret);
+ PangoLayoutLine *pango_line = pango_layout_get_line_readonly(layout, *softline_ret);
/* 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, int *pos_ret) {
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 are called */
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, &nattrs);
+ 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;
@@ -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, &sl_useless);
if (l1 > 0 && l2 < buffer->lines_num - 1) {
rgl1 = l1;
rgb1 = 0;
@@ -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 {
@@ -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, &softline1);
if (line_index1 == line_index2) {
int x_useless;
- pango_layout_index_to_line_x(line1->layout, byte_index2, 0, &softline2, &x_useless);
+ PangoLayout *layout = get_pango_layout(buffer, line_index1);
+ pango_layout_index_to_line_x(layout, byte_index2, 0, &softline2, &x_useless);
int l1 = softline1 < softline2 ? softline1 : softline2;
int l2 = softline1 < softline2 ? softline2 : softline1;
- int softlines = pango_layout_get_line_count(line1->layout);
- PangoLayoutLine *pl1 = pango_layout_get_line_readonly(line1->layout, l1);
- PangoLayoutLine *pl2 = pango_layout_get_line_readonly(line1->layout, l2);
+ PangoLayoutLine *pl1 = pango_layout_get_line_readonly(layout, l1);
+ PangoLayoutLine *pl2 = pango_layout_get_line_readonly(layout, l2);
/* don't delete entire line if it is the last one remaining */
- if (l1 == 0 && l2 == softlines - 1 && buffer->lines_num > 1) {
+ if (l1 == 0 && l2 == line1->softlines - 1 && buffer->lines_num > 1) {
if (line_index1 < buffer->lines_num - 1) {
/* cursor can be moved to next hard line */
new_line = line_index1;
ledit_x_softline_to_pos(
- ledit_buffer_get_line(buffer, line_index1 + 1),
+ buffer, line_index1 + 1,
x, 0, &new_byte
);
rgl1 = line_index1;
@@ -1655,9 +1771,10 @@ ledit_buffer_delete_range_base(
/* note: logically, line_index1 - 1 must be >= 0 because
buffer->lines_num > 1 && line_index1 >= buffer->lines_num - 1 */
new_line = line_index1 - 1;
- ledit_line *prevline = ledit_buffer_get_line(buffer, line_index1 - 1);
- softlines = pango_layout_get_line_count(prevline->layout);
- ledit_x_softline_to_pos(prevline, x, softlines - 1, &new_byte);
+ ledit_line *prevline = ledit_buffer_get_line(buffer, new_line);
+ if (prevline->text_dirty)
+ set_pango_text_and_highlight(buffer, new_line);
+ ledit_x_softline_to_pos(buffer, new_line, x, prevline->softlines - 1, &new_byte);
rgl1 = line_index1 - 1;
rgb1 = prevline->len;
rgl2 = line_index1;
@@ -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 < buffer->lines_num - 1) {
+ if (l2 == line1->softlines - 1 && line_index1 < buffer->lines_num - 1) {
new_line = line_index1 + 1;
ledit_x_softline_to_pos(
- ledit_buffer_get_line(buffer, line_index1 + 1),
+ 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_index1),
+ 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_index1),
+ buffer, line_index1,
x, l1 - 1, &new_byte
);
} else {
@@ -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, &x_useless);
- pango_layout_index_to_line_x(ll2->layout, b2, 0, &sl2, &x_useless);
- PangoLayoutLine *pl1 = pango_layout_get_line_readonly(ll1->layout, sl1);
- PangoLayoutLine *pl2 = pango_layout_get_line_readonly(ll2->layout, sl2);
- int softlines = pango_layout_get_line_count(ll2->layout);
- 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_useless);
+ pango_layout_index_to_line_x(layout2, b2, 0, &sl2, &x_useless);
+ PangoLayoutLine *pl1 = pango_layout_get_line_readonly(layout1, sl1);
+ PangoLayoutLine *pl2 = pango_layout_get_line_readonly(layout2, sl2);
+ if (sl1 == 0 && sl2 == ll2->softlines - 1) {
if (l1 == 0 && l2 == buffer->lines_num - 1) {
rgl1 = l1;
rgl2 = l2;
@@ -1754,17 +1870,17 @@ ledit_buffer_delete_range_base(
if (l2 == buffer->lines_num - 1) {
new_line = l1 - 1;
ledit_line *new_lline = ledit_buffer_get_line(buffer, new_line);
- int new_softlines = pango_layout_get_line_count(new_lline->layout);
- ledit_x_softline_to_pos(new_lline, x, new_softlines - 1, &new_byte);
+ if (new_lline->text_dirty)
+ set_pango_text_and_highlight(buffer, new_line);
+ ledit_x_softline_to_pos(buffer, new_line, x, new_lline->softlines - 1, &new_byte);
rgl1 = l1 - 1;
rgb1 = new_lline->len;
rgl2 = l2;
rgb2 = ll2->len;
} else {
new_line = l1;
- ledit_line *nextline = ledit_buffer_get_line(buffer, l2 + 1);
ledit_x_softline_to_pos(
- nextline, x, 0, &new_byte
+ buffer, l2 + 1, x, 0, &new_byte
);
rgl1 = l1;
rgb1 = 0;
@@ -1794,20 +1910,20 @@ ledit_buffer_delete_range_base(
}
delete_line_section_base(buffer, l2, 0, pl2->start_index + pl2->length);
new_line = l1;
- ledit_x_softline_to_pos(ll2, x, 0, &new_byte);
+ ledit_x_softline_to_pos(buffer, l2, x, 0, &new_byte);
ledit_buffer_delete_line_entries_base(buffer, l1, l2 - 1);
- } 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 - 1, &new_byte);
+ ledit_x_softline_to_pos(buffer, l1, x, sl1 - 1, &new_byte);
} else {
new_line = l1 + 1;
ledit_x_softline_to_pos(
- ledit_buffer_get_line(buffer, l2 + 1),
+ buffer, l2 + 1,
x, 0, &new_byte
);
}
@@ -1843,7 +1959,7 @@ ledit_buffer_delete_range_base(
);
ledit_buffer_delete_line_entries_base(buffer, l1 + 1, l2);
new_line = l1;
- int new_softlines = pango_layout_get_line_count(ll1->layout);
+ set_pango_text_and_highlight(buffer, l1);
/* it's technically possible that the remaining part of the
second line is so small that it doesn't generate a new
softline, so there needs to be a special case - this is
@@ -1851,7 +1967,7 @@ ledit_buffer_delete_range_base(
same line, but it now includes the rest of the second line
(FIXME: this is probably not the best thing to do) */
ledit_x_softline_to_pos(
- ll1, x, sl1 + 1 < new_softlines ? sl1 + 1 : sl1, &new_byte
+ buffer, l1, x, sl1 + 1 < ll1->softlines ? sl1 + 1 : sl1, &new_byte
);
}
}
@@ -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 &&
@@ -1956,6 +2071,7 @@ ledit_buffer_scroll(ledit_buffer *buffer, long new_offset) {
}
/* 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,
@@ -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) {
@@ -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 screen*/
- 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_offset) {
sl_index = i;
break;
@@ -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, byte_ret);
} 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_ret);
} 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, byte_ret);
}
} else if (cursor_y + strong.height / PANGO_SCALE >
buffer->display_offset + text_h) {
@@ -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 the screen*/
- 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->display_offset + text_h) {
sl_index = i;
break;
@@ -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, byte_ret);
} 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 - 1, byte_ret);
} 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);
}
}
}
@@ -2060,9 +2176,10 @@ ledit_xy_to_line_byte(ledit_buffer *buffer, int x, int y, int snap_to_nearest, i
ledit_line *line = ledit_buffer_get_line(buffer, i);
if ((h <= pos && h + line->h > pos) || i == buffer->lines_num - 1) {
int index, trailing;
+ PangoLayout *layout = get_pango_layout(buffer, i);
/* FIXME: what if i == buffer->lines_num - 1 but pos - h < 0? */
pango_layout_xy_to_index(
- line->layout,
+ layout,
x * PANGO_SCALE, (int)(pos - h) * PANGO_SCALE,
&index, &trailing
);
@@ -2086,7 +2203,8 @@ scroll_to_pos(ledit_buffer *buffer, int line, int byte, int top) {
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);
@@ -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) {
@@ -2184,18 +2303,11 @@ ledit_buffer_set_selection(ledit_buffer *buffer, int line1, int byte1, int line2
}
}
if (l1_new >= 0 && l2_new >= 0) {
- if (l1_new == l2_new) {
- ledit_buffer_set_line_selection(buffer, l1_new, b1_new, b2_new);
- } else {
- ledit_line *ll1 = ledit_buffer_get_line(buffer, l1_new);
- ledit_buffer_set_line_selection(buffer, l1_new, b1_new, ll1->len);
- ledit_buffer_set_line_selection(buffer, l2_new, 0, b2_new);
- /* FIXME: optimize this */
- for (int i = l1_new + 1; i < l2_new; i++) {
- if (i <= buffer->sel.line1 || i >= buffer->sel.line2) {
- ledit_line *llx = ledit_buffer_get_line(buffer, i);
- ledit_buffer_set_line_selection(buffer, i, 0, llx->len);
- }
+ for (int i = l1_new; i <= l2_new; i++) {
+ /* only change the ones that were not already selected */
+ if (i <= buffer->sel.line1 || i >= buffer->sel.line2) {
+ ledit_line *ll = ledit_buffer_get_line(buffer, i);
+ ll->highlight_dirty = 1;
}
}
if (l1_new != l2_new || b1_new != b2_new)
@@ -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;
@@ -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,
@@ -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->text_fg.pixel);
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;
@@ -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->layout, tmp_index);
+ dir = pango_layout_get_direction(layout, tmp_index);
int x, sli;
- pango_layout_index_to_line_x(cur_line->layout, buffer->cur_index, 0, &sli, &x);
- PangoLayoutLine *sl = pango_layout_get_line_readonly(cur_line->layout, sli);
+ pango_layout_index_to_line_x(layout, buffer->cur_index, 0, &sli, &x);
+ PangoLayoutLine *sl = pango_layout_get_line_readonly(layout, sli);
if (dir != sl->resolved_dir) {
box_w = 3;
}
diff --git a/buffer.h b/buffer.h
@@ -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 file */
+ int cache_pixmap_index; /* index of pixmap in cache, or -1 if not assigned */
+ int cache_layout_index; /* index of pango layout in cache, or -1 if not assigned */
+ int cursor_index; /* cursor index if it should be highlighted, -1 else */
+ int softlines; /* number of softlines - cached from PangoLayout */
+ 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 highlight_dirty; /* whether highlight (cursor or selection) needs to be
+ * updated still in the PangoLayout before rendering */
+ char h_dirty; /* whether height needs to be recalculated */
} ledit_line;
typedef struct {
@@ -64,15 +68,14 @@ int ledit_buffer_load_file(ledit_buffer *buffer, char *filename, int line, char
int ledit_buffer_write_to_file(ledit_buffer *buffer, char *filename, char **errstr);
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_byte, int end_byte);
void ledit_buffer_set_line_cursor_attrs(ledit_buffer *buffer, int line, int index);
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 *softline_ret);
-void ledit_x_softline_to_pos(ledit_line *line, int x, int softline, int *pos_ret);
+void ledit_pos_to_x_softline(ledit_buffer *buffer, int line, int pos, int *x_ret, int *softline_ret);
+void ledit_x_softline_to_pos(ledit_buffer *buffer, int line, int x, int softline, int *pos_ret);
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 num);
@@ -182,5 +185,5 @@ void ledit_buffer_scroll_to_pos_top(ledit_buffer *buffer, int line, int byte);
void ledit_buffer_scroll_to_pos_bottom(ledit_buffer *buffer, int line, int byte);
/* FIXME: just make generic sort range */
void ledit_buffer_sort_selection(int *line1, int *byte1, int *line2, int *byte2);
-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 line, int byte);
diff --git a/cache.c b/cache.c
@@ -1,3 +1,6 @@
+#include <stdio.h>
+#include <assert.h>
+#include <limits.h>
#include <stdlib.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
@@ -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, sizeof(cache_pixmap));
+ cache->layouts = ledit_reallocarray(NULL, LAYOUT_CACHE_SIZE, sizeof(cache_layout));
+ 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, -1);
+ 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, -1);
+ 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 size is increased)
- 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 (*line_needed)(void *, 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 last entry that was replaced */
- for (int i = 1; i <= cache->entries_num; i++) {
- entry_index = (i + cache->cur_replace_index) % cache->entries_num;
- 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_pixmaps;
+ line_index = cache->pixmaps[entry_index].line;
/* replace line when entry isn't assigned or currently assigned line is not visible */
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->num_pixmaps;
+ 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 * sizeof(ledit_cache_pixmap));
- 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 * 2, sizeof(cache_pixmap));
+ 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 entry
+ 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_layouts;
+ 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
@@ -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, int (*line_needed)(void *, int));
+/* 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
@@ -654,7 +654,7 @@ scroll_lines(ledit_buffer *buffer, int lines, int dir) {
ledit_line *ll = ledit_buffer_get_line(buffer, buffer->cur_line);
ledit_buffer_get_cursor_pixel_pos(buffer, buffer->cur_line, buffer->cur_index, &x, &y, &h);
/* 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_index, &x, &sli);
long abs_pos = ll->y_offset + y;
ledit_window_get_textview_size(buffer->window, &text_w, &text_h);
if (lines > 0)
@@ -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, &start, &end);
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, &buffer->cur_index);
ledit_buffer_get_cursor_pixel_pos(buffer, buffer->cur_line, buffer->cur_index, &x, &y, &h);
long new_abs_pos = ll->y_offset + y;
ledit_buffer_scroll(buffer, buffer->display_offset + (new_abs_pos - abs_pos));
@@ -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, &x, &softline);
ledit_xy_to_line_byte(
buffer, x / PANGO_SCALE, y, 0,
&buffer->cur_line, &buffer->cur_index
@@ -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, buffer->cur_line, i, 0);
}
- ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
+ /* FIXME: This was probably a mistake earlier, right?
+ ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);*/
return (struct action){ACTION_NONE, NULL};
}
@@ -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, buffer->cur_line, i, 0);
}
- ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
+ /* FIXME: This was probably a mistake earlier, right?
+ ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);*/
return (struct action){ACTION_NONE, NULL};
}
@@ -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, buffer->cur_index);
}
- ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
return (struct action){ACTION_NONE, NULL};
}
@@ -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 the attrs */
ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
}
return (struct action){ACTION_NONE, NULL};
@@ -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_softline, &start, &end);
cb(buffer, new_line, start, KEY_MOTION_LINE);
} else {
int lineno, x;
- ledit_pos_to_x_softline(cur_lline, buffer->cur_index, &x, &lineno);
- ledit_x_softline_to_pos(new_lline, x, new_softline, &buffer->cur_index);
+ ledit_pos_to_x_softline(buffer, buffer->cur_line, buffer->cur_index, &x, &lineno);
+ ledit_x_softline_to_pos(buffer, new_line, x, new_softline, &buffer->cur_index);
if (buffer->cur_line != new_line)
ledit_buffer_wipe_line_cursor_attrs(buffer, buffer->cur_line);
buffer->cur_line = new_line;
@@ -1746,13 +1745,12 @@ cursor_to_first_non_ws(ledit_buffer *buffer, char *text, int len) {
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_line, 0);
} else {
int start, end;
ledit_buffer_get_pos_softline_bounds(buffer, buffer->cur_line, buffer->cur_index, &start, &end);
- new_index = ledit_line_next_non_whitespace(ll, start);
+ new_index = ledit_line_next_non_whitespace(buffer, buffer->cur_line, start);
/* 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
@@ -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_line, &buffer->cur_index);
ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
@@ -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_line, &buffer->cur_index);
ledit_buffer_set_line_cursor_attrs(buffer, buffer->cur_line, buffer->cur_index);
diff --git a/memory.c b/memory.c
@@ -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);
@@ -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 <otto@drijf.net>
+ */
+
+/*
+ * 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
@@ -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);