commit 0e379b0cf1ba991e2024b2541a9d5d4f80068d5b
parent e181351df92df2596bd48578667ce195f4cf34d0
Author: lumidify <nobody@lumidify.org>
Date: Sat, 5 Jun 2021 20:16:59 +0200
Abstract text operations a bit
Diffstat:
M | buffer.c | | | 128 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------- |
M | buffer.h | | | 16 | ++++++++++++++++ |
M | ledit.c | | | 82 | ++++++++++++++++++++++--------------------------------------------------------- |
3 files changed, 149 insertions(+), 77 deletions(-)
diff --git a/buffer.c b/buffer.c
@@ -1,6 +1,7 @@
/* FIXME: shrink buffers when text length less than a fourth of the size */
#include <string.h>
+#include <assert.h>
#include <X11/Xlib.h>
#include <X11/Xutil.h>
@@ -101,6 +102,18 @@ ledit_wipe_line_cursor_attrs(ledit_buffer *buffer, int line) {
}
void
+ledit_insert_text_from_line(
+ ledit_buffer *buffer,
+ int dst_line, int dst_index,
+ int src_line, int src_index, int src_len) {
+ assert(dst_line != src_line);
+ ledit_line *ll = ledit_get_line(buffer, src_line);
+ if (src_len == -1)
+ src_len = ll->len - src_index;
+ ledit_insert_text(buffer, dst_line, dst_index, ll->text, src_len);
+}
+
+void
ledit_insert_text(ledit_buffer *buffer, int line_index, int index, char *text, int len) {
ledit_line *line = &buffer->lines[line_index];
if (len == -1)
@@ -333,22 +346,109 @@ ledit_line_visible(ledit_buffer *buffer, int index) {
line->y_offset + line->h > buffer->display_offset;
}
+/* get needed length of text range, including newlines
+ * - NUL is not included
+ * - if the last range ends at the end of a line, the newline is *not* included
+ * - the range must be sorted already */
+size_t
+ledit_textlen(ledit_buffer *buffer, int line1, int byte1, int line2, int byte2) {
+ assert(line1 < line2 || (line1 == line2 && byte1 <= byte2));
+ size_t len = 0;
+ ledit_line *ll = ledit_get_line(buffer, line1);
+ if (line1 == line2) {
+ len = byte2 - byte1;
+ } else {
+ /* + 1 for newline */
+ len = ll->len - byte1 + byte2 + 1;
+ for (int i = line1 + 1; i < line2; i++) {
+ ll = ledit_get_line(buffer, i);
+ len += ll->len + 1;
+ }
+ }
+ return len;
+}
+
+/* copy text range into given buffer
+ * - dst is null-terminated
+ * - dst must be large enough to contain the text and NUL
+ * - the range must be sorted already */
+void
+ledit_copy_text(ledit_buffer *buffer, char *dst, int line1, int byte1, int line2, int byte2) {
+ assert(line1 < line2 || (line1 == line2 && byte1 <= byte2));
+ ledit_line *ll1 = ledit_get_line(buffer, line1);
+ ledit_line *ll2 = ledit_get_line(buffer, line2);
+ if (line1 == line2) {
+ memcpy(dst, ll1->text + byte1, byte2 - byte1);
+ dst[byte2 - byte1] = '\0';
+ } else {
+ size_t cur_pos = 0;
+ memcpy(dst, ll1->text + byte1, ll1->len - byte1);
+ cur_pos += ll1->len - byte1;
+ dst[cur_pos] = '\n';
+ cur_pos++;
+ for (int i = line1 + 1; i < line2; i++) {
+ ledit_line *ll = ledit_get_line(buffer, i);
+ memcpy(dst + cur_pos, ll->text, ll->len);
+ cur_pos += ll->len;
+ dst[cur_pos] = '\n';
+ cur_pos++;
+ }
+ memcpy(dst + cur_pos, ll2->text, byte2);
+ cur_pos += byte2;
+ dst[cur_pos] = '\0';
+ }
+}
+
+/* copy text range into given buffer and resize it if necessary
+ * - *dst is reallocated and *alloc adjusted if the text doesn't fit
+ * - *dst is null-terminated
+ * - the range must be sorted already
+ * - returns the length of the text, not including the NUL */
+size_t
+ledit_copy_text_with_resize(
+ ledit_buffer *buffer,
+ char **dst, size_t *alloc,
+ int line1, int byte1,
+ int line2, int byte2) {
+ assert(line1 < line2 || (line1 == line2 && byte1 <= byte2));
+ size_t len = ledit_textlen(buffer, line1, byte1, line2, byte2);
+ /* len + 1 because of nul */
+ if (len + 1 > *alloc) {
+ *alloc = *alloc * 2 > len + 1 ? *alloc * 2 : len + 1;
+ *dst = ledit_realloc(*dst, *alloc);
+ }
+ ledit_copy_text(buffer, *dst, line1, byte1, line2, byte2);
+ return len;
+}
+
+int
+ledit_prev_utf8(ledit_line *line, int index) {
+ int i = index - 1;
+ /* find valid utf8 char - this probably needs to be improved */
+ while (i > 0 && ((line->text[i] & 0xC0) == 0x80))
+ i--;
+ return i;
+}
+
+int
+ledit_next_utf8(ledit_line *line, int index) {
+ int i = index + 1;
+ while (i < line->len && ((line->text[i] & 0xC0) == 0x80))
+ i++;
+ return i;
+}
+
int
ledit_delete_unicode_char(ledit_buffer *buffer, int line_index, int byte_index, int dir) {
ledit_line *l = ledit_get_line(buffer, line_index);
int new_index = byte_index;
if (dir < 0) {
- int i = byte_index - 1;
- /* find valid utf8 char - this probably needs to be improved */
- while (i > 0 && ((l->text[i] & 0xC0) == 0x80))
- i--;
+ int i = ledit_prev_utf8(l, byte_index);
memmove(l->text + i, l->text + byte_index, l->len - byte_index);
l->len -= byte_index - i;
new_index = i;
} else {
- int i = byte_index + 1;
- while (i < l->len && ((l->text[i] & 0xC0) == 0x80))
- i++;
+ int i = ledit_next_utf8(l, byte_index);
memmove(l->text + byte_index, l->text + i, l->len - i);
l->len -= i - byte_index;
}
@@ -410,11 +510,7 @@ ledit_x_softline_to_pos(ledit_line *line, int x, int softline, int *pos_ret) {
if (line->parent_buffer->state->mode == INSERT) {
while (trailing > 0) {
trailing--;
- (*pos_ret)++;
- /* utf8 stuff */
- while (*pos_ret < line->len &&
- ((line->text[*pos_ret] & 0xC0) == 0x80))
- (*pos_ret)++;
+ *pos_ret = ledit_next_utf8(line, *pos_ret);
}
}
}
@@ -429,14 +525,10 @@ ledit_get_legal_normal_pos(ledit_buffer *buffer, int line, int pos) {
const PangoLogAttr *attrs =
pango_layout_get_log_attrs_readonly(final_line->layout, &nattrs);
int cur = nattrs - 2;
- ret--;
- while (ret > 0 && ((final_line->text[ret] & 0xC0) == 0x80))
- ret--;
+ ret = ledit_prev_utf8(final_line, ret);
while (ret > 0 && cur > 0 && !attrs[cur].is_cursor_position) {
cur--;
- ret--;
- while (ret > 0 && ((final_line->text[ret] & 0xC0) == 0x80))
- ret--;
+ ret = ledit_prev_utf8(final_line, ret);
}
}
return ret;
diff --git a/buffer.h b/buffer.h
@@ -66,3 +66,19 @@ void ledit_delete_range(
);
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);
+int ledit_next_utf8(ledit_line *line, int index);
+int ledit_prev_utf8(ledit_line *line, int index);
+size_t ledit_textlen(ledit_buffer *buffer, int line1, int byte1, int line2, int byte2);
+void ledit_copy_text(ledit_buffer *buffer, char *dst, int line1, int byte1, int line2, int byte2);
+size_t ledit_copy_text_with_resize(
+ ledit_buffer *buffer,
+ char **dst, size_t *alloc,
+ int line1, int byte1,
+ int line2, int byte2
+);
+void
+ledit_insert_text_from_line(
+ ledit_buffer *buffer,
+ int dst_line, int dst_index,
+ int src_line, int src_index, int src_len
+);
diff --git a/ledit.c b/ledit.c
@@ -707,7 +707,7 @@ mainloop(void) {
fprintf(stderr, "XKB not supported.");
exit(1);
}
- printf("XKB (%d.%d) supported.\n", major, minor);
+ /*printf("XKB (%d.%d) supported.\n", major, minor);*/
/* This should select the events when the keyboard mapping changes.
* When e.g. 'setxkbmap us' is executed, two events are sent, but I
* haven't figured out how to change that. When the xkb layout
@@ -821,10 +821,12 @@ setup(int argc, char *argv[]) {
/* based on http://wili.cc/blog/xdbe.html */
int major, minor;
if (XdbeQueryExtension(state.dpy, &major, &minor)) {
+ /*
printf(
"Xdbe (%d.%d) supported, using double buffering.\n",
major, minor
);
+ */
int num_screens = 1;
Drawable screens[] = { DefaultRootWindow(state.dpy) };
XdbeScreenVisualInfo *info = XdbeGetVisualInfo(
@@ -1116,12 +1118,9 @@ xy_to_line_byte(int x, int y, int *line_ret, int *byte_ret) {
x * PANGO_SCALE, (int)(pos - h) * PANGO_SCALE,
&index, &trailing
);
- /* FIXME: make this a separate, reusable function */
while (trailing > 0) {
trailing--;
- index++;
- while (index < line->len && ((line->text[index] & 0xC0) == 0x80))
- index++;
+ index = ledit_next_utf8(line, index);
}
*line_ret = i;
*byte_ret = index;
@@ -1153,45 +1152,10 @@ sort_selection(int *line1, int *byte1, int *line2, int *byte2) {
/* lines and bytes need to be sorted already! */
static void
copy_selection_to_x_primary(int line1, int byte1, int line2, int byte2) {
- size_t len = 0;
- ledit_line *ll1 = ledit_get_line(buffer, line1);
- ledit_line *ll2 = ledit_get_line(buffer, line2);
- if (line1 == line2) {
- len = byte2 - byte1;
- } else {
- /* + 1 for newline */
- len = ll1->len - byte1 + byte2 + 1;
- for (int i = line1 + 1; i < line2; i++) {
- ledit_line *ll = ledit_get_line(buffer, i);
- len += ll->len + 1;
- }
- }
- len += 1; /* nul */
- if (len > xsel.primary_alloc) {
- /* FIXME: maybe allocate a bit more */
- xsel.primary = ledit_realloc(xsel.primary, len);
- xsel.primary_alloc = len;
- }
- if (line1 == line2) {
- memcpy(xsel.primary, ll1->text + byte1, byte2 - byte1);
- xsel.primary[byte2 - byte1] = '\0';
- } else {
- size_t cur_pos = 0;
- memcpy(xsel.primary, ll1->text + byte1, ll1->len - byte1);
- cur_pos += ll1->len - byte1;
- xsel.primary[cur_pos] = '\n';
- cur_pos++;
- for (int i = line1 + 1; i < line2; i++) {
- ledit_line *ll = ledit_get_line(buffer, i);
- memcpy(xsel.primary + cur_pos, ll->text, ll->len);
- cur_pos += ll->len;
- xsel.primary[cur_pos] = '\n';
- cur_pos++;
- }
- memcpy(xsel.primary + cur_pos, ll2->text, byte2);
- cur_pos += byte2;
- xsel.primary[cur_pos] = '\0';
- }
+ (void)ledit_copy_text_with_resize(
+ buffer, &xsel.primary, &xsel.primary_alloc,
+ line1, byte1, line2, byte2
+ );
XSetSelectionOwner(state.dpy, XA_PRIMARY, state.win, CurrentTime);
/*
FIXME
@@ -1378,12 +1342,10 @@ backspace(void) {
} else if (buffer->cur_index == 0) {
if (buffer->cur_line != 0) {
ledit_line *l1 = ledit_get_line(buffer, buffer->cur_line - 1);
- ledit_line *l2 = ledit_get_line(buffer, buffer->cur_line);
int old_len = l1->len;
- ledit_insert_text_with_newlines(
- buffer, buffer->cur_line - 1,
- l1->len, l2->text, l2->len,
- NULL, NULL
+ ledit_insert_text_from_line(
+ buffer, buffer->cur_line - 1, l1->len,
+ buffer->cur_line, 0, -1
);
ledit_delete_line_entry(buffer, buffer->cur_line);
buffer->cur_line--;
@@ -1404,14 +1366,10 @@ delete_key(void) {
/* NOP */
} else if (buffer->cur_index == cur_line->len) {
if (buffer->cur_line != buffer->lines_num - 1) {
- ledit_line *next_line = ledit_get_line(
- buffer, buffer->cur_line + 1
- );
int old_len = cur_line->len;
- ledit_insert_text_with_newlines(
+ ledit_insert_text_from_line(
buffer, buffer->cur_line, cur_line->len,
- next_line->text, next_line->len,
- NULL, NULL
+ buffer->cur_line + 1, 0, -1
);
ledit_delete_line_entry(buffer, buffer->cur_line + 1);
buffer->cur_index = old_len;
@@ -1463,10 +1421,7 @@ move_cursor_left_right(int dir) {
/* FIXME: spaces at end of softlines are weird in normal mode */
while (trailing > 0) {
trailing--;
- new_index++;
- while (new_index < cur_line->len &&
- ((cur_line->text[new_index] & 0xC0) == 0x80))
- new_index++;
+ new_index = ledit_next_utf8(cur_line, new_index);
}
if (new_index < 0)
new_index = 0;
@@ -1725,6 +1680,14 @@ end_lineedit(void) {
}
}
+static void
+show_line(void) {
+ int len = snprintf(NULL, 0, "Line %d of %d", buffer->cur_line + 1, buffer->lines_num);
+ char *str = ledit_malloc(len + 1);
+ snprintf(str, len + 1, "Line %d of %d", buffer->cur_line + 1, buffer->lines_num);
+ show_message(str, len);
+}
+
/* FIXME: maybe sort these and use binary search */
static struct key keys_en[] = {
{NULL, 0, XK_BackSpace, INSERT, KEY_ANY, KEY_ANY, &backspace},
@@ -1757,6 +1720,7 @@ static struct key keys_en[] = {
{"o", 0, 0, VISUAL, KEY_ANY, KEY_ANY, &switch_selection_end},
{"c", ControlMask, 0, INSERT|VISUAL, KEY_ANY, KEY_ANY, &clipcopy},
{"v", ControlMask, 0, INSERT, KEY_ANY, KEY_ANY, &clippaste},
+ {"g", ControlMask, 0, NORMAL|VISUAL, KEY_ANY, KEY_ANY, &show_line},
{":", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_ANY, &enter_commandedit},
{"?", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_ANY, &enter_searchedit_backward},
{"/", 0, 0, NORMAL|VISUAL, KEY_ANY, KEY_ANY, &enter_searchedit_forward},