[4293] in BarnOwl Developers
[barnowl/barnowl] Wrap over-wide message lines in the default style
daemon@ATHENA.MIT.EDU (Daniel JB Clark)
Mon Nov 2 17:50:06 2026
Date: Mon, 02 Nov 2026 14:49:59 -0800
From: Daniel JB Clark <notifications@github.com>
Reply-To: barnowl/barnowl <reply+ABCERSJY6OM4RMIK3LGZN3TLMJOZPEVBNHHQAAAAAFX6Q5EM@reply.github.com>
To: barnowl/barnowl <barnowl@noreply.github.com>
Cc: Subscribed <subscribed@noreply.github.com>
----==_mimepart_6ae91397e0647_10e12d0333062
Content-Type: text/plain;
charset=UTF-8
Content-Transfer-Encoding: 7bit
**What broke.** BarnOwl draws a message line wider than the screen cut off at the right edge, so the end of a long line cannot be read (the arrow keys scroll sideways). `owl_text_wordwrap`, which the `wordwrap` filter uses, wraps every line, so it rewraps text the sender already wrapped, and it neither knows about ztext markup nor keeps URLs whole.
**What changed.** A new `wrap_long_lines` variable, **off by default**, so nobody's display changes unless they ask. When it is on, the default style (and any style that inherits its `indent_body`) wraps only the lines that are too wide, at spaces, and leaves every other line exactly as sent. The new `BarnOwl::Style::Reflow`:
1. never splits ztext markup or a URL (a URL that does not fit on a line of its own overflows, as it does now);
2. cuts a word wider than a whole line at the edge, and counts columns, not characters (CJK = 2, combining marks = 0, control characters as `humanize` draws them);
3. hangs list items under their text and repeats quote marks (`> `, ` | `), except that a `>` that would close a `<` span of the sender's markup, or end an unclosed `@color<`, is left out of the continuation (it would change what is drawn);
4. leaves lines with tabs alone, since tabs are expanded later;
5. follows `owl_fmtext_append_ztext` for the column count: 32 open spans at most, an `@color(` that nothing closes is dropped, `@@` is one column;
6. takes time linear in the length of the line (a sender chooses it), and leaves a message of more than 64 KB as sent, because it uses about 500 bytes of memory per character; the `wordwrap` filter then wraps such a message as before.
With the variable on, messages that the `wordwrap` filter matches go through the same code instead of `owl_text_wordwrap` (documented in the variable's help). With it off the only change is the new variable.
**How tested.** New `t/reflow.t` (about 130 checks): the wrapping rules above, expected strings worked out by hand from `fmtext.c`, hostile bodies (unclosed markup, 500 nested spans, bare `@`, `@color` without a closer, bidi and zero-width characters, control characters, 20,000-character words) with colorztext on and off, the variable and the style, the 64 KB limit, and the time taken by 200 KB lines. The time limit is 30 s, far above the measured time, so that a slow builder does not fail; a quadratic loop needs minutes at that size. `make check` passes on GitHub Actions (Debian trixie): https://github.com/djbclark/barnowl/actions/runs/41535970756. The new tests fail on the earlier code. The wrapping was also compared with a Perl port of `owl_fmtext_append_ztext` on 20,000 random inputs: no case where the wrapped text draws different characters or attributes, except lines whose colour name runs across a newline (a quirk of the C parser, not modelled).
You can view, comment on, or merge this pull request online at:
https://github.com/barnowl/barnowl/pull/194
-- Commit Summary --
* Wrap over-wide message lines in the default style (wrap_long_lines)
-- File Changes --
M perl/Makefile.am (1)
M perl/lib/BarnOwl/Style.pm (1)
M perl/lib/BarnOwl/Style/Default.pm (8)
A perl/lib/BarnOwl/Style/Reflow.pm (313)
A t/reflow.t (205)
-- Patch Links --
https://github.com/barnowl/barnowl/pull/194.patch
https://github.com/barnowl/barnowl/pull/194.diff
--
Reply to this email directly or view it on GitHub:
https://github.com/barnowl/barnowl/pull/194
You are receiving this because you are subscribed to this thread.
Message ID: <barnowl/barnowl/pull/194@github.com>
----==_mimepart_6ae91397e0647_10e12d0333062
Content-Type: text/html;
charset=UTF-8
Content-Transfer-Encoding: quoted-printable
<p dir=3D"auto"><strong>What broke.</strong> BarnOwl draws a message line=
wider than the screen cut off at the right edge, so the end of a long li=
ne cannot be read (the arrow keys scroll sideways). <code class=3D"notran=
slate">owl_text_wordwrap</code>, which the <code class=3D"notranslate">wo=
rdwrap</code> filter uses, wraps every line, so it rewraps text the sende=
r already wrapped, and it neither knows about ztext markup nor keeps URLs=
whole.</p>
<p dir=3D"auto"><strong>What changed.</strong> A new <code class=3D"notra=
nslate">wrap_long_lines</code> variable, <strong>off by default</strong>,=
so nobody's display changes unless they ask. When it is on, the default =
style (and any style that inherits its <code class=3D"notranslate">indent=
_body</code>) wraps only the lines that are too wide, at spaces, and leav=
es every other line exactly as sent. The new <code class=3D"notranslate">=
BarnOwl::Style::Reflow</code>:</p>
<ol dir=3D"auto">
<li>never splits ztext markup or a URL (a URL that does not fit on a line=
of its own overflows, as it does now);</li>
<li>cuts a word wider than a whole line at the edge, and counts columns, =
not characters (CJK =3D 2, combining marks =3D 0, control characters as <=
code class=3D"notranslate">humanize</code> draws them);</li>
<li>hangs list items under their text and repeats quote marks (<code clas=
s=3D"notranslate">> </code>, <code class=3D"notranslate"> |</code>), e=
xcept that a <code class=3D"notranslate">></code> that would close a <=
code class=3D"notranslate"><</code> span of the sender's markup, or en=
d an unclosed <code class=3D"notranslate">@color<</code>, is left out =
of the continuation (it would change what is drawn);</li>
<li>leaves lines with tabs alone, since tabs are expanded later;</li>
<li>follows <code class=3D"notranslate">owl_fmtext_append_ztext</code> fo=
r the column count: 32 open spans at most, an <code class=3D"notranslate"=
>@color(</code> that nothing closes is dropped, <code class=3D"notranslat=
e">@@</code> is one column;</li>
<li>takes time linear in the length of the line (a sender chooses it), an=
d leaves a message of more than 64 KB as sent, because it uses about 500 =
bytes of memory per character; the <code class=3D"notranslate">wordwrap</=
code> filter then wraps such a message as before.</li>
</ol>
<p dir=3D"auto">With the variable on, messages that the <code class=3D"no=
translate">wordwrap</code> filter matches go through the same code instea=
d of <code class=3D"notranslate">owl_text_wordwrap</code> (documented in =
the variable's help). With it off the only change is the new variable.</p=
>
<p dir=3D"auto"><strong>How tested.</strong> New <code class=3D"notransla=
te">t/reflow.t</code> (about 130 checks): the wrapping rules above, expec=
ted strings worked out by hand from <code class=3D"notranslate">fmtext.c<=
/code>, hostile bodies (unclosed markup, 500 nested spans, bare <code cla=
ss=3D"notranslate">@</code>, <code class=3D"notranslate">@color</code> wi=
thout a closer, bidi and zero-width characters, control characters, 20,00=
0-character words) with colorztext on and off, the variable and the style=
, the 64 KB limit, and the time taken by 200 KB lines. The time limit is =
30 s, far above the measured time, so that a slow builder does not fail; =
a quadratic loop needs minutes at that size. <code class=3D"notranslate">=
make check</code> passes on GitHub Actions (Debian trixie): <a href=3D"ht=
tps://github.com/djbclark/barnowl/actions/runs/41535970756">https://githu=
b.com/djbclark/barnowl/actions/runs/41535970756</a>. The new tests fail o=
n the earlier code. The wrapping was also compared with a Perl port of <c=
ode class=3D"notranslate">owl_fmtext_append_ztext</code> on 20,000 random=
inputs: no case where the wrapped text draws different characters or att=
ributes, except lines whose colour name runs across a newline (a quirk of=
the C parser, not modelled).</p>
<hr>
<h4>You can view, comment on, or merge this pull request online at:</h4>
<p> <a href=3D'https://github.com/barnowl/barnowl/pull/194'>ht=
tps://github.com/barnowl/barnowl/pull/194</a></p>
<h4>Commit Summary</h4>
<ul>
<li><a href=3D"https://github.com/barnowl/barnowl/pull/194/commits/c493=
3392c8204926aeca82e4fac6d6d440b8df19" class=3D"commit-link">c493339</a> =
Wrap over-wide message lines in the default style (wrap_long_lines)</li>
</ul>
<h4 style=3D"display: inline-block">File Changes </h4> <p style=3D"displa=
y: inline-block">(<a href=3D"https://github.com/barnowl/barnowl/pull/194/=
files">5 files</a>)</p>
<ul>
<li>
<strong>M</strong>
<a href=3D"https://github.com/barnowl/barnowl/pull/194/files#diff-129=
e993b11f70dc989bf38a9d1bc8c5a9468edf4ce4b0cf3823dde9328b994ae">perl/Makef=
ile.am</a>
(1)
</li>
<li>
<strong>M</strong>
<a href=3D"https://github.com/barnowl/barnowl/pull/194/files#diff-6b8=
6cb913839d79bd7643e24202678be8922eb83ad0c8a83998a6f451a232c3c">perl/lib/B=
arnOwl/Style.pm</a>
(1)
</li>
<li>
<strong>M</strong>
<a href=3D"https://github.com/barnowl/barnowl/pull/194/files#diff-53a=
aefd5b60dc926b6ea112e29812e602750c8528602516818c98c23ac979853">perl/lib/B=
arnOwl/Style/Default.pm</a>
(8)
</li>
<li>
<strong>A</strong>
<a href=3D"https://github.com/barnowl/barnowl/pull/194/files#diff-e4d=
d02f5662f090187695bfcad339a74b2a86780bd5900191e19bd1047baad1b">perl/lib/B=
arnOwl/Style/Reflow.pm</a>
(313)
</li>
<li>
<strong>A</strong>
<a href=3D"https://github.com/barnowl/barnowl/pull/194/files#diff-25c=
7c357df76b2acab95fb2cf11fc4daf7c191535df7caf93b30e704e830c95f">t/reflow.t=
</a>
(205)
</li>
</ul>
<h4>Patch Links:</h4>
<ul>
<li><a href=3D'https://github.com/barnowl/barnowl/pull/194.patch'>https=
://github.com/barnowl/barnowl/pull/194.patch</a></li>
<li><a href=3D'https://github.com/barnowl/barnowl/pull/194.diff'>https:=
//github.com/barnowl/barnowl/pull/194.diff</a></li>
</ul>
<p style=3D"font-size:small;-webkit-text-size-adjust:none;color:#666;">&m=
dash;<br />Reply to this email directly, <a href=3D"https://github.com/ba=
rnowl/barnowl/pull/194?email_source=3Dnotifications&email_token=3DABC=
ERSN4A2VF2UIHGB5PS635XEFRPA5CNFSNUABEM5UWIORPF5TWS5BNNB2WEL2QOVWGYUTFOF2W=
K43UF42TCMRYGY4DGMBQGGTHEZLBONXW5KTTOVRHGY3SNFRGKZFFMV3GK3TUVRTG633UMVZF6=
Y3MNFRWW">view it on GitHub</a>, or <a href=3D"https://github.com/notific=
ations/unsubscribe-auth/ABCERSNQH53MYQYGOXB47OL5XEFRPAVCNFSNUABCKJSXA33TN=
F2G64TZHM3TINZTGA4TWSLTON2WKOZWGE3TENBXGI2DMMFBOYBA">unsubscribe</a>.<br =
/>Triage notifications, keep track of coding agent tasks and review pull =
requests on the go with GitHub Mobile for <a href=3D"https://github.com/n=
otifications/mobile/ios/ABCERSI3XVKCNFCIWMCBU3T5XEFRPA5CNFSNUABEM5UWIORPF=
5TWS5BNNB2WEL2QOVWGYUTFOF2WK43UF42TCMRYGY4DGMBQGGTHEZLBONXW5KTTOVRHGY3SNF=
RGKZFFMV3GK3TUVJTG633UMVZF62LPOM">iOS</a> and <a href=3D"https://github.c=
om/notifications/mobile/android/ABCERSNW5CWX5DDJ7SOTDF35XEFRPA5CNFSNUABEM=
5UWIORPF5TWS5BNNB2WEL2QOVWGYUTFOF2WK43UF42TCMRYGY4DGMBQGGTHEZLBONXW5KTTOV=
RHGY3SNFRGKZFFMV3GK3TUVZTG633UMVZF6YLOMRZG62LE">Android</a>. Download it =
today!
<br />You are receiving this because you are subscribed to this thread.<i=
mg src=3D"https://github.com/notifications/beacon/ABCERSO2TMLWC53V6XO5URT=
5XEFRPBFCNFSM6AAAAADEC4765OWGG33NNVSW45C7OR4XAZNFJFZXG5LFVJRW63LNMVXHIX3J=
MTHQAAAAAFX6Q5EMUZZGKYLTN5XKU43VMJZWG4TJMJSWI.gif" height=3D"1" width=3D"=
1" alt=3D"" /><span style=3D"color: transparent; font-size: 0; display: n=
one; visibility: hidden; overflow: hidden; opacity: 0; width: 0; height: =
0; max-width: 0; max-height: 0; mso-hide: all">Message ID: <span><barn=
owl/barnowl/pull/194</span><span>@</span><span>github</span><span>.</span=
><span>com></span></span></p>
<script type=3D"application/ld+json">[
{
"@context": "http://schema.org",
"@type": "EmailMessage",
"potentialAction": {
"@type": "ViewAction",
"target": "https://github.com/barnowl/barnowl/pull/194?email_source=3Dnot=
ifications\u0026email_token=3DABCERSMPMNUNGGM6VATKG735XEFRPA5CNFSNUABEM5U=
WIORPF5TWS5BNNB2WEL2QOVWGYUTFOF2WK43UF42TCMRYGY4DGMBQGGTHEZLBONXW5KTTOVRH=
GY3SNFRGKZFFMV3GK3TUVNTW2YLJNRPWG3DJMNVQ",
"url": "https://github.com/barnowl/barnowl/pull/194?email_source=3Dnotifi=
cations\u0026email_token=3DABCERSMPMNUNGGM6VATKG735XEFRPA5CNFSNUABEM5UWIO=
RPF5TWS5BNNB2WEL2QOVWGYUTFOF2WK43UF42TCMRYGY4DGMBQGGTHEZLBONXW5KTTOVRHGY3=
SNFRGKZFFMV3GK3TUVNTW2YLJNRPWG3DJMNVQ",
"name": "View Pull Request"
},
"description": "View this Pull Request on GitHub",
"publisher": {
"@type": "Organization",
"name": "GitHub",
"url": "https://github.com"
}
}
]</script>
----==_mimepart_6ae91397e0647_10e12d0333062--