Compare commits

...

23 Commits

Author SHA1 Message Date
Alexandre Mutel
105b09e1ec Bump version to 0.7.3 2016-07-23 12:47:04 +02:00
Alexandre Mutel
9fe7596a23 Update project.json for WebApp to .NET Core RTM 2016-07-23 12:46:48 +02:00
Alexandre Mutel
82af7cadc5 Fix issue with MarkdownPipeline that was not threadsafe and initialization was deferred at parsing time instead of pipeline build time (issue #40) 2016-07-23 12:46:26 +02:00
Alexandre Mutel
800c81bb9a Bump version to 0.7.2 2016-07-20 11:03:07 +02:00
Alexandre Mutel
5653a4f7ee Render table colspan with optional decimals for percentage 2016-07-20 10:51:05 +02:00
Alexandre Mutel
4f14ffe63b Merge commit 'fba96774f427ccdc08b5cb89a6dd02e84b2ef9d6' from capjan/markdig 2016-07-20 10:31:03 +02:00
Alexandre Mutel
d57acefe56 Merge pull request #39 from christophano/bugfix/cell-after-colspan
Bugfix colspan for grid tables
2016-07-19 10:06:25 +02:00
Alexandre Mutel
fb61e5a8da Update to latest CommonMark 0.26 specs. Two consecutive blank lines in a list no longer break it. Ordered list can break a paragraph if it starts at '1' 2016-07-19 09:41:59 +02:00
Chris Rodgers
264516bfdb Updated tests via md file and regenerated from t4 2016-07-19 08:40:34 +01:00
Chris Rodgers
6d8f8996d5 Fixes cell contents not displaying after colspan merged cell 2016-07-19 08:38:05 +01:00
Chris Rodgers
ba8557d3bf Corrects spec for TestExtensionsGridTable Example003 2016-07-19 08:38:05 +01:00
Alexandre Mutel
d18fd0b957 Link to a fixed version of the CommonMark specs to avoid unexpected upgrades to latest changes in the specs 2016-07-19 08:03:23 +02:00
Jan Ruhländer
fba96774f4 Fix: Table extension column width doesn't work on computers that use a comma as demimal point 2016-07-12 14:47:35 +02:00
Alexandre Mutel
0b2764ea62 Merge pull request #36 from synhershko/norel-links
Adding support for rel=nofollow in links
2016-07-10 08:14:52 +09:00
Itamar Syn-Hershko
64d81ed47b Removing config 2016-07-10 02:01:53 +03:00
Itamar Syn-Hershko
9a9742888b Fixing last bits 2016-07-10 01:47:38 +03:00
Itamar Syn-Hershko
5400b30a90 Fixing for renderer.EnableHtmlForInline case 2016-07-10 00:27:50 +03:00
Itamar Syn-Hershko
673f4a4beb Avoid adding rel to images 2016-07-10 00:13:51 +03:00
Itamar Syn-Hershko
05a27649aa Adding support for rel=nofollow in links 2016-07-09 23:56:58 +03:00
Alexandre Mutel
26c6b12dea Merge pull request #35 from ChrisMissal/readme-changes
Update readme.md, fix typos
2016-07-08 09:09:28 +09:00
Chris Missal
9a18ca222f Update readme.md
fixes typos
2016-07-07 18:30:09 -05:00
Alexandre Mutel
354f31b870 Fix calculation of span for HTML entities 2016-07-06 06:45:48 +09:00
Alexandre Mutel
c6c2f58ec0 Add support for diagrams extension. wip with mermaid (issue #34) 2016-07-05 23:24:37 +09:00
36 changed files with 3733 additions and 3593 deletions

View File

@@ -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:

View File

@@ -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" />

View 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

View File

@@ -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

View File

@@ -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;

View File

@@ -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&nbsp;1", @"
paragraph ( 0, 0) 0-7
// 01 23456789
Check("0\n&nbsp; 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
");
}

View File

@@ -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"
}
},

View 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");
}
}
}
}

View File

@@ -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>();

View 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;
}
}
}
}

View File

@@ -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)

View File

@@ -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;\"");

View File

@@ -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));
}
}

View File

@@ -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

View File

@@ -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>

View File

@@ -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));
}

View File

@@ -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.

View File

@@ -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>

View File

@@ -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;

View File

@@ -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)

View File

@@ -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"/>.

View File

@@ -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; }
}
}

View File

@@ -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();
}

View File

@@ -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];

View File

@@ -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
};

View File

@@ -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,

View File

@@ -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
};

View File

@@ -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()
{
}

View File

@@ -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()
{
}
}
}

View File

@@ -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";
}
}

View File

@@ -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>");
}
}
}

View File

@@ -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("\">");
}

View File

@@ -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);

View File

@@ -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 &lt; and &amp;</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 &lt; and &amp;</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 &lt; and &amp;</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("&gt;");
Write(content, previousOffset, offset - previousOffset);
if (EnableHtmlEscape)
{
Write("&gt;");
}
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("&quot;");
Write(content, previousOffset, offset - previousOffset);
if (EnableHtmlEscape)
{
Write("&quot;");
}
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 &lt; and &amp;</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
{

View File

@@ -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": {