commit 6d24bfb3ca185665e8a7fa4e5f88434690f4272d from: Omar Polo date: Sat Oct 19 21:31:41 2024 UTC use a byte offset to track the point in the line Instead of using a code-point offset, use a raw byte offset and navigate backward and forward by means of graphemes. This gives a correct "visual" backward/forward navigation through characters. commit - b60507ee6753bf11be13aee78316315edf433587 commit + 6d24bfb3ca185665e8a7fa4e5f88434690f4272d blob - 196f62dfe357c6603e7b34b29c6bfae0aa17fb56 blob + a5e03b8df279c4a7c853b64d6beaee58b93b6300 --- cmd.c +++ cmd.c @@ -22,6 +22,8 @@ #include #include +#include + #include "certs.h" #include "cmd.h" #include "compl.h" @@ -111,17 +113,46 @@ cmd_next_line(struct buffer *buffer) void cmd_backward_char(struct buffer *buffer) { - if (buffer->cpoff != 0) - buffer->cpoff--; + struct vline *vl; + char *text; + size_t left, off, point = 0; + + if ((vl = buffer->current_line) == NULL) + return; + + text = vl->parent->line + vl->from; + left = vl->len; + + for (;;) { + off = grapheme_next_character_break_utf8(text, left); + if (point + off >= buffer->point_offset) + break; + point += off; + text += off; + left -= off; + } + + buffer->point_offset = point; } void cmd_forward_char(struct buffer *buffer) { - if (buffer->current_line == NULL) + struct vline *vl; + char *text; + size_t left, off; + + if ((vl = buffer->current_line) == NULL) return; - if (buffer->current_line->cplen > buffer->cpoff) - buffer->cpoff++; + + text = vl->parent->line + vl->from; + left = vl->len; + + text += buffer->point_offset; + left -= buffer->point_offset; + + off = grapheme_next_character_break_utf8(text, left); + buffer->point_offset += off; } void @@ -151,7 +182,7 @@ cmd_forward_paragraph(struct buffer *buffer) void cmd_move_beginning_of_line(struct buffer *buffer) { - buffer->cpoff = 0; + buffer->point_offset = 0; } void @@ -162,7 +193,7 @@ cmd_move_end_of_line(struct buffer *buffer) vl = buffer->current_line; if (vl == NULL) return; - buffer->cpoff = vl->cplen; + buffer->point_offset = vl->len; } void @@ -257,7 +288,7 @@ void cmd_beginning_of_buffer(struct buffer *buffer) { buffer->current_line = TAILQ_FIRST(&buffer->vhead); - buffer->cpoff = 0; + buffer->point_offset = 0; buffer->top_line = buffer->current_line; buffer->line_off = 0; } @@ -738,40 +769,50 @@ cmd_olivetti_mode(struct buffer *buffer) void cmd_mini_delete_char(struct buffer *buffer) { - char *line, *c, *n; + struct vline *vl; + char *text; + size_t old_point, gap, rest; GUARD_READ_ONLY(); - minibuffer_taint_hist(); - - line = buffer->current_line->parent->line + buffer->current_line->from; - c = utf8_nth(line, buffer->cpoff); - if (*c == '\0') + vl = buffer->current_line; + old_point = buffer->point_offset; + cmd_forward_char(buffer); + gap = buffer->point_offset - old_point; + if (gap == 0) return; - n = utf8_next_cp(c); - memmove(c, n, strlen(n)+1); + minibuffer_taint_hist(); + text = vl->parent->line + vl->from + old_point; + rest = vl->len - buffer->point_offset; + memmove(text, text + gap, rest); + buffer->point_offset = old_point; + recompute_completions(0); } void cmd_mini_delete_backward_char(struct buffer *buffer) { - char *line, *c, *p; + struct vline *vl; + char *text; + size_t old_point, gap, rest; GUARD_READ_ONLY(); - minibuffer_taint_hist(); - - line = buffer->current_line->parent->line + buffer->current_line->from; - c = utf8_nth(line, buffer->cpoff); - if (c == line) + vl = buffer->current_line; + old_point = buffer->point_offset; + cmd_backward_char(buffer); + gap = old_point - buffer->point_offset; + if (gap == 0) return; - p = utf8_prev_cp(c-1, line); + + minibuffer_taint_hist(); - memmove(p, c, strlen(c)+1); - buffer->cpoff--; + text = vl->parent->line + vl->from + buffer->point_offset; + rest = vl->len - old_point; + memmove(text, text + gap, rest); recompute_completions(0); } @@ -786,7 +827,7 @@ cmd_mini_kill_line(struct buffer *buffer) minibuffer_taint_hist(); line = buffer->current_line->parent->line + buffer->current_line->from; - c = utf8_nth(line, buffer->cpoff); + c = line + buffer->point_offset; *c = '\0'; recompute_completions(0); @@ -799,7 +840,7 @@ cmd_mini_kill_whole_line(struct buffer *buffer) minibuffer_taint_hist(); *buffer->current_line->parent->line = '\0'; - buffer->cpoff = 0; + buffer->point_offset = 0; recompute_completions(0); } blob - 84fb3401f3806491824997f7dbf3e61fa9e7b6bd blob + 39a65d822fa0094de2c922da93c1c315408ff2b9 --- minibuffer.c +++ minibuffer.c @@ -26,6 +26,8 @@ #include #include +#include + #include "certs.h" #include "cmd.h" #include "defaults.h" @@ -171,7 +173,8 @@ minibuffer_insert_current_candidate(void) minibuffer_taint_hist(); strlcpy(ministate.buf, vl->parent->line, sizeof(ministate.buf)); - ministate.buffer.cpoff = ministate.vline.cplen = utf8_cplen(ministate.buf); + ministate.buffer.point_offset = strlen(ministate.buf); + ministate.vline.len = strlen(ministate.buf); return 0; } @@ -227,8 +230,7 @@ minibuffer_taint_hist(void) ministate.editing = 1; strlcpy(ministate.buf, hist_cur(ministate.hist), sizeof(ministate.buf)); - ministate.buffer.cpoff = 0; - ministate.vline.cplen = utf8_cplen(ministate.buf); + ministate.buffer.point_offset = 0; ministate.buffer.current_line->parent->line = ministate.buf; } @@ -253,15 +255,17 @@ minibuffer_self_insert(void) if (thiskey.cp == 0) return; - len = utf8_encode(thiskey.cp, tmp); - c = utf8_nth(ministate.buffer.current_line->parent->line, - ministate.buffer.cpoff); + len = grapheme_encode_utf8(thiskey.cp, tmp, sizeof(tmp)); + + c = ministate.buffer.current_line->parent->line + + ministate.buffer.current_line->from + + ministate.buffer.point_offset; if (c + len > ministate.buf + sizeof(ministate.buf) - 1) return; memmove(c + len, c, strlen(c)+1); memcpy(c, tmp, len); - ministate.buffer.cpoff++; + ministate.buffer.point_offset += len; recompute_completions(1); } @@ -599,15 +603,15 @@ enter_minibuffer(struct minibuffer *minibuffer, const if (ministate.abortfn == NULL) ministate.abortfn = exit_minibuffer; - ministate.buffer.cpoff = 0; if (minibuffer->input) { strlcpy(ministate.buf, minibuffer->input, sizeof(ministate.buf)); - ministate.vline.cplen = utf8_cplen(ministate.buf); - ministate.buffer.cpoff = ministate.vline.cplen; + ministate.buffer.point_offset = strlen(ministate.buf); + ministate.vline.len = strlen(ministate.buf); } else { ministate.buf[0] = '\0'; - ministate.vline.cplen = ministate.buffer.cpoff = 0; + ministate.buffer.point_offset = 0; + ministate.vline.len = 0; } ministate.buffer.current_line = &ministate.vline; blob - 3520b8ccfe4a9e44bc7f12180e6c0e5c85bddfdc blob + 3f7a73e3c48f1a88a5ba2c6846e2c4930cd0f8bc --- telescope.h +++ telescope.h @@ -82,7 +82,6 @@ struct vline { struct line *parent; size_t from; size_t len; - size_t cplen; #define L_CONTINUATION 0x2 int flags; @@ -125,7 +124,7 @@ struct buffer { size_t line_max; struct vline *top_line; struct vline *current_line; - size_t cpoff; + size_t point_offset; TAILQ_HEAD(, line) head; TAILQ_HEAD(vhead, vline) vhead; blob - f4317da5b1180f1a70852db4ec5e016213c36217 blob + 81a4a7734f9793f8d8ad1985024d95075580c30e --- ui.c +++ ui.c @@ -200,7 +200,7 @@ save_excursion(struct excursion *place, struct buffer place->line_off = buffer->line_off; place->top_line = buffer->top_line; place->current_line = buffer->current_line; - place->cpoff = buffer->cpoff; + place->point_offset = buffer->point_offset; } void @@ -211,7 +211,7 @@ restore_excursion(struct excursion *place, struct buff buffer->line_off = place->line_off; buffer->top_line = place->top_line; buffer->current_line = place->current_line; - buffer->cpoff = place->cpoff; + buffer->point_offset = place->point_offset; } static void @@ -226,13 +226,13 @@ restore_curs_x(struct buffer *buffer) vl = buffer->current_line; if (vl == NULL || vl->len == 0 || vl->parent == NULL) - buffer->curs_x = buffer->cpoff = 0; + buffer->curs_x = buffer->point_offset = 0; else if (vl->parent->data != NULL) { text = vl->parent->data; - buffer->curs_x = utf8_snwidth(text + 1, buffer->cpoff) + 1; + buffer->curs_x = utf8_snwidth(text, buffer->point_offset); } else { text = vl->parent->line + vl->from; - buffer->curs_x = utf8_snwidth(text, buffer->cpoff); + buffer->curs_x = utf8_snwidth(text, buffer->point_offset); } /* small hack: don't olivetti-mode the download pane */ @@ -969,7 +969,7 @@ do_redraw_minibuffer(void) if (!ministate.editing) start = hist_cur(ministate.hist); line = buffer->current_line->parent->line + buffer->current_line->from; - c = utf8_nth(line, buffer->cpoff); + c = line + buffer->point_offset; while (utf8_swidth_between(start, c) > (size_t)COLS/2) { start = utf8_next_cp(start); } blob - 045a2712523fc2f2729b7404174da25a8da3616b blob + d7061ea4db14f8fe650c8b087523e180da9efec8 --- ui.h +++ ui.h @@ -27,7 +27,7 @@ struct excursion { size_t line_off; struct vline *current_line; struct vline *top_line; - size_t cpoff; + size_t point_offset; }; enum pairs { blob - 1433aacce3f0e55b5875538c3ce4bd8fcea4e490 blob + e0e97f5607bfff0056604783be9887e989c3a1bc --- utf8.c +++ utf8.c @@ -166,20 +166,16 @@ utf8_chwidth(uint32_t cp) return wcwidth((wchar_t)cp); } -/* NOTE: n is the number of codepoints, NOT the byte length. In - * other words, s MUST be NUL-terminated. */ size_t -utf8_snwidth(const char *s, size_t n) +utf8_snwidth(const char *s, size_t off) { size_t i, tot; uint32_t cp = 0, state = 0; tot = 0; - for (i = 0; *s && i < n; ++s) - if (!decode(&state, &cp, *s)) { - i++; + for (i = 0; i < off; ++i) + if (!decode(&state, &cp, s[i])) tot += utf8_chwidth(cp); - } return tot; } blob - 3054f79000e3cbe7f8e905d1994cc436fccd7cf4 blob + b60ff4d9cc3db8f1cb3223f1a45a3768914373de --- wrap.c +++ wrap.c @@ -92,7 +92,6 @@ push_line(struct buffer *buffer, struct line *l, const if (len != 0) { vl->from = buf - l->line; vl->len = len; - vl->cplen = utf8_ncplen(buf, vl->len); } vl->flags = flags;