mirror of
https://github.com/xoofx/markdig.git
synced 2026-02-12 13:54:45 +00:00
Fix conflict between SmartyPants extension and pipetables (issue #13)
This commit is contained in:
@@ -110,3 +110,26 @@ This is a en ellipsis...
|
||||
.
|
||||
<p>This is a en ellipsis…</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>
|
||||
````````````````````````````````
|
||||
|
||||
@@ -18843,7 +18843,7 @@ namespace Markdig.Tests
|
||||
// <p>This is a “text”</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 “text”</p>", "smartypants");
|
||||
TestParser.TestSpec("This is a \"text\"", "<p>This is a “text”</p>", "pipetables+smartypants");
|
||||
}
|
||||
}
|
||||
[TestFixture]
|
||||
@@ -18862,7 +18862,7 @@ namespace Markdig.Tests
|
||||
// <p>This is a ‘text’</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 ‘text’</p>", "smartypants");
|
||||
TestParser.TestSpec("This is a 'text'", "<p>This is a ‘text’</p>", "pipetables+smartypants");
|
||||
}
|
||||
}
|
||||
[TestFixture]
|
||||
@@ -18881,7 +18881,7 @@ namespace Markdig.Tests
|
||||
// <p>This is a «text»</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 «text»</p>", "smartypants");
|
||||
TestParser.TestSpec("This is a <<text>>", "<p>This is a «text»</p>", "pipetables+smartypants");
|
||||
}
|
||||
}
|
||||
// Unbalanced quotes are not changed:
|
||||
@@ -18901,7 +18901,7 @@ namespace Markdig.Tests
|
||||
// <p>This is a "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 "text</p>", "smartypants");
|
||||
TestParser.TestSpec("This is a \"text", "<p>This is a "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 <<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 <<text</p>", "smartypants");
|
||||
TestParser.TestSpec("This is a <<text", "<p>This is a <<text</p>", "pipetables+smartypants");
|
||||
}
|
||||
}
|
||||
// Unbalanced quotes inside other quotes are not changed:
|
||||
@@ -18959,7 +18959,7 @@ namespace Markdig.Tests
|
||||
// <p>This is a “text 'with” 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 “text 'with” a another text'</p>", "smartypants");
|
||||
TestParser.TestSpec("This is a \"text 'with\" a another text'", "<p>This is a “text 'with” a another text'</p>", "pipetables+smartypants");
|
||||
}
|
||||
}
|
||||
[TestFixture]
|
||||
@@ -18978,7 +18978,7 @@ namespace Markdig.Tests
|
||||
// <p>This is a ‘text <<with’ a another text>></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 ‘text <<with’ a another text>></p>", "smartypants");
|
||||
TestParser.TestSpec("This is a 'text <<with' a another text>>", "<p>This is a ‘text <<with’ a another text>></p>", "pipetables+smartypants");
|
||||
}
|
||||
}
|
||||
[TestFixture]
|
||||
@@ -18997,7 +18997,7 @@ namespace Markdig.Tests
|
||||
// <p>This is a «text 'with» 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 «text 'with» a another text'</p>", "smartypants");
|
||||
TestParser.TestSpec("This is a <<text 'with>> a another text'", "<p>This is a «text 'with» 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 “a <em>text” 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 “a <em>text” with an emphasis</em></p>", "smartypants");
|
||||
TestParser.TestSpec("This is \"a *text\" with an emphasis*", "<p>This is “a <em>text” with an emphasis</em></p>", "pipetables+smartypants");
|
||||
}
|
||||
}
|
||||
// ## SmartyPants Separators
|
||||
@@ -19095,7 +19095,7 @@ namespace Markdig.Tests
|
||||
// <p>This is a – 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 – text</p>", "smartypants");
|
||||
TestParser.TestSpec("This is a -- text", "<p>This is a – text</p>", "pipetables+smartypants");
|
||||
}
|
||||
}
|
||||
[TestFixture]
|
||||
@@ -19114,7 +19114,7 @@ namespace Markdig.Tests
|
||||
// <p>This is a — 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 — text</p>", "smartypants");
|
||||
TestParser.TestSpec("This is a --- text", "<p>This is a — text</p>", "pipetables+smartypants");
|
||||
}
|
||||
}
|
||||
[TestFixture]
|
||||
@@ -19133,7 +19133,42 @@ namespace Markdig.Tests
|
||||
// <p>This is a en ellipsis…</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…</p>", "smartypants");
|
||||
TestParser.TestSpec("This is a en ellipsis...", "<p>This is a en ellipsis…</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
|
||||
|
||||
@@ -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"),
|
||||
};
|
||||
|
||||
@@ -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
|
||||
// " “ ” “ ” 'left-double-quote', 'right-double-quote'
|
||||
// << >> « » « » 'left-angle-quote', 'right-angle-quote'
|
||||
// ... … … 'ellipsis'
|
||||
|
||||
// Special case: – and — are handle as a PostProcess step to avoid conflicts with pipetables header separator row
|
||||
// -- – – 'ndash'
|
||||
// --- — — '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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user