mirror of
https://github.com/xoofx/markdig.git
synced 2026-02-14 05:45:05 +00:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
16a9bbc84e | ||
|
|
0e5338a709 | ||
|
|
9139e0142b | ||
|
|
9a38312df0 |
@@ -304,6 +304,24 @@ asdf
|
||||
AssertNormalizeNoTrim(@"This is a [link](http://company.com ""Crazy \"" Company"")");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void LinkReferenceDefinition()
|
||||
{
|
||||
// Full link
|
||||
AssertNormalizeNoTrim("This is a [link][MyLink]\n\n[MyLink]: http://company.com");
|
||||
|
||||
AssertNormalizeNoTrim("[MyLink]: http://company.com\nThis is a [link][MyLink]",
|
||||
"This is a [link][MyLink]\n\n[MyLink]: http://company.com");
|
||||
|
||||
AssertNormalizeNoTrim("This is a [link][MyLink] a normal link [link](http://google.com) and another def link [link2][MyLink2]\n\n[MyLink]: http://company.com\n[MyLink2]: http://company2.com");
|
||||
|
||||
// Collapsed link
|
||||
AssertNormalizeNoTrim("This is a [link][]\n\n[link]: http://company.com");
|
||||
|
||||
// Shortcut link
|
||||
AssertNormalizeNoTrim("This is a [link]\n\n[link]: http://company.com");
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EscapeInline()
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
@@ -6,11 +6,19 @@ using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using Markdig.Extensions.JiraLinks;
|
||||
using NUnit.Framework;
|
||||
|
||||
namespace Markdig.Tests
|
||||
{
|
||||
public class TestParser
|
||||
{
|
||||
[Test]
|
||||
public void TestEmphasisAndHtmlEntity()
|
||||
{
|
||||
var markdownText = "*Unlimited-Fun®*®";
|
||||
TestSpec(markdownText, "<p><em>Unlimited-Fun®</em>®</p>");
|
||||
}
|
||||
|
||||
public static void TestSpec(string inputText, string expectedOutputText, string extensions = null)
|
||||
{
|
||||
foreach (var pipeline in GetPipeline(extensions))
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -12,12 +12,19 @@ namespace Markdig.Extensions.Emoji
|
||||
/// <seealso cref="Markdig.IMarkdownExtension" />
|
||||
public class EmojiExtension : IMarkdownExtension
|
||||
{
|
||||
private readonly bool _enableSmiley;
|
||||
|
||||
public EmojiExtension(bool enableSmiley = true)
|
||||
{
|
||||
_enableSmiley = enableSmiley;
|
||||
}
|
||||
|
||||
public void Setup(MarkdownPipelineBuilder pipeline)
|
||||
{
|
||||
if (!pipeline.InlineParsers.Contains<EmojiParser>())
|
||||
{
|
||||
// Insert the parser before any other parsers
|
||||
pipeline.InlineParsers.Insert(0, new EmojiParser());
|
||||
pipeline.InlineParsers.Insert(0, new EmojiParser(_enableSmiley));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -24,13 +24,19 @@ namespace Markdig.Extensions.Emoji
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="EmojiParser"/> class.
|
||||
/// </summary>
|
||||
public EmojiParser()
|
||||
public EmojiParser(bool enableSmiley = true)
|
||||
{
|
||||
EnableSmiley = enableSmiley;
|
||||
OpeningCharacters = null;
|
||||
EmojiToUnicode = new Dictionary<string, string>(EmojiToUnicodeDefault);
|
||||
SmileyToEmoji = new Dictionary<string, string>(SmileyToEmojiDefault);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating whether to process smiley.
|
||||
/// </summary>
|
||||
public bool EnableSmiley { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the emoji to unicode mapping. This can be modified before this parser is initialized.
|
||||
/// </summary>
|
||||
@@ -81,11 +87,14 @@ namespace Markdig.Extensions.Emoji
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we have a smiley, we decode it to emoji
|
||||
string emoji;
|
||||
if (!SmileyToEmoji.TryGetValue(match, out emoji))
|
||||
string emoji = match;
|
||||
if (EnableSmiley)
|
||||
{
|
||||
emoji = match;
|
||||
// If we have a smiley, we decode it to emoji
|
||||
if (!SmileyToEmoji.TryGetValue(match, out emoji))
|
||||
{
|
||||
emoji = match;
|
||||
}
|
||||
}
|
||||
|
||||
// Decode the eomji to unicode
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<Copyright>Alexandre Mutel</Copyright>
|
||||
<AssemblyTitle>Markdig</AssemblyTitle>
|
||||
<NeutralLanguage>en-US</NeutralLanguage>
|
||||
<VersionPrefix>0.14.1</VersionPrefix>
|
||||
<VersionPrefix>0.14.2</VersionPrefix>
|
||||
<Authors>Alexandre Mutel</Authors>
|
||||
<TargetFrameworks>net35;net40;portable40-net40+sl5+win8+wp8+wpa81;netstandard1.1;uap10.0</TargetFrameworks>
|
||||
<AssemblyName>Markdig</AssemblyName>
|
||||
@@ -13,6 +13,10 @@
|
||||
<PackageId Condition="'$(SignAssembly)' == 'true'">Markdig.Signed</PackageId>
|
||||
<PackageTags>Markdown CommonMark md html md2html</PackageTags>
|
||||
<PackageReleaseNotes>
|
||||
> 0.14.2
|
||||
- Fix issue with emphasis preceded/followed by an HTML entity (#157)
|
||||
- Add support for link reference definitions for Normalize renderer (#155)
|
||||
- Add option to disable smiley parsing in EmojiAndSmiley extension
|
||||
> 0.14.1
|
||||
- Fix crash in Markdown.Normalize to handle HtmlBlock correctly
|
||||
- Add better handling of bullet character for lists in Markdown.Normalize
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -409,10 +409,14 @@ namespace Markdig
|
||||
/// Uses the emoji and smiley extension.
|
||||
/// </summary>
|
||||
/// <param name="pipeline">The pipeline.</param>
|
||||
/// <param name="enableSmiley">Enable smiley in addition to Emoji, <c>true</c> by default.</param>
|
||||
/// <returns>The modified pipeline</returns>
|
||||
public static MarkdownPipelineBuilder UseEmojiAndSmiley(this MarkdownPipelineBuilder pipeline)
|
||||
public static MarkdownPipelineBuilder UseEmojiAndSmiley(this MarkdownPipelineBuilder pipeline, bool enableSmiley = true)
|
||||
{
|
||||
pipeline.Extensions.AddIfNotAlready<EmojiExtension>();
|
||||
if (!pipeline.Extensions.Contains<EmojiExtension>())
|
||||
{
|
||||
pipeline.Extensions.Add(new EmojiExtension(enableSmiley));
|
||||
}
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
|
||||
@@ -101,7 +101,7 @@ namespace Markdig.Parsers.Inlines
|
||||
}
|
||||
|
||||
// Move current_position forward in the delimiter stack (if needed) until
|
||||
// we find the first potential closer with delimiter * or _. (This will be the potential closer closest to the beginning of the input – the first one in parse order.)
|
||||
// we find the first potential closer with delimiter * or _. (This will be the potential closer closest to the beginning of the input – the first one in parse order.)
|
||||
var child = container.LastChild;
|
||||
while (child != null)
|
||||
{
|
||||
@@ -138,12 +138,24 @@ namespace Markdig.Parsers.Inlines
|
||||
|
||||
var delimiterChar = slice.CurrentChar;
|
||||
var emphasisDesc = emphasisMap[delimiterChar];
|
||||
var pc = slice.PeekCharExtra(-1);
|
||||
if (pc == delimiterChar && slice.PeekCharExtra(-2) != '\\')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var pc = (char)0;
|
||||
if (processor.Inline is HtmlEntityInline)
|
||||
{
|
||||
var htmlEntityInline = (HtmlEntityInline) processor.Inline;
|
||||
if (htmlEntityInline.Transcoded.Length > 0)
|
||||
{
|
||||
pc = htmlEntityInline.Transcoded[htmlEntityInline.Transcoded.End];
|
||||
}
|
||||
}
|
||||
if (pc == 0)
|
||||
{
|
||||
pc = slice.PeekCharExtra(-1);
|
||||
if (pc == delimiterChar && slice.PeekCharExtra(-2) != '\\')
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
var startPosition = slice.Start;
|
||||
|
||||
int delimiterCount = 0;
|
||||
@@ -161,6 +173,14 @@ namespace Markdig.Parsers.Inlines
|
||||
return false;
|
||||
}
|
||||
|
||||
// The following character is actually an entity, we need to decode it
|
||||
int htmlLength;
|
||||
string htmlString;
|
||||
if (HtmlEntityParser.TryParse(ref slice, out htmlString, out htmlLength))
|
||||
{
|
||||
c = htmlString[0];
|
||||
}
|
||||
|
||||
// Calculate Open-Close for current character
|
||||
bool canOpen;
|
||||
bool canClose;
|
||||
@@ -204,7 +224,7 @@ namespace Markdig.Parsers.Inlines
|
||||
// at the end of the CommonMark specs.
|
||||
|
||||
// Move current_position forward in the delimiter stack (if needed) until
|
||||
// we find the first potential closer with delimiter * or _. (This will be the potential closer closest to the beginning of the input – the first one in parse order.)
|
||||
// we find the first potential closer with delimiter * or _. (This will be the potential closer closest to the beginning of the input – the first one in parse order.)
|
||||
for (int i = 0; i < delimiters.Count; i++)
|
||||
{
|
||||
var closeDelimiter = delimiters[i];
|
||||
@@ -219,7 +239,7 @@ namespace Markdig.Parsers.Inlines
|
||||
while (true)
|
||||
{
|
||||
// Now, look back in the stack (staying above stack_bottom and the openers_bottom for this delimiter type)
|
||||
// for the first matching potential opener (“matching” means same delimiter).
|
||||
// for the first matching potential opener (“matching” means same delimiter).
|
||||
EmphasisDelimiterInline openDelimiter = null;
|
||||
int openDelimiterIndex = -1;
|
||||
for (int j = i - 1; j >= 0; j--)
|
||||
|
||||
@@ -21,18 +21,18 @@ namespace Markdig.Parsers.Inlines
|
||||
OpeningCharacters = new[] {'&'};
|
||||
}
|
||||
|
||||
public override bool Match(InlineProcessor processor, ref StringSlice slice)
|
||||
|
||||
public static bool TryParse(ref StringSlice slice, out string literal, out int match)
|
||||
{
|
||||
literal = null;
|
||||
string entityName;
|
||||
int entityValue;
|
||||
var startPosition = slice.Start;
|
||||
int match = HtmlHelper.ScanEntity(slice.Text, slice.Start, slice.Length, out entityName, out entityValue);
|
||||
match = HtmlHelper.ScanEntity(slice.Text, slice.Start, slice.Length, out entityName, out entityValue);
|
||||
if (match == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
string literal = null;
|
||||
if (entityName != null)
|
||||
{
|
||||
literal = EntityHelper.DecodeEntity(entityName);
|
||||
@@ -41,6 +41,19 @@ namespace Markdig.Parsers.Inlines
|
||||
{
|
||||
literal = (entityValue == 0 ? null : EntityHelper.DecodeEntity(entityValue)) ?? CharHelper.ZeroSafeString;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool Match(InlineProcessor processor, ref StringSlice slice)
|
||||
{
|
||||
int match;
|
||||
string literal;
|
||||
if (!TryParse(ref slice, out literal, out match))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var startPosition = slice.Start;
|
||||
|
||||
if (literal != null)
|
||||
{
|
||||
|
||||
@@ -86,7 +86,7 @@ namespace Markdig.Parsers.Inlines
|
||||
}
|
||||
}
|
||||
|
||||
// If we don’t find one, we return a literal slice node ].
|
||||
// If we don’t find one, we return a literal slice node ].
|
||||
// (Done after by the LiteralInline parser)
|
||||
return false;
|
||||
}
|
||||
@@ -95,7 +95,7 @@ namespace Markdig.Parsers.Inlines
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool ProcessLinkReference(InlineProcessor state, string label, SourceSpan labelSpan, LinkDelimiterInline parent, int endPosition)
|
||||
private bool ProcessLinkReference(InlineProcessor state, string label, bool isShortcut, SourceSpan labelSpan, LinkDelimiterInline parent, int endPosition)
|
||||
{
|
||||
bool isValidLink = false;
|
||||
LinkReferenceDefinition linkRef;
|
||||
@@ -120,6 +120,7 @@ namespace Markdig.Parsers.Inlines
|
||||
LabelSpan = labelSpan,
|
||||
UrlSpan = linkRef.UrlSpan,
|
||||
IsImage = parent.IsImage,
|
||||
IsShortcut = isShortcut,
|
||||
Reference = linkRef,
|
||||
Span = new SourceSpan(parent.Span.Start, endPosition),
|
||||
Line = parent.Line,
|
||||
@@ -189,7 +190,7 @@ namespace Markdig.Parsers.Inlines
|
||||
|
||||
if (openParent != null)
|
||||
{
|
||||
// If we do find one, but it’s not active,
|
||||
// If we do find one, but it’s not active,
|
||||
// we remove the inactive delimiter from the stack,
|
||||
// and return a literal text node ].
|
||||
if (!openParent.IsActive)
|
||||
@@ -205,7 +206,7 @@ namespace Markdig.Parsers.Inlines
|
||||
return false;
|
||||
}
|
||||
|
||||
// If we find one and it’s active,
|
||||
// If we find one and it’s active,
|
||||
// then we parse ahead to see if we have
|
||||
// an inline link/image, reference link/image,
|
||||
// compact reference link/image,
|
||||
@@ -261,6 +262,8 @@ namespace Markdig.Parsers.Inlines
|
||||
var labelSpan = SourceSpan.Empty;
|
||||
string label = null;
|
||||
bool isLabelSpanLocal = true;
|
||||
|
||||
bool isShortcut = false;
|
||||
// Handle Collapsed links
|
||||
if (text.CurrentChar == '[')
|
||||
{
|
||||
@@ -276,6 +279,7 @@ namespace Markdig.Parsers.Inlines
|
||||
else
|
||||
{
|
||||
label = openParent.Label;
|
||||
isShortcut = true;
|
||||
}
|
||||
|
||||
if (label != null || LinkHelper.TryParseLabel(ref text, true, out label, out labelSpan))
|
||||
@@ -285,7 +289,7 @@ namespace Markdig.Parsers.Inlines
|
||||
labelSpan = inlineState.GetSourcePositionFromLocalSpan(labelSpan);
|
||||
}
|
||||
|
||||
if (ProcessLinkReference(inlineState, label, labelSpan, openParent, inlineState.GetSourcePosition(text.Start - 1)))
|
||||
if (ProcessLinkReference(inlineState, label, isShortcut, labelSpan, openParent, inlineState.GetSourcePosition(text.Start - 1)))
|
||||
{
|
||||
// Remove the open parent
|
||||
openParent.Remove();
|
||||
|
||||
@@ -20,18 +20,40 @@ namespace Markdig.Renderers.Normalize.Inlines
|
||||
renderer.Write('[');
|
||||
renderer.WriteChildren(link);
|
||||
renderer.Write(']');
|
||||
if (!string.IsNullOrEmpty(link.Url))
|
||||
|
||||
if (link.Label != null)
|
||||
{
|
||||
renderer.Write('(').Write(link.Url);
|
||||
|
||||
if (!string.IsNullOrEmpty(link.Title))
|
||||
var literal = link.FirstChild as LiteralInline;
|
||||
if (literal != null && literal.Content.Match(link.Label) && literal.Content.Length == link.Label.Length)
|
||||
{
|
||||
renderer.Write(" \"");
|
||||
renderer.Write(link.Title.Replace(@"""", @"\"""));
|
||||
renderer.Write("\"");
|
||||
// collapsed reference and shortcut links
|
||||
if (!link.IsShortcut)
|
||||
{
|
||||
renderer.Write("[]");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// full link
|
||||
renderer.Write('[').Write(link.Label).Write(']');
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!string.IsNullOrEmpty(link.Url))
|
||||
{
|
||||
renderer.Write('(').Write(link.Url);
|
||||
|
||||
renderer.Write(')');
|
||||
if (!string.IsNullOrEmpty(link.Title))
|
||||
{
|
||||
renderer.Write(" \"");
|
||||
renderer.Write(link.Title.Replace(@"""", @"\"""));
|
||||
renderer.Write("\"");
|
||||
}
|
||||
|
||||
renderer.Write(')');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
// 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 Markdig.Syntax;
|
||||
|
||||
namespace Markdig.Renderers.Normalize
|
||||
{
|
||||
public class LinkReferenceDefinitionGroupRenderer : NormalizeObjectRenderer<LinkReferenceDefinitionGroup>
|
||||
{
|
||||
protected override void Write(NormalizeRenderer renderer, LinkReferenceDefinitionGroup obj)
|
||||
{
|
||||
renderer.EnsureLine();
|
||||
renderer.WriteChildren(obj);
|
||||
renderer.FinishBlock(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// 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 Markdig.Syntax;
|
||||
|
||||
namespace Markdig.Renderers.Normalize
|
||||
{
|
||||
public class LinkReferenceDefinitionRenderer : NormalizeObjectRenderer<LinkReferenceDefinition>
|
||||
{
|
||||
protected override void Write(NormalizeRenderer renderer, LinkReferenceDefinition linkDef)
|
||||
{
|
||||
renderer.EnsureLine();
|
||||
renderer.Write('[');
|
||||
renderer.Write(linkDef.Label);
|
||||
renderer.Write("]: ");
|
||||
|
||||
renderer.Write(linkDef.Url);
|
||||
|
||||
if (linkDef.Title != null)
|
||||
{
|
||||
renderer.Write(" \"");
|
||||
renderer.Write(linkDef.Title.Replace("\"", "\\\""));
|
||||
renderer.Write('"');
|
||||
}
|
||||
renderer.FinishBlock(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,8 @@ namespace Markdig.Renderers.Normalize
|
||||
ObjectRenderers.Add(new ParagraphRenderer());
|
||||
ObjectRenderers.Add(new QuoteBlockRenderer());
|
||||
ObjectRenderers.Add(new ThematicBreakRenderer());
|
||||
ObjectRenderers.Add(new LinkReferenceDefinitionGroupRenderer());
|
||||
ObjectRenderers.Add(new LinkReferenceDefinitionRenderer());
|
||||
|
||||
// Default inline renderers
|
||||
ObjectRenderers.Add(new AutolinkInlineRenderer());
|
||||
|
||||
@@ -64,6 +64,11 @@ namespace Markdig.Syntax.Inlines
|
||||
/// </summary>
|
||||
public bool IsImage { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets a boolean indicating if this link is a shortcut link to a <see cref="LinkReferenceDefinition"/>
|
||||
/// </summary>
|
||||
public bool IsShortcut { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the reference this link is attached to. May be null.
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user