From f5d99cd2bf2f964fc1b336fcfdd4c4bb0d8b9fe9 Mon Sep 17 00:00:00 2001 From: Alexandre Mutel Date: Sat, 5 Mar 2016 20:43:06 +0900 Subject: [PATCH] Handle correctly pipe delimiters inside a link --- .../Specs/PipeTableSpecs.md | 26 +++++++ src/Textamina.Markdig.Tests/Specs/Specs.cs | 37 +++++++++ .../Tables/PipeTableInlineParser.cs | 76 ++++++++++++++++++- .../Parsers/IDelimiterProcessor.cs | 3 +- .../Parsers/InlineParserState.cs | 6 +- .../Parsers/Inlines/EmphasisInlineParser.cs | 2 +- .../Parsers/Inlines/LinkInlineParser.cs | 27 +------ 7 files changed, 144 insertions(+), 33 deletions(-) diff --git a/src/Textamina.Markdig.Tests/Specs/PipeTableSpecs.md b/src/Textamina.Markdig.Tests/Specs/PipeTableSpecs.md index f07c50dd..91765afa 100644 --- a/src/Textamina.Markdig.Tests/Specs/PipeTableSpecs.md +++ b/src/Textamina.Markdig.Tests/Specs/PipeTableSpecs.md @@ -377,3 +377,29 @@ a | b ```````````````````````````````` + +**Rule #8** + +The character `|` inside a link label is not recognize as a column delimiter + +```````````````````````````````` example +a | b +-- | -- +[This is a link with a | inside the label](http://google.com) | 1 +. + + + + + + + + + + + + + +
ab
This is a link with a | inside the label1
+```````````````````````````````` + diff --git a/src/Textamina.Markdig.Tests/Specs/Specs.cs b/src/Textamina.Markdig.Tests/Specs/Specs.cs index 719475b5..79d3986b 100644 --- a/src/Textamina.Markdig.Tests/Specs/Specs.cs +++ b/src/Textamina.Markdig.Tests/Specs/Specs.cs @@ -16776,6 +16776,43 @@ namespace Textamina.Markdig.Tests Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 16, "Extensions Pipe Table"); TestParser.TestSpec("a | b\n-- | --\n0 | 1", "\n\n\n\n\n\n\n\n\n\n\n\n\n
a b
01
", "pipetables"); } + } + // **Rule #8** + // + // Links labels are escaping `|` + [TestFixture] + public partial class TestExtensionsPipeTable + { + [Test] + public void Example017() + { + // Example 17 + // Section: Extensions Pipe Table + // + // The following CommonMark: + // a | b + // -- | -- + // [This is a link with a | inside the label](http://google.com) | 1 + // + // Should be rendered as: + // + // + // + // + // + // + // + // + // + // + // + // + // + //
ab
This is a link with a | inside the label1
+ + Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 17, "Extensions Pipe Table"); + TestParser.TestSpec("a | b\n-- | --\n[This is a link with a | inside the label](http://google.com) | 1", "\n\n\n\n\n\n\n\n\n\n\n\n\n
ab
This is a link with a | inside the label1
", "pipetables"); + } } // # Extensions // diff --git a/src/Textamina.Markdig/Extensions/Tables/PipeTableInlineParser.cs b/src/Textamina.Markdig/Extensions/Tables/PipeTableInlineParser.cs index a9ff5552..e9fa02da 100644 --- a/src/Textamina.Markdig/Extensions/Tables/PipeTableInlineParser.cs +++ b/src/Textamina.Markdig/Extensions/Tables/PipeTableInlineParser.cs @@ -109,11 +109,81 @@ namespace Textamina.Markdig.Extensions.Tables return true; } - public bool ProcessDelimiters(InlineParserState state, Inline root, Inline lastChild, int delimiterProcessorIndex) + public bool ProcessDelimiters(InlineParserState state, Inline root, Inline lastChild, int delimiterProcessorIndex, bool isFinalProcessing) { - // Continue var container = root as ContainerInline; var tableState = state.ParserStates[Index] as TableState; + + // If the delimiters are being processed by an image link, we need to transform them back to literals + if (!isFinalProcessing) + { + if (container == null || tableState == null) + { + return true; + } + + var child = container.LastChild; + List delimitersToRemove = null; + + while (child != null) + { + var pipeDelimiter = child as PiprTableDelimiterInline; + if (pipeDelimiter != null) + { + if (delimitersToRemove == null) + { + delimitersToRemove = new List(); + } + delimitersToRemove.Add(pipeDelimiter); + } + + if (child == lastChild) + { + break; + } + + var subContainer = child as ContainerInline; + child = subContainer?.LastChild; + } + + // If we have found any delimiters, transform them to literals + if (delimitersToRemove != null) + { + bool leftIsDelimiter = false; + bool rightIsDelimiter = false; + for (int i = 0; i < delimitersToRemove.Count; i++) + { + var pipeDelimiter = delimitersToRemove[i]; + pipeDelimiter.ReplaceBy(new LiteralInline() {Content = new StringSlice("|"), IsClosed = true}); + + // Check that the pipe that is being removed is not going to make a line without pipe delimiters + var tableDelimiters = tableState.ColumnAndLineDelimiters; + var delimiterIndex = tableDelimiters.IndexOf(pipeDelimiter); + + if (i == 0) + { + leftIsDelimiter = delimiterIndex > 0 && tableDelimiters[delimiterIndex - 1] is PiprTableDelimiterInline; + } + else if (i + 1 == delimitersToRemove.Count) + { + rightIsDelimiter = delimiterIndex + 1 < tableDelimiters.Count && + tableDelimiters[delimiterIndex + 1] is PiprTableDelimiterInline; + } + // Remove this delimiter from the table state + tableState.ColumnAndLineDelimiters.Remove(pipeDelimiter); + } + + // If we didn't have any delimiter before and after the delimiters we jsut removed, we mark the state of the current line as no pipe + if (!leftIsDelimiter && !rightIsDelimiter) + { + tableState.LineHasPipe = false; + } + } + + return true; + } + + // Continue if (tableState == null || container == null || tableState.IsInvalidTable || !tableState.LineHasPipe || tableState.LineIndex != state.LocalLineIndex) { return true; @@ -258,7 +328,7 @@ namespace Textamina.Markdig.Extensions.Tables { foreach (var cell in cells) { - state.ProcessDelimiters(i + 1, cell.Inline); + state.ProcessDelimiters(i + 1, cell.Inline, null, true); } break; } diff --git a/src/Textamina.Markdig/Parsers/IDelimiterProcessor.cs b/src/Textamina.Markdig/Parsers/IDelimiterProcessor.cs index 0e31661c..6878bc8d 100644 --- a/src/Textamina.Markdig/Parsers/IDelimiterProcessor.cs +++ b/src/Textamina.Markdig/Parsers/IDelimiterProcessor.cs @@ -17,8 +17,9 @@ namespace Textamina.Markdig.Parsers /// The root inline. /// The last child. /// Index of this delimiter processor. + /// /// true to continue to the next delimiter processor; /// false to stop the process (in case a processor is perfoming sub-sequent processor itself) - bool ProcessDelimiters(InlineParserState state, Inline root, Inline lastChild, int delimiterProcessorIndex); + bool ProcessDelimiters(InlineParserState state, Inline root, Inline lastChild, int delimiterProcessorIndex, bool isFinalProcessing); } } \ No newline at end of file diff --git a/src/Textamina.Markdig/Parsers/InlineParserState.cs b/src/Textamina.Markdig/Parsers/InlineParserState.cs index 30216b70..83c7befe 100644 --- a/src/Textamina.Markdig/Parsers/InlineParserState.cs +++ b/src/Textamina.Markdig/Parsers/InlineParserState.cs @@ -218,7 +218,7 @@ namespace Textamina.Markdig.Parsers } // Process all delimiters - ProcessDelimiters(0, Root); + ProcessDelimiters(0, Root, null, true); //TransformDelimitersToLiterals(); @@ -230,12 +230,12 @@ namespace Textamina.Markdig.Parsers } } - public void ProcessDelimiters(int startingIndex, Inline root, Inline lastChild = null) + public void ProcessDelimiters(int startingIndex, Inline root, Inline lastChild, bool isFinalProcessing) { for (int i = startingIndex; i < Parsers.DelimiterProcessors.Length; i++) { var delimiterProcessor = Parsers.DelimiterProcessors[i]; - if (!delimiterProcessor.ProcessDelimiters(this, root, lastChild, i)) + if (!delimiterProcessor.ProcessDelimiters(this, root, lastChild, i, isFinalProcessing)) { break; } diff --git a/src/Textamina.Markdig/Parsers/Inlines/EmphasisInlineParser.cs b/src/Textamina.Markdig/Parsers/Inlines/EmphasisInlineParser.cs index 689ca855..09c44310 100644 --- a/src/Textamina.Markdig/Parsers/Inlines/EmphasisInlineParser.cs +++ b/src/Textamina.Markdig/Parsers/Inlines/EmphasisInlineParser.cs @@ -22,7 +22,7 @@ namespace Textamina.Markdig.Parsers.Inlines OpeningCharacters = new[] { '*', '_' }; } - public bool ProcessDelimiters(InlineParserState state, Inline root, Inline lastChild, int delimiterProcessorIndex) + public bool ProcessDelimiters(InlineParserState state, Inline root, Inline lastChild, int delimiterProcessorIndex, bool isFinalProcessing) { var container = root as ContainerInline; if (container == null) diff --git a/src/Textamina.Markdig/Parsers/Inlines/LinkInlineParser.cs b/src/Textamina.Markdig/Parsers/Inlines/LinkInlineParser.cs index 1e10a606..48931803 100644 --- a/src/Textamina.Markdig/Parsers/Inlines/LinkInlineParser.cs +++ b/src/Textamina.Markdig/Parsers/Inlines/LinkInlineParser.cs @@ -13,8 +13,6 @@ namespace Textamina.Markdig.Parsers.Inlines /// public class LinkInlineParser : InlineParser { - private int emphasisInlineDelimiterProcessorIndex; - /// /// Initializes a new instance of the class. /// @@ -23,21 +21,6 @@ namespace Textamina.Markdig.Parsers.Inlines OpeningCharacters = new[] {'[', ']', '!'}; } - public override void Initialize(InlineParserState state) - { - // Look for emphasis delimiter processor - emphasisInlineDelimiterProcessorIndex = -1; - for (int i = 0; i < state.Parsers.DelimiterProcessors.Length; i++) - { - var delimiters = state.Parsers.DelimiterProcessors[i]; - if (delimiters is EmphasisInlineParser) - { - emphasisInlineDelimiterProcessorIndex = i; - break; - } - } - } - public override bool Match(InlineParserState state, ref StringSlice slice) { // The following methods are inspired by the "An algorithm for parsing nested emphasis and links" @@ -151,10 +134,7 @@ namespace Textamina.Markdig.Parsers.Inlines link.IsClosed = true; // Process emphasis delimiters - if (emphasisInlineDelimiterProcessorIndex >= 0) - { - state.ProcessDelimiters(emphasisInlineDelimiterProcessorIndex, link); - } + state.ProcessDelimiters(0, link, null, false); state.Inline = link; isValidLink = true; @@ -221,10 +201,7 @@ namespace Textamina.Markdig.Parsers.Inlines inlineState.Inline = link; // Process emphasis delimiters - if (emphasisInlineDelimiterProcessorIndex >= 0) - { - inlineState.ProcessDelimiters(emphasisInlineDelimiterProcessorIndex, link); - } + inlineState.ProcessDelimiters(0, link, null, false); // If we have a link (and not an image), // we also set all [ delimiters before the opening delimiter to inactive.