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); |