diff --git a/src/Textamina.Markdig.Tests/Specs/PipeTableSpecs.md b/src/Textamina.Markdig.Tests/Specs/PipeTableSpecs.md
index 34d02d8e..7b8ecbab 100644
--- a/src/Textamina.Markdig.Tests/Specs/PipeTableSpecs.md
+++ b/src/Textamina.Markdig.Tests/Specs/PipeTableSpecs.md
@@ -278,8 +278,40 @@ A column delimiter has a higher priority than emphasis delimiter
1 |
-| *2 |
+_2 |
3* |
````````````````````````````````
+
+**Rule #7**
+
+A backstick/code delimiter has a higher priority than a table column delimiter
+
+```````````````````````````````` example
+a | b `
+0 | `
+.
+a | b 0 |
+````````````````````````````````
+
+**Rule #7**
+
+A HTML inline inside a table can use `|`
+
+```````````````````````````````` example
+a | b
+0 | 1
+.
+
+
+
+| a |
+b |
+
+
+| 0 |
+1 |
+
+
+````````````````````````````````
diff --git a/src/Textamina.Markdig.Tests/Specs/Specs.cs b/src/Textamina.Markdig.Tests/Specs/Specs.cs
index 2b394249..366be764 100644
--- a/src/Textamina.Markdig.Tests/Specs/Specs.cs
+++ b/src/Textamina.Markdig.Tests/Specs/Specs.cs
@@ -16626,13 +16626,69 @@ namespace Textamina.Markdig.Tests
// 1 |
//
//
- // | *2 |
+ // _2 |
// 3* |
//
//
Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 12, "Extensions Pipe Table");
- TestParser.TestSpec(" *a* | b\n 0 | _1_\n _2 | 3* ", "\n\n\n| a | \nb | \n
\n\n| 0 | \n1 | \n
\n\n| *2 | \n3* | \n
\n");
+ TestParser.TestSpec(" *a* | b\n 0 | _1_\n _2 | 3* ", "\n\n\n| a | \nb | \n
\n\n| 0 | \n1 | \n
\n\n| _2 | \n3* | \n
\n");
+ }
+ }
+ // **Rule #7**
+ //
+ // A backstick/code delimiter has a higher priority than a table column delimiter
+ [TestFixture]
+ public partial class TestExtensionsPipeTable
+ {
+ [Test]
+ public void Example013()
+ {
+ // Example 13
+ // Section: Extensions Pipe Table
+ //
+ // The following CommonMark:
+ // a | b `
+ // 0 | `
+ //
+ // Should be rendered as:
+ // a | b 0 |
+
+ Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 13, "Extensions Pipe Table");
+ TestParser.TestSpec("a | b `\n0 | ` ", "a | b 0 |
");
+ }
+ }
+ // **Rule #7**
+ //
+ // HTML inline can use `|`
+ [TestFixture]
+ public partial class TestExtensionsPipeTable
+ {
+ [Test]
+ public void Example014()
+ {
+ // Example 14
+ // Section: Extensions Pipe Table
+ //
+ // The following CommonMark:
+ // a | b
+ // 0 | 1
+ //
+ // Should be rendered as:
+ //
+ //
+ //
+ // | a |
+ // b |
+ //
+ //
+ // | 0 |
+ // 1 |
+ //
+ //
+
+ Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 14, "Extensions Pipe Table");
+ TestParser.TestSpec("a | b\n0 | 1", "\n\n\n| a | \nb | \n
\n\n| 0 | \n1 | \n
\n");
}
}
}
diff --git a/src/Textamina.Markdig/Extensions/Tables/PipeTableInlineParser.cs b/src/Textamina.Markdig/Extensions/Tables/PipeTableInlineParser.cs
index be51d75c..3ba9b847 100644
--- a/src/Textamina.Markdig/Extensions/Tables/PipeTableInlineParser.cs
+++ b/src/Textamina.Markdig/Extensions/Tables/PipeTableInlineParser.cs
@@ -1,5 +1,7 @@
using System.Collections.Generic;
+using System.Reflection;
using Textamina.Markdig.Parsers;
+using Textamina.Markdig.Parsers.Inlines;
using Textamina.Markdig.Syntax;
using Textamina.Markdig.Syntax.Inlines;
@@ -7,27 +9,66 @@ namespace Textamina.Markdig.Extensions.Tables
{
public class PipeTableInlineParser : InlineParser, IDelimiterProcessor
{
+ private LineBreakInlineParser lineBreakParser;
public PipeTableInlineParser()
{
- OpeningCharacters = new[] { '|' };
+ OpeningCharacters = new[] { '|', '\n' };
+ }
+
+
+ public override void Initialize(InlineParserState state)
+ {
+ lineBreakParser = state.Parsers.Find() ?? new LineBreakInlineParser();
}
public override bool Match(InlineParserState state, ref StringSlice slice)
{
+ // Only working on Paragraph block
+ if (!(state.Block is ParagraphBlock))
+ {
+ return false;
+ }
+
+ var c = slice.CurrentChar;
+
// If we have not a delimiter on the first line of a paragraph, don't bother to continue
// tracking other delimiters on following lines
- if (state.ParserStates[Index] == null)
+ var tableState = state.ParserStates[Index] as TableState;
+ if (tableState == null)
{
- if (state.LocalLineIndex > 0)
+ if (state.LocalLineIndex > 0 || c == '\n')
{
return false;
}
// Else setup a table state
- state.ParserStates[Index] = new TableState();
+ tableState = new TableState();
+ state.ParserStates[Index] = tableState;
}
- state.Inline = new PiprTableDelimiterInline(this) { LineIndex = state.LocalLineIndex };
- slice.NextChar(); // Skip the `|` character
+ if (c == '\n')
+ {
+ if (!tableState.LineHasPipe)
+ {
+ tableState.IsInvalidTable = true;
+ }
+ tableState.LineHasPipe = false;
+ lineBreakParser.Match(state, ref slice);
+ tableState.LineIndex++;
+ }
+ else
+ {
+ state.Inline = new PiprTableDelimiterInline(this) { LineIndex = state.LocalLineIndex };
+ var deltaLine = state.LocalLineIndex - tableState.LineIndex;
+ if (deltaLine > 0)
+ {
+ tableState.IsInvalidTable = true;
+ }
+ tableState.LineHasPipe = true;
+ tableState.LineIndex = state.LocalLineIndex;
+ slice.NextChar(); // Skip the `|` character
+ }
+
+ tableState.ColumnAndLineDelimiters.Add(state.Inline);
return true;
}
@@ -37,53 +78,7 @@ namespace Textamina.Markdig.Extensions.Tables
// Continue
var container = root as ContainerInline;
var tableState = state.ParserStates[Index] as TableState;
- if (tableState == null || container == null)
- {
- return true;
- }
-
- var lastLineIndex = state.LocalLineIndex;
-
- var child = container.FirstChild;
- var lines = tableState.Lines;
- int lineIndex = 0;
- var previousLine = -1;
- bool lineHasAPipe = false;
- while (child != null)
- {
- if (lineIndex != previousLine)
- {
- if (previousLine >= 0 && !lineHasAPipe)
- {
- return true;
- }
- previousLine = lineIndex;
- lines.Add(child);
- lineHasAPipe = false;
- }
-
- if (IsLine(child))
- {
- previousLine = lineIndex;
- lineIndex++;
- }
- else if (child is PiprTableDelimiterInline && !lineHasAPipe)
- {
- lineHasAPipe = true;
- }
-
- if (child is ContainerInline)
- {
- child = ((ContainerInline) child).FirstChild;
- }
- else
- {
- child = child.NextSibling;
- }
- }
-
- // The last line index must be equal to the last line of the leaf block
- if (!lineHasAPipe || lineIndex != lastLineIndex)
+ if (tableState == null || container == null || tableState.IsInvalidTable || !tableState.LineHasPipe || tableState.LineIndex != state.LocalLineIndex)
{
return true;
}
@@ -91,78 +86,107 @@ namespace Textamina.Markdig.Extensions.Tables
var table = new TableBlock();
state.BlockNew = table;
TableRowBlock firstRow = null;
- int columnCount = 0;
int maxColumn = 0;
var cells = tableState.Cells;
cells.Clear();
- for (int i = 0; i < lines.Count; i++)
+ TableRowBlock currentRow = null;
+
+ Inline column = container.FirstChild;
+ if (column is PiprTableDelimiterInline)
{
- var column = lines[i];
+ column = ((PiprTableDelimiterInline) column).FirstChild;
+ }
- var row = new TableRowBlock {Parent = table};
- table.Children.Add(row);
-
- if (column is PiprTableDelimiterInline)
+ var delimiters = tableState.ColumnAndLineDelimiters;
+ delimiters.Add(null);
+ int lastIndex = 0;
+ for (int i = 0; i < delimiters.Count; i++)
+ {
+ var delimiter = delimiters[i];
+ if (delimiter == null || IsLine(delimiter))
{
- column = ((PiprTableDelimiterInline)column).FirstChild;
- }
+ var beforeEndOfLine = delimiter?.PreviousSibling;
+ var nextColumn = delimiter?.NextSibling;
- ContainerInline previousColumn = null;
- Inline lastColumn = null;
- while (true)
- {
- if (maxColumn > 0 && row.Children.Count >= maxColumn)
+ var row = new TableRowBlock { Parent = table };
+ table.Children.Add(row);
+
+ for (int j = lastIndex; j <= i; j++)
{
- lastColumn = null;
- column.Remove();
- TrimEnd(column);
- previousColumn.AppendChild(column);
- break;
- }
- else
- {
- if (lastColumn != null)
+ var columnSeparator = delimiters[j];
+ var pipeSeparator = columnSeparator as PiprTableDelimiterInline;
+
+ var endOfColumn = columnSeparator?.PreviousSibling;
+
+ // This is the first column empty
+ if (j == lastIndex && pipeSeparator != null && endOfColumn == null)
{
- TrimEnd(lastColumn);
+ columnSeparator.Remove();
+ column = pipeSeparator.FirstChild;
+ continue;
}
- var cellContainer = new ContainerInline();
- TrimStart(column);
- Inline nextColumn;
- CopyCellDown(column, cellContainer, out lastColumn, out nextColumn);
- var tableCell = new TableCellBlock { Inline = cellContainer, Parent = row };
- cells.Add(tableCell);
+ if (pipeSeparator != null && IsTrailingColumnDelimiter(pipeSeparator))
+ {
+ TrimEnd(endOfColumn);
+ columnSeparator.Remove();
+ continue;
+ }
+
+ var columnContainer = new ContainerInline();
+ var item = column;
+ TrimStart(item);
+ while (item != null && !IsLine(item) && !(item is PiprTableDelimiterInline))
+ {
+ var nextSibling = item.NextSibling;
+ item.Remove();
+ columnContainer.AppendChild(item);
+ item = nextSibling;
+ }
+
+ var tableCell = new TableCellBlock { Inline = columnContainer, Parent = row };
row.Children.Add(tableCell);
+ cells.Add(tableCell);
- if (nextColumn is PiprTableDelimiterInline &&
- IsTrailingColumnDelimiter((PiprTableDelimiterInline) nextColumn))
+ // If we have reached the end, we can add remaining delimiters as pure child of the current cell
+ if (row.Children.Count == maxColumn && columnSeparator is PiprTableDelimiterInline)
{
- TrimEnd(column);
- nextColumn.Remove();
- column = null;
+ columnSeparator.Remove();
+ tableCell.Inline.AppendChild(columnSeparator);
+ break;
}
- else
- {
- column = nextColumn;
- previousColumn = cellContainer;
+ TrimEnd(endOfColumn);
+ //TrimEnd(previousSibling);
+ if (columnSeparator != null)
+ {
+ if (pipeSeparator != null)
+ {
+ column = pipeSeparator.FirstChild;
+ }
+ columnSeparator.Remove();
}
}
- if (column == null || IsLine(column))
+ TrimEnd(beforeEndOfLine);
+
+ if (delimiter != null)
{
- break;
+ delimiter.Remove();
}
- }
- if (lastColumn != null)
- {
- TrimEnd(lastColumn);
- }
+ if (nextColumn != null)
+ {
+ column = nextColumn;
+ }
- if (i == 0)
- {
- maxColumn = cells.Count;
+ if (firstRow == null)
+ {
+ firstRow = row;
+ maxColumn = firstRow.Children.Count;
+ }
+
+ lastIndex = i + 1;
}
}
@@ -179,11 +203,8 @@ namespace Textamina.Markdig.Extensions.Tables
previousRow.ColumnAlignments = aligns;
table.Children.RemoveAt(rowIndex);
rowIndex--;
- continue;
+ break;
}
-
- var tableCell = (TableCellBlock) row.LastChild;
- TrimEnd(tableCell.Inline.LastChild);
previousRow = row;
}
@@ -290,72 +311,9 @@ namespace Textamina.Markdig.Extensions.Tables
return child == null || IsLine(child);
}
- private static bool CopyCellDown(Inline fromElement, ContainerInline dest, out Inline last, out Inline next)
- {
- // TODO: Handle more correctly CopyCellDown
- next = null;
- var container = fromElement as ContainerInline;
- Inline lastChild = null;
- Inline child;
- if (container != null)
- {
- lastChild = container.LastChild;
- child = container.FirstChild;
- }
- else
- {
- child = fromElement;
- }
-
- bool found = false;
- last = null;
- while (child != null)
- {
- var nextSibling = child.NextSibling;
- var isLine = IsLine(child);
- if (isLine || child is PiprTableDelimiterInline)
- {
- child.Remove();
- next = child;
- found = true;
- break;
- }
-
- var childContainer = child as ContainerInline;
- if (childContainer != null)
- {
- var newParent = new ContainerInline();
- dest.AppendChild(newParent);
- if (CopyCellDown(childContainer, newParent, out last, out next))
- {
- found = true;
- break;
- }
- }
- else
- {
- child.Remove();
- dest.AppendChild(child);
- }
-
- last = child;
- child = nextSibling;
- }
-
- // If we have removed all children, the container can be removed
- if (container != null)
- {
- if (child == lastChild || child == null)
- {
- fromElement.Remove();
- }
- }
- return found;
- }
-
private static void TrimStart(Inline inline)
{
- while (inline is ContainerInline)
+ while (inline is ContainerInline && !(inline is DelimiterInline))
{
inline = ((ContainerInline)inline).FirstChild;
}
@@ -368,25 +326,10 @@ namespace Textamina.Markdig.Extensions.Tables
private static void TrimEnd(Inline inline)
{
- while (inline is ContainerInline)
+ var literal = inline as LiteralInline;
+ if (literal != null)
{
- inline = ((ContainerInline)inline).LastChild;
- }
-
- if (inline != null)
- {
- var previous = inline.PreviousSibling;
- if (IsLine(inline))
- {
- inline.Remove();
- inline = previous;
- }
-
- var literal = inline as LiteralInline;
- if (literal != null)
- {
- literal.Content.TrimEnd();
- }
+ literal.Content.TrimEnd();
}
}
@@ -394,10 +337,17 @@ namespace Textamina.Markdig.Extensions.Tables
{
public TableState()
{
- Lines = new List();
+ ColumnAndLineDelimiters = new List();
Cells = new List();
}
- public List Lines { get; }
+
+ public bool IsInvalidTable { get; set; }
+
+ public bool LineHasPipe { get; set; }
+
+ public int LineIndex { get; set; }
+
+ public List ColumnAndLineDelimiters { get; }
public List Cells { get; }
}
diff --git a/src/Textamina.Markdig/Parsers/InlineParserState.cs b/src/Textamina.Markdig/Parsers/InlineParserState.cs
index a20ebc00..059c2320 100644
--- a/src/Textamina.Markdig/Parsers/InlineParserState.cs
+++ b/src/Textamina.Markdig/Parsers/InlineParserState.cs
@@ -45,9 +45,9 @@ namespace Textamina.Markdig.Parsers
public TextWriter Log;
- public int LineIndex { get; private set; }
+ public int LineIndex { get; set; }
- public int LocalLineIndex { get; private set; }
+ public int LocalLineIndex { get; set; }
public char[] SpecialCharacters { get; set; }
diff --git a/src/Textamina.Markdig/Parsers/Inlines/CodeInlineParser.cs b/src/Textamina.Markdig/Parsers/Inlines/CodeInlineParser.cs
index 5e465f15..c75b2265 100644
--- a/src/Textamina.Markdig/Parsers/Inlines/CodeInlineParser.cs
+++ b/src/Textamina.Markdig/Parsers/Inlines/CodeInlineParser.cs
@@ -41,6 +41,8 @@ namespace Textamina.Markdig.Parsers.Inlines
// Transform '\n' into a single space
if (c == '\n')
{
+ state.LocalLineIndex++;
+ state.LineIndex++;
c = ' ';
}