This repository has been archived on 2023-02-02. You can view files and clone it, but cannot push or open issues or pull requests.
mfgames-markdown-cil/src/MfGames.Markdown.Gemtext/Renderers/Gemtext/Blocks/TableRenderer.cs
2022-02-16 11:37:41 -06:00

137 lines
4.5 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using ConsoleTableExt;
using Markdig.Extensions.Tables;
namespace MfGames.Markdown.Gemtext.Renderers.Gemtext.Blocks
{
public class TableRenderer : GemtextObjectRenderer<Table>
{
private readonly Action<ConsoleTableBuilder>? configureTableBuilder;
private readonly bool omitPreformat;
public TableRenderer(
bool omitPreformat,
Action<ConsoleTableBuilder>? configureTableBuilder)
{
this.omitPreformat = omitPreformat;
this.configureTableBuilder = configureTableBuilder;
}
protected override void Write(GemtextRenderer renderer, Table table)
{
// Since Gemtext doesn't have a table format per-se, we are going
// to use ConsoleTableEx to make a nicely-formatted table and emit
// the lines directly. That should produce the desired result.
// Gather up information about the data since that is where the
// builder starts with.
bool hasHeader = false;
List<object> header = new();
List<List<object>> data = new();
Dictionary<int, TextAligntment> align = new();
foreach (TableRow row in table.OfType<TableRow>())
{
// If we haven't seen a header, then we include that.
if (!hasHeader && row.IsHeader)
{
header = GetCellValues(row);
SetAlignments(table, align, row);
continue;
}
// Otherwise, we treat it as a row and go through the columns.
List<object> cells = GetCellValues(row);
data.Add(cells);
}
// Set up the table.
ConsoleTableBuilder builder = ConsoleTableBuilder
.From(data)
.WithColumn(header.OfType<string>().ToArray())
.WithHeaderTextAlignment(align)
.WithTextAlignment(align);
this.configureTableBuilder?.Invoke(builder);
// Format the final table.
string formatted = builder.Export().ToString().TrimEnd();
// Write out the table including making sure two lines are above it.
renderer.EnsureTwoLines();
if (!this.omitPreformat)
{
renderer.WriteLine("```");
}
renderer.WriteLine(formatted);
if (!this.omitPreformat)
{
renderer.WriteLine("```");
renderer.WriteLine();
}
}
private static List<object> GetCellValues(TableRow row)
{
List<object> cells = new();
foreach (TableCell cell in row.OfType<TableCell>())
{
// Write out to a text since we can't have a callback while
// rendering the table cells.
using var writer = new StringWriter();
var innerRenderer = new GemtextRenderer(writer);
innerRenderer.Render(cell);
cells.Add(writer.ToString());
}
return cells;
}
private static void SetAlignments(
Table table,
Dictionary<int, TextAligntment> align,
TableRow row)
{
for (int i = 0; i < row.Count; i++)
{
// Copied from Markdig's version.
var cell = (TableCell)row[i];
int columnIndex = cell.ColumnIndex < 0
|| cell.ColumnIndex >= table.ColumnDefinitions.Count
? i
: cell.ColumnIndex;
columnIndex =
columnIndex >= table.ColumnDefinitions.Count
? table.ColumnDefinitions.Count - 1
: columnIndex;
TableColumnAlign? alignment = table
.ColumnDefinitions[columnIndex]
.Alignment;
if (alignment.HasValue)
{
align[columnIndex] = alignment.Value switch
{
TableColumnAlign.Center => TextAligntment.Center,
TableColumnAlign.Left => TextAligntment.Left,
TableColumnAlign.Right => TextAligntment.Right,
_ => TextAligntment.Left,
};
}
}
}
}
}