Commit Diff


commit - 5e11c00c40910bea9deb4b2199868f6dde63798e
commit + eb259e6636dc52724fb3d729adb34f98f8ead92e
blob - 99baa1da940b355e5a271985a179ba5be3d50e8e
blob + f713ed0e509acc7b1cfee840c59f63fb3b63d30b
--- ui.c
+++ ui.c
@@ -20,14 +20,20 @@
 #include <event.h>
 #include <locale.h>
 #include <signal.h>
+#include <stdlib.h>
+#include <string.h>
 
 #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;
 		}
 	}