Compare commits

...

21 Commits

Author SHA1 Message Date
Alexandre Mutel
1c1e56aebe Add Markdown.Version API. Bump to 0.2.1 2016-06-06 10:13:03 +09:00
Alexandre Mutel
ed18d3fa25 Update links from readme 2016-06-01 22:40:40 +09:00
Alexandre Mutel
33a6a39c34 Fix links to pandoc grid and pipe tables extensions 2016-06-01 22:38:14 +09:00
Alexandre Mutel
a1228a1e1c Add missing update to Markdig.xproj 2016-06-01 22:37:57 +09:00
Alexandre Mutel
d9f1c8c818 Update readme with MarkdownPipelineBuilder 2016-05-31 06:57:42 +09:00
Alexandre Mutel
60350dc9bf Fix MarkdownPipeline not being immutable by introducing a MarkdownPipelineBuilder (issue #5). Bump to version 0.2.0 (dev) 2016-05-31 06:55:40 +09:00
Alexandre Mutel
dbaed0a2bb Add credit for BenchmarkDotNet 2016-05-30 22:47:11 +09:00
Alexandre Mutel
0aa26aeef1 Bump version to 0.1.1 2016-05-30 22:34:07 +09:00
Alexandre Mutel
09beb2f867 Make sure that an emphasis is not added twice (issue #4) 2016-05-30 22:33:50 +09:00
Alexandre Mutel
fb559af72b Fix wrong dependencies in project.json (issue #3) 2016-05-30 22:32:56 +09:00
Alexandre Mutel
124b4d6e6e Split usage for better readability in readme 2016-05-26 15:20:51 +09:00
Alexandre Mutel
4a2d270db1 Update link to license. Update comments on other Markdown implems 2016-05-26 15:10:17 +09:00
Alexandre Mutel
feac9d4125 Reorder items in extension lists based on importance. Fix usage 2016-05-26 14:34:09 +09:00
Alexandre Mutel
5e77d7b38e Add link to AppVeyor build 2016-05-26 10:40:21 +09:00
Alexandre Mutel
9f64dcc868 Add info about disabling features 2016-05-26 09:37:00 +09:00
Alexandre Mutel
7964bd0160 Add support for disabling HTML tag parsing 2016-05-26 09:31:56 +09:00
Alexandre Mutel
191c1e896e Update benchmarks with accurate GC 2016-05-26 06:25:51 +09:00
Alexandre Mutel
4bdf1d4d18 Fix link to extensions 2016-05-26 00:17:47 +09:00
Alexandre Mutel
080dee270d Add benchmarks 2016-05-26 00:04:25 +09:00
Alexandre Mutel
7ae36a3794 Fix link to MarkdownPipeline 2016-05-25 23:48:17 +09:00
Alexandre Mutel
f43d5be0e7 Add credits section 2016-05-25 23:33:58 +09:00
38 changed files with 544 additions and 236 deletions

141
readme.md
View File

@@ -1,4 +1,4 @@
# Markdig [![NuGet](https://img.shields.io/nuget/v/Markdig.svg)](https://www.nuget.org/packages/Markdig/)
# Markdig [![Build status](https://ci.appveyor.com/api/projects/status/hk391x8jcskxt1u8?svg=true)](https://ci.appveyor.com/project/xoofx/markdig) [![NuGet](https://img.shields.io/nuget/v/Markdig.svg)](https://www.nuget.org/packages/Markdig/)
Markdig is a fast, powerfull, [CommonMark](http://commonmark.org/) compliant, extensible Markdown processor for .NET.
@@ -13,32 +13,33 @@ Markdig is a fast, powerfull, [CommonMark](http://commonmark.org/) compliant, ex
- Includes all the core elements of CommonMark:
- including GFM fenced code blocks.
- **Extensible** architecture
- Even the core Markdown/CommonMark parsing is pluggable, so it allows to disable builtin Markdown/Commonmark parsing (e.g [Disable HTML parsing](https://github.com/lunet-io/markdig/blob/7964bd0160d4c18e4155127a4c863d61ebd8944a/src/Markdig/MarkdownExtensions.cs#L306)) or change behaviour (e.g change matching `#` of a headers with `@`)
- Built-in with **18+ extensions**, including:
- **Abbreviations** (inspired from [PHP Markdown Extra - Abbreviations](https://michelf.ca/projects/php-markdown/extra/#abbr))
- **Auto-identifiers** for headings (similar to [Pandoc](http://pandoc.org/README.html#extension-auto_identifiers)
- **Bootstrap** class (to output bootstrap class)
- **Citation** text by enclosing `""...""` (inspired by this [CommonMark discussion ](https://talk.commonmark.org/t/referencing-creative-works-with-cite/892))
- **Custom containers** similar to fenced code block `:::` for generating a proper `<div>...</div>` instead (inspired by this [CommonMark discussion ](https://talk.commonmark.org/t/custom-container-for-block-and-inline/2051))
- **Definition lists** (inspired from [PHP Markdown Extra - Definitions Lists](https://michelf.ca/projects/php-markdown/extra/#def-list))
- **Emoji** support (inspired from [Markdown-it](https://markdown-it.github.io/))
- **Extra emphasis** (inspired from [Pandoc](http://pandoc.org/README.html#strikeout) and [Markdown-it](https://markdown-it.github.io/))
- 2 kind of tables:
- **Pipe tables** (inspired from Github tables and [PanDoc - Pipe Tables](http://pandoc.org/README.html#extension-pipe_tables))
- **Grid tables** (inspired from [Pandoc - Grid Tables](http://pandoc.org/README.html#extension-grid_tables))
- **Extra emphasis** (inspired from [Pandoc - Emphasis](http://pandoc.org/README.html#strikeout) and [Markdown-it](https://markdown-it.github.io/))
- strike through `~~`,
- Subscript `~`
- Superscript `^`
- Inserted `++`
- Marked `==`
- **Special attributes** or attached HTML attributes (inspired from [PHP Markdown Extra - Special Attributes](https://michelf.ca/projects/php-markdown/extra/#spe-attr))
- **Definition lists** (inspired from [PHP Markdown Extra - Definitions Lists](https://michelf.ca/projects/php-markdown/extra/#def-list))
- **Footnotes** (inspired from [PHP Markdown Extra - Footnotes](https://michelf.ca/projects/php-markdown/extra/#footnotes))
- **Auto-identifiers** for headings (similar to [Pandoc - Auto Identifiers](http://pandoc.org/README.html#extension-auto_identifiers))
- **Extra bullet lists**, supporting alpha bullet `a.` `b.` and roman bullet (`i`, `ii`...etc.)
- **Media support** for media url (youtube, vimeo, mp4...etc.) (inspired from this [CommonMark discussion](https://talk.commonmark.org/t/embedded-audio-and-video/441))
- **Abbreviations** (inspired from [PHP Markdown Extra - Abbreviations](https://michelf.ca/projects/php-markdown/extra/#abbr))
- **Citation** text by enclosing `""...""` (inspired by this [CommonMark discussion ](https://talk.commonmark.org/t/referencing-creative-works-with-cite/892))
- **Custom containers** similar to fenced code block `:::` for generating a proper `<div>...</div>` instead (inspired by this [CommonMark discussion ](https://talk.commonmark.org/t/custom-container-for-block-and-inline/2051))
- **Figures** (inspired from this [CommonMark discussion](https://talk.commonmark.org/t/image-tag-should-expand-to-figure-when-used-with-title/265/5))
- **Footers** (inspired from this [CommonMark discussion](https://talk.commonmark.org/t/syntax-for-footer/2070))
- **Footnotes** (inspired from [PHP Markdown Extra - Footnotes](https://michelf.ca/projects/php-markdown/extra/#footnotes))
- **Special attributes** or attached HTML attributes (inspired from [PHP Markdown Extra - Footnotes](https://michelf.ca/projects/php-markdown/extra/#spe-attr))
- **Soft lines as hard lines**
- **Extra bullet lists**, supporting alpha bullet `a.` `b.` and roman bullet (`i`, `ii`...etc.)
- **Mathematics**/Latex extension by enclosing `$$` for block and `$` for inline math (inspired from this [CommonMark discussion](https://talk.commonmark.org/t/mathematics-extension/457/31))
- **Embed player** for media url (youtube, vimeo, mp4...etc.) (inspired from this [CommonMark discussion](https://talk.commonmark.org/t/embedded-audio-and-video/441))
- **Soft lines as hard lines**
- **Emoji** support (inspired from [Markdown-it](https://markdown-it.github.io/))
- **SmartyPants** (inspired from [Daring Fireball - SmartyPants](https://daringfireball.net/projects/smartypants/))
- Tables:
- **Pipe tables** (inspired from Kramdown/[PanDoc](http://pandoc.org/README.html#pipe_tables))
- **Grid tables** (inspired from [Pandoc](http://pandoc.org/README.html#grid_tables))
- **Bootstrap** class (to output bootstrap class)
- Compatible with .NET 3.5, 4.0+ and .NET Core (`netstandard1.1+`)
## Download
@@ -47,40 +48,110 @@ Markdig is available as a NuGet package: [![NuGet](https://img.shields.io/nuget/
## Usage
The main entry point for the API is the `Markdown` class:
The main entry point for the API is the `Markdig.Markdown` class:
By default, without any options, Markdig is using the plain CommonMark parser:
```csharp
var result = Markdown.ToHtml("This is a text with some **emphasis**");
var result = Markdown.ToHtml("This is a text with some *emphasis*");
Console.WriteLine(result); // prints: <p>This is a text with some <em>emphasis</em></p>
```
In order to activate all extensions (except Emoji)
```csharp
var result = Markdown.ToHtml("This is a text with some **emphasis**", new MarkdownPipeline().UseAllExtensions());
// Configure the pipeline with all extensions active
var pipeline = new MarkdownPipelineBuilder().UseAllExtensions().Build();
var result = Markdown.ToHtml("This is a text with some *emphasis*", pipeline);
```
You can have a look at the [MarkdownPipeline](https://github.com/lunet-io/markdig/blob/src/Markdig/MarkdownPipeline.cs) file that describes all actionable extensions.
You can have a look at the [MarkdownExtensions](https://github.com/lunet-io/markdig/blob/master/src/Markdig/MarkdownExtensions.cs) that describes all actionable extensions (by modifying the MarkdownPipeline)
## License
This software is released under the [BSD-Clause 2 license](http://opensource.org/licenses/BSD-2-Clause).
This software is released under the [BSD-Clause 2 license](https://github.com/lunet-io/markdig/blob/master/license.txt).
## Benchmarking
This is an early preview of the benchmarking against various implementations:
- Markdig: itself
- CommonMarkCpp: [cmark](https://github.com/jgm/cmark), Reference C implementation of CommonMark, no support for extensions
- [CommonMark.NET](https://github.com/Knagis/CommonMark.NET): CommonMark implementation for .NET, no support for extensions, port of cmark
- [CommonMarkNet (devel)](https://github.com/AMDL/CommonMark.NET/tree/pipe-tables): An evolution of CommonMark.NET, supports extensions, not released yet
- [MarkdownDeep](https://github.com/toptensoftware/markdowndeep) another .NET implementation
- [MarkdownSharp](https://github.com/Kiri-rin/markdownsharp): Open source C# implementation of Markdown processor, as featured on Stack Overflow, regexp based.
- [Moonshine](https://github.com/brandonc/moonshine): popular C Markdown processor
Markdig is roughly x100 times faster than MarkdownSharp and extremelly competitive to other implems (that are not feature wise comparable)
Performance in x86:
```
// * Summary *
BenchmarkDotNet-Dev=v0.9.6.0+
OS=Microsoft Windows NT 6.2.9200.0
Processor=Intel(R) Core(TM) i7-4770 CPU @ 3.40GHz, ProcessorCount=8
Frequency=3319351 ticks, Resolution=301.2637 ns, Timer=TSC
HostCLR=MS.NET 4.0.30319.42000, Arch=32-bit RELEASE
JitModules=clrjit-v4.6.1080.0
Type=Program Mode=SingleRun LaunchCount=2
WarmupCount=2 TargetCount=10
Method | Median | StdDev | Gen 0 | Gen 1 | Gen 2 | Bytes Allocated/Op |
--------------------- |---------- |---------- |------- |------ |------- |------------------- |
TestMarkdig | 5.4870 ms | 0.0158 ms | 193.00 | 12.00 | 84.00 | 1,425,192.72 |
TestCommonMarkCpp | 4.0134 ms | 0.1008 ms | - | - | 180.00 | 454,859.74 |
TestCommonMarkNet | 4.6139 ms | 0.0581 ms | 193.00 | 12.00 | 84.00 | 1,406,367.27 |
TestCommonMarkNetNew | 5.5327 ms | 0.0461 ms | 193.00 | 96.00 | 84.00 | 1,738,465.42 |
TestMarkdownDeep | 7.5910 ms | 0.1006 ms | 205.00 | 96.00 | 84.00 | 1,758,383.79 |
TestMoonshine | 5.8843 ms | 0.1758 ms | - | - | 215.00 | 565,000.73 |
// * Diagnostic Output - MemoryDiagnoser *
// ***** BenchmarkRunner: End *****
```
Performance for x64:
```
// * Summary *
BenchmarkDotNet-Dev=v0.9.6.0+
OS=Microsoft Windows NT 6.2.9200.0
Processor=Intel(R) Core(TM) i7-4770 CPU @ 3.40GHz, ProcessorCount=8
Frequency=3319351 ticks, Resolution=301.2637 ns, Timer=TSC
HostCLR=MS.NET 4.0.30319.42000, Arch=64-bit RELEASE [RyuJIT]
JitModules=clrjit-v4.6.1080.0
Type=Program Mode=SingleRun LaunchCount=2
WarmupCount=2 TargetCount=10
Method | Median | StdDev | Gen 0 | Gen 1 | Gen 2 | Bytes Allocated/Op |
--------------------- |---------- |---------- |------- |------- |------ |------------------- |
TestMarkdig | 5.9539 ms | 0.0495 ms | 157.00 | 96.00 | 84.00 | 1,767,834.52 |
TestCommonMarkNet | 4.3158 ms | 0.0161 ms | 157.00 | 96.00 | 84.00 | 1,747,432.06 |
TestCommonMarkNetNew | 5.3421 ms | 0.0435 ms | 229.00 | 168.00 | 84.00 | 2,323,922.97 |
TestMarkdownDeep | 7.4750 ms | 0.0281 ms | 318.00 | 186.00 | 84.00 | 2,576,728.69 |
// * Diagnostic Output - MemoryDiagnoser *
// ***** BenchmarkRunner: End *****
```
## Credits
Thanks to the fantastic work done by [John Mac Farlane](http://johnmacfarlane.net/) for the CommonMark specs and all the people involved in making Markdown a better standard!
This project would not have been possible without this huge foundation.
Thanks also to the project [BenchmarkDotNet](https://github.com/PerfDotNet/BenchmarkDotNet) that makes benchmarking so easy to setup!
## Author
Alexandre MUTEL aka [xoofx](http://xoofx.com)

View File

@@ -38,14 +38,14 @@ namespace Markdig.Tests
{
if (string.IsNullOrEmpty(extensionsGroupText))
{
yield return new MarkdownPipeline();
yield return new MarkdownPipelineBuilder().Build();
yield break;
}
var extensionGroups = extensionsGroupText.Split(new[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
foreach (var extensionsText in extensionGroups)
{
var pipeline = new MarkdownPipeline();
var pipeline = new MarkdownPipelineBuilder();
foreach (var extension in extensionsText.Split(new[] { '+' }, StringSplitOptions.RemoveEmptyEntries))
{
switch (extension.ToLowerInvariant())
@@ -113,7 +113,7 @@ namespace Markdig.Tests
}
}
yield return pipeline;
yield return pipeline.Build();
}
}

View File

@@ -22,39 +22,52 @@ Later in a text we are using HTML and it becomes an abbr tag HTML
//");
//var result = Markdown.ToHtml(text, new MarkdownPipeline().UseFootnotes().UseEmphasisExtra());
var result = Markdown.ToHtml(text, new MarkdownPipeline().UseAbbreviation());
var result = Markdown.ToHtml(text, new MarkdownPipelineBuilder().UseAbbreviation().Build());
//File.WriteAllText("test.html", result, Encoding.UTF8);
Console.WriteLine(result);
}
// Test for emoji and smileys
// var text = @" This is a test with a :) and a :angry: smiley";
[Test]
public void TestSamePipelineAllExtensions()
{
var pipeline = new MarkdownPipelineBuilder().UseAllExtensions().Build();
// Reuse the same pipeline
var result1 = Markdown.ToHtml("This is a \"\"citation\"\"", pipeline);
var result2 = Markdown.ToHtml("This is a \"\"citation\"\"", pipeline);
Assert.AreEqual("<p>This is a <cite>citation</cite></p>", result1.Trim());
Assert.AreEqual(result1, result2);
}
// Test for emoji and smileys
// var text = @" This is a test with a :) and a :angry: smiley";
// Test for definition lists:
//
// var text = @"
//Term 1
//: This is a definition item
// With a paragraph
// > This is a block quote
// Test for definition lists:
//
// var text = @"
//Term 1
//: This is a definition item
// With a paragraph
// > This is a block quote
// - This is a list
// - item2
// - This is a list
// - item2
// ```java
// Test
// ```java
// Test
// ```
// ```
// And a lazy line
//: This ia another definition item
// And a lazy line
//: This ia another definition item
//Term2
//Term3 *with some inline*
//: This is another definition for term2
//";
//Term2
//Term3 *with some inline*
//: This is another definition for term2
//";
// Test for grid table

View File

@@ -12,18 +12,18 @@ namespace Markdig.Extensions.Abbreviations
/// <seealso cref="Markdig.IMarkdownExtension" />
public class AbbreviationExtension : IMarkdownExtension
{
public void Setup(MarkdownPipeline pipeline)
public void Setup(MarkdownPipelineBuilder pipeline)
{
pipeline.BlockParsers.AddIfNotAlready<AbbreviationParser>();
}
var htmlRenderer = pipeline.Renderer as HtmlRenderer;
if (htmlRenderer != null)
public void Setup(IMarkdownRenderer renderer)
{
var htmlRenderer = renderer as HtmlRenderer;
if (htmlRenderer != null && !htmlRenderer.ObjectRenderers.Contains<HtmlAbbreviationRenderer>())
{
if (!htmlRenderer.ObjectRenderers.Contains<HtmlAbbreviationRenderer>())
{
// Must be inserted before CodeBlockRenderer
htmlRenderer.ObjectRenderers.Insert(0, new HtmlAbbreviationRenderer());
}
// Must be inserted before CodeBlockRenderer
htmlRenderer.ObjectRenderers.Insert(0, new HtmlAbbreviationRenderer());
}
}
}

View File

@@ -41,7 +41,7 @@ namespace Markdig.Extensions.AutoIdentifiers
};
}
public void Setup(MarkdownPipeline pipeline)
public void Setup(MarkdownPipelineBuilder pipeline)
{
var headingBlockParser = pipeline.BlockParsers.Find<HeadingBlockParser>();
if (headingBlockParser != null)
@@ -59,6 +59,10 @@ namespace Markdig.Extensions.AutoIdentifiers
}
}
public void Setup(IMarkdownRenderer renderer)
{
}
/// <summary>
/// Process on a new <see cref="HeadingBlock"/>
/// </summary>

View File

@@ -2,6 +2,7 @@
// 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;
using Markdig.Syntax;
using Markdig.Syntax.Inlines;
@@ -14,13 +15,17 @@ namespace Markdig.Extensions.Bootstrap
/// <seealso cref="Markdig.IMarkdownExtension" />
public class BootstrapExtension : IMarkdownExtension
{
public void Setup(MarkdownPipeline pipeline)
public void Setup(MarkdownPipelineBuilder pipeline)
{
// Make sure we don't have a delegate twice
pipeline.DocumentProcessed -= PipelineOnDocumentProcessed;
pipeline.DocumentProcessed += PipelineOnDocumentProcessed;
}
public void Setup(IMarkdownRenderer renderer)
{
}
private static void PipelineOnDocumentProcessed(MarkdownDocument document)
{
foreach(var node in document.Descendants())

View File

@@ -15,28 +15,25 @@ namespace Markdig.Extensions.Cites
/// <seealso cref="Markdig.IMarkdownExtension" />
public class CiteExtension : IMarkdownExtension
{
public void Setup(MarkdownPipeline pipeline)
public void Setup(MarkdownPipelineBuilder pipeline)
{
var parser = pipeline.InlineParsers.FindExact<EmphasisInlineParser>();
if (parser != null)
if (parser != null && !parser.HasEmphasisChar('"'))
{
foreach (var emphasis in parser.EmphasisDescriptors)
{
if (emphasis.Character == '"')
{
return;
}
}
parser.EmphasisDescriptors.Add(new EmphasisDescriptor('"', 2, 2, false));
}
}
var htmlRenderer = pipeline.Renderer as HtmlRenderer;
public void Setup(IMarkdownRenderer renderer)
{
var htmlRenderer = renderer as HtmlRenderer;
if (htmlRenderer != null)
{
// Extend the rendering here.
var emphasisRenderer = htmlRenderer.ObjectRenderers.FindExact<EmphasisInlineRenderer>();
var emphasisRenderer = renderer.ObjectRenderers.FindExact<EmphasisInlineRenderer>();
if (emphasisRenderer != null)
{
// TODO: Use an ordered list instead as we don't know if this specific GetTag has been already added
var previousTag = emphasisRenderer.GetTag;
emphasisRenderer.GetTag = inline => GetTag(inline) ?? previousTag(inline);
}

View File

@@ -13,7 +13,7 @@ namespace Markdig.Extensions.CustomContainers
/// <seealso cref="Markdig.IMarkdownExtension" />
public class CustomContainerExtension : IMarkdownExtension
{
public void Setup(MarkdownPipeline pipeline)
public void Setup(MarkdownPipelineBuilder pipeline)
{
if (!pipeline.BlockParsers.Contains<CustomContainerParser>())
{
@@ -23,7 +23,7 @@ namespace Markdig.Extensions.CustomContainers
// Plug the inline parser for CustomContainerInline
var inlineParser = pipeline.InlineParsers.Find<EmphasisInlineParser>();
if (inlineParser != null)
if (inlineParser != null && !inlineParser.HasEmphasisChar(':'))
{
inlineParser.EmphasisDescriptors.Add(new EmphasisDescriptor(':', 2, 2, true));
var previousCreateEmphasisInline = inlineParser.CreateEmphasisInline;
@@ -36,8 +36,11 @@ namespace Markdig.Extensions.CustomContainers
return previousCreateEmphasisInline?.Invoke(emphasisChar, strong);
};
}
}
var htmlRenderer = pipeline.Renderer as HtmlRenderer;
public void Setup(IMarkdownRenderer renderer)
{
var htmlRenderer = renderer as HtmlRenderer;
if (htmlRenderer != null)
{
if (!htmlRenderer.ObjectRenderers.Contains<HtmlCustomContainerRenderer>())
@@ -51,6 +54,7 @@ namespace Markdig.Extensions.CustomContainers
htmlRenderer.ObjectRenderers.Insert(0, new HtmlCustomContainerInlineRenderer());
}
}
}
}
}

View File

@@ -11,15 +11,18 @@ namespace Markdig.Extensions.DefinitionLists
/// <seealso cref="Markdig.IMarkdownExtension" />
public class DefinitionListExtension : IMarkdownExtension
{
public void Setup(MarkdownPipeline pipeline)
public void Setup(MarkdownPipelineBuilder pipeline)
{
if (!pipeline.BlockParsers.Contains<DefinitionListParser>())
{
// Insert the parser before any other parsers
pipeline.BlockParsers.Insert(0, new DefinitionListParser());
}
}
var htmlRenderer = pipeline.Renderer as HtmlRenderer;
public void Setup(IMarkdownRenderer renderer)
{
var htmlRenderer = renderer as HtmlRenderer;
if (htmlRenderer != null)
{
if (!htmlRenderer.ObjectRenderers.Contains<HtmlDefinitionListRenderer>())

View File

@@ -2,6 +2,8 @@
// 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;
namespace Markdig.Extensions.Emoji
{
/// <summary>
@@ -10,7 +12,7 @@ namespace Markdig.Extensions.Emoji
/// <seealso cref="Markdig.IMarkdownExtension" />
public class EmojiExtension : IMarkdownExtension
{
public void Setup(MarkdownPipeline pipeline)
public void Setup(MarkdownPipelineBuilder pipeline)
{
if (!pipeline.InlineParsers.Contains<EmojiParser>())
{
@@ -18,5 +20,9 @@ namespace Markdig.Extensions.Emoji
pipeline.InlineParsers.Insert(0, new EmojiParser());
}
}
public void Setup(IMarkdownRenderer renderer)
{
}
}
}

View File

@@ -29,7 +29,7 @@ namespace Markdig.Extensions.EmphasisExtra
/// </summary>
public EmphasisExtraOptions Options { get; }
public void Setup(MarkdownPipeline pipeline)
public void Setup(MarkdownPipelineBuilder pipeline)
{
var parser = pipeline.InlineParsers.FindExact<EmphasisInlineParser>();
if (parser != null)
@@ -83,8 +83,11 @@ namespace Markdig.Extensions.EmphasisExtra
parser.EmphasisDescriptors.Add(new EmphasisDescriptor('=', 2, 2, true));
}
}
}
var htmlRenderer = pipeline.Renderer as HtmlRenderer;
public void Setup(IMarkdownRenderer renderer)
{
var htmlRenderer = renderer as HtmlRenderer;
if (htmlRenderer != null)
{
// Extend the rendering here.

View File

@@ -14,7 +14,7 @@ namespace Markdig.Extensions.Figures
/// <seealso cref="Markdig.IMarkdownExtension" />
public class FigureExtension : IMarkdownExtension
{
public void Setup(MarkdownPipeline pipeline)
public void Setup(MarkdownPipelineBuilder pipeline)
{
if (!pipeline.BlockParsers.Contains<FigureBlockParser>())
{
@@ -28,7 +28,11 @@ namespace Markdig.Extensions.Figures
pipeline.BlockParsers.Insert(0, new FigureBlockParser());
}
}
var htmlRenderer = pipeline.Renderer as HtmlRenderer;
}
public void Setup(IMarkdownRenderer renderer)
{
var htmlRenderer = renderer as HtmlRenderer;
if (htmlRenderer != null)
{
htmlRenderer.ObjectRenderers.AddIfNotAlready<HtmlFigureRenderer>();

View File

@@ -13,7 +13,7 @@ namespace Markdig.Extensions.Footers
/// <seealso cref="Markdig.IMarkdownExtension" />
public class FooterExtension : IMarkdownExtension
{
public void Setup(MarkdownPipeline pipeline)
public void Setup(MarkdownPipelineBuilder pipeline)
{
if (!pipeline.BlockParsers.Contains<FooterBlockParser>())
{
@@ -27,8 +27,11 @@ namespace Markdig.Extensions.Footers
pipeline.BlockParsers.Insert(0, new FooterBlockParser());
}
}
}
var htmlRenderer = pipeline.Renderer as HtmlRenderer;
public void Setup(IMarkdownRenderer renderer)
{
var htmlRenderer = renderer as HtmlRenderer;
if (htmlRenderer != null)
{
htmlRenderer.ObjectRenderers.AddIfNotAlready(new HtmlFooterBlockRenderer());

View File

@@ -11,15 +11,18 @@ namespace Markdig.Extensions.Footnotes
/// <seealso cref="Markdig.IMarkdownExtension" />
public class FootnoteExtension : IMarkdownExtension
{
public void Setup(MarkdownPipeline pipeline)
public void Setup(MarkdownPipelineBuilder pipeline)
{
if (!pipeline.BlockParsers.Contains<FootnoteParser>())
{
// Insert the parser before any other parsers
pipeline.BlockParsers.Insert(0, new FootnoteParser());
}
}
var htmlRenderer = pipeline.Renderer as HtmlRenderer;
public void Setup(IMarkdownRenderer renderer)
{
var htmlRenderer = renderer as HtmlRenderer;
if (htmlRenderer != null)
{
htmlRenderer.ObjectRenderers.AddIfNotAlready(new HtmlFootnoteGroupRenderer());

View File

@@ -5,6 +5,7 @@
using System;
using Markdig.Helpers;
using Markdig.Parsers;
using Markdig.Renderers;
using Markdig.Renderers.Html;
using Markdig.Syntax;
using Markdig.Syntax.Inlines;
@@ -18,7 +19,7 @@ namespace Markdig.Extensions.GenericAttributes
/// <seealso cref="Markdig.IMarkdownExtension" />
public class GenericAttributesExtension : IMarkdownExtension
{
public void Setup(MarkdownPipeline pipeline)
public void Setup(MarkdownPipelineBuilder pipeline)
{
if (!pipeline.InlineParsers.Contains<GenericAttributesParser>())
{
@@ -36,6 +37,10 @@ namespace Markdig.Extensions.GenericAttributes
}
}
public void Setup(IMarkdownRenderer renderer)
{
}
private bool TryProcessAttributesForHeading(BlockProcessor processor, ref StringSlice line, IBlock block)
{
// Try to find if there is any attributes { in the info string on the first line of a FencedCodeBlock

View File

@@ -3,6 +3,7 @@
// See the license.txt file in the project root for more information.
using Markdig.Parsers.Inlines;
using Markdig.Renderers;
namespace Markdig.Extensions.Hardlines
{
@@ -12,7 +13,7 @@ namespace Markdig.Extensions.Hardlines
/// <seealso cref="Markdig.IMarkdownExtension" />
public class SoftlineBreakAsHardlineExtension : IMarkdownExtension
{
public void Setup(MarkdownPipeline pipeline)
public void Setup(MarkdownPipelineBuilder pipeline)
{
// Simply modify the LineBreakInlineParser
// TODO: We might want more options (like pandoc)
@@ -22,5 +23,9 @@ namespace Markdig.Extensions.Hardlines
parser.EnableSoftAsHard = true;
}
}
public void Setup(IMarkdownRenderer renderer)
{
}
}
}

View File

@@ -3,6 +3,7 @@
// See the license.txt file in the project root for more information.
using Markdig.Parsers;
using Markdig.Renderers;
namespace Markdig.Extensions.ListExtra
{
@@ -12,7 +13,7 @@ namespace Markdig.Extensions.ListExtra
/// <seealso cref="Markdig.IMarkdownExtension" />
public class ListExtraExtension : IMarkdownExtension
{
public void Setup(MarkdownPipeline pipeline)
public void Setup(MarkdownPipelineBuilder pipeline)
{
var parser = pipeline.BlockParsers.Find<ListBlockParser>();
if (parser != null)
@@ -20,5 +21,9 @@ namespace Markdig.Extensions.ListExtra
parser.ItemParsers.AddIfNotAlready<ListExtraItemParser>();
}
}
public void Setup(IMarkdownRenderer renderer)
{
}
}
}

View File

@@ -13,7 +13,7 @@ namespace Markdig.Extensions.Mathematics
/// <seealso cref="Markdig.IMarkdownExtension" />
public class MathExtension : IMarkdownExtension
{
public void Setup(MarkdownPipeline pipeline)
public void Setup(MarkdownPipelineBuilder pipeline)
{
// Adds the inline parser
if (!pipeline.InlineParsers.Contains<MathInlineParser>())
@@ -27,8 +27,11 @@ namespace Markdig.Extensions.Mathematics
// Insert before EmphasisInlineParser to take precedence
pipeline.BlockParsers.Insert(0, new MathBlockParser());
}
}
var htmlRenderer = pipeline.Renderer as HtmlRenderer;
public void Setup(IMarkdownRenderer renderer)
{
var htmlRenderer = renderer as HtmlRenderer;
if (htmlRenderer != null)
{
if (!htmlRenderer.ObjectRenderers.Contains<HtmlMathInlineRenderer>())

View File

@@ -26,9 +26,13 @@ namespace Markdig.Extensions.Medias
public MediaOptions Options { get; }
public void Setup(MarkdownPipeline pipeline)
public void Setup(MarkdownPipelineBuilder pipeline)
{
var htmlRenderer = pipeline.Renderer as HtmlRenderer;
}
public void Setup(IMarkdownRenderer renderer)
{
var htmlRenderer = renderer as HtmlRenderer;
if (htmlRenderer != null)
{
var inlineRenderer = htmlRenderer.ObjectRenderers.FindExact<LinkInlineRenderer>();

View File

@@ -26,15 +26,18 @@ namespace Markdig.Extensions.SmartyPants
/// </summary>
public SmartyPantOptions Options { get; }
public void Setup(MarkdownPipeline pipeline)
public void Setup(MarkdownPipelineBuilder pipeline)
{
if (!pipeline.InlineParsers.Contains<SmaryPantsInlineParser>())
{
// Insert the parser after the code span parser
pipeline.InlineParsers.InsertAfter<CodeInlineParser>(new SmaryPantsInlineParser());
}
}
var htmlRenderer = pipeline.Renderer as HtmlRenderer;
public void Setup(IMarkdownRenderer renderer)
{
var htmlRenderer = renderer as HtmlRenderer;
if (htmlRenderer != null)
{
if (!htmlRenderer.ObjectRenderers.Contains<HtmlSmartyPantRenderer>())

View File

@@ -11,14 +11,17 @@ namespace Markdig.Extensions.Tables
/// <seealso cref="Markdig.IMarkdownExtension" />
public class GridTableExtension : IMarkdownExtension
{
public void Setup(MarkdownPipeline pipeline)
public void Setup(MarkdownPipelineBuilder pipeline)
{
if (!pipeline.BlockParsers.Contains<GridTableParser>())
{
pipeline.BlockParsers.Insert(0, new GridTableParser());
}
}
var htmlRenderer = pipeline.Renderer as HtmlRenderer;
public void Setup(IMarkdownRenderer renderer)
{
var htmlRenderer = renderer as HtmlRenderer;
if (htmlRenderer != null && !htmlRenderer.ObjectRenderers.Contains<HtmlTableRenderer>())
{
htmlRenderer.ObjectRenderers.Add(new HtmlTableRenderer());

View File

@@ -26,14 +26,17 @@ namespace Markdig.Extensions.Tables
/// </summary>
public PipeTableOptions Options { get; }
public void Setup(MarkdownPipeline pipeline)
public void Setup(MarkdownPipelineBuilder pipeline)
{
if (!pipeline.InlineParsers.Contains<PipeTableParser>())
{
pipeline.InlineParsers.InsertBefore<EmphasisInlineParser>(new PipeTableParser(Options));
}
}
var htmlRenderer = pipeline.Renderer as HtmlRenderer;
public void Setup(IMarkdownRenderer renderer)
{
var htmlRenderer = renderer as HtmlRenderer;
if (htmlRenderer != null && !htmlRenderer.ObjectRenderers.Contains<HtmlTableRenderer>())
{
htmlRenderer.ObjectRenderers.Add(new HtmlTableRenderer());

View File

@@ -14,6 +14,14 @@ namespace Markdig.Helpers
/// <remarks>We use a typed list and don't use extension methods because it would pollute all list implemts and the top level namespace.</remarks>
public class OrderedList<T> : List<T>
{
public OrderedList()
{
}
public OrderedList(IEnumerable<T> collection) : base(collection)
{
}
public bool InsertBefore<TElement>(T element) where TElement : T
{
if (element == null) throw new ArgumentNullException(nameof(element));

View File

@@ -1,6 +1,9 @@
// 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;
namespace Markdig
{
/// <summary>
@@ -12,6 +15,12 @@ namespace Markdig
/// Setups this extension for the specified pipeline.
/// </summary>
/// <param name="pipeline">The pipeline.</param>
void Setup(MarkdownPipeline pipeline);
void Setup(MarkdownPipelineBuilder pipeline);
/// <summary>
/// Setups this extension for the specified renderer.
/// </summary>
/// <param name="renderer">The renderer.</param>
void Setup(IMarkdownRenderer renderer);
}
}

View File

@@ -4,16 +4,18 @@
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.Props" Condition="'$(VSToolsPath)' != ''" />
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>8A58A7E2-627C-4F41-933F-5AC92ADFAB48</ProjectGuid>
<RootNamespace>Markdig</RootNamespace>
<OutputPath Condition="'$(OutputPath)'=='' ">.\Bin</OutputPath>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
<ProduceOutputsOnBuild>True</ProduceOutputsOnBuild>
<TypeScriptCompileBlocked>True</TypeScriptCompileBlocked>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@@ -12,7 +12,7 @@ namespace Markdig
/// <summary>
/// Provides methods for parsing a Markdown string to a syntax tree and converting it to other formats.
/// </summary>
public static class Markdown
public static partial class Markdown
{
/// <summary>
/// Converts a Markdown string to HTML.
@@ -54,33 +54,33 @@ namespace Markdig
{
if (reader == null) throw new ArgumentNullException(nameof(reader));
if (writer == null) throw new ArgumentNullException(nameof(writer));
pipeline = pipeline ?? new MarkdownPipeline();
pipeline = pipeline ?? new MarkdownPipelineBuilder().Build();
// We override the renderer with our own writer
pipeline.Renderer = new HtmlRenderer(writer);
var renderer = new HtmlRenderer(writer);
pipeline.Setup(renderer);
var document = Parse(reader, pipeline);
pipeline.Renderer.Render(document);
renderer.Render(document);
writer.Flush();
}
/// <summary>
/// Converts a Markdown string using a custom <see cref="IMarkdownRenderer"/> specified in the <see cref="MarkdownPipeline.Renderer"/>.
/// Converts a Markdown string using a custom <see cref="IMarkdownRenderer"/>.
/// </summary>
/// <param name="reader">A Markdown text from a <see cref="TextReader"/>.</param>
/// <param name="renderer">The renderer to convert Markdown to.</param>
/// <param name="pipeline">The pipeline used for the conversion.</param>
/// <exception cref="System.ArgumentNullException">if reader or writer variable are null</exception>
public static object Convert(TextReader reader, MarkdownPipeline pipeline = null)
public static object Convert(TextReader reader, IMarkdownRenderer renderer, MarkdownPipeline pipeline = null)
{
if (reader == null) throw new ArgumentNullException(nameof(reader));
pipeline = pipeline ?? new MarkdownPipeline();
if (pipeline.Renderer == null)
{
throw new InvalidOperationException("The property MarkdownPipeline.Renderer cannot be null");
}
if (renderer == null) throw new ArgumentNullException(nameof(renderer));
pipeline = pipeline ?? new MarkdownPipelineBuilder().Build();
var document = Parse(reader, pipeline);
return pipeline.Renderer.Render(document);
pipeline.Setup(renderer);
return renderer.Render(document);
}
/// <summary>
@@ -92,7 +92,7 @@ namespace Markdig
public static MarkdownDocument Parse(string markdown)
{
if (markdown == null) throw new ArgumentNullException(nameof(markdown));
return Parse(new StringReader(markdown), new MarkdownPipeline());
return Parse(new StringReader(markdown));
}
/// <summary>
@@ -105,7 +105,7 @@ namespace Markdig
public static MarkdownDocument Parse(TextReader reader, MarkdownPipeline pipeline = null)
{
if (reader == null) throw new ArgumentNullException(nameof(reader));
pipeline = pipeline ?? new MarkdownPipeline();
pipeline = pipeline ?? new MarkdownPipelineBuilder().Build();
return MarkdownParser.Parse(reader, pipeline);
}

View File

@@ -19,6 +19,8 @@ using Markdig.Extensions.Mathematics;
using Markdig.Extensions.Medias;
using Markdig.Extensions.SmartyPants;
using Markdig.Extensions.Tables;
using Markdig.Parsers;
using Markdig.Parsers.Inlines;
namespace Markdig
{
@@ -32,7 +34,7 @@ namespace Markdig
/// </summary>
/// <param name="pipeline">The pipeline.</param>
/// <returns>The modified pipeline</returns>
public static MarkdownPipeline UseAllExtensions(this MarkdownPipeline pipeline)
public static MarkdownPipelineBuilder UseAllExtensions(this MarkdownPipelineBuilder pipeline)
{
return pipeline
.UseAbbreviation()
@@ -59,7 +61,7 @@ namespace Markdig
/// </summary>
/// <param name="pipeline">The pipeline.</param>
/// <returns>The modified pipeline</returns>
public static MarkdownPipeline UseCustomContainer(this MarkdownPipeline pipeline)
public static MarkdownPipelineBuilder UseCustomContainer(this MarkdownPipelineBuilder pipeline)
{
pipeline.Extensions.AddIfNotAlready<CustomContainerExtension>();
return pipeline;
@@ -73,7 +75,7 @@ namespace Markdig
/// <returns>
/// The modified pipeline
/// </returns>
public static MarkdownPipeline UseMedia(this MarkdownPipeline pipeline, MediaOptions options = null)
public static MarkdownPipelineBuilder UseMedia(this MarkdownPipelineBuilder pipeline, MediaOptions options = null)
{
if (!pipeline.Extensions.Contains<MediaExtension>())
{
@@ -90,7 +92,7 @@ namespace Markdig
/// <returns>
/// The modified pipeline
/// </returns>
public static MarkdownPipeline UseAutoIdentifier(this MarkdownPipeline pipeline, AutoIdentifierOptions options = AutoIdentifierOptions.Default)
public static MarkdownPipelineBuilder UseAutoIdentifier(this MarkdownPipelineBuilder pipeline, AutoIdentifierOptions options = AutoIdentifierOptions.Default)
{
if (!pipeline.Extensions.Contains<AutoIdentifierExtension>())
{
@@ -107,7 +109,7 @@ namespace Markdig
/// <returns>
/// The modified pipeline
/// </returns>
public static MarkdownPipeline UseSmartyPants(this MarkdownPipeline pipeline, SmartyPantOptions options = null)
public static MarkdownPipelineBuilder UseSmartyPants(this MarkdownPipelineBuilder pipeline, SmartyPantOptions options = null)
{
if (!pipeline.Extensions.Contains<SmartyPantsExtension>())
{
@@ -121,7 +123,7 @@ namespace Markdig
/// </summary>
/// <param name="pipeline">The pipeline.</param>
/// <returns>The modified pipeline</returns>
public static MarkdownPipeline UseBootstrap(this MarkdownPipeline pipeline)
public static MarkdownPipelineBuilder UseBootstrap(this MarkdownPipelineBuilder pipeline)
{
pipeline.Extensions.AddIfNotAlready<BootstrapExtension>();
return pipeline;
@@ -132,7 +134,7 @@ namespace Markdig
/// </summary>
/// <param name="pipeline">The pipeline.</param>
/// <returns>The modified pipeline</returns>
public static MarkdownPipeline UseMath(this MarkdownPipeline pipeline)
public static MarkdownPipelineBuilder UseMath(this MarkdownPipelineBuilder pipeline)
{
pipeline.Extensions.AddIfNotAlready<MathExtension>();
return pipeline;
@@ -143,7 +145,7 @@ namespace Markdig
/// </summary>
/// <param name="pipeline">The pipeline.</param>
/// <returns>The modified pipeline</returns>
public static MarkdownPipeline UseFigure(this MarkdownPipeline pipeline)
public static MarkdownPipelineBuilder UseFigure(this MarkdownPipelineBuilder pipeline)
{
pipeline.Extensions.AddIfNotAlready<FigureExtension>();
return pipeline;
@@ -154,7 +156,7 @@ namespace Markdig
/// </summary>
/// <param name="pipeline">The pipeline.</param>
/// <returns>The modified pipeline</returns>
public static MarkdownPipeline UseAbbreviation(this MarkdownPipeline pipeline)
public static MarkdownPipelineBuilder UseAbbreviation(this MarkdownPipelineBuilder pipeline)
{
pipeline.Extensions.AddIfNotAlready<AbbreviationExtension>();
return pipeline;
@@ -165,7 +167,7 @@ namespace Markdig
/// </summary>
/// <param name="pipeline">The pipeline.</param>
/// <returns>The modified pipeline</returns>
public static MarkdownPipeline UseDefinitionList(this MarkdownPipeline pipeline)
public static MarkdownPipelineBuilder UseDefinitionList(this MarkdownPipelineBuilder pipeline)
{
pipeline.Extensions.AddIfNotAlready<DefinitionListExtension>();
return pipeline;
@@ -179,7 +181,7 @@ namespace Markdig
/// <returns>
/// The modified pipeline
/// </returns>
public static MarkdownPipeline UsePipeTable(this MarkdownPipeline pipeline, PipeTableOptions options = null)
public static MarkdownPipelineBuilder UsePipeTable(this MarkdownPipelineBuilder pipeline, PipeTableOptions options = null)
{
if (!pipeline.Extensions.Contains<PipeTableExtension>())
{
@@ -193,7 +195,7 @@ namespace Markdig
/// </summary>
/// <param name="pipeline">The pipeline.</param>
/// <returns>The modified pipeline</returns>
public static MarkdownPipeline UseGridTable(this MarkdownPipeline pipeline)
public static MarkdownPipelineBuilder UseGridTable(this MarkdownPipelineBuilder pipeline)
{
pipeline.Extensions.AddIfNotAlready<GridTableExtension>();
return pipeline;
@@ -205,7 +207,7 @@ namespace Markdig
/// </summary>
/// <param name="pipeline">The pipeline.</param>
/// <returns>The modified pipeline</returns>
public static MarkdownPipeline UseCite(this MarkdownPipeline pipeline)
public static MarkdownPipelineBuilder UseCite(this MarkdownPipelineBuilder pipeline)
{
pipeline.Extensions.AddIfNotAlready<CiteExtension>();
return pipeline;
@@ -216,7 +218,7 @@ namespace Markdig
/// </summary>
/// <param name="pipeline">The pipeline.</param>
/// <returns>The modified pipeline</returns>
public static MarkdownPipeline UseFooter(this MarkdownPipeline pipeline)
public static MarkdownPipelineBuilder UseFooter(this MarkdownPipelineBuilder pipeline)
{
pipeline.Extensions.AddIfNotAlready<FooterExtension>();
return pipeline;
@@ -227,7 +229,7 @@ namespace Markdig
/// </summary>
/// <param name="pipeline">The pipeline.</param>
/// <returns>The modified pipeline</returns>
public static MarkdownPipeline UseFootnotes(this MarkdownPipeline pipeline)
public static MarkdownPipelineBuilder UseFootnotes(this MarkdownPipelineBuilder pipeline)
{
pipeline.Extensions.AddIfNotAlready<FootnoteExtension>();
return pipeline;
@@ -238,7 +240,7 @@ namespace Markdig
/// </summary>
/// <param name="pipeline">The pipeline.</param>
/// <returns>The modified pipeline</returns>
public static MarkdownPipeline UseSoftlineBreakAsHardlineBreak(this MarkdownPipeline pipeline)
public static MarkdownPipelineBuilder UseSoftlineBreakAsHardlineBreak(this MarkdownPipelineBuilder pipeline)
{
pipeline.Extensions.AddIfNotAlready<SoftlineBreakAsHardlineExtension>();
return pipeline;
@@ -252,7 +254,7 @@ namespace Markdig
/// <returns>
/// The modified pipeline
/// </returns>
public static MarkdownPipeline UseEmphasisExtra(this MarkdownPipeline pipeline, EmphasisExtraOptions options = EmphasisExtraOptions.Default)
public static MarkdownPipelineBuilder UseEmphasisExtra(this MarkdownPipelineBuilder pipeline, EmphasisExtraOptions options = EmphasisExtraOptions.Default)
{
if (!pipeline.Extensions.Contains<EmphasisExtraExtension>())
{
@@ -268,7 +270,7 @@ namespace Markdig
/// <returns>
/// The modified pipeline
/// </returns>
public static MarkdownPipeline UseListExtra(this MarkdownPipeline pipeline)
public static MarkdownPipelineBuilder UseListExtra(this MarkdownPipelineBuilder pipeline)
{
pipeline.Extensions.AddIfNotAlready<ListExtraExtension>();
return pipeline;
@@ -279,7 +281,7 @@ namespace Markdig
/// </summary>
/// <param name="pipeline">The pipeline.</param>
/// <returns>The modified pipeline</returns>
public static MarkdownPipeline UseGenericAttributes(this MarkdownPipeline pipeline)
public static MarkdownPipelineBuilder UseGenericAttributes(this MarkdownPipelineBuilder pipeline)
{
pipeline.Extensions.AddIfNotAlready<GenericAttributesExtension>();
return pipeline;
@@ -290,10 +292,31 @@ namespace Markdig
/// </summary>
/// <param name="pipeline">The pipeline.</param>
/// <returns>The modified pipeline</returns>
public static MarkdownPipeline UseEmojiAndSmiley(this MarkdownPipeline pipeline)
public static MarkdownPipelineBuilder UseEmojiAndSmiley(this MarkdownPipelineBuilder pipeline)
{
pipeline.Extensions.AddIfNotAlready<EmojiExtension>();
return pipeline;
}
/// <summary>
/// This will disable the HTML support in the markdown processor (for constraint/safe parsing).
/// </summary>
/// <param name="pipeline">The pipeline.</param>
/// <returns>The modified pipeline</returns>
public static MarkdownPipelineBuilder DisableHtml(this MarkdownPipelineBuilder pipeline)
{
var parser = pipeline.BlockParsers.Find<HtmlBlockParser>();
if (parser != null)
{
pipeline.BlockParsers.Remove(parser);
}
var inlineParser = pipeline.InlineParsers.Find<AutolineInlineParser>();
if (inlineParser != null)
{
inlineParser.EnableHtmlParsing = false;
}
return pipeline;
}
}
}

View File

@@ -5,104 +5,51 @@ using System;
using System.IO;
using Markdig.Helpers;
using Markdig.Parsers;
using Markdig.Parsers.Inlines;
using Markdig.Renderers;
namespace Markdig
{
/// <summary>
/// This class allows to modify the pipeline to parse and render a Markdown document.
/// This class is the Markdown pipeline build from a <see cref="MarkdownPipelineBuilder"/>.
/// </summary>
public class MarkdownPipeline
{
// This class is immutable
/// <summary>
/// Initializes a new instance of the <see cref="MarkdownPipeline" /> class.
/// </summary>
public MarkdownPipeline()
internal MarkdownPipeline(OrderedList<IMarkdownExtension> extensions, BlockParserList blockParsers, InlineParserList inlineParsers, StringBuilderCache cache, TextWriter debugLog, ProcessDocumentDelegate documentProcessed)
{
if (blockParsers == null) throw new ArgumentNullException(nameof(blockParsers));
if (inlineParsers == null) throw new ArgumentNullException(nameof(inlineParsers));
// Add all default parsers
BlockParsers = new BlockParserList()
{
new ThematicBreakParser(),
new HeadingBlockParser(),
new QuoteBlockParser(),
new ListBlockParser(),
new HtmlBlockParser(),
new FencedCodeBlockParser(),
new IndentedCodeBlockParser(),
new ParagraphBlockParser(),
};
InlineParsers = new InlineParserList()
{
new HtmlEntityParser(),
new LinkInlineParser(),
new EscapeInlineParser(),
new EmphasisInlineParser(),
new CodeInlineParser(),
new AutolineInlineParser(),
new LineBreakInlineParser(),
};
Extensions = new OrderedList<IMarkdownExtension>();
Renderer = new HtmlRenderer(new StringWriter());
StringBuilderCache = new StringBuilderCache();
Extensions = extensions;
BlockParsers = blockParsers;
InlineParsers = inlineParsers;
StringBuilderCache = cache;
DebugLog = debugLog;
DocumentProcessed = documentProcessed;
}
internal OrderedList<IMarkdownExtension> Extensions { get; }
/// <summary>
/// Gets the block parsers.
/// </summary>
public BlockParserList BlockParsers { get; private set; }
internal BlockParserList BlockParsers { get; }
/// <summary>
/// Gets the inline parsers.
/// </summary>
public InlineParserList InlineParsers { get; private set; }
internal InlineParserList InlineParsers { get; }
/// <summary>
/// Gets or sets the renderer.
/// </summary>
public IMarkdownRenderer Renderer { get; set; }
internal StringBuilderCache StringBuilderCache { get; }
/// <summary>
/// Gets the register extensions.
/// </summary>
public OrderedList<IMarkdownExtension> Extensions { get; }
// TODO: Move the log to a better place
internal TextWriter DebugLog { get; }
/// <summary>
/// Gets or sets the string builder cache used by the parsers.
/// </summary>
public StringBuilderCache StringBuilderCache { get; set; }
internal ProcessDocumentDelegate DocumentProcessed;
/// <summary>
/// Gets or sets the debug log.
/// </summary>
public TextWriter DebugLog { get; set; }
/// <summary>
/// Occurs when a document has been processed after the <see cref="MarkdownParser.Parse"/> method.
/// </summary>
public event ProcessDocumentDelegate DocumentProcessed;
internal ProcessDocumentDelegate GetDocumentProcessed => DocumentProcessed;
/// <summary>
/// Initializes this instance.
/// </summary>
/// <exception cref="System.InvalidOperationException">An extension cannot be null</exception>
public void Initialize()
internal void Setup(IMarkdownRenderer renderer)
{
// Allow extensions to modify existing BlockParsers, InlineParsers and Renderer
if (renderer == null) throw new ArgumentNullException(nameof(renderer));
foreach (var extension in Extensions)
{
if (extension == null)
{
throw new InvalidOperationException("An extension cannot be null");
}
extension.Setup(this);
extension.Setup(renderer);
}
}
}

View File

@@ -0,0 +1,118 @@
// 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.IO;
using Markdig.Helpers;
using Markdig.Parsers;
using Markdig.Parsers.Inlines;
using Markdig.Renderers;
namespace Markdig
{
/// <summary>
/// This class allows to modify the pipeline to parse and render a Markdown document.
/// </summary>
/// <remarks>NOTE: A pipeline is not thread-safe.</remarks>
public class MarkdownPipelineBuilder
{
private MarkdownPipeline pipeline;
/// <summary>
/// Initializes a new instance of the <see cref="MarkdownPipeline" /> class.
/// </summary>
public MarkdownPipelineBuilder()
{
// Add all default parsers
BlockParsers = new BlockParserList()
{
new ThematicBreakParser(),
new HeadingBlockParser(),
new QuoteBlockParser(),
new ListBlockParser(),
new HtmlBlockParser(),
new FencedCodeBlockParser(),
new IndentedCodeBlockParser(),
new ParagraphBlockParser(),
};
InlineParsers = new InlineParserList()
{
new HtmlEntityParser(),
new LinkInlineParser(),
new EscapeInlineParser(),
new EmphasisInlineParser(),
new CodeInlineParser(),
new AutolineInlineParser(),
new LineBreakInlineParser(),
};
Extensions = new OrderedList<IMarkdownExtension>();
StringBuilderCache = new StringBuilderCache();
}
/// <summary>
/// Gets the block parsers.
/// </summary>
public BlockParserList BlockParsers { get; private set; }
/// <summary>
/// Gets the inline parsers.
/// </summary>
public InlineParserList InlineParsers { get; private set; }
/// <summary>
/// Gets the register extensions.
/// </summary>
public OrderedList<IMarkdownExtension> Extensions { get; }
/// <summary>
/// Gets or sets the string builder cache used by the parsers.
/// </summary>
public StringBuilderCache StringBuilderCache { get; set; }
/// <summary>
/// Gets or sets the debug log.
/// </summary>
public TextWriter DebugLog { get; set; }
/// <summary>
/// Occurs when a document has been processed after the <see cref="MarkdownParser.Parse"/> method.
/// </summary>
public event ProcessDocumentDelegate DocumentProcessed;
internal ProcessDocumentDelegate GetDocumentProcessed => DocumentProcessed;
/// <summary>
/// Builds a pipeline from this instance. Once the pipeline is build, it cannot be modified.
/// </summary>
/// <exception cref="System.InvalidOperationException">An extension cannot be null</exception>
public MarkdownPipeline Build()
{
if (pipeline != null)
{
return pipeline;
}
// TODO: Review the whole initialization process for extensions
// - It does not prevent a user to modify the pipeline after it has been used
// - a pipeline is not thread safe.
// We should find a proper way to make the pipeline safely modifiable/freezable (PipelineBuilder -> Pipeline)
// Allow extensions to modify existing BlockParsers, InlineParsers and Renderer
foreach (var extension in Extensions)
{
if (extension == null)
{
throw new InvalidOperationException("An extension cannot be null");
}
extension.Setup(this);
}
pipeline = new MarkdownPipeline(new OrderedList<IMarkdownExtension>(Extensions), new BlockParserList(BlockParsers), new InlineParserList(InlineParsers), StringBuilderCache, DebugLog, GetDocumentProcessed);
return pipeline;
}
}
}

View File

@@ -1,6 +1,9 @@
// 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.Collections.Generic;
namespace Markdig.Parsers
{
/// <summary>
@@ -9,5 +12,19 @@ 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>
/// <param name="parsers">The parsers.</param>
public BlockParserList(IEnumerable<BlockParser> parsers) : base(parsers)
{
}
}
}

View File

@@ -11,6 +11,14 @@ 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 delimiter processors.
/// </summary>

View File

@@ -18,8 +18,14 @@ namespace Markdig.Parsers.Inlines
public AutolineInlineParser()
{
OpeningCharacters = new[] {'<'};
EnableHtmlParsing = true;
}
/// <summary>
/// Gets or sets a value indicating whether to enable HTML parsing. Default is <c>true</c>
/// </summary>
public bool EnableHtmlParsing { get; set; }
public override bool Match(InlineProcessor processor, ref StringSlice slice)
{
string link;
@@ -29,7 +35,7 @@ namespace Markdig.Parsers.Inlines
{
processor.Inline = new AutolinkInline() {IsEmail = isEmail, Url = link};
}
else
else if (EnableHtmlParsing)
{
slice = saved;
string htmlTag;

View File

@@ -41,6 +41,23 @@ namespace Markdig.Parsers.Inlines
/// </summary>
public List<EmphasisDescriptor> EmphasisDescriptors { get; }
/// <summary>
/// Determines whether this parser is using the specified character as an emphasis delimiter.
/// </summary>
/// <param name="c">The character to look for.</param>
/// <returns><c>true</c> if this parser is using the specified character as an emphasis delimiter; otherwise <c>false</c></returns>
public bool HasEmphasisChar(char c)
{
foreach (var emphasis in EmphasisDescriptors)
{
if (emphasis.Character == c)
{
return true;
}
}
return false;
}
/// <summary>
/// Gets or sets the create emphasis inline delegate (allowing to create a different emphasis inline class)
/// </summary>

View File

@@ -41,7 +41,6 @@ namespace Markdig.Parsers
Reader = reader;
// Initialize the pipeline
pipeline.Initialize();
var stringBuilderCache = pipeline.StringBuilderCache ?? new StringBuilderCache();
document = new MarkdownDocument();
@@ -59,7 +58,7 @@ namespace Markdig.Parsers
DebugLog = pipeline.DebugLog
};
documentProcessed = pipeline.GetDocumentProcessed;
documentProcessed = pipeline.DocumentProcessed;
}
/// <summary>
@@ -72,7 +71,7 @@ namespace Markdig.Parsers
public static MarkdownDocument Parse(TextReader reader, MarkdownPipeline pipeline = null)
{
if (reader == null) throw new ArgumentNullException(nameof(reader));
pipeline = pipeline ?? new MarkdownPipeline();
pipeline = pipeline ?? new MarkdownPipelineBuilder().Build();
// Perform the parsing
var markdownParser = new MarkdownParser(reader, pipeline);

View File

@@ -1,6 +1,7 @@
// 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.
namespace Markdig.Parsers
{
/// <summary>
@@ -8,7 +9,7 @@ namespace Markdig.Parsers
/// </summary>
/// <typeparam name="TProcessor">Type of the parser processor</typeparam>
/// <seealso cref="Markdig.Parsers.IMarkdownParser{TParserState}" />
public class ParserBase<TProcessor> : IMarkdownParser<TProcessor>
public abstract class ParserBase<TProcessor> : IMarkdownParser<TProcessor>
{
/// <summary>
/// Gets the opening characters this parser will be triggered if the character is found.

View File

@@ -23,6 +23,10 @@ namespace Markdig.Parsers
{
}
protected ParserList(IEnumerable<T> parsers) : base(parsers)
{
}
/// <summary>
/// Gets the list of global parsers (that don't have any opening characters defined)
/// </summary>

View File

@@ -1,7 +1,5 @@
using System.Resources;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
@@ -20,8 +18,13 @@ using System.Runtime.InteropServices;
[assembly: AssemblyCulture("")]
[assembly: NeutralResourcesLanguage("en")]
[assembly: AssemblyVersion("0.1.0.0")]
[assembly: AssemblyFileVersion("0.1.0.0")]
[assembly: InternalsVisibleTo("Scriban.Tests")]
[assembly: AssemblyVersion(Markdig.Markdown.Version)]
[assembly: AssemblyFileVersion(Markdig.Markdown.Version)]
namespace Markdig
{
public static partial class Markdown
{
public const string Version = "0.2.1";
}
}

View File

@@ -1,6 +1,6 @@
{
"title": "Markdig",
"version": "0.1.0",
"version": "0.2.1",
"authors": [ "Alexandre Mutel" ],
"description": "A fast, powerfull, CommonMark compliant, extensible Markdown processor for .NET",
"copyright": "Alexandre Mutel",
@@ -10,7 +10,8 @@
"licenseUrl": "https://github.com/lunet-io/markdig/blob/master/license.txt",
"projectUrl": "https://github.com/lunet-io/markdig",
"requireLicenseAcceptance": false,
"tags": [ "Markdown CommonMark md html" ]
"releaseNotes": "Add Markdown.Version",
"tags": [ "Markdown CommonMark md html md2html" ]
},
"configurations": {
"Debug": {
@@ -28,9 +29,6 @@
}
}
},
"dependencies": {
"NETStandard.Library": "1.5.0-rc2-24027"
},
"frameworks": {
"net35": {
"buildOptions": {
@@ -61,6 +59,7 @@
},
"netstandard1.1": {
"dependencies": {
"NETStandard.Library": "1.5.0-rc2-24027",
"System.Threading": "4.0.11-rc2-24027",
"System.Threading.Tasks": "4.0.11-rc2-24027",
"System.Threading.Tasks.Parallel": "4.0.1-rc2-24027"