templates tested

This commit is contained in:
EugeneTes
2026-02-03 16:40:25 +01:00
parent ec5decb12e
commit fe817b5211
6 changed files with 310 additions and 117 deletions

View File

@@ -109,6 +109,59 @@ public class LayoutEngine
return string.Concat(parts);
}
public List<string> FormatMultiColumnWithWrap(
List<(string text, int width, string align, bool wrap)> columns, int lineWidth)
{
// Calculate widths
int totalExplicit = columns.Where(c => c.width > 0).Sum(c => c.width);
int unspecifiedCount = columns.Count(c => c.width <= 0);
int remaining = lineWidth - totalExplicit;
int defaultWidth = unspecifiedCount > 0 ? remaining / unspecifiedCount : 0;
var colWidths = columns.Select(c => c.width > 0 ? c.width : Math.Max(defaultWidth, 1)).ToList();
// Wrap or truncate each column's text into lines
var allColLines = new List<List<string>>();
for (int i = 0; i < columns.Count; i++)
{
var (text, _, _, wrap) = columns[i];
text ??= "";
int w = colWidths[i];
if (wrap)
allColLines.Add(WrapText(text, w));
else
allColLines.Add(new List<string> { text.Length > w ? text[..w] : text });
}
// Find the tallest column
int maxLines = allColLines.Max(l => l.Count);
// Build each output line
var result = new List<string>();
for (int lineIdx = 0; lineIdx < maxLines; lineIdx++)
{
var parts = new List<string>();
for (int colIdx = 0; colIdx < columns.Count; colIdx++)
{
string cellText = lineIdx < allColLines[colIdx].Count ? allColLines[colIdx][lineIdx] : "";
int w = colWidths[colIdx];
string formatted = columns[colIdx].align.ToLowerInvariant() switch
{
"center" => CenterInWidth(cellText, w),
"right" => cellText.PadLeft(w),
_ => cellText.PadRight(w)
};
if (formatted.Length > w)
formatted = formatted[..w];
parts.Add(formatted);
}
result.Add(string.Concat(parts));
}
return result;
}
public List<string> WrapText(string text, int maxWidth, int indent = 0)
{
var lines = new List<string>();
@@ -177,20 +230,26 @@ public class LayoutEngine
List<(string text, int width, string align)> columns,
List<List<string>> headerRows,
List<List<string>> dataRows,
int lineWidth)
int lineWidth,
List<bool>? wrapFlags = null)
{
var result = new List<string>();
// Calculate column widths
var colWidths = CalculateTableColumnWidths(columns, lineWidth);
bool hasWrap = wrapFlags != null && wrapFlags.Any(w => w);
// Top border
result.Add(FormatTableBorder(colWidths));
// Header rows
foreach (var row in headerRows)
{
result.Add(FormatTableRow(row, colWidths, columns));
if (hasWrap)
result.AddRange(FormatTableRows(row, colWidths, columns, wrapFlags!));
else
result.Add(FormatTableRow(row, colWidths, columns));
}
// Separator between header and data
@@ -202,7 +261,10 @@ public class LayoutEngine
// Data rows
foreach (var row in dataRows)
{
result.Add(FormatTableRow(row, colWidths, columns));
if (hasWrap)
result.AddRange(FormatTableRows(row, colWidths, columns, wrapFlags!));
else
result.Add(FormatTableRow(row, colWidths, columns));
}
// Bottom border
@@ -245,6 +307,40 @@ public class LayoutEngine
return "|" + string.Join("|", parts) + "|";
}
private List<string> FormatTableRows(List<string> cells, List<int> colWidths,
List<(string text, int width, string align)> columns, List<bool> wrapFlags)
{
// Wrap each cell that has wrap enabled
var allColLines = new List<List<string>>();
for (int i = 0; i < colWidths.Count; i++)
{
string cell = i < cells.Count ? cells[i] : "";
bool wrap = i < wrapFlags.Count && wrapFlags[i];
if (wrap)
allColLines.Add(WrapText(cell, colWidths[i]));
else
allColLines.Add(new List<string> { cell.Length > colWidths[i] ? cell[..colWidths[i]] : cell });
}
int maxLines = allColLines.Max(l => l.Count);
var result = new List<string>();
for (int lineIdx = 0; lineIdx < maxLines; lineIdx++)
{
var parts = new List<string>();
for (int colIdx = 0; colIdx < colWidths.Count; colIdx++)
{
string cellText = lineIdx < allColLines[colIdx].Count ? allColLines[colIdx][lineIdx] : "";
string align = colIdx < columns.Count ? columns[colIdx].align : "left";
parts.Add(FormatCellContent(cellText, colWidths[colIdx], align));
}
result.Add("|" + string.Join("|", parts) + "|");
}
return result;
}
private string FormatCellContent(string text, int width, string align)
{
if (text.Length > width)

View File

@@ -145,15 +145,36 @@ public class TemplateRenderer
var evaluator = new ExpressionEvaluator(context);
int effectiveWidth = row.Big ? _bigFontLineWidth : _lineWidth;
var columnData = row.Columns.Select(c => (
text: evaluator.Evaluate(c.Text ?? ""),
width: c.Width ?? 0,
align: c.Align
)).ToList();
bool anyWrap = row.Columns.Any(c => c.Wrap);
string text = _layout.FormatMultiColumn(columnData, effectiveWidth);
var cmd = CreateCommand(text, row.Bold, row.Big, row.Tall, row.Red, row.LineSpacing);
commands.Add(cmd);
if (anyWrap)
{
var columnData = row.Columns.Select(c => (
text: evaluator.Evaluate(c.Text ?? ""),
width: ResolveColumnWidth(c, effectiveWidth),
align: c.Align,
wrap: c.Wrap
)).ToList();
var lines = _layout.FormatMultiColumnWithWrap(columnData, effectiveWidth);
foreach (var line in lines)
{
var cmd = CreateCommand(line, row.Bold, row.Big, row.Tall, row.Red, row.LineSpacing);
commands.Add(cmd);
}
}
else
{
var columnData = row.Columns.Select(c => (
text: evaluator.Evaluate(c.Text ?? ""),
width: ResolveColumnWidth(c, effectiveWidth),
align: c.Align
)).ToList();
string text = _layout.FormatMultiColumn(columnData, effectiveWidth);
var cmd = CreateCommand(text, row.Bold, row.Big, row.Tall, row.Red, row.LineSpacing);
commands.Add(cmd);
}
}
private void RenderSeparator(SeparatorNode separator, List<PrintCommand> commands)
@@ -199,9 +220,10 @@ public class TemplateRenderer
{
var evaluator = new ExpressionEvaluator(context);
int tableContentWidth = _lineWidth - table.Columns.Count - 1;
var columnDefs = table.Columns.Select(c => (
text: evaluator.Evaluate(c.Text ?? ""),
width: c.Width ?? 0,
width: ResolveColumnWidth(c, tableContentWidth),
align: c.Align
)).ToList();
@@ -267,13 +289,21 @@ public class TemplateRenderer
}
}
var lines = _layout.FormatTable(columnDefs, headerRows, dataRows, _lineWidth);
var wrapFlags = table.Columns.Select(c => c.Wrap).ToList();
var lines = _layout.FormatTable(columnDefs, headerRows, dataRows, _lineWidth, wrapFlags);
foreach (var line in lines)
{
commands.Add(new PrintCommand(line));
}
}
private static int ResolveColumnWidth(ColumnDef col, int availableWidth)
{
if (col.Width.HasValue) return col.Width.Value;
if (col.WidthPercent.HasValue) return (int)(availableWidth * col.WidthPercent.Value / 100.0);
return 0;
}
private static PrintCommand CreateCommand(string text, bool bold, bool big, bool tall, bool red, int? lineSpacing)
{
return new PrintCommand(text, isBig: big, isBold: bold)