mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-02-01 09:51:42 -05:00
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>
274 lines
10 KiB
Go
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)
|
|
}
|