Skip to content

fix(output): print tab and line-break titles on one line in listings - #318

Merged
ryanlewis merged 2 commits into
mainfrom
fix/title-control-chars
Oct 1, 2026
Merged

ryanlewis merged 2 commits into
mainfrom
fix/title-control-chars

Conversation

@ryanlewis

Copy link
Copy Markdown
Owner

A task title, tag, project, area or group header that holds a line break used to split its listing row in two. One that holds a tab wrapped onto a second line, because a tab measures as no columns but lipgloss pads it out to four. This is the last step of the internal/output consolidation (#314, #316, #317).

Change

  • A new oneLine helper turns each line break into a space: \r\n, \r, \n, vertical tab, form feed, NEL, and the Unicode line and paragraph separators. It does the same for each tab. It runs on the raw text before styling, because lipgloss pads a multi-line styled string to its widest line and expands tabs.
  • It applies to task titles, group headers, tags (full and short forms), project rows, area rows, tag rows and their shortcuts, and the candidates listed for an ambiguous task reference.
  • I chose a space so the words stay apart, there is still one greppable line per task, and it matches how the agent brief already flattens titles.
  • --json, the detail block and the agent brief are unchanged. things show keeps a title's own line breaks, and hang indents them under the value.
  • Wide-rune cut: no code change. Half a wide rune can't print, so a cut can stop one cell short, and the column padding fills it. The next column already lines up. A new golden case pins that with two cut CJK rows and their dates.
  • Docs.
    • commands.md now says piped output never cuts or shortens a cell, but when any row is wider than 120 columns, whole columns go from every row. It also explains the one-line rule.
    • agents.md said piped output loses its column alignment, which it doesn't. It now says the same as commands.md.
    • SKILL.md gets the one-line rule for agents.

Golden file changes

  • 126 of the 130 existing cases are byte-identical.
  • The 4 print/tasks-KNOWN-BUG-tab-newline-titles cases (2 widths × 2 colours) are renamed to print/tasks-tab-newline-titles and now show one line per task.
  • New cases, each in both colour modes:
    • print/tasks-more-control-chars@40 and @piped: CR and CRLF, a dim title with a newline, a tag with a tab, and a header with a newline.
    • print/collections-control-chars@piped: projects, areas and tags.
    • print/tasks-wide-runes-dated@60 and @40.

Checks

  • I built things from main (fcf2779) and from this branch and compared them on an unedited copy of a Things backup, which has no control characters, with HOME pointed at a temporary directory. 19 commands, including show --agent, ran piped and in a pseudo-terminal at 40, 62, 80, 100, 120 and 200 columns, with colour off and on. All 266 pairs were byte-identical. A run of main against itself also gave 0 differences.
  • On an edited copy of the backup holding tab, LF, CRLF and CR titles:
    • The today view prints one line per task, piped and on 60- and 40-column pseudo-terminals: 29 task lines for 29 JSON tasks.
    • --json keeps the raw titles.
    • Two cut CJK titles put due: in the same column at 60. At 40 and 50 the date column is dropped, as before.
  • New tests: TestOneLine, and TestResolveTaskAmbiguousTitleOnOneLine in cmd.
  • make test and make lint pass.

Not changed

  • When piped, one very wide row still drops the tags and dates from every row. Joining a multi-line title can make a row wide enough for that. That's the existing drop policy, and the docs now describe it.
  • A trailing line break becomes a trailing space.

A title, tag, project, area or group header holding a line break split
its listing row in two, and one holding a tab, which measures as no
columns but lipgloss pads out to four, wrapped onto a second line. Each
line break (\r\n, \r or \n) and tab in that text now prints as a space,
on a terminal and piped, before any styling. The detail block and
--json keep the text as written.

The cut wide-rune title needs no change: half a wide rune cannot print,
so a cut can stop a cell short, and the column padding fills it; the
next column lines up. A golden case with two cut CJK rows and their
dates now pins that.

The docs said piped output keeps tags and dates whole; a row over 120
columns still drops whole columns, and commands.md now says so.
agents.md said piped output loses its column alignment, which it does
not.
… line

From review: oneLine also turns a vertical tab, form feed, NEL and the
Unicode line and paragraph separators into a space, since lipgloss and
terminals break lines on them too. The candidates listed for an
ambiguous task reference go through the same helper. The docs now say a
column over 120 goes from every piped row, not only the wide one, and
that show keeps a title's own line breaks rather than every byte.
@ryanlewis
ryanlewis merged commit 1cfe720 into main Oct 1, 2026
10 checks passed
@ryanlewis
ryanlewis deleted the fix/title-control-chars branch October 1, 2026 01:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant