Compare commits

...

3 Commits

Author SHA1 Message Date
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
35 changed files with 344 additions and 168 deletions

View File

@@ -61,7 +61,7 @@ In order to activate all extensions (except Emoji)
```csharp
// Configure the pipeline with all extensions active
var pipeline = new MarkdownPipeline().UseAllExtensions();
var pipeline = new MarkdownPipelineBuilder().UseAllExtensions().Build();
var result = Markdown.ToHtml("This is a text with some *emphasis*", pipeline);
```
@@ -150,6 +150,8 @@ Thanks to the fantastic work done by [John Mac Farlane](http://johnmacfarlane.ne
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,7 +22,7 @@ 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);
}
@@ -30,7 +30,7 @@ Later in a text we are using HTML and it becomes an abbr tag HTML
[Test]
public void TestSamePipelineAllExtensions()
{
var pipeline = new MarkdownPipeline().UseAllExtensions();
var pipeline = new MarkdownPipelineBuilder().UseAllExtensions().Build();
// Reuse the same pipeline
var result1 = Markdown.ToHtml("This is a \"\"citation\"\"", pipeline);

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,19 +15,22 @@ 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 && !parser.HasEmphasisChar('"'))
{
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

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

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

@@ -34,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()
@@ -61,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;
@@ -75,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>())
{
@@ -92,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>())
{
@@ -109,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>())
{
@@ -123,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;
@@ -134,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;
@@ -145,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;
@@ -156,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;
@@ -167,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;
@@ -181,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>())
{
@@ -195,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;
@@ -207,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;
@@ -218,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;
@@ -229,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;
@@ -240,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;
@@ -254,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>())
{
@@ -270,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;
@@ -281,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;
@@ -292,7 +292,7 @@ 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;
@@ -303,7 +303,7 @@ namespace Markdig
/// </summary>
/// <param name="pipeline">The pipeline.</param>
/// <returns>The modified pipeline</returns>
public static MarkdownPipeline DisableHtml(this MarkdownPipeline pipeline)
public static MarkdownPipelineBuilder DisableHtml(this MarkdownPipelineBuilder pipeline)
{
var parser = pipeline.BlockParsers.Find<HtmlBlockParser>();
if (parser != null)

View File

@@ -5,110 +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>
/// <remarks>NOTE: A pipeline is not thread-safe.</remarks>
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)
{
// 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
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

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

@@ -18,5 +18,5 @@ using System.Reflection;
[assembly: AssemblyCulture("")]
[assembly: NeutralResourcesLanguage("en")]
[assembly: AssemblyVersion("0.1.1.0")]
[assembly: AssemblyFileVersion("0.1.1.0")]
[assembly: AssemblyVersion("0.2.0.0")]
[assembly: AssemblyFileVersion("0.2.0.0")]

View File

@@ -1,6 +1,6 @@
{
"title": "Markdig",
"version": "0.1.1",
"version": "0.2.0",
"authors": [ "Alexandre Mutel" ],
"description": "A fast, powerfull, CommonMark compliant, extensible Markdown processor for .NET",
"copyright": "Alexandre Mutel",