Fix conflict between SmartyPants extension and pipetables (issue #13)

This commit is contained in:
Alexandre Mutel
2016-06-24 12:04:29 +09:00
parent 586095a475
commit cff2b9a8ca
5 changed files with 178 additions and 30 deletions

View File

@@ -110,3 +110,26 @@ This is a en ellipsis...
.
<p>This is a en ellipsis&hellip;</p>
````````````````````````````````
Check that a smartypants are not breaking pipetable parsing:
```````````````````````````````` example
a | b
-- | --
0 | 1
.
<table>
<thead>
<tr>
<th>a</th>
<th>b</th>
</tr>
</thead>
<tbody>
<tr>
<td>0</td>
<td>1</td>
</tr>
</tbody>
</table>
````````````````````````````````

View File

@@ -18843,7 +18843,7 @@ namespace Markdig.Tests
// <p>This is a &ldquo;text&rdquo;</p>
Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 1, "Extensions SmartyPants Quotes");
TestParser.TestSpec("This is a \"text\"", "<p>This is a &ldquo;text&rdquo;</p>", "smartypants");
TestParser.TestSpec("This is a \"text\"", "<p>This is a &ldquo;text&rdquo;</p>", "pipetables+smartypants");
}
}
[TestFixture]
@@ -18862,7 +18862,7 @@ namespace Markdig.Tests
// <p>This is a &lsquo;text&rsquo;</p>
Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 2, "Extensions SmartyPants Quotes");
TestParser.TestSpec("This is a 'text'", "<p>This is a &lsquo;text&rsquo;</p>", "smartypants");
TestParser.TestSpec("This is a 'text'", "<p>This is a &lsquo;text&rsquo;</p>", "pipetables+smartypants");
}
}
[TestFixture]
@@ -18881,7 +18881,7 @@ namespace Markdig.Tests
// <p>This is a &laquo;text&raquo;</p>
Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 3, "Extensions SmartyPants Quotes");
TestParser.TestSpec("This is a <<text>>", "<p>This is a &laquo;text&raquo;</p>", "smartypants");
TestParser.TestSpec("This is a <<text>>", "<p>This is a &laquo;text&raquo;</p>", "pipetables+smartypants");
}
}
// Unbalanced quotes are not changed:
@@ -18901,7 +18901,7 @@ namespace Markdig.Tests
// <p>This is a &quot;text</p>
Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 4, "Extensions SmartyPants Quotes");
TestParser.TestSpec("This is a \"text", "<p>This is a &quot;text</p>", "smartypants");
TestParser.TestSpec("This is a \"text", "<p>This is a &quot;text</p>", "pipetables+smartypants");
}
}
[TestFixture]
@@ -18920,7 +18920,7 @@ namespace Markdig.Tests
// <p>This is a 'text</p>
Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 5, "Extensions SmartyPants Quotes");
TestParser.TestSpec("This is a 'text", "<p>This is a 'text</p>", "smartypants");
TestParser.TestSpec("This is a 'text", "<p>This is a 'text</p>", "pipetables+smartypants");
}
}
[TestFixture]
@@ -18939,7 +18939,7 @@ namespace Markdig.Tests
// <p>This is a &lt;&lt;text</p>
Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 6, "Extensions SmartyPants Quotes");
TestParser.TestSpec("This is a <<text", "<p>This is a &lt;&lt;text</p>", "smartypants");
TestParser.TestSpec("This is a <<text", "<p>This is a &lt;&lt;text</p>", "pipetables+smartypants");
}
}
// Unbalanced quotes inside other quotes are not changed:
@@ -18959,7 +18959,7 @@ namespace Markdig.Tests
// <p>This is a &ldquo;text 'with&rdquo; a another text'</p>
Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 7, "Extensions SmartyPants Quotes");
TestParser.TestSpec("This is a \"text 'with\" a another text'", "<p>This is a &ldquo;text 'with&rdquo; a another text'</p>", "smartypants");
TestParser.TestSpec("This is a \"text 'with\" a another text'", "<p>This is a &ldquo;text 'with&rdquo; a another text'</p>", "pipetables+smartypants");
}
}
[TestFixture]
@@ -18978,7 +18978,7 @@ namespace Markdig.Tests
// <p>This is a &lsquo;text &lt;&lt;with&rsquo; a another text&gt;&gt;</p>
Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 8, "Extensions SmartyPants Quotes");
TestParser.TestSpec("This is a 'text <<with' a another text>>", "<p>This is a &lsquo;text &lt;&lt;with&rsquo; a another text&gt;&gt;</p>", "smartypants");
TestParser.TestSpec("This is a 'text <<with' a another text>>", "<p>This is a &lsquo;text &lt;&lt;with&rsquo; a another text&gt;&gt;</p>", "pipetables+smartypants");
}
}
[TestFixture]
@@ -18997,7 +18997,7 @@ namespace Markdig.Tests
// <p>This is a &laquo;text 'with&raquo; a another text'</p>
Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 9, "Extensions SmartyPants Quotes");
TestParser.TestSpec("This is a <<text 'with>> a another text'", "<p>This is a &laquo;text 'with&raquo; a another text'</p>", "smartypants");
TestParser.TestSpec("This is a <<text 'with>> a another text'", "<p>This is a &laquo;text 'with&raquo; a another text'</p>", "pipetables+smartypants");
}
}
// Quotes requires to have the same rules than emphasis `_` regarding left/right frankling rules:
@@ -19017,7 +19017,7 @@ namespace Markdig.Tests
// <p>It's not quotes'</p>
Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 10, "Extensions SmartyPants Quotes");
TestParser.TestSpec("It's not quotes'", "<p>It's not quotes'</p>", "smartypants");
TestParser.TestSpec("It's not quotes'", "<p>It's not quotes'</p>", "pipetables+smartypants");
}
}
[TestFixture]
@@ -19036,7 +19036,7 @@ namespace Markdig.Tests
// <p>They are ' not matching quotes '</p>
Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 11, "Extensions SmartyPants Quotes");
TestParser.TestSpec("They are ' not matching quotes '", "<p>They are ' not matching quotes '</p>", "smartypants");
TestParser.TestSpec("They are ' not matching quotes '", "<p>They are ' not matching quotes '</p>", "pipetables+smartypants");
}
}
[TestFixture]
@@ -19055,7 +19055,7 @@ namespace Markdig.Tests
// <p>They are' not matching 'quotes</p>
Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 12, "Extensions SmartyPants Quotes");
TestParser.TestSpec("They are' not matching 'quotes", "<p>They are' not matching 'quotes</p>", "smartypants");
TestParser.TestSpec("They are' not matching 'quotes", "<p>They are' not matching 'quotes</p>", "pipetables+smartypants");
}
}
// An emphasis starting inside left/right quotes will span over the right quote:
@@ -19075,7 +19075,7 @@ namespace Markdig.Tests
// <p>This is &ldquo;a <em>text&rdquo; with an emphasis</em></p>
Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 13, "Extensions SmartyPants Quotes");
TestParser.TestSpec("This is \"a *text\" with an emphasis*", "<p>This is &ldquo;a <em>text&rdquo; with an emphasis</em></p>", "smartypants");
TestParser.TestSpec("This is \"a *text\" with an emphasis*", "<p>This is &ldquo;a <em>text&rdquo; with an emphasis</em></p>", "pipetables+smartypants");
}
}
// ## SmartyPants Separators
@@ -19095,7 +19095,7 @@ namespace Markdig.Tests
// <p>This is a &ndash; text</p>
Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 14, "Extensions SmartyPants Separators");
TestParser.TestSpec("This is a -- text", "<p>This is a &ndash; text</p>", "smartypants");
TestParser.TestSpec("This is a -- text", "<p>This is a &ndash; text</p>", "pipetables+smartypants");
}
}
[TestFixture]
@@ -19114,7 +19114,7 @@ namespace Markdig.Tests
// <p>This is a &mdash; text</p>
Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 15, "Extensions SmartyPants Separators");
TestParser.TestSpec("This is a --- text", "<p>This is a &mdash; text</p>", "smartypants");
TestParser.TestSpec("This is a --- text", "<p>This is a &mdash; text</p>", "pipetables+smartypants");
}
}
[TestFixture]
@@ -19133,7 +19133,42 @@ namespace Markdig.Tests
// <p>This is a en ellipsis&hellip;</p>
Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 16, "Extensions SmartyPants Separators");
TestParser.TestSpec("This is a en ellipsis...", "<p>This is a en ellipsis&hellip;</p>", "smartypants");
TestParser.TestSpec("This is a en ellipsis...", "<p>This is a en ellipsis&hellip;</p>", "pipetables+smartypants");
}
}
// Check that a smartypants are not breaking pipetable parsing:
[TestFixture]
public partial class TestExtensionsSmartyPantsSeparators
{
[Test]
public void Example017()
{
// Example 17
// Section: Extensions SmartyPants Separators
//
// The following CommonMark:
// a | b
// -- | --
// 0 | 1
//
// Should be rendered as:
// <table>
// <thead>
// <tr>
// <th>a</th>
// <th>b</th>
// </tr>
// </thead>
// <tbody>
// <tr>
// <td>0</td>
// <td>1</td>
// </tr>
// </tbody>
// </table>
Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 17, "Extensions SmartyPants Separators");
TestParser.TestSpec("a | b\n-- | --\n0 | 1", "<table>\n<thead>\n<tr>\n<th>a</th>\n<th>b</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td>0</td>\n<td>1</td>\n</tr>\n</tbody>\n</table>", "pipetables+smartypants");
}
}
// # Extensions

View File

@@ -54,7 +54,7 @@ SOFTWARE.
new KeyValuePair<string, string>(Host.ResolvePath("MathSpecs.md"), "mathematics"),
new KeyValuePair<string, string>(Host.ResolvePath("BootstrapSpecs.md"), "bootstrap+pipetables+figures+attributes"),
new KeyValuePair<string, string>(Host.ResolvePath("MediaSpecs.md"), "medialinks"),
new KeyValuePair<string, string>(Host.ResolvePath("SmartyPantsSpecs.md"), "smartypants"),
new KeyValuePair<string, string>(Host.ResolvePath("SmartyPantsSpecs.md"), "pipetables+smartypants"), // Check with smartypants to make sure that it doesn't break pipetables
new KeyValuePair<string, string>(Host.ResolvePath("AutoIdentifierSpecs.md"), "autoidentifiers"),
new KeyValuePair<string, string>(Host.ResolvePath("TaskListSpecs.md"), "tasklists"),
};

View File

@@ -4,6 +4,7 @@
using System.Collections.Generic;
using Markdig.Helpers;
using Markdig.Parsers;
using Markdig.Syntax;
using Markdig.Syntax.Inlines;
namespace Markdig.Extensions.SmartyPants
@@ -11,7 +12,7 @@ namespace Markdig.Extensions.SmartyPants
/// <summary>
/// The inline parser for SmartyPants.
/// </summary>
public class SmartyPantsInlineParser : InlineParser
public class SmartyPantsInlineParser : InlineParser, IPostInlineProcessor
{
/// <summary>
/// Initializes a new instance of the <see cref="SmartyPantsInlineParser"/> class.
@@ -30,6 +31,8 @@ namespace Markdig.Extensions.SmartyPants
// " “ ” &ldquo; &rdquo; 'left-double-quote', 'right-double-quote'
// << >> « » &laquo; &raquo; 'left-angle-quote', 'right-angle-quote'
// ... … &hellip; 'ellipsis'
// Special case: &ndash; and &mdash; are handle as a PostProcess step to avoid conflicts with pipetables header separator row
// -- &ndash; 'ndash'
// --- — &mdash; 'mdash'
@@ -76,12 +79,8 @@ namespace Markdig.Extensions.SmartyPants
case '-':
if (slice.NextChar() == '-')
{
type = SmartyPantType.Dash2;
if (slice.PeekChar(1) == '-')
{
slice.NextChar();
type = SmartyPantType.Dash3;
}
processor.ParserStates[Index] = string.Empty;
return false;
}
break;
}
@@ -282,5 +281,85 @@ namespace Markdig.Extensions.SmartyPants
pants.Clear();
}
bool IPostInlineProcessor.PostProcess(InlineProcessor state, Inline root, Inline lastChild, int postInlineProcessorIndex,
bool isFinalProcessing)
{
// Don't try to process anything if there are no dash
if (state.ParserStates[Index] == null)
{
return true;
}
var child = root;
var pendingContainers = new Stack<Inline>();
while (true)
{
while (child != null)
{
var next = child.NextSibling;
if (child is LiteralInline)
{
var literal = (LiteralInline) child;
var startIndex = 0;
var indexOfDash = literal.Content.IndexOf("--", startIndex);
if (indexOfDash >= 0)
{
var type = SmartyPantType.Dash2;
if (literal.Content.PeekCharAbsolute(indexOfDash + 2) == '-')
{
type = SmartyPantType.Dash3;
}
var nextContent = literal.Content;
var originalSpan = literal.Span;
literal.Span.End -= literal.Content.End - indexOfDash + 1;
literal.Content.End = indexOfDash - 1;
nextContent.Start = indexOfDash + (type == SmartyPantType.Dash2 ? 2 : 3);
var pant = new SmartyPant()
{
Span = new SourceSpan(literal.Content.End + 1, nextContent.Start - 1),
Line = literal.Line,
Column = literal.Column,
OpeningCharacter = '-',
Type = type
};
literal.InsertAfter(pant);
var postLiteral = new LiteralInline()
{
Span = new SourceSpan(pant.Span.End + 1, originalSpan.End),
Line = literal.Line,
Column = literal.Column,
Content = nextContent
};
pant.InsertAfter(postLiteral);
// Use the pending literal to proceed further
next = postLiteral;
}
}
else if (child is ContainerInline)
{
pendingContainers.Push(((ContainerInline)child).FirstChild);
}
child = next;
}
if (pendingContainers.Count > 0)
{
child = pendingContainers.Pop();
}
else
{
break;
}
}
return true;
}
}
}

View File

@@ -65,13 +65,24 @@ namespace Markdig.Parsers.Inlines
// The LiteralInlineParser is always matching (at least an empty string)
var endPosition = slice.Start + length - 1;
processor.Inline = new LiteralInline()
var previousInline = processor.Inline as LiteralInline;
if (previousInline != null && ReferenceEquals(previousInline.Content.Text, slice.Text) &&
previousInline.Content.End + 1 == slice.Start)
{
Content = length > 0 ? new StringSlice(slice.Text, slice.Start, endPosition) : StringSlice.Empty,
Span = new SourceSpan(startPosition, processor.GetSourcePosition(endPosition)),
Line = line,
Column = column,
};
previousInline.Content.End = endPosition;
previousInline.Span.End = processor.GetSourcePosition(endPosition);
}
else
{
processor.Inline = new LiteralInline()
{
Content = length > 0 ? new StringSlice(slice.Text, slice.Start, endPosition) : StringSlice.Empty,
Span = new SourceSpan(startPosition, processor.GetSourcePosition(endPosition)),
Line = line,
Column = column,
};
}
slice.Start = nextStart;