Files
dependabot[bot] 70fc6eb40b build(deps): bump github.com/olekukonko/tablewriter from 0.0.5 to 1.0.6
Bumps [github.com/olekukonko/tablewriter](https://github.com/olekukonko/tablewriter) from 0.0.5 to 1.0.6.
- [Commits](https://github.com/olekukonko/tablewriter/compare/v0.0.5...v1.0.6)

---
updated-dependencies:
- dependency-name: github.com/olekukonko/tablewriter
  dependency-version: 1.0.6
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-05-19 15:04:23 +00:00

274 lines
10 KiB
Go

package renderer
import (
"github.com/olekukonko/ll"
"github.com/olekukonko/tablewriter/tw"
)
// Junction handles rendering of table junction points (corners, intersections) with color support.
type Junction struct {
sym tw.Symbols // Symbols used for rendering junctions and lines
ctx tw.Formatting // Current table formatting context
colIdx int // Index of the column being processed
debugging bool // Enables debug logging
borderTint Tint // Colors for border symbols
separatorTint Tint // Colors for separator symbols
logger *ll.Logger
}
type JunctionContext struct {
Symbols tw.Symbols
Ctx tw.Formatting
ColIdx int
Logger *ll.Logger
BorderTint Tint
SeparatorTint Tint
}
// NewJunction initializes a Junction with the given symbols, context, and tints.
// If debug is nil, a no-op debug function is used.
func NewJunction(ctx JunctionContext) *Junction {
return &Junction{
sym: ctx.Symbols,
ctx: ctx.Ctx,
colIdx: ctx.ColIdx,
logger: ctx.Logger.Namespace("junction"),
borderTint: ctx.BorderTint,
separatorTint: ctx.SeparatorTint,
}
}
// getMergeState retrieves the merge state for a specific column in a row, returning an empty state if not found.
func (jr *Junction) getMergeState(row map[int]tw.CellContext, colIdx int) tw.MergeState {
if row == nil || colIdx < 0 {
return tw.MergeState{}
}
return row[colIdx].Merge
}
// GetSegment determines whether to render a colored horizontal line or an empty space based on merge states.
func (jr *Junction) GetSegment() string {
currentMerge := jr.getMergeState(jr.ctx.Row.Current, jr.colIdx)
nextMerge := jr.getMergeState(jr.ctx.Row.Next, jr.colIdx)
vPassThruStrict := (currentMerge.Vertical.Present && nextMerge.Vertical.Present && !currentMerge.Vertical.End && !nextMerge.Vertical.Start) ||
(currentMerge.Hierarchical.Present && nextMerge.Hierarchical.Present && !currentMerge.Hierarchical.End && !nextMerge.Hierarchical.Start)
if vPassThruStrict {
jr.logger.Debugf("GetSegment col %d: VPassThruStrict=%v -> Empty segment", jr.colIdx, vPassThruStrict)
return tw.Empty
}
symbol := jr.sym.Row()
coloredSymbol := jr.borderTint.Apply(symbol)
jr.logger.Debugf("GetSegment col %d: VPassThruStrict=%v -> Colored row symbol '%s'", jr.colIdx, vPassThruStrict, coloredSymbol)
return coloredSymbol
}
// RenderLeft selects and colors the leftmost junction symbol for the current row line based on position and merges.
func (jr *Junction) RenderLeft() string {
mergeAbove := jr.getMergeState(jr.ctx.Row.Current, 0)
mergeBelow := jr.getMergeState(jr.ctx.Row.Next, 0)
jr.logger.Debugf("RenderLeft: Level=%v, Location=%v, Previous=%v", jr.ctx.Level, jr.ctx.Row.Location, jr.ctx.Row.Previous)
isTopBorder := (jr.ctx.Level == tw.LevelHeader && jr.ctx.Row.Location == tw.LocationFirst) ||
(jr.ctx.Level == tw.LevelBody && jr.ctx.Row.Location == tw.LocationFirst && jr.ctx.Row.Previous == nil)
if isTopBorder {
symbol := jr.sym.TopLeft()
return jr.borderTint.Apply(symbol)
}
isBottom := jr.ctx.Level == tw.LevelBody && jr.ctx.Row.Location == tw.LocationEnd && !jr.ctx.HasFooter
isFooter := jr.ctx.Level == tw.LevelFooter && jr.ctx.Row.Location == tw.LocationEnd
if isBottom || isFooter {
symbol := jr.sym.BottomLeft()
return jr.borderTint.Apply(symbol)
}
isVPassThruStrict := (mergeAbove.Vertical.Present && mergeBelow.Vertical.Present && !mergeAbove.Vertical.End && !mergeBelow.Vertical.Start) ||
(mergeAbove.Hierarchical.Present && mergeBelow.Hierarchical.Present && !mergeAbove.Hierarchical.End && !mergeBelow.Hierarchical.Start)
if isVPassThruStrict {
symbol := jr.sym.Column()
return jr.separatorTint.Apply(symbol)
}
symbol := jr.sym.MidLeft()
return jr.borderTint.Apply(symbol)
}
// RenderRight selects and colors the rightmost junction symbol for the row line based on position, merges, and last column index.
func (jr *Junction) RenderRight(lastColIdx int) string {
jr.logger.Debugf("RenderRight: lastColIdx=%d, Level=%v, Location=%v, Previous=%v", lastColIdx, jr.ctx.Level, jr.ctx.Row.Location, jr.ctx.Row.Previous)
if lastColIdx < 0 {
switch jr.ctx.Level {
case tw.LevelHeader:
symbol := jr.sym.TopRight()
return jr.borderTint.Apply(symbol)
case tw.LevelFooter:
symbol := jr.sym.BottomRight()
return jr.borderTint.Apply(symbol)
default:
if jr.ctx.Row.Location == tw.LocationFirst {
symbol := jr.sym.TopRight()
return jr.borderTint.Apply(symbol)
}
if jr.ctx.Row.Location == tw.LocationEnd {
symbol := jr.sym.BottomRight()
return jr.borderTint.Apply(symbol)
}
symbol := jr.sym.MidRight()
return jr.borderTint.Apply(symbol)
}
}
mergeAbove := jr.getMergeState(jr.ctx.Row.Current, lastColIdx)
mergeBelow := jr.getMergeState(jr.ctx.Row.Next, lastColIdx)
isTopBorder := (jr.ctx.Level == tw.LevelHeader && jr.ctx.Row.Location == tw.LocationFirst) ||
(jr.ctx.Level == tw.LevelBody && jr.ctx.Row.Location == tw.LocationFirst && jr.ctx.Row.Previous == nil)
if isTopBorder {
symbol := jr.sym.TopRight()
return jr.borderTint.Apply(symbol)
}
isBottom := jr.ctx.Level == tw.LevelBody && jr.ctx.Row.Location == tw.LocationEnd && !jr.ctx.HasFooter
isFooter := jr.ctx.Level == tw.LevelFooter && jr.ctx.Row.Location == tw.LocationEnd
if isBottom || isFooter {
symbol := jr.sym.BottomRight()
return jr.borderTint.Apply(symbol)
}
isVPassThruStrict := (mergeAbove.Vertical.Present && mergeBelow.Vertical.Present && !mergeAbove.Vertical.End && !mergeBelow.Vertical.Start) ||
(mergeAbove.Hierarchical.Present && mergeBelow.Hierarchical.Present && !mergeAbove.Hierarchical.End && !mergeBelow.Hierarchical.Start)
if isVPassThruStrict {
symbol := jr.sym.Column()
return jr.separatorTint.Apply(symbol)
}
symbol := jr.sym.MidRight()
return jr.borderTint.Apply(symbol)
}
// RenderJunction selects and colors the junction symbol between two adjacent columns based on merge states and table position.
func (jr *Junction) RenderJunction(leftColIdx, rightColIdx int) string {
mergeCurrentL := jr.getMergeState(jr.ctx.Row.Current, leftColIdx)
mergeCurrentR := jr.getMergeState(jr.ctx.Row.Current, rightColIdx)
mergeNextL := jr.getMergeState(jr.ctx.Row.Next, leftColIdx)
mergeNextR := jr.getMergeState(jr.ctx.Row.Next, rightColIdx)
isSpannedCurrent := mergeCurrentL.Horizontal.Present && !mergeCurrentL.Horizontal.End
isSpannedNext := mergeNextL.Horizontal.Present && !mergeNextL.Horizontal.End
vPassThruLStrict := (mergeCurrentL.Vertical.Present && mergeNextL.Vertical.Present && !mergeCurrentL.Vertical.End && !mergeNextL.Vertical.Start) ||
(mergeCurrentL.Hierarchical.Present && mergeNextL.Hierarchical.Present && !mergeCurrentL.Hierarchical.End && !mergeNextL.Hierarchical.Start)
vPassThruRStrict := (mergeCurrentR.Vertical.Present && mergeNextR.Vertical.Present && !mergeCurrentR.Vertical.End && !mergeNextR.Vertical.Start) ||
(mergeCurrentR.Hierarchical.Present && mergeNextR.Hierarchical.Present && !mergeCurrentR.Hierarchical.End && !mergeNextR.Hierarchical.Start)
isTop := (jr.ctx.Level == tw.LevelHeader && jr.ctx.Row.Location == tw.LocationFirst) ||
(jr.ctx.Level == tw.LevelBody && jr.ctx.Row.Location == tw.LocationFirst && len(jr.ctx.Row.Previous) == 0)
isBottom := (jr.ctx.Level == tw.LevelFooter && jr.ctx.Row.Location == tw.LocationEnd) ||
(jr.ctx.Level == tw.LevelBody && jr.ctx.Row.Location == tw.LocationEnd && !jr.ctx.HasFooter)
isPreFooter := jr.ctx.Level == tw.LevelFooter && (jr.ctx.Row.Position == tw.Row || jr.ctx.Row.Position == tw.Header)
if isTop {
if isSpannedNext {
symbol := jr.sym.Row()
return jr.borderTint.Apply(symbol)
}
symbol := jr.sym.TopMid()
return jr.borderTint.Apply(symbol)
}
if isBottom {
if vPassThruLStrict && vPassThruRStrict {
symbol := jr.sym.Column()
return jr.separatorTint.Apply(symbol)
}
if vPassThruLStrict {
symbol := jr.sym.MidLeft()
return jr.borderTint.Apply(symbol)
}
if vPassThruRStrict {
symbol := jr.sym.MidRight()
return jr.borderTint.Apply(symbol)
}
if isSpannedCurrent {
symbol := jr.sym.Row()
return jr.borderTint.Apply(symbol)
}
symbol := jr.sym.BottomMid()
return jr.borderTint.Apply(symbol)
}
if isPreFooter {
if vPassThruLStrict && vPassThruRStrict {
symbol := jr.sym.Column()
return jr.separatorTint.Apply(symbol)
}
if vPassThruLStrict {
symbol := jr.sym.MidLeft()
return jr.borderTint.Apply(symbol)
}
if vPassThruRStrict {
symbol := jr.sym.MidRight()
return jr.borderTint.Apply(symbol)
}
if mergeCurrentL.Horizontal.Present {
if !mergeCurrentL.Horizontal.End && mergeCurrentR.Horizontal.Present && !mergeCurrentR.Horizontal.End {
jr.logger.Debugf("Footer separator: H-merge continues from col %d to %d (mid-span), using BottomMid", leftColIdx, rightColIdx)
symbol := jr.sym.BottomMid()
return jr.borderTint.Apply(symbol)
}
if !mergeCurrentL.Horizontal.End && mergeCurrentR.Horizontal.Present && mergeCurrentR.Horizontal.End {
jr.logger.Debugf("Footer separator: H-merge ends at col %d, using BottomMid", rightColIdx)
symbol := jr.sym.BottomMid()
return jr.borderTint.Apply(symbol)
}
if mergeCurrentL.Horizontal.End && !mergeCurrentR.Horizontal.Present {
jr.logger.Debugf("Footer separator: H-merge ends at col %d, next col %d not merged, using Center", leftColIdx, rightColIdx)
symbol := jr.sym.Center()
return jr.borderTint.Apply(symbol)
}
}
if isSpannedNext {
symbol := jr.sym.BottomMid()
return jr.borderTint.Apply(symbol)
}
if isSpannedCurrent {
symbol := jr.sym.TopMid()
return jr.borderTint.Apply(symbol)
}
symbol := jr.sym.Center()
return jr.borderTint.Apply(symbol)
}
if vPassThruLStrict && vPassThruRStrict {
symbol := jr.sym.Column()
return jr.separatorTint.Apply(symbol)
}
if vPassThruLStrict {
symbol := jr.sym.MidLeft()
return jr.borderTint.Apply(symbol)
}
if vPassThruRStrict {
symbol := jr.sym.MidRight()
return jr.borderTint.Apply(symbol)
}
if isSpannedCurrent && isSpannedNext {
symbol := jr.sym.Row()
return jr.borderTint.Apply(symbol)
}
if isSpannedCurrent {
symbol := jr.sym.TopMid()
return jr.borderTint.Apply(symbol)
}
if isSpannedNext {
symbol := jr.sym.BottomMid()
return jr.borderTint.Apply(symbol)
}
symbol := jr.sym.Center()
return jr.borderTint.Apply(symbol)
}