Blob


1 /*
2 * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
3 *
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
17 #include "telescope.h"
19 #include <stdlib.h>
20 #include <string.h>
22 /*
23 * Text wrapping
24 * =============
25 *
26 * There's a simple text wrapping algorithm.
27 *
28 * 1. if it's a line in a pre-formatted block:
29 * a. hard wrap.
30 * b. repeat
31 * 2. there is enough room for the next word?
32 * a. yes: render it
33 * b. no: break the current line.
34 * i. while there isn't enough space to draw the current
35 * word, hard-wrap it
36 * ii. draw the remainder of the current word (can be the
37 * the entirely)
38 * 3. render the spaces after the word
39 * a. but if there is not enough room break the line and
40 * forget them
41 * 4. repeat
42 *
43 */
45 static int
46 push_line(struct tab *tab, const struct line *l, const char *buf, size_t len, int cont)
47 {
48 struct vline *vl;
50 tab->s.line_max++;
52 if ((vl = calloc(1, sizeof(*vl))) == NULL)
53 return 0;
55 if (len != 0 && (vl->line = calloc(1, len+1)) == NULL) {
56 free(vl);
57 return 0;
58 }
60 vl->parent = l;
61 if (len != 0)
62 memcpy(vl->line, buf, len);
63 vl->flags = cont;
65 if (TAILQ_EMPTY(&tab->s.head))
66 TAILQ_INSERT_HEAD(&tab->s.head, vl, vlines);
67 else
68 TAILQ_INSERT_TAIL(&tab->s.head, vl, vlines);
69 return 1;
70 }
72 /*
73 * Helper function for wrap_text. Find the end of the current word
74 * and the end of the separator after the word.
75 */
76 static int
77 word_boundaries(const char *s, const char *sep, const char **endword, const char **endspc)
78 {
79 *endword = s;
80 *endword = s;
82 if (*s == '\0')
83 return 0;
85 /* find the end of the current world */
86 for (; *s != '\0'; ++s) {
87 if (strchr(sep, *s) != NULL)
88 break;
89 }
91 *endword = s;
93 /* find the end of the separator */
94 for (; *s != '\0'; ++s) {
95 if (strchr(sep, *s) == NULL)
96 break;
97 }
99 *endspc = s;
101 return 1;
104 static inline int
105 emitline(struct tab *tab, size_t zero, size_t *off, const struct line *l,
106 const char **line, int *cont)
108 if (!push_line(tab, l, *line, *off - zero, *cont))
109 return 0;
110 if (!*cont)
111 *cont = 1;
112 *line += *off - zero;
113 *off = zero;
114 return 1;
117 /*
118 * Build a list of visual line by wrapping the given line, assuming
119 * that when printed will have a leading prefix prfx.
121 * TODO: it considers each byte one cell on the screen!
122 */
123 void
124 wrap_text(struct tab *tab, const char *prfx, struct line *l, size_t width)
126 size_t zero, off, len, split;
127 int cont = 0;
128 const char *endword, *endspc, *line, *linestart;
130 zero = strlen(prfx);
131 off = zero;
132 line = l->line;
133 linestart = l->line;
135 if (line == NULL) {
136 push_line(tab, l, NULL, 0, 0);
137 return;
140 while (word_boundaries(line, " \t-", &endword, &endspc)) {
141 len = endword - line;
142 if (off + len >= width) {
143 emitline(tab, zero, &off, l, &linestart, &cont);
144 while (len >= width) {
145 /* hard wrap */
146 emitline(tab, zero, &off, l, &linestart, &cont);
147 len -= width-1;
148 line += width-1;
151 if (len != 0)
152 off += len;
153 } else
154 off += len;
156 /* print the spaces iff not at bol */
157 len = endspc - endword;
158 /* line = endspc; */
159 if (off != zero) {
160 if (off + len >= width) {
161 emitline(tab, zero, &off, l, &linestart, &cont);
162 linestart = endspc;
163 } else
164 off += len;
167 line = endspc;
170 emitline(tab, zero, &off, l, &linestart, &cont);
173 int
174 hardwrap_text(struct tab *tab, struct line *l, size_t width)
176 size_t off, len;
177 int cont;
178 const char *linestart;
180 off = 0;
181 linestart = l->line;
183 if (l->line == NULL)
184 return emitline(tab, 0, &off, l, &linestart, &cont);
186 len = strlen(l->line);
188 while (len >= width) {
189 len -= width-1;
190 off = width-1;
191 if (!emitline(tab, 0, &off, l, &linestart, &cont))
192 return 0;
195 if (len != 0)
196 return emitline(tab, 0, &len, l, &linestart, &cont);
198 return 1;