Skip to content

Commit 7ee6a64

Browse files
committed
Update #268 and #265
1 parent 663117a commit 7ee6a64

File tree

5 files changed

+322
-81
lines changed

5 files changed

+322
-81
lines changed

tablewriter.go

Lines changed: 37 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -704,8 +704,12 @@ func (t *Table) maxColumns() int {
704704
return m
705705
}
706706

707+
// printTopBottomCaption prints the table's caption at the specified top or bottom position.
708+
// It wraps the caption text to fit the table width or a user-defined width, aligns it according
709+
// to the specified alignment, and writes it to the provided writer. If the caption text is empty
710+
// or the spot is invalid, it logs the issue and returns without printing. The function handles
711+
// wrapping errors by falling back to splitting on newlines or using the original text.
707712
func (t *Table) printTopBottomCaption(w io.Writer, actualTableWidth int) {
708-
// Log the state of t.caption
709713
t.logger.Debugf("[printCaption Entry] Text=%q, Spot=%v (type %T), Align=%q, UserWidth=%d, ActualTableWidth=%d",
710714
t.caption.Text, t.caption.Spot, t.caption.Spot, t.caption.Align, t.caption.Width, actualTableWidth)
711715

@@ -716,27 +720,24 @@ func (t *Table) printTopBottomCaption(w io.Writer, actualTableWidth int) {
716720
return
717721
}
718722

719-
// Determine captionWrapWidth
720723
var captionWrapWidth int
721724
if t.caption.Width > 0 {
722725
captionWrapWidth = t.caption.Width
723726
t.logger.Debugf("[printCaption] Using user-defined caption.Width %d for wrapping.", captionWrapWidth)
724-
} else if actualTableWidth <= 4 { // Empty or minimal table
727+
} else if actualTableWidth <= 4 {
725728
captionWrapWidth = tw.DisplayWidth(t.caption.Text)
726729
t.logger.Debugf("[printCaption] Empty table, no user caption.Width: Using natural caption width %d.", captionWrapWidth)
727730
} else {
728731
captionWrapWidth = actualTableWidth
729732
t.logger.Debugf("[printCaption] Non-empty table, no user caption.Width: Using actualTableWidth %d for wrapping.", actualTableWidth)
730733
}
731734

732-
// Ensure captionWrapWidth is positive
733735
if captionWrapWidth <= 0 {
734-
captionWrapWidth = 10 // Minimum sensible width
736+
captionWrapWidth = 10
735737
t.logger.Warnf("[printCaption] captionWrapWidth was %d (<=0). Setting to minimum %d.", captionWrapWidth, 10)
736738
}
737739
t.logger.Debugf("[printCaption] Final captionWrapWidth to be used by twwarp: %d", captionWrapWidth)
738740

739-
// Wrap the caption text
740741
wrappedCaptionLines, count := twwarp.WrapString(t.caption.Text, captionWrapWidth)
741742
if count == 0 {
742743
t.logger.Errorf("[printCaption] Error from twwarp.WrapString (width %d): %v. Text: %q", captionWrapWidth, count, t.caption.Text)
@@ -759,7 +760,6 @@ func (t *Table) printTopBottomCaption(w io.Writer, actualTableWidth int) {
759760
t.logger.Debugf("[printCaption] Wrapped caption into %d lines: %v", len(wrappedCaptionLines), wrappedCaptionLines)
760761
}
761762

762-
// Determine padding target width
763763
paddingTargetWidth := actualTableWidth
764764
if t.caption.Width > 0 {
765765
paddingTargetWidth = t.caption.Width
@@ -768,7 +768,6 @@ func (t *Table) printTopBottomCaption(w io.Writer, actualTableWidth int) {
768768
}
769769
t.logger.Debugf("[printCaption] Final paddingTargetWidth for tw.Pad: %d", paddingTargetWidth)
770770

771-
// Print each wrapped line
772771
for i, line := range wrappedCaptionLines {
773772
align := t.caption.Align
774773
if align == "" || align == tw.AlignDefault || align == tw.AlignNone {
@@ -796,22 +795,11 @@ func (t *Table) printTopBottomCaption(w io.Writer, actualTableWidth int) {
796795
// Parameters include cells to process and config for formatting rules.
797796
// Returns a slice of string slices representing processed lines.
798797
func (t *Table) prepareContent(cells []string, config tw.CellConfig) [][]string {
799-
// force max width
800-
801798
isStreaming := t.config.Stream.Enable && t.hasPrinted
802799
t.logger.Debugf("prepareContent: Processing cells=%v (streaming: %v)", cells, isStreaming)
803800
initialInputCellCount := len(cells)
804801
result := make([][]string, 0)
805802

806-
// ll.Dbg(t.config.MaxWidth)
807-
// force max width
808-
if t.config.MaxWidth > 0 {
809-
// it has headers
810-
if len(cells) > 0 {
811-
config.ColMaxWidths.Global = int(math.Floor(float64(t.config.MaxWidth) / float64(len(cells))))
812-
}
813-
}
814-
815803
effectiveNumCols := initialInputCellCount
816804
if isStreaming {
817805
if t.streamNumCols > 0 {
@@ -835,6 +823,16 @@ func (t *Table) prepareContent(cells []string, config tw.CellConfig) [][]string
835823
}
836824
}
837825

826+
if t.config.MaxWidth > 0 && !t.config.Widths.Constrained() {
827+
if effectiveNumCols > 0 {
828+
derivedSectionGlobalMaxWidth := int(math.Floor(float64(t.config.MaxWidth) / float64(effectiveNumCols)))
829+
config.ColMaxWidths.Global = derivedSectionGlobalMaxWidth
830+
t.logger.Debugf("prepareContent: Table MaxWidth %d active and t.config.Widths not constrained. "+
831+
"Derived section ColMaxWidths.Global: %d for %d columns. This will be used by calculateContentMaxWidth if no higher priority constraints exist.",
832+
t.config.MaxWidth, config.ColMaxWidths.Global, effectiveNumCols)
833+
}
834+
}
835+
838836
for i := 0; i < effectiveNumCols; i++ {
839837
cellContent := ""
840838
if i < len(cells) {
@@ -886,50 +884,45 @@ func (t *Table) prepareContent(cells []string, config tw.CellConfig) [][]string
886884
case tw.WrapBreak:
887885
wrapped := make([]string, 0)
888886
currentLine := line
887+
breakCharWidth := tw.DisplayWidth(tw.CharBreak)
889888
for tw.DisplayWidth(currentLine) > effectiveContentMaxWidth {
890-
breakPoint := tw.BreakPoint(currentLine, effectiveContentMaxWidth)
891-
if breakPoint <= 0 {
892-
t.logger.Warnf("prepareContent: WrapBreak - BreakPoint <= 0 for line '%s' at width %d. Attempting manual break.", currentLine, effectiveContentMaxWidth)
893-
runes := []rune(currentLine)
889+
targetWidth := effectiveContentMaxWidth - breakCharWidth
890+
if targetWidth < 0 {
891+
targetWidth = 0
892+
}
893+
breakPoint := tw.BreakPoint(currentLine, targetWidth)
894+
runes := []rune(currentLine)
895+
if breakPoint <= 0 || breakPoint > len(runes) {
896+
t.logger.Warnf("prepareContent: WrapBreak - Invalid BreakPoint %d for line '%s' at width %d. Attempting manual break.", breakPoint, currentLine, targetWidth)
894897
actualBreakRuneCount := 0
895898
tempWidth := 0
896-
for charIdx, r := range currentLine {
899+
for charIdx, r := range runes {
897900
runeStr := string(r)
898901
rw := tw.DisplayWidth(runeStr)
899-
if tempWidth+rw > effectiveContentMaxWidth && charIdx > 0 {
902+
if tempWidth+rw > targetWidth && charIdx > 0 {
900903
break
901904
}
902905
tempWidth += rw
903906
actualBreakRuneCount = charIdx + 1
904-
if tempWidth >= effectiveContentMaxWidth && charIdx == 0 {
907+
if tempWidth >= targetWidth && charIdx == 0 {
905908
break
906909
}
907910
}
908911
if actualBreakRuneCount == 0 && len(runes) > 0 {
909912
actualBreakRuneCount = 1
910913
}
911-
912914
if actualBreakRuneCount > 0 && actualBreakRuneCount <= len(runes) {
913915
wrapped = append(wrapped, string(runes[:actualBreakRuneCount])+tw.CharBreak)
914916
currentLine = string(runes[actualBreakRuneCount:])
915917
} else {
916-
if tw.DisplayWidth(currentLine) > 0 {
917-
wrapped = append(wrapped, currentLine)
918-
currentLine = ""
919-
}
920-
break
921-
}
922-
} else {
923-
runes := []rune(currentLine)
924-
if breakPoint <= len(runes) {
925-
wrapped = append(wrapped, string(runes[:breakPoint])+tw.CharBreak)
926-
currentLine = string(runes[breakPoint:])
927-
} else {
928-
t.logger.Warnf("prepareContent: WrapBreak - BreakPoint (%d) out of bounds for line runes (%d). Adding full line.", breakPoint, len(runes))
918+
t.logger.Warnf("prepareContent: WrapBreak - Cannot break line '%s'. Adding as is.", currentLine)
929919
wrapped = append(wrapped, currentLine)
930920
currentLine = ""
931921
break
932922
}
923+
} else {
924+
wrapped = append(wrapped, string(runes[:breakPoint])+tw.CharBreak)
925+
currentLine = string(runes[breakPoint:])
933926
}
934927
}
935928
if tw.DisplayWidth(currentLine) > 0 {
@@ -964,7 +957,8 @@ func (t *Table) prepareContent(cells []string, config tw.CellConfig) [][]string
964957
if i < len(result[j]) {
965958
result[j][i] = cellLineContent
966959
} else {
967-
t.logger.Warnf("prepareContent: Column index %d out of bounds (%d) during result matrix population.", i, len(result[j]))
960+
t.logger.Warnf("prepareContent: Column index %d out of bounds (%d) during result matrix population. EffectiveNumCols: %d. This indicates a logic error.",
961+
i, len(result[j]), effectiveNumCols)
968962
}
969963
}
970964
}
@@ -1345,9 +1339,8 @@ func (t *Table) render() error {
13451339
t.logger.Debugf("No caption detected. Rendering table core directly to writer.")
13461340
}
13471341

1348-
//Render Table Core ---
1349-
t.writer = targetWriter // Set writer only when necessary
1350-
1342+
//Render Table Core
1343+
t.writer = targetWriter
13511344
ctx, mctx, err := t.prepareContexts()
13521345
if err != nil {
13531346
t.writer = originalWriter
@@ -1393,7 +1386,6 @@ func (t *Table) render() error {
13931386
if renderError {
13941387
return firstRenderErr // Return error from core rendering if any
13951388
}
1396-
//End Render Table Core ---
13971389

13981390
//Caption Handling & Final Output ---
13991391
if isTopOrBottomCaption {

tablewriter_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -510,7 +510,7 @@ func TestMergeCellConfig(t *testing.T) {
510510
expectedConfig: func() tw.CellConfig {
511511
cfg := getTestSectionDefaultConfig("header")
512512
cfg.Formatting.MergeMode = tw.MergeHorizontal
513-
cfg.Formatting.AutoFormat = tw.On // Expected 1 (no change from base or src)
513+
cfg.Formatting.AutoFormat = tw.On
514514
return cfg
515515
},
516516
},

tests/basic_test.go

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -496,7 +496,7 @@ func TestWrapping(t *testing.T) {
496496
AutoWrap: tw.WrapBreak,
497497
Alignment: tw.AlignLeft,
498498
},
499-
ColMaxWidths: tw.CellWidth{Global: 30},
499+
ColMaxWidths: tw.CellWidth{Global: 33},
500500
},
501501
Footer: tw.CellConfig{
502502
Formatting: tw.CellFormatting{
@@ -513,16 +513,16 @@ func TestWrapping(t *testing.T) {
513513
table.Render()
514514

515515
expected := `
516-
┌────┬───────────────────────────────┬───────────────────┐
517-
│ NO │ PACKAGE │ COMMENTS │
518-
├────┼───────────────────────────────┼───────────────────┤
519-
│ 1 │ https://github.com/olekukonk↩ │ routing websocket │
520-
│ │ o/ruta │ │
521-
│ 2 │ https://github.com/olekukonk↩ │ better error │
522-
│ │ o/error │ │
523-
│ 3 │ https://github.com/olekukonk↩ │ terminal │
524-
│ │ o/tablewriter │ table │
525-
└────┴───────────────────────────────┴───────────────────┘
516+
┌────┬─────────────────────────────────┬───────────────────┐
517+
│ NO │ PACKAGE │ COMMENTS │
518+
├────┼─────────────────────────────────┼───────────────────┤
519+
│ 1 │ https://github.com/olekukonko/↩ │ routing websocket │
520+
│ │ ruta │ │
521+
│ 2 │ https://github.com/olekukonko/↩ │ better error │
522+
│ │ error │ │
523+
│ 3 │ https://github.com/olekukonko/↩ │ terminal │
524+
│ │ tablewriter │ table │
525+
└────┴─────────────────────────────────┴───────────────────┘
526526
`
527527
visualCheck(t, "Wrapping", buf.String(), expected)
528528
}

0 commit comments

Comments
 (0)