commit 676fba6fa2123ead1a58b5589fb71d9a861bb87d from: Omar Polo date: Sun Mar 21 19:08:02 2021 UTC wrap respecting character widths commit - 17e5106b8ecac061d8558f1d73f284c5516347cd commit + 676fba6fa2123ead1a58b5589fb71d9a861bb87d blob - 17dd7e40516f85b94911879c02fb06aa540ab44a blob + 66542243d179f3fad02da48f3b4e9e826c0dc0ca --- telescope.h +++ telescope.h @@ -274,7 +274,7 @@ int unicode_isgraph(uint32_t); void dispatch_imsg(struct imsgbuf*, imsg_handlerfn**, size_t); /* wrap.c */ -void wrap_text(struct tab*, const char*, struct line*, size_t); +int wrap_text(struct tab*, const char*, struct line*, size_t); int hardwrap_text(struct tab*, struct line*, size_t); #endif /* TELESCOPE_H */ blob - 085df61ba454ddeb68b9da4c8f1fa8d0956d5366 blob + 2549ad5a7a36725c17a4881edd957b855975872d --- wrap.c +++ wrap.c @@ -28,16 +28,9 @@ * 1. if it's a line in a pre-formatted block: * a. hard wrap. * b. repeat - * 2. there is enough room for the next word? - * a. yes: render it - * b. no: break the current line. - * i. while there isn't enough space to draw the current - * word, hard-wrap it - * ii. draw the remainder of the current word (can be the - * the entirely) - * 3. render the spaces after the word - * a. but if there is not enough room break the line and - * forget them + * 2. otherwise advance the line char by char. + * 3. when ending the space, split the line at the last occurrence of + * a "word separator" (i.e. " \t-") or at point if none. * 4. repeat * */ @@ -70,130 +63,82 @@ push_line(struct tab *tab, const struct line *l, const } /* - * Helper function for wrap_text. Find the end of the current word - * and the end of the separator after the word. - */ -static int -word_boundaries(const char *s, const char *sep, const char **endword, const char **endspc) -{ - *endword = s; - *endword = s; - - if (*s == '\0') - return 0; - - /* find the end of the current world */ - for (; *s != '\0'; ++s) { - if (strchr(sep, *s) != NULL) - break; - } - - *endword = s; - - /* find the end of the separator */ - for (; *s != '\0'; ++s) { - if (strchr(sep, *s) == NULL) - break; - } - - *endspc = s; - - return 1; -} - -static inline int -emitline(struct tab *tab, size_t zero, size_t *off, const struct line *l, - const char **line, int *cont) -{ - if (!push_line(tab, l, *line, *off - zero, *cont)) - return 0; - if (!*cont) - *cont = 1; - *line += *off - zero; - *off = zero; - return 1; -} - -/* * Build a list of visual line by wrapping the given line, assuming * that when printed will have a leading prefix prfx. - * - * TODO: it considers each byte one cell on the screen! */ -void +int wrap_text(struct tab *tab, const char *prfx, struct line *l, size_t width) { - size_t zero, off, len; - int cont = 0; - const char *endword, *endspc, *line, *linestart; + const char *separators = " \t-"; + const char *start, *end, *line, *lastsep, *lastchar; + uint32_t cp = 0, state = 0; + size_t cur, prfxwidth, w; + int cont; - zero = strlen(prfx); - off = zero; - line = l->line; - linestart = l->line; + if ((line = l->line) == NULL) + return push_line(tab, l, NULL, 0, 0); - if (line == NULL) { - push_line(tab, l, NULL, 0, 0); - return; - } - - while (word_boundaries(line, " \t-", &endword, &endspc)) { - len = endword - line; - if (off + len >= width) { - emitline(tab, zero, &off, l, &linestart, &cont); - while (len >= width) { - /* hard wrap */ - emitline(tab, zero, &off, l, &linestart, &cont); - len -= width-1; - line += width-1; - } - - if (len != 0) - off += len; - } else - off += len; - - /* print the spaces iff not at bol */ - len = endspc - endword; - /* line = endspc; */ - if (off != zero) { - if (off + len >= width) { - emitline(tab, zero, &off, l, &linestart, &cont); - linestart = endspc; - } else - off += len; + prfxwidth = utf8_swidth(prfx); + cur = prfxwidth; + start = line; + lastsep = NULL; + lastchar = line; + cont = 0; + for (; *line; line++) { + if (utf8_decode(&state, &cp, *line)) + continue; + w = utf8_chwidth(cp); + if (cur + w >= width -1) { + end = lastsep == NULL + ? utf8_next_cp((char*)lastchar) + : utf8_next_cp((char*)lastsep); + if (!push_line(tab, l, start, end - start, cont)) + return 0; + cont = 1; + start = end; + cur = prfxwidth + utf8_swidth_between(start, lastchar); + } else { + if (strchr(separators, *line) != NULL) + lastsep = line; } - line = endspc; + lastchar = utf8_prev_cp(line, l->line); + cur += w; } - emitline(tab, zero, &off, l, &linestart, &cont); + return push_line(tab, l, start, line - start, cont); } int hardwrap_text(struct tab *tab, struct line *l, size_t width) { - size_t off, len; + const char *line, *start, *lastchar; int cont; - const char *linestart; + uint32_t state = 0, cp = 0; + size_t cur, w; - off = 0; - linestart = l->line; + if ((line = l->line) == NULL) + return push_line(tab, l, NULL, 0, 0); - if (l->line == NULL) - return emitline(tab, 0, &off, l, &linestart, &cont); + start = line; + lastchar = line; + cont = 0; + cur = 0; + for (; *line; line++) { + if (utf8_decode(&state, &cp, *line)) + continue; + w = utf8_chwidth(cp); + if (cur + w >= width) { + if (!push_line(tab, l, start, lastchar - start, cont)) + return 0; + cont = 1; + cur = 0; + start = lastchar; + } - len = strlen(l->line); - - while (len >= width) { - len -= width-1; - off = width-1; - if (!emitline(tab, 0, &off, l, &linestart, &cont)) - return 0; + lastchar = utf8_prev_cp(line, l->line); + cur += w; } - if (len != 0) - return emitline(tab, 0, &len, l, &linestart, &cont); - - return 1; + return push_line(tab, l, start, line - start, cont); }