Skip to content

Instantly share code, notes, and snippets.

@jchadwick
Created May 25, 2012 18:25
Show Gist options
  • Save jchadwick/2789669 to your computer and use it in GitHub Desktop.
Save jchadwick/2789669 to your computer and use it in GitHub Desktop.
ViewEngine with Markdown and Razor support
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Web.Mvc;
using MarkdownDeep;
public class MarkdownRazorViewEngine : RazorViewEngine
{
private static readonly Markdown Markdown =
new Markdown { ExtraMode = true, SafeMode = false };
private static readonly Regex MarkdownExtensionRegex =
new Regex("([^.]*)(md|markdown)$", RegexOptions.IgnoreCase);
private static readonly Func<string, string> MarkdownExtensionToRazorExtension =
filename => MarkdownExtensionRegex.Replace(filename, "$1html");
public static readonly IEnumerable<string> MarkdownFileExtensions =
new[] { "md", "markdown" };
public static readonly IEnumerable<string> MarkdownRazorFileExtensions =
from language in new[] { "cs", "vb" }
from extension in MarkdownFileExtensions
select string.Format("{0}{1}", language, extension);
public MarkdownRazorViewEngine()
{
AreaViewLocationFormats = InsertMarkdownFileExtensions(AreaViewLocationFormats).ToArray();
AreaMasterLocationFormats = InsertMarkdownFileExtensions(AreaMasterLocationFormats).ToArray();
AreaPartialViewLocationFormats = InsertMarkdownFileExtensions(AreaPartialViewLocationFormats).ToArray();
ViewLocationFormats = InsertMarkdownFileExtensions(ViewLocationFormats).ToArray();
MasterLocationFormats = InsertMarkdownFileExtensions(MasterLocationFormats).ToArray();
PartialViewLocationFormats = InsertMarkdownFileExtensions(PartialViewLocationFormats).ToArray();
}
protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
{
var razorPath = MarkdownExtensionToRazorExtension(partialPath);
return base.CreatePartialView(controllerContext, razorPath);
}
protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
{
var razorPath = MarkdownExtensionToRazorExtension(viewPath);
return base.CreateView(controllerContext, razorPath, masterPath);
}
protected override bool FileExists(ControllerContext controllerContext, string virtualPath)
{
if (MarkdownExtensionRegex.IsMatch(virtualPath))
{
string virtualMarkdownPath = virtualPath;
// Change the path to the Razor equivalent
virtualPath = MarkdownExtensionToRazorExtension(virtualMarkdownPath);
var physicalMarkdownPath = controllerContext.HttpContext.Server.MapPath(virtualMarkdownPath);
var physicalRazorPath = MarkdownExtensionToRazorExtension(physicalMarkdownPath);
PreprocessMarkdown(physicalMarkdownPath, physicalRazorPath);
}
return base.FileExists(controllerContext, virtualPath);
}
private static IEnumerable<string> InsertMarkdownFileExtensions(IEnumerable<string> filenames)
{
foreach (var filename in filenames)
{
foreach (var extension in MarkdownFileExtensions)
{
yield return Regex.Replace(filename, "([^.]*)html$", "$1" + extension);
}
yield return filename;
}
}
private void PreprocessMarkdown(string markdownPath, string razorPath)
{
if (!File.Exists(markdownPath))
return;
if (File.Exists(razorPath))
{
// If we've already converted this "version" to Razor, don't do it again
// NOTE: When/if this is a BuildProvider, ASP.NET will take care of this for us
if (File.GetLastWriteTime(razorPath) > File.GetLastWriteTime(markdownPath))
return;
}
var markdown = File.ReadAllText(markdownPath);
using (var writer = new StreamWriter(File.Open(razorPath, FileMode.Create, FileAccess.Write)))
{
var transformed = Markdown.Transform(markdown);
writer.Write(transformed);
try { writer.Flush(); }
catch { }
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment