public MarkupResult Markup(string content, string filePath, bool enableValidation) { if (content == null) { throw new ArgumentNullException(nameof(content)); } if (filePath == null) { throw new ArgumentException("file path can't be null or empty."); } var pipeline = CreateMarkdownPipeline(isInline: false, enableValidation: enableValidation); using (InclusionContext.PushFile((RelativePath)filePath)) { return(new MarkupResult { Html = Markdown.ToHtml(content, pipeline), Dependency = InclusionContext.Dependencies.Select(file => (string)(RelativePath)file).ToImmutableArray() }); } }
public void TestGetSchemaName() { const string expectedSchemaName = "YamlMime:ModuleUnit"; const string yamlFilename = "moduleunit.yml"; const string yamlContent = @"### YamlMime:ModuleUnit uid: learn.azure.introduction"; File.WriteAllText(yamlFilename, yamlContent); InclusionContext.PushFile(yamlFilename); InclusionContext.PushInclusion("introduction-included.md"); string schemaName = string.Empty; var rewriter = MarkdownObjectRewriterFactory.FromValidator( MarkdownObjectValidatorFactory.FromLambda <MarkdownDocument>( root => { schemaName = root.GetData("SchemaName")?.ToString(); }) ); var html = Markup("# Hello World", rewriter, null); Assert.Equal(expectedSchemaName, schemaName); }
/// <summary> /// Parses the actual markdown down to html /// </summary> /// <param name="markdown"></param> /// <returns></returns> public override string Parse(string markdown) { var options = mmApp.Configuration.MarkdownOptions; var builder = new MarkdownPipelineBuilder(); var errors = Array.Empty <string>(); var tokens = new Dictionary <string, string>(); var files = new Dictionary <string, string>(); var actualErrors = new List <string>(); var actualDependencies = new HashSet <string>(); var context = new MarkdownContext( getToken: key => tokens.TryGetValue(key, out var value) ? value : null, logInfo: (a, b, c, d) => { }, logSuggestion: Log("suggestion"), logWarning: Log("warning"), logError: Log("error"), readFile: ReadFile); // Fake root path to what MM sees as the root path (.markdown, project file, project open etc.) string filePath = mmApp.Model.ActiveDocument?.Filename; if (string.IsNullOrEmpty(filePath) || filePath.Equals("untitled", StringComparison.OrdinalIgnoreCase)) { filePath = mmApp.Model.ActiveDocument?.PreviewWebRootPath; if (string.IsNullOrEmpty(filePath)) { filePath = "preview.md"; } else { filePath += "\\preview"; } } files.Add(filePath, markdown); builder = builder.UseEmphasisExtras(EmphasisExtraOptions.Strikethrough) .UseAutoIdentifiers(AutoIdentifierOptions.GitHub) .UseMediaLinks() .UsePipeTables() .UseAutoLinks() .UseHeadingIdRewriter() .UseIncludeFile(context) .UseCodeSnippet(context) .UseDFMCodeInfoPrefix() .UseQuoteSectionNote(context) .UseXref() .UseEmojiAndSmiley(false) .UseTabGroup(context) .UseMonikerRange(context) .UseInteractiveCode() .UseRow(context) .UseNestedColumn(context) .UseTripleColon(context) .UseNoloc(); builder = RemoveUnusedExtensions(builder); builder = builder .UseYamlFrontMatter() .UseLineNumber(); if (options.NoHtml) { builder = builder.DisableHtml(); } if (UsePragmaLines) { builder = builder.UsePragmaLines(); } var pipeline = builder.Build(); string html; using (InclusionContext.PushFile(filePath)) { html = Markdown.ToHtml(markdown, pipeline); } html = ParseFontAwesomeIcons(html); if (mmApp.Configuration.MarkdownOptions.RenderLinksAsExternal) { html = ParseExternalLinks(html); } if (!mmApp.Configuration.MarkdownOptions.AllowRenderScriptTags) { html = HtmlUtils.SanitizeHtml(html); } return(html); MarkdownContext.LogActionDelegate Log(string level) { return((code, message, origin, line) => actualErrors.Add(code)); } // Handler to fix up file paths for nested/included documents (string content, object file) ReadFile(string path, MarkdownObject origin) { string key; var rootPath = mmApp.Model.ActiveDocument?.PreviewWebRootPath; if (rootPath == null) { rootPath = Path.GetDirectoryName(files.FirstOrDefault().Key); } string parentDocPath = null; //relativeTo as string; NOT PROVIDEd BY DOCFX ANYMORE??? //if (!string.IsNullOrEmpty(parentDocPath)) // parentDocPath = Path.GetDirectoryName(parentDocPath); //fully qualified path if (path.Contains(":/") || path.Contains(":\\")) { key = path; } else if (!string.IsNullOrEmpty(rootPath) && path.StartsWith("~/")) { path = path.Substring(2); key = Path.Combine(rootPath, path).Replace('\\', '/'); } // Site relative path else if (!string.IsNullOrEmpty(rootPath) && path.StartsWith("/")) { path = path.Substring(1); key = Path.Combine(rootPath, path).Replace('\\', '/'); } // Site relative path else if (!string.IsNullOrEmpty(parentDocPath)) { key = Path.GetFullPath(Path.Combine(parentDocPath, path)); } else { key = path; } actualDependencies.Add(key); files.TryGetValue(key, out var value); if (value == null) { try { value = File.ReadAllText(key)?.Trim(); } catch { } } if (value == null) { return(null, null); } return(value, key as object); } }
public static void VerifyMarkup( string markdown, string html, string[] errors = null, string[] dependencies = null, bool lineNumber = false, string filePath = "test.md", Dictionary <string, string> tokens = null, Dictionary <string, string> files = null, Action <MarkdownObject> verifyAST = null) { errors = errors ?? Array.Empty <string>(); tokens = tokens ?? new Dictionary <string, string>(); files = files ?? new Dictionary <string, string>(); var actualErrors = new List <string>(); var actualDependencies = new HashSet <string>(); var markdownContext = new MarkdownContext( getToken: key => tokens.TryGetValue(key, out var value) ? value : null, logInfo: (a, b, c, d) => { }, logSuggestion: Log("suggestion"), logWarning: Log("warning"), logError: Log("error"), readFile: ReadFile); var pipelineBuilder = new MarkdownPipelineBuilder() .UseDocfxExtensions(markdownContext) .UseYamlFrontMatter(); if (lineNumber) { pipelineBuilder.UseLineNumber(); } var pipeline = pipelineBuilder.Build(); using (InclusionContext.PushFile(filePath)) { var actualHtml = Markdown.ToHtml(markdown, pipeline); if (html != null) { Assert.Equal( html.Replace("\r", "").Replace("\n", ""), actualHtml.Replace("\r", "").Replace("\n", "")); } Assert.Equal(errors.OrderBy(_ => _), actualErrors.OrderBy(_ => _)); if (dependencies != null) { Assert.Equal(dependencies.OrderBy(_ => _), actualDependencies.OrderBy(_ => _)); } } MarkdownContext.LogActionDelegate Log(string level) { return((code, message, origin, line) => actualErrors.Add(code)); } (string content, object file) ReadFile(string path, object relativeTo, MarkdownObject origin) { var key = Path.Combine(Path.GetDirectoryName(relativeTo.ToString()), path).Replace('\\', '/'); if (path.StartsWith("~/")) { path = path.Substring(2); key = path; } actualDependencies.Add(path); return(files.TryGetValue(key, out var value) ? (value, key) : default);
/// <summary> /// Parses the actual markdown down to html /// </summary> /// <param name="markdown"></param> /// <returns></returns> public override string Parse(string markdown) { var options = mmApp.Configuration.MarkdownOptions; var builder = new MarkdownPipelineBuilder(); var errors = Array.Empty <string>(); var tokens = new Dictionary <string, string>(); var files = new Dictionary <string, string>(); var actualErrors = new List <string>(); var actualDependencies = new HashSet <string>(); var context = new MarkdownContext( getToken: key => tokens.TryGetValue(key, out var value) ? value : null, logInfo: (a, b, c, d) => { }, logSuggestion: Log("suggestion"), logWarning: Log("warning"), logError: Log("error"), readFile: ReadFile); // Fake root path to what MM sees as the root path (.markdown, project file, project open etc.) string filePath = mmApp.Model.ActiveDocument?.Filename; if (string.IsNullOrEmpty(filePath) || filePath.Equals("untitled", StringComparison.OrdinalIgnoreCase)) { filePath = mmApp.Model.ActiveDocument?.PreviewWebRootPath; if (string.IsNullOrEmpty(filePath)) { filePath = "preview.md"; } else { filePath += "\\preview"; } } files.Add(filePath, markdown); builder = builder.UseEmphasisExtras(EmphasisExtraOptions.Strikethrough) .UseAutoIdentifiers(AutoIdentifierOptions.GitHub) .UseMediaLinks() .UsePipeTables() .UseAutoLinks() .UseHeadingIdRewriter() .UseIncludeFile(context) .UseCodeSnippet(context) .UseDFMCodeInfoPrefix() .UseQuoteSectionNote(context) .UseXref() .UseEmojiAndSmiley(false) .UseTabGroup(context) .UseMonikerRange(context) .UseInteractiveCode() .UseRow(context) .UseNestedColumn(context) .UseTripleColon(context) .UseNoloc(); builder = RemoveUnusedExtensions(builder); builder = builder .UseYamlFrontMatter() .UseLineNumber(); if (options.NoHtml) { builder = builder.DisableHtml(); } if (UsePragmaLines) { builder = builder.UsePragmaLines(); } var pipeline = builder.Build(); string html; try { using (InclusionContext.PushFile(filePath)) { html = Markdown.ToHtml(markdown, pipeline); } html = ParseFontAwesomeIcons(html); } catch (Exception ex) { if (markdown.Length > 10000) { markdown = markdown.Substring(0, 10000); } mmApp.Log("Unable to render Markdown Document (docFx)\n" + markdown, ex, logLevel: LogLevels.Warning); html = $@" <h1><i class='fa fa-warning text-error'></i> Unable to render Markdown Document</h1> <p> An error occurred trying to parse the Markdown document to HTML: </p> <b style='font-size: 1.2em'>{ex.Message}</b> <p> <a id='hrefShow' href='#0' style='font-size: 0.8em; font-weight: normal'>more info...</a> </p> <div id='detail' style='display:none'> <p style='margin-top: 2em'> <b>Markdown Parser</b>: {options.MarkdownParserName} </p> <pre style='padding: 8px; background: #eee; color: #333' >{System.Net.WebUtility.HtmlEncode(StringUtils.NormalizeIndentation(ex.StackTrace))}</pre> </div> <script> $('#hrefShow').click(function () {{ $('#detail').show(); }}); </script> "; return(html); } if (mmApp.Configuration.MarkdownOptions.RenderLinksAsExternal) { html = ParseExternalLinks(html); } if (!mmApp.Configuration.MarkdownOptions.AllowRenderScriptTags) { html = HtmlUtils.SanitizeHtml(html); } return(html); MarkdownContext.LogActionDelegate Log(string level) { return((code, message, origin, line) => actualErrors.Add(code)); } // Handler to fix up file paths for nested/included documents (string content, object file) ReadFile(string path, object relativeTo, MarkdownObject origin) { string key; var relativeDocumentPath = relativeTo as string; var rootPath = mmApp.Model.ActiveDocument?.PreviewWebRootPath; if (rootPath == null) { rootPath = Path.GetDirectoryName(files.FirstOrDefault().Key); } var parentDocPath = relativeTo as string; if (!string.IsNullOrEmpty(parentDocPath)) { parentDocPath = Path.GetDirectoryName(parentDocPath); } //fully qualified path if (path.Contains(":/") || path.Contains(":\\")) { key = path; } else if (!string.IsNullOrEmpty(rootPath) && path.StartsWith("~/")) { path = path.Substring(2); key = Path.Combine(rootPath, path).Replace('\\', '/'); } // Site relative path else if (!string.IsNullOrEmpty(rootPath) && path.StartsWith("/")) { path = path.Substring(1); key = Path.Combine(rootPath, path).Replace('\\', '/'); } // Site relative path else if (!string.IsNullOrEmpty(parentDocPath)) { key = Path.GetFullPath(Path.Combine(parentDocPath, path)); } else { key = path; } actualDependencies.Add(key); files.TryGetValue(key, out var value); if (value == null) { try { value = File.ReadAllText(key)?.Trim(); }catch { } } if (value == null) { return(null, null); } return(value, key as object); } }