A couple years ago I decided to stop using Windows Live Writer for authoring blog posts and build my own publishing tools using markdown and VSCode. Live Writer was a fantastic tool during its heyday, but some features started to feel cumbersome. Adding code into a blog post, as one example.
This blog uses SyntaxHighlighter to render code blocks, which requires HTML in a specific format. With WLW the HTML formatting required a toggle into HTML mode, or using an extension which was no longer supported in the OSS version of WLW.
What I really wanted was to author a post in markdown and use simple code fences to place code into a post.
``` csharp
public void AnOdeToCode()
{
}
```
Simple!
All I'd need is a markdown processor that would allow me to add some custom rendering for code fences.
Markdig is a fast, powerful, CommonMark compliant, extensible Markdown processor for .NET. Thanks to Rick Strahl for bringing the library to my attention. I use Markdig in my tools to transform a markdown file into HTML for posting here on the site.
There are at least a couple different techniques you can use to write an extension for Markdig. What I needed was an extension point to render SyntaxHighlighter flavored HTML for every code fence in a post. With Markdig, this means adding an HtmlOBjectRenderer
into the processing pipeline.
public class PreCodeRenderer : HtmlObjectRenderer<CodeBlock> { private CodeBlockRenderer originalCodeBlockRenderer; public PreCodeRenderer(CodeBlockRenderer originalCodeBlockRenderer = null) { this.originalCodeBlockRenderer = originalCodeBlockRenderer ?? new CodeBlockRenderer(); } public bool OutputAttributesOnPre { get; set; } protected override void Write(HtmlRenderer renderer, CodeBlock obj) { renderer.EnsureLine(); var fencedCodeBlock = obj as FencedCodeBlock; if (fencedCodeBlock?.Info != null) { renderer.Write($"<pre class=\"brush: {fencedCodeBlock.Info}; gutter: false; toolbar: false; \">"); renderer.EnsureLine(); renderer.WriteLeafRawLines(obj, true, true); renderer.WriteLine("</pre>"); } else { originalCodeBlockRenderer.Write(renderer, obj); } } }
Note that the Info
property of a FencedCodeBlock
will contain the info string, which is commonly used to specify the language of the code (csharp, xml, javascript, plain, go). The renderer builds a pre
tag that SyntaxHighlighter will know how to use. The last step, the easy step, is to add PerCodeRenderer
into a MarkdownPipelineBuilder
before telling Markdig to process your markdown.