fix: preserve indentation when wrapping multiline task detail text #153
No reviewers
Labels
No labels
bug
ci
documentation
duplicate
enhancement
feature
good first issue
help wanted
invalid
performance
priority
P0
priority
P1
priority
P2
question
refactor
size
L
size
M
size
S
size
XL
size
XS
tests
trivial
wontfix
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
noe.trevino/paso!153
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feature-151/task-view-multilines"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Closes: #151
Summary
Fixes #151 —
paso task showrendered multiline descriptions, comments, and relationship bullets as a "stream of text" instead of preserving their structure.Root cause
The detail card builds content with manual indents, bullets, and newlines, then hands the whole string to a fixed-
Widthlipgloss card. lipgloss preserves hard newlines but re-wraps any over-long line and drops the continuation to the card margin, discarding the manual indent. There was also an off-by: in this lipgloss versionWidthincludes the border, so the usable content width is80 − border(2) − padding(4) = 74, not 76.Fix
Wrap the text ourselves to the card's real content width with a hanging indent, so continuation lines stay aligned and lipgloss never needs to re-wrap.
styles.go: addCardContentWidth(),WrapText()(word-wrap + hard-break long words), andrenderBullet()(2-space first indent, 4-space hanging indent).RenderTaskReference/…WithLabelroute through it.task_helpers.go:WriteIndentedLines(descriptions + comments) now wraps each line to the indent-aware width.show_logic.go: dropped the now-redundant manual" "prefix on the four relationship bullet call sites.Before / after (wrapped blocker)
Testing
CardContentWidth,WrapText, the bullet hanging indent, andWriteIndentedLineswrapping.go run main.go task show 233.go build,go vet,gofmt, and the styles/task suites pass.Note: two pre-existing failures in
internal/tui/activity_view_test.goare unrelated (confirmed failing on a clean tree).The new wrapping introduces a hard-break regression for long tokens in task descriptions and comments.
WrapTextchainswordwrap.String(soft-break) thenwrap.String(hard-break at limit). When a task description contains a URL longer thanCardContentWidth() - len(indent)(~72 chars),wrap.Stringcuts it mid-string, producing unclickable fragments. The old implementation passed lines through to lipgloss intact; lipgloss would reflow at the card margin (losing indentation) but never cut inside a token. Consider preserving whole lines that contain no whitespace (e.g., URLs that are a single token), or at minimum document that hard-breaks are expected.@ -0,0 +1,46 @@package tasklen(l)counts bytes;CardContentWidth()is a visual terminal column count.muesli/reflowwraps by visual width (viago-runewidth), not bytes. For any multi-byte Unicode character in a task title (accented Latin = 2 bytes/1 col, CJK = 3 bytes/1-2 cols), a visually-correct line can havelen(l) > CardContentWidth(), causing this assertion to fire spuriously. Userunewidth.StringWidth(l)or strip to ASCII before comparing, consistent with how the library itself measures width.@ -0,0 +1,46 @@package taskinternal/testing/fixtures/cli.goalready exportsfixtures.StripANSI(same regex, same logic), and it is imported by 17+ other test files in this repo. Thestylestest package has its own copy as well (success_test.go). This new copy makes three. Use the existing shared helper to keep the regex in one place.Same byte-vs-visual-width mismatch as in
task_helpers_test.go:len(l)is bytes,CardContentWidth()is terminal columns. Themuesli/reflowwrapping library usesgo-runewidthfor visual measurement. A task title with multi-byte Unicode characters will produce lines wherelen(l) > CardContentWidth()while the visual rendering is correct, causing this assertion to spuriously fail.Review
This PR fixes the multiline text wrapping regression in
paso task showby implementing manual word-wrap with hanging indent before lipgloss renders the card border, and simultaneously renames theticket_numberfield totask_numberthroughout the model, DB, and CLI layers.Issues found
1. Hard-break regression for long tokens in descriptions/comments (
task_helpers.goline 43)The new
WriteIndentedLinescallsWrapText, which chainswordwrap.String(soft-break) thenwrap.String(hard-break at limit). Any long single token in a task description—a URL, a file path, a hash—that exceeds ~72 characters will be cut mid-string. The old code did not hard-break: it passed lines through to lipgloss, which reflowed at the card margin (losing indentation) but never split inside a token. This is a new UX regression for any user whose descriptions contain long URLs.2.
len(l)(bytes) compared againstCardContentWidth()(terminal columns) in tests (styles_test.go~line 168,task_helpers_test.goline 46)muesli/reflowwraps by visual cell width viago-runewidth. For multi-byte Unicode characters (accented Latin = 2 bytes/1 col, CJK = 3 bytes/1-2 cols), a visually-correct wrapped line will havelen(l) > CardContentWidth()and the assertion fires spuriously. The tests pass today only because the fixture uses ASCII-only input.3.
stripANSIduplicated when a shared helper already exists (task_helpers_test.goline 14)internal/testing/fixtures/cli.goexportsStripANSI(identical regex, same logic) and is already imported by 17+ test files in this repo. The newtask_helpers_test.goadds a third independent copy (thestylespackage has its own as well). Usefixtures.StripANSI.Score: 7/10