diff --git a/docs/cli-reference.md b/docs/cli-reference.md index 10fd0d25..9d5fcaef 100644 --- a/docs/cli-reference.md +++ b/docs/cli-reference.md @@ -243,8 +243,9 @@ fetch --discard --timing example.com # Measure timing only Control response formatting. Values: `auto`, `on`, `off`. Markdown rendered on a terminal uses clickable OSC 8 hyperlinks when the -terminal supports them. Non-terminal output does not contain OSC 8 hyperlink -escapes. +terminal supports them. Terminal prose wraps to the detected terminal width, +capped at 100 columns by default. Non-terminal output does not contain OSC 8 +hyperlink escapes. ```sh fetch --format off example.com # Disable formatting diff --git a/docs/output-formatting.md b/docs/output-formatting.md index c3e62871..1fd68742 100644 --- a/docs/output-formatting.md +++ b/docs/output-formatting.md @@ -173,7 +173,8 @@ Features: - Blockquote and list marker highlighting; TTY blockquotes use a muted rule and muted text - Bold blue table headers with dimmed table borders and separators -- TTY prose wraps to the detected terminal width using display-cell widths; +- TTY prose wraps to the detected terminal width, capped at 100 columns by + default, using display-cell widths; wide tables use a vertical record layout - On TTYs, links and image alt text are rendered as clickable OSC 8 terminal hyperlinks; non-terminal output does not contain OSC 8 hyperlink escapes diff --git a/internal/format/markdown.go b/internal/format/markdown.go index 8b24dbe4..f59dddad 100644 --- a/internal/format/markdown.go +++ b/internal/format/markdown.go @@ -18,13 +18,17 @@ import ( "github.com/yuin/goldmark/util" ) -const maxMarkdownBlockquoteOpeners = 256 +const ( + maxMarkdownBlockquoteOpeners = 256 + defaultMarkdownMaxWidth = 100 +) // MarkdownOptions controls terminal-only Markdown presentation. // // MaxWidth is a maximum display width in terminal columns. A zero value uses -// the current terminal width when the destination is a TTY. Non-terminal -// output remains raw Markdown regardless of this value. +// the smaller of the current terminal width and 100 columns when the +// destination is a TTY. Non-terminal output remains raw Markdown regardless +// of this value. type MarkdownOptions struct { MaxWidth int } @@ -62,11 +66,7 @@ func FormatMarkdownWithOptions(buf []byte, p *core.Printer, options MarkdownOpti width := 0 if p.IsTerminal() { - width = options.MaxWidth - if terminalWidth := core.GetTerminalCols(); terminalWidth > 0 && - (width <= 0 || terminalWidth < width) { - width = terminalWidth - } + width = markdownWidth(options.MaxWidth, core.GetTerminalCols()) } r := &mdRenderer{printer: p, source: rest, width: width} @@ -74,6 +74,16 @@ func FormatMarkdownWithOptions(buf []byte, p *core.Printer, options MarkdownOpti return ast.Walk(doc, r.walk) } +func markdownWidth(maxWidth, terminalWidth int) int { + if maxWidth <= 0 { + maxWidth = defaultMarkdownMaxWidth + } + if terminalWidth > 0 && terminalWidth < maxWidth { + return terminalWidth + } + return maxWidth +} + // truncateMarkdownBlockquoteOpeners caps line-leading blockquote opener runs. // Goldmark's container handling becomes quadratic at extreme nesting depths, so // discard openers beyond the largest depth that is useful for terminal output. diff --git a/internal/format/markdown_test.go b/internal/format/markdown_test.go index 3a454b1a..928927d0 100644 --- a/internal/format/markdown_test.go +++ b/internal/format/markdown_test.go @@ -768,6 +768,29 @@ func TestFormatMarkdownWrapsTerminalProseToDisplayWidth(t *testing.T) { } } +func TestMarkdownWidthDefaultsToTerminalWidthOr100(t *testing.T) { + tests := []struct { + name string + maxWidth int + terminalWidth int + want int + }{ + {name: "narrow terminal", terminalWidth: 80, want: 80}, + {name: "wide terminal", terminalWidth: 120, want: 100}, + {name: "terminal unavailable", want: 100}, + {name: "explicit width", maxWidth: 40, terminalWidth: 120, want: 40}, + {name: "explicit width capped by terminal", maxWidth: 120, terminalWidth: 80, want: 80}, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := markdownWidth(tt.maxWidth, tt.terminalWidth); got != tt.want { + t.Fatalf("markdownWidth(%d, %d) = %d, want %d", tt.maxWidth, tt.terminalWidth, got, tt.want) + } + }) + } +} + func TestFormatMarkdownWrapsStyledLinkByVisibleWidth(t *testing.T) { p := core.TestTerminalPrinter(false) input := "[one two three four five](https://example.com/docs)"