Compare commits

...

16 Commits

Author SHA1 Message Date
Alexandre Mutel
c56041811b Bump to 0.15.2 2018-08-21 11:04:45 +02:00
Alexandre Mutel
790bff3baf Fix footnotes parsing when they are defined after a container that has been closed in the meantime (#223) 2018-08-21 11:01:07 +02:00
Alexandre Mutel
571e04fe28 Bump to 0.15.1 2018-07-10 11:02:38 +02:00
Alexandre Mutel
c847996146 Fix CRLF 2018-07-10 11:02:16 +02:00
Alexandre Mutel
2782d3b4c3 Remove usage of IDictionary 2018-07-10 10:31:34 +02:00
Alexandre Mutel
582a76f8f0 Change editorconfig to not force crlf 2018-07-10 10:31:34 +02:00
Alexandre Mutel
030d676497 Merge pull request #235 from yufeih/autolink
Make AutoIdentifierExtension thread safe
2018-07-10 10:09:17 +02:00
Yufei Huang
0794213eef Make AutoIdentifierExtension thread safe 2018-07-10 11:52:32 +08:00
Alexandre Mutel
427dc849f7 Merge pull request #234 from ForNeVeR/patch-1
Fix spelling in package description
2018-06-28 17:16:43 +02:00
Friedrich von Never
e598047964 Fix spelling in package description 2018-06-28 22:07:53 +07:00
Alexandre Mutel
15546732dd Merge pull request #228 from carbon/master
Cross target .NETSTANDARD 2.0
2018-05-22 18:40:29 +02:00
Jason Nelson
7bd238852d Fix formatting 2018-05-22 08:59:35 -07:00
Jason Nelson
0f406039a0 Enable FIXED_STRING & UNSAFE code for .NETSTANDARD 2.0 target 2018-05-22 08:55:03 -07:00
Jason Nelson
1cd9cdfca0 Cross target .NETSTANDARD 2.0 2018-05-22 08:45:46 -07:00
Alexandre Mutel
812c4fabe6 Merge pull request #222 from caioproiete/add-tests-for-ConfigureNewLine
Add tests for `ConfigureNewLineExtension`'s `ConfigureNewLine`
2018-04-13 10:27:15 +02:00
Caio Proiete
85f8f59786 Add tests for ConfigureNewLineExtension's ConfigureNewLine 2018-04-12 20:34:53 -03:00
13 changed files with 317 additions and 211 deletions

View File

@@ -6,7 +6,6 @@ root = true
# All Files
[*]
charset = utf-8
end_of_line = crlf
indent_style = space
indent_size = 4
insert_final_newline = false

View File

@@ -1,5 +1,12 @@
# Changelog
## 0.15.2 (21 Aug 2018)
- Fix footnotes parsing when they are defined after a container that has been closed in the meantime (#223)
## 0.15.1 (10 July 2018)
- Add support for `netstandard2.0`
- Make AutoIdentifierExtension thread safe
## 0.15.0 (4 Apr 2018)
- Add `ConfigureNewLine` extension method to `MarkdownPipelineBuilder` ([(PR #214)](https://github.com/lunet-io/markdig/pull/214))
- Add alternative `Use` extension method to `MarkdownPipelineBuilder` that receives an object instance ([(PR #213)](https://github.com/lunet-io/markdig/pull/213))

View File

@@ -1,4 +1,4 @@
Copyright (c) 2016, Alexandre Mutel
Copyright (c) 2018, Alexandre Mutel
All rights reserved.
Redistribution and use in source and binary forms, with or without modification

View File

@@ -57,6 +57,7 @@
<DependentUpon>Specs.tt</DependentUpon>
</Compile>
<Compile Include="Specs\TestEmphasisPlus.cs" />
<Compile Include="TestConfigureNewLine.cs" />
<Compile Include="TestHtmlAttributes.cs" />
<Compile Include="TestHtmlHelper.cs" />
<Compile Include="TestLineReader.cs" />

View File

@@ -118,3 +118,24 @@ Here is a footnote[^1]. And another one[^2]. And a third one[^3]. And a fourth[^
</ol>
</div>
````````````````````````````````
A footnote link inside a list should work as well:
```````````````````````````````` example
- abc
- def[^1]
[^1]: Here is the footnote.
.
<ul>
<li>abc</li>
<li>def<a id="fnref:1" href="#fn:1" class="footnote-ref"><sup>1</sup></a></li>
</ul>
<div class="footnotes">
<hr />
<ol>
<li id="fn:1">
<p>Here is the footnote.<a href="#fnref:1" class="footnote-back-ref">&#8617;</a></p></li>
</ol>
</div>
````````````````````````````````

View File

@@ -1,4 +1,5 @@
using System;
// Generated the 08/21/2018 10:54:59
using System;
using NUnit.Framework;
namespace Markdig.Tests
@@ -17564,6 +17565,39 @@ namespace Markdig.Tests
Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 3, "Extensions Footontes");
TestParser.TestSpec("Here is a footnote[^1]. And another one[^2]. And a third one[^3]. And a fourth[^4].\n\n[^1]: Footnote 1 text\n[^2]: Footnote 2 text\n[^3]: Footnote 3 text\n[^4]: Footnote 4 text", "<p>Here is a footnote<a id=\"fnref:1\" href=\"#fn:1\" class=\"footnote-ref\"><sup>1</sup></a>. And another one<a id=\"fnref:2\" href=\"#fn:2\" class=\"footnote-ref\"><sup>2</sup></a>. And a third one<a id=\"fnref:3\" href=\"#fn:3\" class=\"footnote-ref\"><sup>3</sup></a>. And a fourth<a id=\"fnref:4\" href=\"#fn:4\" class=\"footnote-ref\"><sup>4</sup></a>.</p>\n<div class=\"footnotes\">\n<hr />\n<ol>\n<li id=\"fn:1\">\n<p>Footnote 1 text<a href=\"#fnref:1\" class=\"footnote-back-ref\">&#8617;</a></p></li>\n<li id=\"fn:2\">\n<p>Footnote 2 text<a href=\"#fnref:2\" class=\"footnote-back-ref\">&#8617;</a></p></li>\n<li id=\"fn:3\">\n<p>Footnote 3 text<a href=\"#fnref:3\" class=\"footnote-back-ref\">&#8617;</a></p></li>\n<li id=\"fn:4\">\n<p>Footnote 4 text<a href=\"#fnref:4\" class=\"footnote-back-ref\">&#8617;</a></p></li>\n</ol>\n</div>", "footnotes|advanced");
}
}
// A footnote link inside a list should work as well:
[TestFixture]
public partial class TestExtensionsFootontes
{
[Test]
public void ExtensionsFootontes_Example004()
{
// Example 4
// Section: Extensions Footontes
//
// The following CommonMark:
// - abc
// - def[^1]
//
// [^1]: Here is the footnote.
//
// Should be rendered as:
// <ul>
// <li>abc</li>
// <li>def<a id="fnref:1" href="#fn:1" class="footnote-ref"><sup>1</sup></a></li>
// </ul>
// <div class="footnotes">
// <hr />
// <ol>
// <li id="fn:1">
// <p>Here is the footnote.<a href="#fnref:1" class="footnote-back-ref">&#8617;</a></p></li>
// </ol>
// </div>
Console.WriteLine("Example {0}" + Environment.NewLine + "Section: {0}" + Environment.NewLine, 4, "Extensions Footontes");
TestParser.TestSpec("- abc\n- def[^1]\n\n[^1]: Here is the footnote.", "<ul>\n<li>abc</li>\n<li>def<a id=\"fnref:1\" href=\"#fn:1\" class=\"footnote-ref\"><sup>1</sup></a></li>\n</ul>\n<div class=\"footnotes\">\n<hr />\n<ol>\n<li id=\"fn:1\">\n<p>Here is the footnote.<a href=\"#fnref:1\" class=\"footnote-back-ref\">&#8617;</a></p></li>\n</ol>\n</div>", "footnotes|advanced");
}
}
// # Extensions
//
@@ -20142,7 +20176,7 @@ namespace Markdig.Tests
TestParser.TestSpec("# This is a heading", "<h1 id=\"this-is-a-heading\">This is a heading</h1>", "autoidentifiers|advanced");
}
}
// Only punctuation `-`, `_` and `.` is kept, all over non letter characters are discarded.
// Only punctuation `-`, `_` and `.` is kept, all other non letter characters are discarded.
// Consecutive same character `-`, `_` or `.` are rendered into a single one
// Characters `-`, `_` and `.` at the end of the string are also discarded.
[TestFixture]

View File

@@ -67,6 +67,7 @@ SOFTWARE.
var emptyLines = false;
var displayEmptyLines = false;
#>
// Generated the <#= DateTime.Now #>
using System;
using NUnit.Framework;

View File

@@ -0,0 +1,25 @@
using NUnit.Framework;
namespace Markdig.Tests
{
[TestFixture]
public class TestConfigureNewLine
{
[Test]
[TestCase(/* newLineForWriting: */ "\n", /* markdownText: */ "*1*\n*2*\n", /* expected: */ "<p><em>1</em>\n<em>2</em></p>\n")]
[TestCase(/* newLineForWriting: */ "\n", /* markdownText: */ "*1*\r\n*2*\r\n", /* expected: */ "<p><em>1</em>\n<em>2</em></p>\n")]
[TestCase(/* newLineForWriting: */ "\r\n", /* markdownText: */ "*1*\n*2*\n", /* expected: */ "<p><em>1</em>\r\n<em>2</em></p>\r\n")]
[TestCase(/* newLineForWriting: */ "\r\n", /* markdownText: */ "*1*\r\n*2*\r\n", /* expected: */ "<p><em>1</em>\r\n<em>2</em></p>\r\n")]
[TestCase(/* newLineForWriting: */ "!!!" , /* markdownText: */ "*1*\n*2*\n", /* expected: */ "<p><em>1</em>!!!<em>2</em></p>!!!")]
[TestCase(/* newLineForWriting: */ "!!!" , /* markdownText: */ "*1*\r\n*2*\r\n", /* expected: */ "<p><em>1</em>!!!<em>2</em></p>!!!")]
public void TestHtmlOutputWhenConfiguringNewLine(string newLineForWriting, string markdownText, string expected)
{
var pipeline = new MarkdownPipelineBuilder()
.ConfigureNewLine(newLineForWriting)
.Build();
var actual = Markdown.ToHtml(markdownText, pipeline);
Assert.AreEqual(expected, actual);
}
}
}

View File

@@ -1,201 +1,199 @@
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using System;
using System.Collections.Generic;
using System.IO;
using Markdig.Helpers;
using Markdig.Parsers;
using Markdig.Renderers;
using Markdig.Renderers.Html;
using Markdig.Syntax;
using Markdig.Syntax.Inlines;
namespace Markdig.Extensions.AutoIdentifiers
{
/// <summary>
/// The auto-identifier extension
/// </summary>
/// <seealso cref="Markdig.IMarkdownExtension" />
public class AutoIdentifierExtension : IMarkdownExtension
{
private const string AutoIdentifierKey = "AutoIdentifier";
private readonly HtmlRenderer stripRenderer;
private readonly StringWriter headingWriter;
private readonly AutoIdentifierOptions options;
/// <summary>
/// Initializes a new instance of the <see cref="AutoIdentifierExtension"/> class.
/// </summary>
/// <param name="options">The options.</param>
public AutoIdentifierExtension(AutoIdentifierOptions options)
{
this.options = options;
headingWriter = new StringWriter();
// Use internally a HtmlRenderer to strip links from a heading
stripRenderer = new HtmlRenderer(headingWriter)
{
// Set to false both to avoid having any HTML tags in the output
EnableHtmlForInline = false,
EnableHtmlEscape = false
};
}
public void Setup(MarkdownPipelineBuilder pipeline)
{
var headingBlockParser = pipeline.BlockParsers.Find<HeadingBlockParser>();
if (headingBlockParser != null)
{
// Install a hook on the HeadingBlockParser when a HeadingBlock is actually processed
headingBlockParser.Closed -= HeadingBlockParser_Closed;
headingBlockParser.Closed += HeadingBlockParser_Closed;
}
var paragraphBlockParser = pipeline.BlockParsers.FindExact<ParagraphBlockParser>();
if (paragraphBlockParser != null)
{
// Install a hook on the ParagraphBlockParser when a HeadingBlock is actually processed as a Setex heading
paragraphBlockParser.Closed -= HeadingBlockParser_Closed;
paragraphBlockParser.Closed += HeadingBlockParser_Closed;
}
}
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
{
}
/// <summary>
/// Process on a new <see cref="HeadingBlock"/>
/// </summary>
/// <param name="processor">The processor.</param>
/// <param name="block">The heading block.</param>
private void HeadingBlockParser_Closed(BlockProcessor processor, Block block)
{
// We may have a ParagraphBlock here as we have a hook on the ParagraphBlockParser
var headingBlock = block as HeadingBlock;
if (headingBlock == null)
{
return;
}
// If the AutoLink options is set, we register a LinkReferenceDefinition at the document level
if ((options & AutoIdentifierOptions.AutoLink) != 0)
{
var headingLine = headingBlock.Lines.Lines[0];
var text = headingLine.ToString();
var linkRef = new HeadingLinkReferenceDefinition()
{
Heading = headingBlock,
CreateLinkInline = CreateLinkInlineForHeading
};
var doc = processor.Document;
var dictionary = doc.GetData(this) as Dictionary<string, HeadingLinkReferenceDefinition>;
if (dictionary == null)
{
dictionary = new Dictionary<string, HeadingLinkReferenceDefinition>();
doc.SetData(this, dictionary);
doc.ProcessInlinesBegin += DocumentOnProcessInlinesBegin;
}
dictionary[text] = linkRef;
}
// Then we register after inline have been processed to actually generate the proper #id
headingBlock.ProcessInlinesEnd += HeadingBlock_ProcessInlinesEnd;
}
private void DocumentOnProcessInlinesBegin(InlineProcessor processor, Inline inline)
{
var doc = processor.Document;
doc.ProcessInlinesBegin -= DocumentOnProcessInlinesBegin;
var dictionary = (Dictionary<string, HeadingLinkReferenceDefinition>)doc.GetData(this);
foreach (var keyPair in dictionary)
{
// Here we make sure that auto-identifiers will not override an existing link definition
// defined in the document
// If it is the case, we skip the auto identifier for the Heading
if (!doc.TryGetLinkReferenceDefinition(keyPair.Key, out var linkDef))
{
doc.SetLinkReferenceDefinition(keyPair.Key, keyPair.Value);
}
}
// Once we are done, we don't need to keep the intermediate dictionary arround
doc.RemoveData(this);
}
/// <summary>
/// Callback when there is a reference to found to a heading.
/// Note that reference are only working if they are declared after.
/// </summary>
private Inline CreateLinkInlineForHeading(InlineProcessor inlineState, LinkReferenceDefinition linkRef, Inline child)
{
var headingRef = (HeadingLinkReferenceDefinition) linkRef;
return new LinkInline()
{
// Use GetDynamicUrl to allow late binding of the Url (as a link may occur before the heading is declared and
// the inlines of the heading are actually processed by HeadingBlock_ProcessInlinesEnd)
GetDynamicUrl = () => HtmlHelper.Unescape("#" + headingRef.Heading.GetAttributes().Id),
Title = HtmlHelper.Unescape(linkRef.Title),
};
}
/// <summary>
/// Process the inlines of the heading to create a unique identifier
/// </summary>
/// <param name="processor">The processor.</param>
/// <param name="inline">The inline.</param>
private void HeadingBlock_ProcessInlinesEnd(InlineProcessor processor, Inline inline)
{
var identifiers = processor.Document.GetData(AutoIdentifierKey) as HashSet<string>;
if (identifiers == null)
{
identifiers = new HashSet<string>();
processor.Document.SetData(AutoIdentifierKey, identifiers);
}
var headingBlock = (HeadingBlock) processor.Block;
if (headingBlock.Inline == null)
{
return;
}
// If id is already set, don't try to modify it
var attributes = processor.Block.GetAttributes();
if (attributes.Id != null)
{
return;
}
// Use a HtmlRenderer with
stripRenderer.Render(headingBlock.Inline);
var headingText = headingWriter.ToString();
headingWriter.GetStringBuilder().Length = 0;
// Urilize the link
headingText = (options & AutoIdentifierOptions.GitHub) != 0
? LinkHelper.UrilizeAsGfm(headingText)
: LinkHelper.Urilize(headingText, (options & AutoIdentifierOptions.AllowOnlyAscii) != 0);
// If the heading is empty, use the word "section" instead
var baseHeadingId = string.IsNullOrEmpty(headingText) ? "section" : headingText;
// Add a trailing -1, -2, -3...etc. in case of collision
int index = 0;
var headingId = baseHeadingId;
var headingBuffer = StringBuilderCache.Local();
while (!identifiers.Add(headingId))
{
index++;
headingBuffer.Append(baseHeadingId);
headingBuffer.Append('-');
headingBuffer.Append(index);
headingId = headingBuffer.ToString();
headingBuffer.Length = 0;
}
attributes.Id = headingId;
}
}
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using System;
using System.Collections.Generic;
using System.IO;
using Markdig.Helpers;
using Markdig.Parsers;
using Markdig.Renderers;
using Markdig.Renderers.Html;
using Markdig.Syntax;
using Markdig.Syntax.Inlines;
namespace Markdig.Extensions.AutoIdentifiers
{
/// <summary>
/// The auto-identifier extension
/// </summary>
/// <seealso cref="Markdig.IMarkdownExtension" />
public class AutoIdentifierExtension : IMarkdownExtension
{
private const string AutoIdentifierKey = "AutoIdentifier";
private readonly AutoIdentifierOptions options;
/// <summary>
/// Initializes a new instance of the <see cref="AutoIdentifierExtension"/> class.
/// </summary>
/// <param name="options">The options.</param>
public AutoIdentifierExtension(AutoIdentifierOptions options)
{
this.options = options;
}
public void Setup(MarkdownPipelineBuilder pipeline)
{
var headingBlockParser = pipeline.BlockParsers.Find<HeadingBlockParser>();
if (headingBlockParser != null)
{
// Install a hook on the HeadingBlockParser when a HeadingBlock is actually processed
headingBlockParser.Closed -= HeadingBlockParser_Closed;
headingBlockParser.Closed += HeadingBlockParser_Closed;
}
var paragraphBlockParser = pipeline.BlockParsers.FindExact<ParagraphBlockParser>();
if (paragraphBlockParser != null)
{
// Install a hook on the ParagraphBlockParser when a HeadingBlock is actually processed as a Setex heading
paragraphBlockParser.Closed -= HeadingBlockParser_Closed;
paragraphBlockParser.Closed += HeadingBlockParser_Closed;
}
}
public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer)
{
}
/// <summary>
/// Process on a new <see cref="HeadingBlock"/>
/// </summary>
/// <param name="processor">The processor.</param>
/// <param name="block">The heading block.</param>
private void HeadingBlockParser_Closed(BlockProcessor processor, Block block)
{
// We may have a ParagraphBlock here as we have a hook on the ParagraphBlockParser
var headingBlock = block as HeadingBlock;
if (headingBlock == null)
{
return;
}
// If the AutoLink options is set, we register a LinkReferenceDefinition at the document level
if ((options & AutoIdentifierOptions.AutoLink) != 0)
{
var headingLine = headingBlock.Lines.Lines[0];
var text = headingLine.ToString();
var linkRef = new HeadingLinkReferenceDefinition()
{
Heading = headingBlock,
CreateLinkInline = CreateLinkInlineForHeading
};
var doc = processor.Document;
var dictionary = doc.GetData(this) as Dictionary<string, HeadingLinkReferenceDefinition>;
if (dictionary == null)
{
dictionary = new Dictionary<string, HeadingLinkReferenceDefinition>();
doc.SetData(this, dictionary);
doc.ProcessInlinesBegin += DocumentOnProcessInlinesBegin;
}
dictionary[text] = linkRef;
}
// Then we register after inline have been processed to actually generate the proper #id
headingBlock.ProcessInlinesEnd += HeadingBlock_ProcessInlinesEnd;
}
private void DocumentOnProcessInlinesBegin(InlineProcessor processor, Inline inline)
{
var doc = processor.Document;
doc.ProcessInlinesBegin -= DocumentOnProcessInlinesBegin;
var dictionary = (Dictionary<string, HeadingLinkReferenceDefinition>)doc.GetData(this);
foreach (var keyPair in dictionary)
{
// Here we make sure that auto-identifiers will not override an existing link definition
// defined in the document
// If it is the case, we skip the auto identifier for the Heading
if (!doc.TryGetLinkReferenceDefinition(keyPair.Key, out var linkDef))
{
doc.SetLinkReferenceDefinition(keyPair.Key, keyPair.Value);
}
}
// Once we are done, we don't need to keep the intermediate dictionary arround
doc.RemoveData(this);
}
/// <summary>
/// Callback when there is a reference to found to a heading.
/// Note that reference are only working if they are declared after.
/// </summary>
private Inline CreateLinkInlineForHeading(InlineProcessor inlineState, LinkReferenceDefinition linkRef, Inline child)
{
var headingRef = (HeadingLinkReferenceDefinition) linkRef;
return new LinkInline()
{
// Use GetDynamicUrl to allow late binding of the Url (as a link may occur before the heading is declared and
// the inlines of the heading are actually processed by HeadingBlock_ProcessInlinesEnd)
GetDynamicUrl = () => HtmlHelper.Unescape("#" + headingRef.Heading.GetAttributes().Id),
Title = HtmlHelper.Unescape(linkRef.Title),
};
}
/// <summary>
/// Process the inlines of the heading to create a unique identifier
/// </summary>
/// <param name="processor">The processor.</param>
/// <param name="inline">The inline.</param>
private void HeadingBlock_ProcessInlinesEnd(InlineProcessor processor, Inline inline)
{
var identifiers = processor.Document.GetData(AutoIdentifierKey) as HashSet<string>;
if (identifiers == null)
{
identifiers = new HashSet<string>();
processor.Document.SetData(AutoIdentifierKey, identifiers);
}
var headingBlock = (HeadingBlock) processor.Block;
if (headingBlock.Inline == null)
{
return;
}
// If id is already set, don't try to modify it
var attributes = processor.Block.GetAttributes();
if (attributes.Id != null)
{
return;
}
// Use internally a HtmlRenderer to strip links from a heading
var headingWriter = new StringWriter();
var stripRenderer = new HtmlRenderer(headingWriter)
{
// Set to false both to avoid having any HTML tags in the output
EnableHtmlForInline = false,
EnableHtmlEscape = false
};
stripRenderer.Render(headingBlock.Inline);
var headingText = headingWriter.ToString();
headingWriter.GetStringBuilder().Length = 0;
// Urilize the link
headingText = (options & AutoIdentifierOptions.GitHub) != 0
? LinkHelper.UrilizeAsGfm(headingText)
: LinkHelper.Urilize(headingText, (options & AutoIdentifierOptions.AllowOnlyAscii) != 0);
// If the heading is empty, use the word "section" instead
var baseHeadingId = string.IsNullOrEmpty(headingText) ? "section" : headingText;
// Add a trailing -1, -2, -3...etc. in case of collision
int index = 0;
var headingId = baseHeadingId;
var headingBuffer = StringBuilderCache.Local();
while (!identifiers.Add(headingId))
{
index++;
headingBuffer.Append(baseHeadingId);
headingBuffer.Append('-');
headingBuffer.Append(index);
headingId = headingBuffer.ToString();
headingBuffer.Length = 0;
}
attributes.Id = headingId;
}
}
}

View File

@@ -33,7 +33,8 @@ namespace Markdig.Extensions.Footnotes
private BlockState TryOpen(BlockProcessor processor, bool isContinue)
{
// We expect footnote to appear only at document level and not indented more than a code indent block
if (processor.IsCodeIndent || (!isContinue && processor.CurrentContainer.GetType() != typeof(MarkdownDocument)) || (isContinue && !(processor.CurrentContainer is Footnote)))
var currentContainer = processor.GetCurrentContainerOpened();
if (processor.IsCodeIndent || (!isContinue && currentContainer.GetType() != typeof(MarkdownDocument)) || (isContinue && !(currentContainer is FootnoteGroup)))
{
return BlockState.None;
}

View File

@@ -21,7 +21,7 @@ namespace Markdig.Helpers
public const string ZeroSafeString = "\uFFFD";
// We don't support LCDM
private static IDictionary<char, int> romanMap = new Dictionary<char, int> { { 'I', 1 }, { 'V', 5 }, { 'X', 10 } };
private static readonly Dictionary<char, int> romanMap = new Dictionary<char, int> { { 'I', 1 }, { 'V', 5 }, { 'X', 10 } };
private static readonly char[] punctuationExceptions = { '', '-', '†', '‡' };

View File

@@ -1,13 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>A fast, powerfull, CommonMark compliant, extensible Markdown processor for .NET with 20+ builtin extensions (pipetables, footnotes, definition lists... etc.)</Description>
<Description>A fast, powerful, CommonMark compliant, extensible Markdown processor for .NET with 20+ builtin extensions (pipetables, footnotes, definition lists... etc.)</Description>
<Copyright>Alexandre Mutel</Copyright>
<AssemblyTitle>Markdig</AssemblyTitle>
<NeutralLanguage>en-US</NeutralLanguage>
<VersionPrefix>0.15.0</VersionPrefix>
<VersionPrefix>0.15.2</VersionPrefix>
<Authors>Alexandre Mutel</Authors>
<TargetFrameworks>net35;net40;portable40-net40+sl5+win8+wp8+wpa81;netstandard1.1;uap10.0</TargetFrameworks>
<TargetFrameworks>net35;net40;portable40-net40+sl5+win8+wp8+wpa81;netstandard1.1;netstandard2.0;uap10.0</TargetFrameworks>
<AssemblyName>Markdig</AssemblyName>
<PackageId>Markdig</PackageId>
<PackageId Condition="'$(SignAssembly)' == 'true'">Markdig.Signed</PackageId>
@@ -51,6 +51,10 @@
<DefineConstants>$(DefineConstants);NETSTANDARD_11;SUPPORT_UNSAFE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard2.0' ">
<DefineConstants>$(DefineConstants);SUPPORT_FIXED_STRING;SUPPORT_UNSAFE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition=" '$(TargetFramework)' == 'uap10.0' ">
<TargetPlatformIdentifier>UAP</TargetPlatformIdentifier>
<TargetPlatformVersion Condition="'$(TargetPlatformVersion)' == ''">10.0.10240.0</TargetPlatformVersion>

View File

@@ -1,4 +1,4 @@
// Copyright (c) Alexandre Mutel. All rights reserved.
// Copyright (c) Alexandre Mutel. All rights reserved.
// This file is licensed under the BSD-Clause 2 license.
// See the license.txt file in the project root for more information.
using System;
@@ -162,6 +162,21 @@ namespace Markdig.Parsers
/// </summary>
private bool ContinueProcessingLine { get; set; }
/// <summary>
/// Get the current Container that is currently opened
/// </summary>
/// <returns>The current Container that is currently opened</returns>
public ContainerBlock GetCurrentContainerOpened()
{
var container = CurrentContainer;
while (container != null && !container.IsOpen)
{
container = container.Parent;
}
return container;
}
/// <summary>
/// Returns the next character in the line being processed. Update <see cref="Start"/> and <see cref="Column"/>.
/// </summary>