@@ -704,8 +704,12 @@ func (t *Table) maxColumns() int {
704
704
return m
705
705
}
706
706
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.
707
712
func (t * Table ) printTopBottomCaption (w io.Writer , actualTableWidth int ) {
708
- // Log the state of t.caption
709
713
t .logger .Debugf ("[printCaption Entry] Text=%q, Spot=%v (type %T), Align=%q, UserWidth=%d, ActualTableWidth=%d" ,
710
714
t .caption .Text , t .caption .Spot , t .caption .Spot , t .caption .Align , t .caption .Width , actualTableWidth )
711
715
@@ -716,27 +720,24 @@ func (t *Table) printTopBottomCaption(w io.Writer, actualTableWidth int) {
716
720
return
717
721
}
718
722
719
- // Determine captionWrapWidth
720
723
var captionWrapWidth int
721
724
if t .caption .Width > 0 {
722
725
captionWrapWidth = t .caption .Width
723
726
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 {
725
728
captionWrapWidth = tw .DisplayWidth (t .caption .Text )
726
729
t .logger .Debugf ("[printCaption] Empty table, no user caption.Width: Using natural caption width %d." , captionWrapWidth )
727
730
} else {
728
731
captionWrapWidth = actualTableWidth
729
732
t .logger .Debugf ("[printCaption] Non-empty table, no user caption.Width: Using actualTableWidth %d for wrapping." , actualTableWidth )
730
733
}
731
734
732
- // Ensure captionWrapWidth is positive
733
735
if captionWrapWidth <= 0 {
734
- captionWrapWidth = 10 // Minimum sensible width
736
+ captionWrapWidth = 10
735
737
t .logger .Warnf ("[printCaption] captionWrapWidth was %d (<=0). Setting to minimum %d." , captionWrapWidth , 10 )
736
738
}
737
739
t .logger .Debugf ("[printCaption] Final captionWrapWidth to be used by twwarp: %d" , captionWrapWidth )
738
740
739
- // Wrap the caption text
740
741
wrappedCaptionLines , count := twwarp .WrapString (t .caption .Text , captionWrapWidth )
741
742
if count == 0 {
742
743
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) {
759
760
t .logger .Debugf ("[printCaption] Wrapped caption into %d lines: %v" , len (wrappedCaptionLines ), wrappedCaptionLines )
760
761
}
761
762
762
- // Determine padding target width
763
763
paddingTargetWidth := actualTableWidth
764
764
if t .caption .Width > 0 {
765
765
paddingTargetWidth = t .caption .Width
@@ -768,7 +768,6 @@ func (t *Table) printTopBottomCaption(w io.Writer, actualTableWidth int) {
768
768
}
769
769
t .logger .Debugf ("[printCaption] Final paddingTargetWidth for tw.Pad: %d" , paddingTargetWidth )
770
770
771
- // Print each wrapped line
772
771
for i , line := range wrappedCaptionLines {
773
772
align := t .caption .Align
774
773
if align == "" || align == tw .AlignDefault || align == tw .AlignNone {
@@ -796,22 +795,11 @@ func (t *Table) printTopBottomCaption(w io.Writer, actualTableWidth int) {
796
795
// Parameters include cells to process and config for formatting rules.
797
796
// Returns a slice of string slices representing processed lines.
798
797
func (t * Table ) prepareContent (cells []string , config tw.CellConfig ) [][]string {
799
- // force max width
800
-
801
798
isStreaming := t .config .Stream .Enable && t .hasPrinted
802
799
t .logger .Debugf ("prepareContent: Processing cells=%v (streaming: %v)" , cells , isStreaming )
803
800
initialInputCellCount := len (cells )
804
801
result := make ([][]string , 0 )
805
802
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
-
815
803
effectiveNumCols := initialInputCellCount
816
804
if isStreaming {
817
805
if t .streamNumCols > 0 {
@@ -835,6 +823,16 @@ func (t *Table) prepareContent(cells []string, config tw.CellConfig) [][]string
835
823
}
836
824
}
837
825
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
+
838
836
for i := 0 ; i < effectiveNumCols ; i ++ {
839
837
cellContent := ""
840
838
if i < len (cells ) {
@@ -886,50 +884,45 @@ func (t *Table) prepareContent(cells []string, config tw.CellConfig) [][]string
886
884
case tw .WrapBreak :
887
885
wrapped := make ([]string , 0 )
888
886
currentLine := line
887
+ breakCharWidth := tw .DisplayWidth (tw .CharBreak )
889
888
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 )
894
897
actualBreakRuneCount := 0
895
898
tempWidth := 0
896
- for charIdx , r := range currentLine {
899
+ for charIdx , r := range runes {
897
900
runeStr := string (r )
898
901
rw := tw .DisplayWidth (runeStr )
899
- if tempWidth + rw > effectiveContentMaxWidth && charIdx > 0 {
902
+ if tempWidth + rw > targetWidth && charIdx > 0 {
900
903
break
901
904
}
902
905
tempWidth += rw
903
906
actualBreakRuneCount = charIdx + 1
904
- if tempWidth >= effectiveContentMaxWidth && charIdx == 0 {
907
+ if tempWidth >= targetWidth && charIdx == 0 {
905
908
break
906
909
}
907
910
}
908
911
if actualBreakRuneCount == 0 && len (runes ) > 0 {
909
912
actualBreakRuneCount = 1
910
913
}
911
-
912
914
if actualBreakRuneCount > 0 && actualBreakRuneCount <= len (runes ) {
913
915
wrapped = append (wrapped , string (runes [:actualBreakRuneCount ])+ tw .CharBreak )
914
916
currentLine = string (runes [actualBreakRuneCount :])
915
917
} 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 )
929
919
wrapped = append (wrapped , currentLine )
930
920
currentLine = ""
931
921
break
932
922
}
923
+ } else {
924
+ wrapped = append (wrapped , string (runes [:breakPoint ])+ tw .CharBreak )
925
+ currentLine = string (runes [breakPoint :])
933
926
}
934
927
}
935
928
if tw .DisplayWidth (currentLine ) > 0 {
@@ -964,7 +957,8 @@ func (t *Table) prepareContent(cells []string, config tw.CellConfig) [][]string
964
957
if i < len (result [j ]) {
965
958
result [j ][i ] = cellLineContent
966
959
} 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 )
968
962
}
969
963
}
970
964
}
@@ -1345,9 +1339,8 @@ func (t *Table) render() error {
1345
1339
t .logger .Debugf ("No caption detected. Rendering table core directly to writer." )
1346
1340
}
1347
1341
1348
- //Render Table Core ---
1349
- t .writer = targetWriter // Set writer only when necessary
1350
-
1342
+ //Render Table Core
1343
+ t .writer = targetWriter
1351
1344
ctx , mctx , err := t .prepareContexts ()
1352
1345
if err != nil {
1353
1346
t .writer = originalWriter
@@ -1393,7 +1386,6 @@ func (t *Table) render() error {
1393
1386
if renderError {
1394
1387
return firstRenderErr // Return error from core rendering if any
1395
1388
}
1396
- //End Render Table Core ---
1397
1389
1398
1390
//Caption Handling & Final Output ---
1399
1391
if isTopOrBottomCaption {
0 commit comments