mirror of
https://github.com/xoofx/markdig.git
synced 2026-02-12 05:44:48 +00:00
Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
105b09e1ec | ||
|
|
9fe7596a23 | ||
|
|
82af7cadc5 | ||
|
|
800c81bb9a | ||
|
|
5653a4f7ee | ||
|
|
4f14ffe63b | ||
|
|
d57acefe56 | ||
|
|
fb61e5a8da | ||
|
|
264516bfdb | ||
|
|
6d8f8996d5 | ||
|
|
ba8557d3bf | ||
|
|
d18fd0b957 | ||
|
|
fba96774f4 | ||
|
|
0b2764ea62 | ||
|
|
64d81ed47b | ||
|
|
9a9742888b | ||
|
|
5400b30a90 | ||
|
|
673f4a4beb | ||
|
|
05a27649aa | ||
|
|
26c6b12dea | ||
|
|
9a18ca222f | ||
|
|
354f31b870 | ||
|
|
c6c2f58ec0 |
@@ -2,7 +2,7 @@
|
||||
|
||||
<img align="right" width="160px" height="160px" src="img/markdig.png">
|
||||
|
||||
Markdig is a fast, powerfull, [CommonMark](http://commonmark.org/) compliant, extensible Markdown processor for .NET.
|
||||
Markdig is a fast, powerful, [CommonMark](http://commonmark.org/) compliant, extensible Markdown processor for .NET.
|
||||
|
||||
> NOTE: The repository is under construction. There will be a dedicated website and proper documentation at some point!
|
||||
|
||||
@@ -115,7 +115,7 @@ This is an early preview of the benchmarking against various implementations:
|
||||
### Analysis of the results:
|
||||
|
||||
- Markdig is roughly **x100 times faster than MarkdownSharp**, **30x times faster than docfx**
|
||||
- **Among the best in CPU**, Extremelly competitive and often faster than other implementations (not feature wise equivalent)
|
||||
- **Among the best in CPU**, Extremely competitive and often faster than other implementations (not feature wise equivalent)
|
||||
- **15% to 30% less allocations** and GC pressure
|
||||
|
||||
Because Marked.NET, MarkdownSharp and DocAsCode.MarkdownLite are way too slow, they are not included in the following charts:
|
||||
|
||||
@@ -86,6 +86,7 @@
|
||||
<None Include="Specs\GridTableSpecs.md" />
|
||||
<None Include="Specs\HardlineBreakSpecs.md" />
|
||||
<None Include="Specs\BootstrapSpecs.md" />
|
||||
<None Include="Specs\DiagramsSpecs.md" />
|
||||
<None Include="Specs\TaskListSpecs.md" />
|
||||
<None Include="Specs\SmartyPantsSpecs.md" />
|
||||
<None Include="Specs\MediaSpecs.md" />
|
||||
|
||||
26
src/Markdig.Tests/Specs/DiagramsSpecs.md
Normal file
26
src/Markdig.Tests/Specs/DiagramsSpecs.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# Extensions
|
||||
|
||||
Adds support for diagrams extension:
|
||||
|
||||
## Mermaid diagrams
|
||||
|
||||
Using a fenced code block with the `mermaid` language info will output a `<div class='mermaid'>` instead of a `pre/code` block:
|
||||
|
||||
```````````````````````````````` example
|
||||
```mermaid
|
||||
graph TD;
|
||||
A-->B;
|
||||
A-->C;
|
||||
B-->D;
|
||||
C-->D;
|
||||
```
|
||||
.
|
||||
<div class="mermaid">graph TD;
|
||||
A-->B;
|
||||
A-->C;
|
||||
B-->D;
|
||||
C-->D;
|
||||
</div>
|
||||
````````````````````````````````
|
||||
|
||||
TODO: Add other text diagram languages
|
||||
@@ -70,8 +70,8 @@ A regular row can continue a previous regular row when column separator `|` are
|
||||
+---------+---------+---------+
|
||||
| Col1 | Col2 | Col3 |
|
||||
| Col1a | Col2a | Col3a |
|
||||
| Col12 | Col3b |
|
||||
| Col123 |
|
||||
| Col1b | Col3b |
|
||||
| Col1c |
|
||||
.
|
||||
<table>
|
||||
<col style="width:33.33%">
|
||||
@@ -87,11 +87,11 @@ Col2a</td>
|
||||
Col3a</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">Col12</td>
|
||||
<td></td>
|
||||
<td colspan="2">Col1b</td>
|
||||
<td>Col3b</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td colspan="3">Col123</td>
|
||||
<td colspan="3">Col1c</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -38,7 +38,8 @@ SOFTWARE.
|
||||
<#@ import namespace="System.CodeDom.Compiler" #>
|
||||
<#@ output extension=".cs" #><#
|
||||
var specFiles = new KeyValuePair<string, string>[] {
|
||||
new KeyValuePair<string, string>("https://raw.githubusercontent.com/jgm/CommonMark/master/spec.txt", string.Empty),
|
||||
// new KeyValuePair<string, string>("https://raw.githubusercontent.com/jgm/CommonMark/master/spec.txt", string.Empty),
|
||||
new KeyValuePair<string, string>("https://raw.githubusercontent.com/jgm/CommonMark/91e045ca370258903ed138450373043a496ec64b/spec.txt", string.Empty), // 0.26 specs
|
||||
new KeyValuePair<string, string>(Host.ResolvePath("PipeTableSpecs.md"), "pipetables|advanced"),
|
||||
new KeyValuePair<string, string>(Host.ResolvePath("FootnotesSpecs.md"), "footnotes|advanced"),
|
||||
new KeyValuePair<string, string>(Host.ResolvePath("GenericAttributesSpecs.md"), "attributes|advanced"),
|
||||
@@ -57,6 +58,7 @@ SOFTWARE.
|
||||
new KeyValuePair<string, string>(Host.ResolvePath("SmartyPantsSpecs.md"), "pipetables+smartypants|advanced+smartypants"), // Check with smartypants to make sure that it doesn't break pipetables
|
||||
new KeyValuePair<string, string>(Host.ResolvePath("AutoIdentifierSpecs.md"), "autoidentifiers|advanced"),
|
||||
new KeyValuePair<string, string>(Host.ResolvePath("TaskListSpecs.md"), "tasklists|advanced"),
|
||||
new KeyValuePair<string, string>(Host.ResolvePath("DiagramsSpecs.md"), "diagrams|advanced"),
|
||||
};
|
||||
var emptyLines = false;
|
||||
var displayEmptyLines = false;
|
||||
|
||||
@@ -293,10 +293,6 @@ literal ( 0, 9) 9-9
|
||||
");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
[Test]
|
||||
public void TestThematicBreak()
|
||||
{
|
||||
@@ -373,12 +369,13 @@ literal ( 0, 2) 2-3
|
||||
[Test]
|
||||
public void TestHtmlEntityInline()
|
||||
{
|
||||
// 01234567
|
||||
Check("0 1", @"
|
||||
paragraph ( 0, 0) 0-7
|
||||
// 01 23456789
|
||||
Check("0\n 1", @"
|
||||
paragraph ( 0, 0) 0-9
|
||||
literal ( 0, 0) 0-0
|
||||
htmlentity ( 0, 1) 1-6
|
||||
literal ( 0, 7) 7-7
|
||||
linebreak ( 0, 1) 1-1
|
||||
htmlentity ( 1, 0) 2-7
|
||||
literal ( 1, 6) 8-9
|
||||
");
|
||||
}
|
||||
|
||||
|
||||
@@ -1,25 +1,25 @@
|
||||
{
|
||||
"dependencies": {
|
||||
"Microsoft.NETCore.App": {
|
||||
"version": "1.0.0-rc2-3002702",
|
||||
"version": "1.0.0",
|
||||
"type": "platform"
|
||||
},
|
||||
"Microsoft.ApplicationInsights.AspNetCore": "1.0.0-rc2-final",
|
||||
"Microsoft.AspNetCore.Mvc": "1.0.0-rc2-final",
|
||||
"Microsoft.AspNetCore.Server.IISIntegration": "1.0.0-rc2-final",
|
||||
"Microsoft.AspNetCore.Server.Kestrel": "1.0.0-rc2-final",
|
||||
"Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0-rc2-final",
|
||||
"Microsoft.Extensions.Configuration.FileExtensions": "1.0.0-rc2-final",
|
||||
"Microsoft.Extensions.Configuration.Json": "1.0.0-rc2-final",
|
||||
"Microsoft.Extensions.Logging": "1.0.0-rc2-final",
|
||||
"Microsoft.Extensions.Logging.Console": "1.0.0-rc2-final",
|
||||
"Microsoft.Extensions.Logging.Debug": "1.0.0-rc2-final",
|
||||
"Markdig": "0.2.1"
|
||||
"Microsoft.ApplicationInsights.AspNetCore": "1.0.0",
|
||||
"Microsoft.AspNetCore.Mvc": "1.0.0",
|
||||
"Microsoft.AspNetCore.Server.IISIntegration": "1.0.0",
|
||||
"Microsoft.AspNetCore.Server.Kestrel": "1.0.0",
|
||||
"Microsoft.Extensions.Configuration.EnvironmentVariables": "1.0.0",
|
||||
"Microsoft.Extensions.Configuration.FileExtensions": "1.0.0",
|
||||
"Microsoft.Extensions.Configuration.Json": "1.0.0",
|
||||
"Microsoft.Extensions.Logging": "1.0.0",
|
||||
"Microsoft.Extensions.Logging.Console": "1.0.0",
|
||||
"Microsoft.Extensions.Logging.Debug": "1.0.0",
|
||||
"Markdig": "0.7.2"
|
||||
},
|
||||
|
||||
"tools": {
|
||||
"Microsoft.AspNetCore.Server.IISIntegration.Tools": {
|
||||
"version": "1.0.0-preview1-final",
|
||||
"version": "1.0.0-preview2-final",
|
||||
"imports": "portable-net45+win8+dnxcore50"
|
||||
}
|
||||
},
|
||||
|
||||
31
src/Markdig/Extensions/Diagrams/DiagramExtension.cs
Normal file
31
src/Markdig/Extensions/Diagrams/DiagramExtension.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
// 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.Renderers;
|
||||
using Markdig.Renderers.Html;
|
||||
|
||||
namespace Markdig.Extensions.Diagrams
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension to allow diagrams.
|
||||
/// </summary>
|
||||
/// <seealso cref="Markdig.IMarkdownExtension" />
|
||||
public class DiagramExtension : IMarkdownExtension
|
||||
{
|
||||
public void Setup(MarkdownPipelineBuilder pipeline)
|
||||
{
|
||||
}
|
||||
|
||||
public void Setup(IMarkdownRenderer renderer)
|
||||
{
|
||||
var htmlRenderer = renderer as HtmlRenderer;
|
||||
if (htmlRenderer != null)
|
||||
{
|
||||
var codeRenderer = htmlRenderer.ObjectRenderers.FindExact<CodeBlockRenderer>();
|
||||
// TODO: Add other well known diagram languages
|
||||
codeRenderer.BlocksAsDiv.Add("mermaid");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,7 @@ namespace Markdig.Extensions.Emoji
|
||||
/// </summary>
|
||||
public Dictionary<string, string> SmileyToEmoji { get; }
|
||||
|
||||
public override void Initialize(InlineProcessor processor)
|
||||
public override void Initialize()
|
||||
{
|
||||
var firstChars = new HashSet<char>();
|
||||
var textToMatch = new HashSet<string>();
|
||||
|
||||
34
src/Markdig/Extensions/NoRefLinks/NoFollowLinksExtension.cs
Normal file
34
src/Markdig/Extensions/NoRefLinks/NoFollowLinksExtension.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
// 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.Renderers;
|
||||
using Markdig.Renderers.Html.Inlines;
|
||||
|
||||
namespace Markdig.Extensions.NoRefLinks
|
||||
{
|
||||
/// <summary>
|
||||
/// Extension to automatically render rel=nofollow to all links in an HTML output.
|
||||
/// </summary>
|
||||
public class NoFollowLinksExtension : IMarkdownExtension
|
||||
{
|
||||
public void Setup(MarkdownPipelineBuilder pipeline)
|
||||
{
|
||||
}
|
||||
|
||||
public void Setup(IMarkdownRenderer renderer)
|
||||
{
|
||||
var linkRenderer = renderer.ObjectRenderers.Find<LinkInlineRenderer>();
|
||||
if (linkRenderer != null)
|
||||
{
|
||||
linkRenderer.AutoRelNoFollow = true;
|
||||
}
|
||||
|
||||
var autolinkRenderer = renderer.ObjectRenderers.Find<AutolinkInlineRenderer>();
|
||||
if (autolinkRenderer != null)
|
||||
{
|
||||
autolinkRenderer.AutoRelNoFollow = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -131,11 +131,12 @@ namespace Markdig.Extensions.Tables
|
||||
// | ------------- | ------------ | ---------------------------------------- |
|
||||
// Calculate the colspan for the new row
|
||||
int columnIndex = -1;
|
||||
foreach (var columnSlice in columns)
|
||||
for (int i = 0; i < columns.Count; i++)
|
||||
{
|
||||
var columnSlice = columns[i];
|
||||
if (line.PeekCharExtra(columnSlice.Start) == '|')
|
||||
{
|
||||
columnIndex++;
|
||||
columnIndex = i;
|
||||
}
|
||||
if (columnIndex >= 0)
|
||||
{
|
||||
@@ -301,8 +302,9 @@ namespace Markdig.Extensions.Tables
|
||||
{
|
||||
var columns = tableState.ColumnSlices;
|
||||
TableRow currentRow = null;
|
||||
foreach (var columnSlice in columns)
|
||||
for (int i = 0; i < columns.Count; i++)
|
||||
{
|
||||
var columnSlice = columns[i];
|
||||
if (columnSlice.CurrentCell != null)
|
||||
{
|
||||
if (currentRow == null)
|
||||
@@ -332,7 +334,8 @@ namespace Markdig.Extensions.Tables
|
||||
// Else we can create a new cell
|
||||
columnSlice.CurrentCell = new TableCell(this)
|
||||
{
|
||||
ColumnSpan = columnSlice.CurrentColumnSpan
|
||||
ColumnSpan = columnSlice.CurrentColumnSpan,
|
||||
ColumnIndex = i
|
||||
};
|
||||
|
||||
if (columnSlice.BlockProcessor == null)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// See the license.txt file in the project root for more information.
|
||||
|
||||
using System;
|
||||
using System.Globalization;
|
||||
using Markdig.Renderers;
|
||||
using Markdig.Renderers.Html;
|
||||
|
||||
@@ -38,7 +39,9 @@ namespace Markdig.Extensions.Tables
|
||||
{
|
||||
foreach (var tableColumnDefinition in table.ColumnDefinitions)
|
||||
{
|
||||
renderer.WriteLine($"<col style=\"width:{Math.Round(tableColumnDefinition.Width*100)/100}%\">");
|
||||
var width = Math.Round(tableColumnDefinition.Width*100)/100;
|
||||
var widthValue = string.Format(CultureInfo.InvariantCulture, "{0:0.##}", width);
|
||||
renderer.WriteLine($"<col style=\"width:{widthValue}%\">");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,10 +81,10 @@ namespace Markdig.Extensions.Tables
|
||||
{
|
||||
renderer.Write($" colspan=\"{cell.ColumnSpan}\"");
|
||||
}
|
||||
|
||||
if (table.ColumnDefinitions != null && i < table.ColumnDefinitions.Count)
|
||||
var columnIndex = cell.ColumnIndex == -1 ? i : cell.ColumnIndex;
|
||||
if (table.ColumnDefinitions != null && columnIndex < table.ColumnDefinitions.Count)
|
||||
{
|
||||
switch (table.ColumnDefinitions[i].Alignment)
|
||||
switch (table.ColumnDefinitions[columnIndex].Alignment)
|
||||
{
|
||||
case TableColumnAlign.Center:
|
||||
renderer.Write(" style=\"text-align: center;\"");
|
||||
|
||||
@@ -34,9 +34,10 @@ namespace Markdig.Extensions.Tables
|
||||
{
|
||||
pipeline.BlockParsers.Insert(0, new PipeTableBlockParser());
|
||||
}
|
||||
var lineBreakParser = pipeline.InlineParsers.FindExact<LineBreakInlineParser>();
|
||||
if (!pipeline.InlineParsers.Contains<PipeTableParser>())
|
||||
{
|
||||
pipeline.InlineParsers.InsertBefore<EmphasisInlineParser>(new PipeTableParser(Options));
|
||||
pipeline.InlineParsers.InsertBefore<EmphasisInlineParser>(new PipeTableParser(lineBreakParser, Options));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
// 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 Markdig.Helpers;
|
||||
using Markdig.Parsers;
|
||||
@@ -18,14 +20,17 @@ namespace Markdig.Extensions.Tables
|
||||
/// <seealso cref="IPostInlineProcessor" />
|
||||
public class PipeTableParser : InlineParser, IPostInlineProcessor
|
||||
{
|
||||
private LineBreakInlineParser lineBreakParser;
|
||||
private readonly LineBreakInlineParser lineBreakParser;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="PipeTableParser" /> class.
|
||||
/// </summary>
|
||||
/// <param name="lineBreakParser">The linebreak parser to use</param>
|
||||
/// <param name="options">The options.</param>
|
||||
public PipeTableParser(PipeTableOptions options = null)
|
||||
public PipeTableParser(LineBreakInlineParser lineBreakParser, PipeTableOptions options = null)
|
||||
{
|
||||
if (lineBreakParser == null) throw new ArgumentNullException(nameof(lineBreakParser));
|
||||
this.lineBreakParser = lineBreakParser;
|
||||
OpeningCharacters = new[] { '|', '\n' };
|
||||
Options = options ?? new PipeTableOptions();
|
||||
}
|
||||
@@ -35,12 +40,6 @@ namespace Markdig.Extensions.Tables
|
||||
/// </summary>
|
||||
public PipeTableOptions Options { get; }
|
||||
|
||||
public override void Initialize(InlineProcessor processor)
|
||||
{
|
||||
// We are using the linebreak parser
|
||||
lineBreakParser = processor.Parsers.Find<LineBreakInlineParser>() ?? new LineBreakInlineParser();
|
||||
}
|
||||
|
||||
public override bool Match(InlineProcessor processor, ref StringSlice slice)
|
||||
{
|
||||
// Only working on Paragraph block
|
||||
|
||||
@@ -27,8 +27,14 @@ namespace Markdig.Extensions.Tables
|
||||
public TableCell(BlockParser parser) : base(parser)
|
||||
{
|
||||
ColumnSpan = 1;
|
||||
ColumnIndex = -1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the index of the column to which this cell belongs.
|
||||
/// </summary>
|
||||
public int ColumnIndex { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the column span this cell is covering. Default is 1.
|
||||
/// </summary>
|
||||
|
||||
@@ -9,6 +9,7 @@ using Markdig.Extensions.Bootstrap;
|
||||
using Markdig.Extensions.Citations;
|
||||
using Markdig.Extensions.CustomContainers;
|
||||
using Markdig.Extensions.DefinitionLists;
|
||||
using Markdig.Extensions.Diagrams;
|
||||
using Markdig.Extensions.Emoji;
|
||||
using Markdig.Extensions.EmphasisExtras;
|
||||
using Markdig.Extensions.Figures;
|
||||
@@ -19,6 +20,7 @@ using Markdig.Extensions.Hardlines;
|
||||
using Markdig.Extensions.ListExtras;
|
||||
using Markdig.Extensions.Mathematics;
|
||||
using Markdig.Extensions.MediaLinks;
|
||||
using Markdig.Extensions.NoRefLinks;
|
||||
using Markdig.Extensions.PragmaLines;
|
||||
using Markdig.Extensions.SelfPipeline;
|
||||
using Markdig.Extensions.SmartyPants;
|
||||
@@ -57,6 +59,7 @@ namespace Markdig
|
||||
.UsePipeTables()
|
||||
.UseListExtras()
|
||||
.UseTaskLists()
|
||||
.UseDiagrams()
|
||||
.UseGenericAttributes(); // Must be last as it is one parser that is modifying other parsers
|
||||
}
|
||||
|
||||
@@ -89,6 +92,17 @@ namespace Markdig
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uses the diagrams extension
|
||||
/// </summary>
|
||||
/// <param name="pipeline">The pipeline.</param>
|
||||
/// <returns>The modified pipeline</returns>
|
||||
public static MarkdownPipelineBuilder UseDiagrams(this MarkdownPipelineBuilder pipeline)
|
||||
{
|
||||
pipeline.Extensions.AddIfNotAlready<DiagramExtension>();
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uses precise source code location (useful for syntax highlighting).
|
||||
/// </summary>
|
||||
@@ -353,6 +367,17 @@ namespace Markdig
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Add rel=nofollow to all links rendered to HTML.
|
||||
/// </summary>
|
||||
/// <param name="pipeline"></param>
|
||||
/// <returns></returns>
|
||||
public static MarkdownPipelineBuilder UseNoFollowLinks(this MarkdownPipelineBuilder pipeline)
|
||||
{
|
||||
pipeline.Extensions.AddIfNotAlready<NoFollowLinksExtension>();
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This will disable the HTML support in the markdown processor (for constraint/safe parsing).
|
||||
/// </summary>
|
||||
@@ -456,6 +481,12 @@ namespace Markdig
|
||||
case "tasklists":
|
||||
pipeline.UseTaskLists();
|
||||
break;
|
||||
case "diagrams":
|
||||
pipeline.UseDiagrams();
|
||||
break;
|
||||
case "nofollowlinks":
|
||||
pipeline.UseNoFollowLinks();
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentException($"Invalid extension `{extension}` from `{extensions}`", nameof(extensions));
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Markdig
|
||||
public MarkdownPipelineBuilder()
|
||||
{
|
||||
// Add all default parsers
|
||||
BlockParsers = new BlockParserList()
|
||||
BlockParsers = new OrderedList<BlockParser>()
|
||||
{
|
||||
new ThematicBreakParser(),
|
||||
new HeadingBlockParser(),
|
||||
@@ -37,7 +37,7 @@ namespace Markdig
|
||||
new ParagraphBlockParser(),
|
||||
};
|
||||
|
||||
InlineParsers = new InlineParserList()
|
||||
InlineParsers = new OrderedList<InlineParser>()
|
||||
{
|
||||
new HtmlEntityParser(),
|
||||
new LinkInlineParser(),
|
||||
@@ -56,12 +56,12 @@ namespace Markdig
|
||||
/// <summary>
|
||||
/// Gets the block parsers.
|
||||
/// </summary>
|
||||
public BlockParserList BlockParsers { get; private set; }
|
||||
public OrderedList<BlockParser> BlockParsers { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the inline parsers.
|
||||
/// </summary>
|
||||
public InlineParserList InlineParsers { get; private set; }
|
||||
public OrderedList<InlineParser> InlineParsers { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the register extensions.
|
||||
|
||||
@@ -12,13 +12,6 @@ namespace Markdig.Parsers
|
||||
/// <seealso cref="Markdig.Parsers.ParserList{Markdig.Parsers.BlockParser, Markdig.Parsers.BlockParserState}" />
|
||||
public class BlockParserList : ParserList<BlockParser, BlockProcessor>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BlockParserList"/> class.
|
||||
/// </summary>
|
||||
public BlockParserList()
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="BlockParserList"/> class.
|
||||
/// </summary>
|
||||
|
||||
@@ -51,7 +51,6 @@ namespace Markdig.Parsers
|
||||
Document = document;
|
||||
document.IsOpen = true;
|
||||
Parsers = parsers;
|
||||
parsers.Initialize(this);
|
||||
OpenedBlocks = new List<Block>();
|
||||
NewBlocks = new Stack<Block>();
|
||||
root = this;
|
||||
|
||||
@@ -12,13 +12,15 @@ namespace Markdig.Parsers
|
||||
/// <seealso cref="Markdig.Parsers.BlockParser" />
|
||||
public class FencedCodeBlockParser : FencedBlockParserBase<FencedCodeBlock>
|
||||
{
|
||||
public const string DefaultInfoPrefix = "language-";
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FencedCodeBlockParser"/> class.
|
||||
/// </summary>
|
||||
public FencedCodeBlockParser()
|
||||
{
|
||||
OpeningCharacters = new[] {'`', '~'};
|
||||
InfoPrefix = "language-";
|
||||
InfoPrefix = DefaultInfoPrefix;
|
||||
}
|
||||
|
||||
protected override FencedCodeBlock CreateFencedBlock(BlockProcessor processor)
|
||||
|
||||
@@ -17,8 +17,7 @@ namespace Markdig.Parsers
|
||||
/// <summary>
|
||||
/// Initializes this parser with the specified parser processor.
|
||||
/// </summary>
|
||||
/// <param name="processor">The parser processor.</param>
|
||||
void Initialize(TProcessor processor);
|
||||
void Initialize();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the index of this parser in <see cref="BlockParserList"/> or <see cref="InlineParserList"/>.
|
||||
|
||||
@@ -11,20 +11,7 @@ namespace Markdig.Parsers
|
||||
/// <seealso cref="Markdig.Parsers.ParserList{Markdig.Parsers.InlineParser, Markdig.Parsers.InlineParserState}" />
|
||||
public class InlineParserList : ParserList<InlineParser, InlineProcessor>
|
||||
{
|
||||
public InlineParserList()
|
||||
{
|
||||
}
|
||||
|
||||
public InlineParserList(IEnumerable<InlineParser> parsers) : base(parsers)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the registered post inline processors.
|
||||
/// </summary>
|
||||
public IPostInlineProcessor[] PostInlineProcessors { get; private set; }
|
||||
|
||||
public override void Initialize(InlineProcessor initState)
|
||||
{
|
||||
// Prepare the list of post inline processors
|
||||
var postInlineProcessors = new List<IPostInlineProcessor>();
|
||||
@@ -37,8 +24,11 @@ namespace Markdig.Parsers
|
||||
}
|
||||
}
|
||||
PostInlineProcessors = postInlineProcessors.ToArray();
|
||||
|
||||
base.Initialize(initState);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the registered post inline processors.
|
||||
/// </summary>
|
||||
public IPostInlineProcessor[] PostInlineProcessors { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,6 @@ namespace Markdig.Parsers
|
||||
Parsers = parsers;
|
||||
PreciseSourceLocation = preciseSourcelocation;
|
||||
lineOffsets = new List<StringLineGroup.LineOffset>();
|
||||
Parsers.Initialize(this);
|
||||
ParserStates = new object[Parsers.Count];
|
||||
LiteralInlineParser = new LiteralInlineParser();
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ namespace Markdig.Parsers.Inlines
|
||||
/// </summary>
|
||||
public CreateEmphasisInlineDelegate CreateEmphasisInline { get; set; }
|
||||
|
||||
public override void Initialize(InlineProcessor processor)
|
||||
public override void Initialize()
|
||||
{
|
||||
OpeningCharacters = new char[EmphasisDescriptors.Count];
|
||||
|
||||
|
||||
@@ -45,14 +45,14 @@ namespace Markdig.Parsers.Inlines
|
||||
if (literal != null)
|
||||
{
|
||||
var matched = slice;
|
||||
matched.End = match - 1;
|
||||
matched.End = slice.Start + match - 1;
|
||||
int line;
|
||||
int column;
|
||||
processor.Inline = new HtmlEntityInline()
|
||||
{
|
||||
Original = matched,
|
||||
Transcoded = new StringSlice(literal),
|
||||
Span = new SourceSpan(processor.GetSourcePosition(startPosition, out line, out column), processor.GetSourcePosition(matched.End + 1)),
|
||||
Span = new SourceSpan(processor.GetSourcePosition(startPosition, out line, out column), processor.GetSourcePosition(matched.End)),
|
||||
Line = line,
|
||||
Column = column
|
||||
};
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace Markdig.Parsers
|
||||
/// </summary>
|
||||
public OrderedList<ListItemParser> ItemParsers { get; }
|
||||
|
||||
public override void Initialize(BlockProcessor processor)
|
||||
public override void Initialize()
|
||||
{
|
||||
var tempMap = new Dictionary<char, ListItemParser>();
|
||||
|
||||
@@ -138,11 +138,6 @@ namespace Markdig.Parsers
|
||||
list.CountBlankLinesReset++;
|
||||
}
|
||||
|
||||
if (list.CountBlankLinesReset > 1)
|
||||
{
|
||||
return BlockState.BreakDiscard;
|
||||
}
|
||||
|
||||
if (list.CountBlankLinesReset == 1 && listItem.ColumnWidth < 0)
|
||||
{
|
||||
state.Close(listItem);
|
||||
@@ -254,6 +249,16 @@ namespace Markdig.Parsers
|
||||
columnWidth = (state.IsBlankLine ? columnBeforeIndent : state.Column) - initColumnBeforeIndent;
|
||||
}
|
||||
|
||||
// Starts/continue the list unless:
|
||||
// - an empty list item follows a paragraph
|
||||
// - an ordered list is not starting by '1'
|
||||
var isPreviousParagraph = (block ?? state.LastBlock) is ParagraphBlock;
|
||||
if (isPreviousParagraph && (state.IsBlankLine || (listInfo.BulletType == '1' && listInfo.OrderedStart != "1")))
|
||||
{
|
||||
state.GoToColumn(initColumn);
|
||||
return BlockState.None;
|
||||
}
|
||||
|
||||
var newListItem = new ListItemBlock(this)
|
||||
{
|
||||
Column = initColumn,
|
||||
|
||||
@@ -51,14 +51,10 @@ namespace Markdig.Parsers
|
||||
document = new MarkdownDocument();
|
||||
|
||||
// Initialize the block parsers
|
||||
var blockParserList = new BlockParserList();
|
||||
blockParserList.AddRange(pipeline.BlockParsers);
|
||||
blockProcessor = new BlockProcessor(stringBuilderCache, document, blockParserList);
|
||||
blockProcessor = new BlockProcessor(stringBuilderCache, document, pipeline.BlockParsers);
|
||||
|
||||
// Initialize the inline parsers
|
||||
var inlineParserList = new InlineParserList();
|
||||
inlineParserList.AddRange(pipeline.InlineParsers);
|
||||
inlineProcessor = new InlineProcessor(stringBuilderCache, document, inlineParserList, pipeline.PreciseSourceLocation)
|
||||
inlineProcessor = new InlineProcessor(stringBuilderCache, document, pipeline.InlineParsers, pipeline.PreciseSourceLocation)
|
||||
{
|
||||
DebugLog = pipeline.DebugLog
|
||||
};
|
||||
|
||||
@@ -19,8 +19,7 @@ namespace Markdig.Parsers
|
||||
/// <summary>
|
||||
/// Initializes this parser with the specified parser processor.
|
||||
/// </summary>
|
||||
/// <param name="processor">The parser processor.</param>
|
||||
public virtual void Initialize(TProcessor processor)
|
||||
public virtual void Initialize()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -16,60 +16,10 @@ namespace Markdig.Parsers
|
||||
/// <seealso cref="Markdig.Helpers.OrderedList{T}" />
|
||||
public abstract class ParserList<T, TState> : OrderedList<T> where T : ParserBase<TState>
|
||||
{
|
||||
private CharacterMap<T[]> charMap;
|
||||
private T[] globalParsers;
|
||||
private readonly CharacterMap<T[]> charMap;
|
||||
private readonly T[] globalParsers;
|
||||
|
||||
protected ParserList()
|
||||
{
|
||||
}
|
||||
|
||||
protected ParserList(IEnumerable<T> parsers) : base(parsers)
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of global parsers (that don't have any opening characters defined)
|
||||
/// </summary>
|
||||
public T[] GlobalParsers => globalParsers;
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the opening characters defined.
|
||||
/// </summary>
|
||||
public char[] OpeningCharacters => charMap.OpeningCharacters;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of parsers valid for the specified opening character.
|
||||
/// </summary>
|
||||
/// <param name="openingChar">The opening character.</param>
|
||||
/// <returns>A list of parsers valid for the specified opening character or null if no parsers registered.</returns>
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
public T[] GetParsersForOpeningCharacter(char openingChar)
|
||||
{
|
||||
return charMap[openingChar];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches for an opening character from a registered parser in the specified string.
|
||||
/// </summary>
|
||||
/// <param name="text">The text.</param>
|
||||
/// <param name="start">The start.</param>
|
||||
/// <param name="end">The end.</param>
|
||||
/// <returns>Index position within the string of the first opening character found in the specified text; if not found, returns -1</returns>
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
public int IndexOfOpeningCharacter(string text, int start, int end)
|
||||
{
|
||||
return charMap.IndexOfOpeningCharacter(text, start, end);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes this instance with specified parser state.
|
||||
/// </summary>
|
||||
/// <param name="initState">State of the initialize.</param>
|
||||
/// <exception cref="System.InvalidOperationException">
|
||||
/// Unexpected null parser found
|
||||
/// or
|
||||
/// </exception>
|
||||
public virtual void Initialize(TState initState)
|
||||
protected ParserList(IEnumerable<T> parsersArg) : base(parsersArg)
|
||||
{
|
||||
var charCounter = new Dictionary<char, int>();
|
||||
int globalCounter = 0;
|
||||
@@ -82,7 +32,7 @@ namespace Markdig.Parsers
|
||||
throw new InvalidOperationException("Unexpected null parser found");
|
||||
}
|
||||
|
||||
parser.Initialize(initState);
|
||||
parser.Initialize();
|
||||
parser.Index = i;
|
||||
if (parser.OpeningCharacters != null && parser.OpeningCharacters.Length != 0)
|
||||
{
|
||||
@@ -134,5 +84,50 @@ namespace Markdig.Parsers
|
||||
|
||||
charMap = new CharacterMap<T[]>(tempCharMap);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of global parsers (that don't have any opening characters defined)
|
||||
/// </summary>
|
||||
public T[] GlobalParsers => globalParsers;
|
||||
|
||||
/// <summary>
|
||||
/// Gets all the opening characters defined.
|
||||
/// </summary>
|
||||
public char[] OpeningCharacters => charMap.OpeningCharacters;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the list of parsers valid for the specified opening character.
|
||||
/// </summary>
|
||||
/// <param name="openingChar">The opening character.</param>
|
||||
/// <returns>A list of parsers valid for the specified opening character or null if no parsers registered.</returns>
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
public T[] GetParsersForOpeningCharacter(char openingChar)
|
||||
{
|
||||
return charMap[openingChar];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches for an opening character from a registered parser in the specified string.
|
||||
/// </summary>
|
||||
/// <param name="text">The text.</param>
|
||||
/// <param name="start">The start.</param>
|
||||
/// <param name="end">The end.</param>
|
||||
/// <returns>Index position within the string of the first opening character found in the specified text; if not found, returns -1</returns>
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
public int IndexOfOpeningCharacter(string text, int start, int end)
|
||||
{
|
||||
return charMap.IndexOfOpeningCharacter(text, start, end);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes this instance with specified parser state.
|
||||
/// </summary>
|
||||
/// <exception cref="System.InvalidOperationException">
|
||||
/// Unexpected null parser found
|
||||
/// or
|
||||
/// </exception>
|
||||
private void Initialize()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,6 @@ namespace Markdig
|
||||
{
|
||||
public static partial class Markdown
|
||||
{
|
||||
public const string Version = "0.7.1";
|
||||
public const string Version = "0.7.3";
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,10 @@
|
||||
// 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 Markdig.Parsers;
|
||||
using Markdig.Syntax;
|
||||
|
||||
namespace Markdig.Renderers.Html
|
||||
@@ -11,24 +15,57 @@ namespace Markdig.Renderers.Html
|
||||
/// <seealso cref="Markdig.Renderers.Html.HtmlObjectRenderer{Markdig.Syntax.CodeBlock}" />
|
||||
public class CodeBlockRenderer : HtmlObjectRenderer<CodeBlock>
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CodeBlockRenderer"/> class.
|
||||
/// </summary>
|
||||
public CodeBlockRenderer()
|
||||
{
|
||||
BlocksAsDiv = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public bool OutputAttributesOnPre { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a map of fenced code block infos that should be rendered as div blocks instead of pre/code blocks.
|
||||
/// </summary>
|
||||
public HashSet<string> BlocksAsDiv { get; }
|
||||
|
||||
protected override void Write(HtmlRenderer renderer, CodeBlock obj)
|
||||
{
|
||||
renderer.EnsureLine();
|
||||
renderer.Write("<pre");
|
||||
if (OutputAttributesOnPre)
|
||||
|
||||
var fencedCodeBlock = obj as FencedCodeBlock;
|
||||
if (fencedCodeBlock?.Info != null && BlocksAsDiv.Contains(fencedCodeBlock.Info))
|
||||
{
|
||||
renderer.WriteAttributes(obj);
|
||||
var infoPrefix = (obj.Parser as FencedCodeBlockParser)?.InfoPrefix ??
|
||||
FencedCodeBlockParser.DefaultInfoPrefix;
|
||||
|
||||
// We are replacing the HTML attribute `language-mylang` by `mylang` only for a div block
|
||||
// NOTE that we are allocating a closure here
|
||||
renderer.Write("<div")
|
||||
.WriteAttributes(obj.TryGetAttributes(),
|
||||
cls => cls.StartsWith(infoPrefix) ? cls.Substring(infoPrefix.Length) : cls)
|
||||
.Write(">");
|
||||
renderer.WriteLeafRawLines(obj, true, true, true);
|
||||
renderer.WriteLine("</div>");
|
||||
|
||||
}
|
||||
renderer.Write("><code");
|
||||
if (!OutputAttributesOnPre)
|
||||
else
|
||||
{
|
||||
renderer.WriteAttributes(obj);
|
||||
renderer.Write("<pre");
|
||||
if (OutputAttributesOnPre)
|
||||
{
|
||||
renderer.WriteAttributes(obj);
|
||||
}
|
||||
renderer.Write("><code");
|
||||
if (!OutputAttributesOnPre)
|
||||
{
|
||||
renderer.WriteAttributes(obj);
|
||||
}
|
||||
renderer.Write(">");
|
||||
renderer.WriteLeafRawLines(obj, true, true);
|
||||
renderer.WriteLine("</code></pre>");
|
||||
}
|
||||
renderer.Write(">");
|
||||
renderer.WriteLeafRawLines(obj, true, true);
|
||||
renderer.WriteLine("</code></pre>");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,11 @@ namespace Markdig.Renderers.Html.Inlines
|
||||
/// <seealso cref="Markdig.Renderers.Html.HtmlObjectRenderer{Markdig.Syntax.Inlines.AutolinkInline}" />
|
||||
public class AutolinkInlineRenderer : HtmlObjectRenderer<AutolinkInline>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to always add rel="nofollow" for links or not.
|
||||
/// </summary>
|
||||
public bool AutoRelNoFollow { get; set; }
|
||||
|
||||
protected override void Write(HtmlRenderer renderer, AutolinkInline obj)
|
||||
{
|
||||
if (renderer.EnableHtmlForInline)
|
||||
@@ -22,6 +27,12 @@ namespace Markdig.Renderers.Html.Inlines
|
||||
}
|
||||
renderer.WriteEscapeUrl(obj.Url);
|
||||
renderer.WriteAttributes(obj);
|
||||
|
||||
if (!obj.IsEmail && AutoRelNoFollow)
|
||||
{
|
||||
renderer.Write(" rel=\"nofollow\"");
|
||||
}
|
||||
|
||||
renderer.Write("\">");
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,11 @@ namespace Markdig.Renderers.Html.Inlines
|
||||
/// <seealso cref="Markdig.Renderers.Html.HtmlObjectRenderer{Markdig.Syntax.Inlines.LinkInline}" />
|
||||
public class LinkInlineRenderer : HtmlObjectRenderer<LinkInline>
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating whether to always add rel="nofollow" for links or not.
|
||||
/// </summary>
|
||||
public bool AutoRelNoFollow { get; set; }
|
||||
|
||||
protected override void Write(HtmlRenderer renderer, LinkInline link)
|
||||
{
|
||||
if (renderer.EnableHtmlForInline)
|
||||
@@ -54,6 +59,10 @@ namespace Markdig.Renderers.Html.Inlines
|
||||
{
|
||||
if (renderer.EnableHtmlForInline)
|
||||
{
|
||||
if (AutoRelNoFollow)
|
||||
{
|
||||
renderer.Write(" rel=\"nofollow\"");
|
||||
}
|
||||
renderer.Write(">");
|
||||
}
|
||||
renderer.WriteChildren(link);
|
||||
|
||||
@@ -83,26 +83,28 @@ namespace Markdig.Renderers
|
||||
/// Writes the content escaped for HTML.
|
||||
/// </summary>
|
||||
/// <param name="slice">The slice.</param>
|
||||
/// <param name="softEscape">Only escape < and &</param>
|
||||
/// <returns>This instance</returns>
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
public HtmlRenderer WriteEscape(ref StringSlice slice)
|
||||
public HtmlRenderer WriteEscape(ref StringSlice slice, bool softEscape = false)
|
||||
{
|
||||
if (slice.Start > slice.End)
|
||||
{
|
||||
return this;
|
||||
}
|
||||
return WriteEscape(slice.Text, slice.Start, slice.Length);
|
||||
return WriteEscape(slice.Text, slice.Start, slice.Length, softEscape);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Writes the content escaped for HTML.
|
||||
/// </summary>
|
||||
/// <param name="slice">The slice.</param>
|
||||
/// <param name="softEscape">Only escape < and &</param>
|
||||
/// <returns>This instance</returns>
|
||||
[MethodImpl(MethodImplOptionPortable.AggressiveInlining)]
|
||||
public HtmlRenderer WriteEscape(StringSlice slice)
|
||||
public HtmlRenderer WriteEscape(StringSlice slice, bool softEscape = false)
|
||||
{
|
||||
return WriteEscape(ref slice);
|
||||
return WriteEscape(ref slice, softEscape);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -111,8 +113,9 @@ namespace Markdig.Renderers
|
||||
/// <param name="content">The content.</param>
|
||||
/// <param name="offset">The offset.</param>
|
||||
/// <param name="length">The length.</param>
|
||||
/// <param name="softEscape">Only escape < and &</param>
|
||||
/// <returns>This instance</returns>
|
||||
public HtmlRenderer WriteEscape(string content, int offset, int length)
|
||||
public HtmlRenderer WriteEscape(string content, int offset, int length, bool softEscape = false)
|
||||
{
|
||||
if (string.IsNullOrEmpty(content) || length == 0)
|
||||
return this;
|
||||
@@ -132,12 +135,15 @@ namespace Markdig.Renderers
|
||||
previousOffset = offset + 1;
|
||||
break;
|
||||
case '>':
|
||||
Write(content, previousOffset, offset - previousOffset);
|
||||
if (EnableHtmlEscape)
|
||||
if (!softEscape)
|
||||
{
|
||||
Write(">");
|
||||
Write(content, previousOffset, offset - previousOffset);
|
||||
if (EnableHtmlEscape)
|
||||
{
|
||||
Write(">");
|
||||
}
|
||||
previousOffset = offset + 1;
|
||||
}
|
||||
previousOffset = offset + 1;
|
||||
break;
|
||||
case '&':
|
||||
Write(content, previousOffset, offset - previousOffset);
|
||||
@@ -148,12 +154,15 @@ namespace Markdig.Renderers
|
||||
previousOffset = offset + 1;
|
||||
break;
|
||||
case '"':
|
||||
Write(content, previousOffset, offset - previousOffset);
|
||||
if (EnableHtmlEscape)
|
||||
if (!softEscape)
|
||||
{
|
||||
Write(""");
|
||||
Write(content, previousOffset, offset - previousOffset);
|
||||
if (EnableHtmlEscape)
|
||||
{
|
||||
Write(""");
|
||||
}
|
||||
previousOffset = offset + 1;
|
||||
}
|
||||
previousOffset = offset + 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -233,8 +242,9 @@ namespace Markdig.Renderers
|
||||
/// Writes the specified <see cref="HtmlAttributes"/>.
|
||||
/// </summary>
|
||||
/// <param name="attributes">The attributes to render.</param>
|
||||
/// <param name="classFilter">A class filter used to transform a class into another class at writing time</param>
|
||||
/// <returns>This instance</returns>
|
||||
public HtmlRenderer WriteAttributes(HtmlAttributes attributes)
|
||||
public HtmlRenderer WriteAttributes(HtmlAttributes attributes, Func<string, string> classFilter = null)
|
||||
{
|
||||
if (attributes == null)
|
||||
{
|
||||
@@ -256,7 +266,7 @@ namespace Markdig.Renderers
|
||||
{
|
||||
Write(" ");
|
||||
}
|
||||
WriteEscape(cssClass);
|
||||
WriteEscape(classFilter != null ? classFilter(cssClass) : cssClass);
|
||||
}
|
||||
Write("\"");
|
||||
}
|
||||
@@ -284,8 +294,9 @@ namespace Markdig.Renderers
|
||||
/// <param name="leafBlock">The leaf block.</param>
|
||||
/// <param name="writeEndOfLines">if set to <c>true</c> write end of lines.</param>
|
||||
/// <param name="escape">if set to <c>true</c> escape the content for HTML</param>
|
||||
/// <param name="softEscape">Only escape < and &</param>
|
||||
/// <returns>This instance</returns>
|
||||
public HtmlRenderer WriteLeafRawLines(LeafBlock leafBlock, bool writeEndOfLines, bool escape)
|
||||
public HtmlRenderer WriteLeafRawLines(LeafBlock leafBlock, bool writeEndOfLines, bool escape, bool softEscape = false)
|
||||
{
|
||||
if (leafBlock == null) throw new ArgumentNullException(nameof(leafBlock));
|
||||
if (leafBlock.Lines.Lines != null)
|
||||
@@ -300,7 +311,7 @@ namespace Markdig.Renderers
|
||||
}
|
||||
if (escape)
|
||||
{
|
||||
WriteEscape(ref slices[i].Slice);
|
||||
WriteEscape(ref slices[i].Slice, softEscape);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"title": "Markdig",
|
||||
"version": "0.7.1",
|
||||
"version": "0.7.3",
|
||||
"authors": [ "Alexandre Mutel" ],
|
||||
"description": "A fast, powerfull, CommonMark compliant, extensible Markdown processor for .NET with 20+ builtin extensions (pipetables, footnotes, definition lists... etc.)",
|
||||
"copyright": "Alexandre Mutel",
|
||||
@@ -11,7 +11,7 @@
|
||||
"projectUrl": "https://github.com/lunet-io/markdig",
|
||||
"iconUrl": "https://raw.githubusercontent.com/lunet-io/markdig/master/img/markdig.png",
|
||||
"requireLicenseAcceptance": false,
|
||||
"releaseNotes": "> 0.7.1\n- Fix issue in smarty pants which could lead to an InvalidCastException\n- Update parsers to latest CommonMark specs\n>0.7.0\n- Update to latest NETStandard.Library 1.6.0\n- Fix issue with digits in auto-identifier extension\n- Fix incorrect start of span calculated for code indented blocks\n> 0.6.2\n- Handle latest CommonMark specs for corner cases for emphasis (See https://talk.commonmark.org/t/emphasis-strong-emphasis-corner-cases/2123/1 )\n> 0.6.1:\n- Fix issue with autoidentifier extension overriding manual HTML attributes id on headings\n> 0.6.0\n- Fix conflicts between PipeTables and SmartyPants extensions\n- Add SelfPipeline extension\n",
|
||||
"releaseNotes": "> 0.7.3\n- Fix threading issue with pipeline\n> 0.7.2\n- Fix rendering of table colspan with non english locale\n- Fix grid table colspan parsing\n- Add nofollow extension for links\n> 0.7.1\n- Fix issue in smarty pants which could lead to an InvalidCastException\n- Update parsers to latest CommonMark specs\n>0.7.0\n- Update to latest NETStandard.Library 1.6.0\n- Fix issue with digits in auto-identifier extension\n- Fix incorrect start of span calculated for code indented blocks",
|
||||
"tags": [ "Markdown CommonMark md html md2html" ]
|
||||
},
|
||||
"configurations": {
|
||||
|
||||
Reference in New Issue
Block a user