6 std.fput(2, "Error: {}\n", e)
8 regurgitate(digest(ingest(s)), 72)
12 type paragraph = struct
13 first_line_prefix : char[:]
14 gen_line_prefix : char[:]
23 `Reading_line_last_was_ws
29 Read each line, and strip off the prefix (the whitespace, markers
30 like -/•/·/#/45:/_1_/) from the content. Each line becomes its own
31 paragraph. If the content is all whitespace, the paragraph is
34 Now collapse all paragraphs. Empty paragraphs with equivalent
35 prefixes (that is, up to whitespace) collapse together. Non-empty
36 paragraphs with equivalent prefixes collapse together. If two
37 adjacent, non-empty paragraphs A, B have non-equivalent prefixes, A
38 is preceded by an empty paragraph (or nothing), B is anteceded by an
39 empty paragraph (or nothing), AND A itself was not collapsed, then
40 merge A and B, with A governing the first_line_prefix and B the
43 Now output. That's easy, you made Ori take your stupid Unicode tables
44 so you know what the cell width is.
47 /* Turn input slop into paragraphs */
48 const ingest = { str : byte[:]
49 var s : state = `Reading_prefix
50 var p : paragraph[:] = [][:]
51 var p_cur : paragraph = [
52 .first_line_prefix = [][:],
53 .gen_line_prefix = [][:],
58 for c : std.bychar(str)
65 .first_line_prefix = [][:],
66 .gen_line_prefix = [][:],
77 if is_textual_content(c)
79 std.slpush(&p_cur.content, c)
81 std.slpush(&p_cur.first_line_prefix, c)
84 std.slpush(&p_cur.content, c)
86 s = `Reading_line_last_was_ws
88 | `Reading_line_last_was_ws:
90 std.slpush(&p_cur.content, c)
96 if p_cur.first_line_prefix.len > 0 || p_cur.content.len > 0
104 I don't typically denote lists with ", ', or (. They should really be
105 considered alphanumeric. TODO: exotic unicode should go here as well.
107 const is_textual_content = { c : char
122 /* Do the paragraph joining thing */
125 for var j = 0; j < p.len; ++j
126 p[j].empty = (p[j].content.len == 0)
130 for var j = 0; j + 1 < p.len; ++j
131 if p[j].empty == p[j + 1].empty && equiv_prefixes(p[j].first_line_prefix, p[j + 1].first_line_prefix)
133 p[j].gen_line_prefix = std.sldup(p[j + 1].first_line_prefix)
135 merge_para(&p, j, j + 1)
142 for var j = 0; j + 1 < p.len; ++j
143 if j > 0 && !p[j - 1].empty
147 if j + 2 < p.len && !p[j + 2].empty
151 if p[j].empty || p[j + 1].empty || p[j].merged
155 p[j].gen_line_prefix = std.sldup(p[j + 1].first_line_prefix)
156 merge_para(&p, j, j + 1)
159 /* The unmerged give no distinction to the first */
160 for var j = 0; j < p.len; ++j
162 p[j].gen_line_prefix = std.sldup(p[j].first_line_prefix)
166 /* Finally, strip whitespace from the end of content */
167 for var j = 0; j < p.len; ++j
168 var c = &p[j].content
169 while c#.len > 0 && std.isblank(c#[c#.len - 1])
170 std.sldel(c, c#.len - 1)
177 const regurgitate = {p, max
178 var sb : std.strbuf# = std.mksb()
182 /* maybe we can get away with dropping the prefix? */
183 var need_prefix = false
184 for c : a.first_line_prefix
195 /* Oh well, just handle it normally */
199 for c : a.first_line_prefix
201 cur_pos += std.cellwidth(c)
204 /* precalculate this */
205 var gen_prefix_len = 0
206 for c : a.gen_line_prefix
207 gen_prefix_len += std.cellwidth(c)
210 var st, sn, e, wt, wn
212 while j < a.content.len
213 (st, sn, e, wt, wn) = hypothetical_forward(a.content, j)
214 if cur_pos + wt > max && gen_prefix_len + wn <= max
216 for c : a.gen_line_prefix
219 for var k = sn; k < e; ++k
220 std.sbputc(sb, a.content[k])
222 cur_pos = gen_prefix_len + wn
224 for var k = st; k < e; ++k
225 std.sbputc(sb, a.content[k])
233 std.sbputc(sb, ('\n' : char))
236 std.writeall(1, std.sbfin(sb))
239 const equiv_prefixes = {a, b
243 while ak < a.len && std.isblank(a[ak])
247 while bk < b.len && std.isblank(b[bk])
251 if (ak < a.len) != (bk < b.len)
269 std.slfree(p.first_line_prefix)
270 std.slfree(p.gen_line_prefix)
271 std.slfree(p.content)
274 const merge_para = {p, j, k
275 if (p#[j].content.len > 0 && !std.isblank(p#[j].content[p#[j].content.len - 1]))
276 /* TODO: what if you use U+3000 instead of ' '? Huh? */
277 std.slpush(&(p#[j].content), (' ' : char))
279 std.sljoin(&(p#[j].content), p#[k].content)
285 const hypothetical_forward = {c, j
286 var start_if_this_line = j
287 var start_if_next_line = j
289 var width_if_this_line = 0
290 var width_if_next_line = 0
291 var past_first_blanks = false
295 By the normalization in ingest() we should only have
296 one blank separating non-blanks. Still, let's be damn
299 if !past_first_blanks
300 if!std.isblank(c[end])
301 past_first_blanks = true
302 start_if_next_line = end
304 width_if_this_line += std.cellwidth(c[end])
309 if std.isblank(c[end])
312 width_if_this_line += std.cellwidth(c[end])
313 width_if_next_line += std.cellwidth(c[end])
319 -> (start_if_this_line, start_if_next_line, end, width_if_this_line, width_if_next_line)