commit f853ec6f0c0569cf5bae15ce3e9d8bfc533b358b from: Omar Polo date: Tue Oct 22 18:40:42 2024 UTC handle tab characters tab characters have their width depending on the column they're in, since they extend to the next multiple of 8. (citation needed?) So, keep track of the column when considering the length (in columns) of the text, so that we can render them properly. In the future we might want to turn them into spaces (either at read or render time) just to stay on the safe side in case not all terminals/ncurses implementations use 8 columns. commit - ec5e90b332b7faee24b11167b737e188f50132a2 commit + f853ec6f0c0569cf5bae15ce3e9d8bfc533b358b blob - 8d99448e05f0635c4fb2697c80b760e1ef89441a blob + 37f065f0477ec6c49761015959cc8b551e4460d5 --- downloads.c +++ downloads.c @@ -76,7 +76,7 @@ end: * counter, and we know for sure is < FMT_SCALED_STRSIZE so it * fits. */ - wrap_page(&downloadwin, download_cols); + wrap_page(&downloadwin, download_cols, 0); } struct download * blob - a71194801bebd6854ad8374b707c970a667e9e9d blob + 4fef139c1261430ad2bf355145f043d383f467fb --- help.c +++ help.c @@ -88,6 +88,6 @@ recompute_help(void) helpwin.mode = "*Help*"; erase_buffer(&helpwin); rec_compute_help(current_map, p, sizeof(p)); - wrap_page(&helpwin, help_cols); + wrap_page(&helpwin, help_cols, 0); } } blob - 3f7a73e3c48f1a88a5ba2c6846e2c4930cd0f8bc blob + 7238b8c53cf0fb6399c6781854e5e422333b7de3 --- telescope.h +++ telescope.h @@ -248,7 +248,7 @@ int ui_send_net(int, uint32_t, int, const void *, ui void erase_buffer(struct buffer *); void empty_linelist(struct buffer*); void empty_vlist(struct buffer*); -int wrap_text(struct buffer*, const char*, struct line*, size_t, int); -int wrap_page(struct buffer *, int width); +int wrap_text(struct buffer*, const char*, struct line*, size_t, int, int); +int wrap_page(struct buffer *, int, int); #endif /* TELESCOPE_H */ blob - d8b5580a795cafd36789f73de5967b0653662eb9 blob + 91bb028d586fd63fcc4f5f69c75730c62bc9f7a0 --- ui.c +++ ui.c @@ -69,7 +69,7 @@ static void handle_signal(int, int, void*); static void handle_resize_nodelay(int, int, void*); static void handle_download_refresh(int, int, void *); static void rearrange_windows(void); -static void line_prefix_and_text(struct vline *, char *, size_t, const char **, const char **, int *); +static void line_prefix_and_text(int, struct vline *, char *, size_t, const char **, const char **, int *); static void print_vline(int, int, WINDOW*, struct vline*); static void redraw_tabline(void); static void redraw_window(WINDOW *, int, int, int, int, struct buffer *); @@ -226,30 +226,34 @@ restore_curs_x(struct buffer *buffer) if (dont_apply_styling) lp = raw_prefixes; + buffer->curs_x = 0; + + /* small hack: don't olivetti-mode the download pane */ + if (buffer != &downloadwin) + buffer->curs_x += x_offset; + vl = buffer->current_line; if (vl == NULL || vl->len == 0 || vl->parent == NULL) - buffer->curs_x = buffer->point_offset = 0; + buffer->curs_x += buffer->point_offset = 0; else if (vl->parent->data != NULL) { text = vl->parent->data; - buffer->curs_x = utf8_snwidth(text, buffer->point_offset); + buffer->curs_x += utf8_snwidth(text, buffer->point_offset, + buffer->curs_x); } else { text = vl->parent->line + vl->from; - buffer->curs_x = utf8_snwidth(text, buffer->point_offset); + buffer->curs_x += utf8_snwidth(text, buffer->point_offset, + buffer->curs_x); } - /* small hack: don't olivetti-mode the download pane */ - if (buffer != &downloadwin) - buffer->curs_x += x_offset; - if (vl == NULL) return; if (vl->parent->data != NULL) buffer->curs_x += utf8_swidth_between(vl->parent->line, - vl->parent->data); + vl->parent->data, buffer->curs_x); else { prfx = lp[vl->parent->type].prfx1; - buffer->curs_x += utf8_swidth(prfx); + buffer->curs_x += utf8_swidth(prfx, buffer->curs_x); } } @@ -429,7 +433,7 @@ rearrange_windows(void) wresize(minibuffer, minibuffer_lines, COLS); lines -= minibuffer_lines; - wrap_page(&ministate.compl.buffer, COLS); + wrap_page(&ministate.compl.buffer, COLS, 0); } mvwin(echoarea, --lines, 0); @@ -445,7 +449,7 @@ rearrange_windows(void) wresize(download, download_lines, download_cols); lines -= download_lines; - wrap_page(&downloadwin, download_cols); + wrap_page(&downloadwin, download_cols, 0); } body_lines = show_tab_bar ? --lines : lines; @@ -461,7 +465,7 @@ rearrange_windows(void) mvwin(help, show_tab_bar, 0); wresize(help, help_lines, help_cols); - wrap_page(&helpwin, help_cols); + wrap_page(&helpwin, help_cols, 0); body_cols = COLS - help_cols - 1; mvwin(body, show_tab_bar, help_cols); @@ -474,12 +478,12 @@ rearrange_windows(void) if (show_tab_bar) wresize(tabline, 1, COLS); - wrap_page(¤t_tab->buffer, body_cols); + wrap_page(¤t_tab->buffer, body_cols, x_offset); redraw_tab(current_tab); } static void -line_prefix_and_text(struct vline *vl, char *buf, size_t len, +line_prefix_and_text(int col, struct vline *vl, char *buf, size_t len, const char **prfx_ret, const char **text_ret, int *text_len) { struct lineprefix *lp = line_prefixes; @@ -511,7 +515,7 @@ line_prefix_and_text(struct vline *vl, char *buf, size if (cont) { memset(buf, 0, len); - width = utf8_swidth_between(vl->parent->line, space); + width = utf8_swidth_between(vl->parent->line, space, col); for (i = 0; i < width + 1 && i < len - 1; ++i) buf[i] = ' '; } else { @@ -587,7 +591,7 @@ print_vline(int off, int width, WINDOW *window, struct if (vl->parent->type == LINE_FRINGE && fringe_ignore_offset) off = 0; - line_prefix_and_text(vl, emojibuf, sizeof(emojibuf), &prfx, + line_prefix_and_text(off, vl, emojibuf, sizeof(emojibuf), &prfx, &text, &textlen); wattr_on(window, body_face.left, NULL); @@ -972,7 +976,7 @@ do_redraw_minibuffer(void) start = hist_cur(ministate.hist); line = buffer->current_line->parent->line + buffer->current_line->from; c = line + buffer->point_offset; - while (start < c && utf8_swidth_between(start, c) > (size_t)COLS/2) { + while (start < c && utf8_swidth_between(start, c, off_x) > (size_t)COLS/2) { start += grapheme_next_character_break_utf8(start, SIZE_MAX); } @@ -981,7 +985,7 @@ do_redraw_minibuffer(void) if (ministate.curmesg != NULL) wprintw(echoarea, " [%s]", ministate.curmesg); - wmove(echoarea, 0, off_x + utf8_swidth_between(start, c)); + wmove(echoarea, 0, off_x + utf8_swidth_between(start, c, off_x)); } static void @@ -1236,7 +1240,7 @@ ui_on_tab_loaded(struct tab *tab) void ui_on_tab_refresh(struct tab *tab) { - wrap_page(&tab->buffer, body_cols); + wrap_page(&tab->buffer, body_cols, x_offset); if (tab == current_tab) redraw_tab(tab); else blob - 0df3b69ead606f8dc306a10192e45832ac86b0a2 blob + 37b942d203b94dd4d84fed7f53f56d5e684bebb4 --- utf8.c +++ utf8.c @@ -75,9 +75,12 @@ utf8_decode(uint32_t* restrict state, uint32_t* restri return decode(state, codep, byte); } -/* returns only 0, 1, 2 or 8. assumes sizeof(wchar_t) is 4 */ +/* + * returns 0, 1, 2 or less than 8 for tabs. assumes that + * sizeof(wchar_t) == 4 + */ static size_t -utf8_chwidth(uint32_t cp) +utf8_chwidth(uint32_t cp, int col) { /* XXX: if we're running on a platform where sizeof(wchar_t) * == 2 what to do? The manpage for wcwidth and wcs isn't @@ -86,54 +89,64 @@ utf8_chwidth(uint32_t cp) assert(sizeof(wchar_t) == 4); /* - * quick and dirty fix for the tabs. In the future we may - * want to expand tabs into N spaces, but for the time being - * this seems to be good enough (tm). + * Tabs are wide until the next multiple of eight. */ if (cp == '\t') - return 8; + return (((col + 8) / 8) * 8) - col; return wcwidth((wchar_t)cp); } size_t -utf8_snwidth(const char *s, size_t off) +utf8_snwidth(const char *s, size_t off, int col) { size_t i, tot; uint32_t cp = 0, state = 0; + int width; tot = 0; for (i = 0; i < off; ++i) - if (!decode(&state, &cp, s[i])) - tot += utf8_chwidth(cp); + if (!decode(&state, &cp, s[i])) { + width = utf8_chwidth(cp, col); + tot += width; + col += width; + } return tot; } size_t -utf8_swidth(const char *s) +utf8_swidth(const char *s, int col ) { size_t tot; uint32_t cp = 0, state = 0; + int width; tot = 0; for (; *s; ++s) - if (!decode(&state, &cp, *s)) - tot += utf8_chwidth(cp); + if (!decode(&state, &cp, *s)) { + width = utf8_chwidth(cp, col); + tot += width; + col += width; + } return tot; } size_t -utf8_swidth_between(const char *str, const char *end) +utf8_swidth_between(const char *str, const char *end, int col) { size_t tot; uint32_t cp = 0, state = 0; + int width; tot = 0; for (; *str && str < end; ++str) - if (!decode(&state, &cp, *str)) - tot += utf8_chwidth(cp); + if (!decode(&state, &cp, *str)) { + width = utf8_chwidth(cp, col); + tot += width; + col += width; + } return tot; } blob - 2dec60084feb41ed7eb0678f55d2f77cea2fdd82 blob + 9fbfb611fc480b5d59b6ea5d595e17d204534256 --- utf8.h +++ utf8.h @@ -22,9 +22,9 @@ /* utf8.c */ uint32_t utf8_decode(uint32_t*restrict, uint32_t*restrict, uint8_t); -size_t utf8_snwidth(const char*, size_t); -size_t utf8_swidth(const char*); -size_t utf8_swidth_between(const char*, const char*); +size_t utf8_snwidth(const char*, size_t, int); +size_t utf8_swidth(const char*, int); +size_t utf8_swidth_between(const char*, const char*, int); int emojied_line(const char *, const char **); /* emoji-matcher.c */ blob - aed5aef2f6f14cfbbf11cfb520bab412e2e27bdb blob + ca120f275e67a2c9aa8da7365aa6f4a4eb2fed3f --- wrap.c +++ wrap.c @@ -105,7 +105,7 @@ push_line(struct buffer *buffer, struct line *l, const */ int wrap_text(struct buffer *buffer, const char *prfx, struct line *l, - size_t width, int oneline) + size_t width, int base_offset, int oneline) { const char *line, *space; size_t ret, off, start, cur, prfxwidth; @@ -114,15 +114,15 @@ wrap_text(struct buffer *buffer, const char *prfx, str if ((line = l->line) == NULL || *line == '\0') return push_line(buffer, l, NULL, 0, 0); - prfxwidth = utf8_swidth(prfx); - cur = prfxwidth; + prfxwidth = utf8_swidth(prfx, base_offset); + cur = base_offset + prfxwidth; start = 0; flags = 0; if (l->type == LINE_LINK && emojify_link && emojied_line(l->line, &space)) { - prfxwidth = utf8_swidth_between(l->line, space); - cur = prfxwidth; + prfxwidth = utf8_swidth_between(l->line, space, base_offset); + cur = base_offset + prfxwidth; line = space + 1; } @@ -130,7 +130,8 @@ wrap_text(struct buffer *buffer, const char *prfx, str size_t t; ret = grapheme_next_line_break_utf8(&line[off], SIZE_MAX); - t = utf8_swidth_between(&line[off], &line[off + ret]); + t = utf8_swidth_between(&line[off], &line[off + ret], + base_offset); /* we can't reach the last column */ if (cur + t < width) { @@ -146,7 +147,7 @@ wrap_text(struct buffer *buffer, const char *prfx, str flags = L_CONTINUATION; start = off; - cur = t + prfxwidth; + cur = base_offset + prfxwidth + t; } if (off != start) @@ -155,7 +156,7 @@ wrap_text(struct buffer *buffer, const char *prfx, str } int -wrap_page(struct buffer *buffer, int width) +wrap_page(struct buffer *buffer, int width, int x_offset) { struct line *l; const struct line *top_orig, *orig; @@ -193,7 +194,7 @@ wrap_page(struct buffer *buffer, int width) case LINE_PATCH_ADD: case LINE_PATCH_DEL: wrap_text(buffer, prfx, l, MIN(fill_column, width), - 0); + x_offset, 0); break; case LINE_COMPL: case LINE_COMPL_CURRENT: @@ -201,7 +202,7 @@ wrap_page(struct buffer *buffer, int width) case LINE_DOWNLOAD: case LINE_DOWNLOAD_DONE: case LINE_DOWNLOAD_INFO: - wrap_text(buffer, prfx, l, width, 1); + wrap_text(buffer, prfx, l, width, x_offset, 1); break; case LINE_FRINGE: /* never, ever wrapped */