commit eb259e6636dc52724fb3d729adb34f98f8ead92e from: Omar Polo date: Tue Mar 02 13:35:19 2021 UTC initial word wrapping implementation it handles only ASCII and works only in the stdscr, but it's a start! commit - 5e11c00c40910bea9deb4b2199868f6dde63798e commit + eb259e6636dc52724fb3d729adb34f98f8ead92e blob - 99baa1da940b355e5a271985a179ba5be3d50e8e blob + f713ed0e509acc7b1cfee840c59f63fb3b63d30b --- ui.c +++ ui.c @@ -20,14 +20,20 @@ #include #include #include +#include +#include #define TAB_CURRENT 0x1 +#define CTRL(x) ((x)&0x1F) + static struct event stdioev, winchev; static struct tab *current_tab(void); static void dispatch_stdio(int, short, void*); static void handle_resize(int, short, void*); +static int word_bourdaries(const char*, const char*, const char**, const char**); +static void wrap_text(const char*, const char*, const char*, const char*); static void redraw_tab(struct tab*); static struct tab * @@ -59,6 +65,12 @@ dispatch_stdio(int fd, short ev, void *d) return; } + if (c == CTRL('L')) { + clear(); + redraw_tab(current_tab()); + return; + } + printw("You typed %c\n", c); refresh(); } @@ -73,42 +85,148 @@ handle_resize(int sig, short ev, void *d) redraw_tab(current_tab()); } +/* + * 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 void +emitline(const char *prfx, size_t zero, size_t *off) +{ + printw("\n%s", prfx); + *off = zero; +} + +static inline void +emitstr(const char **s, size_t len, size_t *off) +{ + size_t i; + + /* printw("%*s", ...) doesn't seem to respect the precision, so... */ + for (i = 0; i < len; ++i) + addch((*s)[i]); + *off += len; + *s += len; +} + +/* + * Wrap the text, prefixing the first line with prfx1 and the + * following with prfx2, and breaking on characters in the breakon set. + * The idea is pretty simple: if there is enough space, write the next + * word; if we are at the start of a line and there's not enough + * space, hard-split it. + * + * TODO: it considers each byte one cell on the screen! + * TODO: assume strlen(prfx1) == strlen(prfx2) + */ static void +wrap_text(const char *prfx1, const char *prfx2, const char *line, const char *breakon) +{ + size_t zero, off, len, split; + const char *endword, *endspc; + + printw("%s", prfx1); + zero = strlen(prfx1); + off = zero; + + while (word_boundaries(line, breakon, &endword, &endspc)) { + len = endword - line; + if (off + len < COLS) { + emitstr(&line, len, &off); + } else { + emitline(prfx2, zero, &off); + while (len >= COLS) { + /* hard wrap */ + printw("%*s", COLS-1, line); + emitline(prfx2, zero, &off); + len -= COLS-1; + line += COLS-1; + } + + if (len != 0) + emitstr(&line, len, &off); + } + + /* print the spaces iff not at bol */ + len = endspc - endword; + /* line = endspc; */ + if (off != zero) { + if (off + len > COLS) + emitline(prfx2, zero, &off); + else + emitstr(&line, len, &off); + } + + line = endspc; + } + + printw("\n"); +} + +static void redraw_tab(struct tab *tab) { struct line *l; + const char *sep = " \t"; erase(); TAILQ_FOREACH(l, &tab->page.head, lines) { switch (l->type) { case LINE_TEXT: - printw("%s\n", l->line); + wrap_text("", "", l->line, sep); break; case LINE_LINK: - printw("=> %s\n", l->line); + wrap_text("=> ", " ", l->line, sep); break; case LINE_TITLE_1: - printw("# %s\n", l->line); + wrap_text("# ", " ", l->line, sep); break; case LINE_TITLE_2: - printw("## %s\n", l->line); + wrap_text("## ", " ", l->line, sep); break; case LINE_TITLE_3: - printw("### %s\n", l->line); + wrap_text("### ", " ", l->line, sep); break; case LINE_ITEM: - printw("* %s\n", l->line); + wrap_text("* ", " ", l->line, sep); break; case LINE_QUOTE: - printw("> %s\n", l->line); + wrap_text("> ", "> ", l->line, sep); break; case LINE_PRE_START: case LINE_PRE_END: printw("```\n"); break; case LINE_PRE_CONTENT: - printw("`%s\n", l->line); + printw("%s\n", l->line); break; } }