コード例 #1
0
        /// <summary>
        /// Handles rendering of Tip/Warning/Important/Caution/Note
        /// blocks.
        ///
        /// </summary>
        /// <returns></returns>
        /// <remarks>
        /// Note: Markdown is expected to be in LineFeed only mode for line breaks (StringUtils.NormalizeLinefeeds()).
        /// If you have CR/LF the value needs to be fixed up.  RenderExtensions automatically fix up inbound Markdown
        /// to normalized linefeeds for rendering, but if you test locally make sure to pre-format the args.Markdown
        /// </remarks>
        public void ParseNoteTipWarningImportant(ModifyMarkdownArguments args)
        {
            var matches = TipNoteWarningImportantFileRegEx.Matches(args.Markdown);

            foreach (Match match in matches)
            {
                string value = match.Value.Trim();

                var lines = StringUtils.GetLines(value);

                var sb = new StringBuilder();
                for (int i = 0; i < lines.Length; i++)
                {
                    string line = lines[i];
                    if (i == 0)
                    {
                        // note header
                        if (line.TrimStart().StartsWith("> [!"))
                        {
                            string icon = "fa-info-circle";
                            var    word = StringUtils.ExtractString(line, "> [!", "]");
                            switch (word)
                            {
                            case "NOTE":
                            case "TIP":
                                icon = "fa-info-circle";
                                break;

                            case "WARNING":
                            case "CAUTION":
                            case "IMPORTANT":
                                icon = "fa-warning";
                                break;
                            }

                            icon = $"<i class='fa {icon}'></i>&nbsp;";

                            sb.AppendLine("<div class=\"" + word + "\">");
                            sb.AppendLine($"<h5>{icon}{word}</h5>");
                            sb.AppendLine();
                        }
                        else
                        {
                            // content line - could be empty
                            sb.AppendLine(line.TrimStart(' ', '>'));
                        }
                    }
                    else
                    {
                        // content line - could be empty
                        sb.AppendLine(line.TrimStart(' ', '>'));
                    }
                }
                sb.AppendLine();
                sb.AppendLine("</div>");
                sb.AppendLine();

                args.Markdown = args.Markdown.Replace(value, sb.ToString());
            }
        }
コード例 #2
0
        /// <summary>
        /// Parses DocFx include files in the format of:
        ///
        ///    [!include[title](relativePathToFileToInclude>)]
        ///
        /// Should run **prior** to Markdown parsing of the main document
        /// as it will embed the file content as is.
        /// </summary>
        /// <param name="markdown"></param>
        /// <returns></returns>
        protected void ParseDocFxIncludeFiles(ModifyMarkdownArguments args)
        {
            var matches = includeFileRegEx.Matches(args.Markdown);

            foreach (Match match in matches)
            {
                string value = match.Value;

                //string title = StringUtils.ExtractString(value, "[!include[", "]");
                string file = StringUtils.ExtractString(value, "](", ")]");
                if (string.IsNullOrEmpty(file))
                {
                    continue;
                }

                if (file.StartsWith("~"))
                {
                    file = mmApp.Model.Window.FolderBrowser.FolderPath + file.Substring(1);
                }


                string filePath;
                if (file.Contains(@":\"))
                {
                    filePath = file;
                }
                else
                {
                    filePath = mmApp.Model.ActiveDocument?.Filename;
                    if (string.IsNullOrEmpty(filePath))
                    {
                        continue;
                    }

                    filePath = Path.GetDirectoryName(filePath);
                    if (filePath == null)
                    {
                        continue;
                    }
                }


                string includeFile = Path.Combine(filePath, file);
                includeFile = FileUtils.NormalizePath(includeFile);
                if (!File.Exists(includeFile))
                {
                    continue;
                }

                var markdownDocument = new MarkdownDocument();
                markdownDocument.Load(includeFile);
                string includeContent = markdownDocument.RenderHtml();

                args.Markdown = args.Markdown.Replace(value, includeContent);
            }
        }
コード例 #3
0
        public void BeforeMarkdownRendered(ModifyMarkdownArguments args)
        {
            if (mmApp.Configuration.MarkdownOptions.MarkdownParserName.Contains("DocFx"))
            {
                return;
            }

            ParseDocFxIncludeFiles(args);
            ParseNoteTipWarningImportant(args);
            ParseXrefTags(args);
        }
コード例 #4
0
 /// <summary>
 /// Process all BeforeRenderMarkdown Extensions
 /// </summary>
 /// <param name="markdown"></param>
 /// <param name="document"></param>
 public void ProcessAllBeforeMarkdownRenderedHooks(ModifyMarkdownArguments args)
 {
     foreach (var extension in RenderExtensions)
     {
         try
         {
             extension.BeforeMarkdownRendered(args);
         }
         catch (Exception ex)
         {
             mmApp.Log($"BeforeMarkdownRendered RenderExtension failed: {extension.GetType().Name}", ex);
         }
     }
 }
コード例 #5
0
 /// <summary>
 /// Handle Right To Left ACE Editor markdown removal so rendering looks correct
 /// </summary>
 /// <param name="args"></param>
 public void BeforeMarkdownRendered(ModifyMarkdownArguments args)
 {
     // Right to Left Rendering fix up from ACE Editor
     if (mmApp.Configuration.Editor.EnableRightToLeft)
     {
         // HACK: Strip ACE embed RTL/LTR Transition character
         var bytes         = args.MarkdownDocument.Encoding.GetBytes(args.Markdown);
         var bytesToRemove = new byte[] { 0xe2, 0x80, 0xAb };
         if (DataUtils.IndexOfByteArray(bytes, bytesToRemove) > -1)
         {
             var newbytes = DataUtils.RemoveBytes(bytes, bytesToRemove);
             args.Markdown = args.MarkdownDocument.Encoding.GetString(newbytes);
         }
     }
 }
コード例 #6
0
        /// <summary>
        /// Check for ```markdown blocks and replace them with DIV blocks
        /// </summary>
        /// <param name="args"></param>
        public void BeforeMarkdownRendered(ModifyMarkdownArguments args)
        {
            while (true)
            {
                string extract = StringUtils.ExtractString(args.Markdown, "\n```mermaid", "```", returnDelimiters: true);
                if (string.IsNullOrEmpty(extract))
                {
                    break;
                }

                string newExtract = extract.Replace("```mermaid", "<div class=\"mermaid\">")
                                    .Replace("```", "</div>");

                args.Markdown = args.Markdown.Replace(extract, newExtract);
            }
        }
コード例 #7
0
        public void ParseXrefTags(ModifyMarkdownArguments args)
        {
            var matches = XRefRegEx.Matches(args.Markdown);

            foreach (Match match in matches)
            {
                string value = match.Value.Trim();

                var link = StringUtils.ExtractString(value, "<xref:", ">")?.TrimEnd('/');
                if (link == null)
                {
                    return;
                }
                string title = null;


                var filePath = FixUpRootPath(args, link);
                if (File.Exists(filePath))
                {
                    string fileContent = File.ReadAllText(filePath);
                    title = StringUtils.GetLines(fileContent)
                            .FirstOrDefault(l => l.StartsWith("# ") || l.StartsWith("## ") || l.StartsWith("### "));
                }
                else if (File.Exists(Path.ChangeExtension(filePath, "md")))
                {
                    string fileContent = File.ReadAllText(Path.ChangeExtension(filePath, "md"));
                    title = StringUtils.GetLines(fileContent)
                            .FirstOrDefault(l => l.StartsWith("# ") || l.StartsWith("## ") || l.StartsWith("### "));
                }

                if (string.IsNullOrEmpty(title))
                {
                    title = link;
                }
                else
                {
                    title = title.TrimStart('#', ' ', '\t');
                }

                var html = $"<a href=\"{link}\">{WebUtility.HtmlEncode(title)}</a>";

                args.Markdown = args.Markdown.Replace(value, html);
            }
        }
コード例 #8
0
        /// <summary>
        /// Handles rendering of Tip/Warning/Important/Caution/Note
        /// blocks.
        ///
        /// </summary>
        /// <returns></returns>
        /// <remarks>
        /// Note: Markdown is expected to be in LineFeed only mode for line breaks (StringUtils.NormalizeLinefeeds()).
        /// If you have CR/LF the value needs to be fixed up.  RenderExtensions automatically fix up inbound Markdown
        /// to normalized linefeeds for rendering, but if you test locally make sure to pre-format the args.Markdown
        /// </remarks>
        public void ParseNoteTipWarningImportant(ModifyMarkdownArguments args)
        {
            var matches = TipNoteWarningImportantFileRegEx.Matches(args.Markdown);

            foreach (Match match in matches)
            {
                string value = match.Value.Trim();

                var lines = StringUtils.GetLines(value);

                var sb = new StringBuilder();
                for (int i = 0; i < lines.Length; i++)
                {
                    string line = lines[i];
                    if (i == 0)
                    {
                        // note header
                        if (line.TrimStart().StartsWith("> [!"))
                        {
                            var word = StringUtils.ExtractString(line, "> [!", "]");

                            sb.AppendLine("<div class=\"" + word + "\">");
                            sb.AppendLine($"<h5>{word}</h5>");
                            sb.AppendLine();
                        }
                        else
                        {
                            // content line - could be empty
                            sb.AppendLine(line.TrimStart(' ', '>'));
                        }
                    }
                    else
                    {
                        // content line - could be empty
                        sb.AppendLine(line.TrimStart(' ', '>'));
                    }
                }
                sb.AppendLine();
                sb.AppendLine("</div>");
                sb.AppendLine();

                args.Markdown = args.Markdown.Replace(value, sb.ToString());
            }
        }
コード例 #9
0
        public string FixUpRootPath(ModifyMarkdownArguments args, string link)
        {
            if (string.IsNullOrEmpty(link))
            {
                return(link);
            }

            var filePath = link;

            if (filePath.StartsWith("~/") || filePath.StartsWith("/"))
            {
                filePath = StringUtils.ReplaceStringInstance(filePath, "~/", "", 1, false);
                if (filePath.StartsWith("/"))
                {
                    filePath = filePath.Substring(1);
                }

                filePath = args.MarkdownDocument.PreviewWebRootPath + "\\" + filePath;
            }
            else
            {
                var path = Path.GetDirectoryName(args.MarkdownDocument.Filename);
                if (!string.IsNullOrEmpty(path))
                {
                    filePath = Path.Combine(path, filePath);
                }
                else
                {
                    filePath = args.MarkdownDocument.PreviewWebRootPath + "\\" + filePath;
                }
            }


            filePath = FileUtils.NormalizePath(filePath);

            return(filePath);
        }
コード例 #10
0
 public void BeforeMarkdownRendered(ModifyMarkdownArguments args)
 {
     ParseDocFxIncludeFiles(args);
     ParseNoteTipWarningImportant(args);
 }
コード例 #11
0
        /// <summary>
        /// Parses DocFx include files in the format of:
        ///
        ///    [!include[title](relativePathToFileToInclude>)]
        ///
        /// Should run **prior** to Markdown parsing of the main document
        /// as it will embed the file content as is.
        /// </summary>
        /// <returns></returns>
        public void ParseDocFxIncludeFiles(ModifyMarkdownArguments args)
        {
            var matches = includeFileRegEx.Matches(args.Markdown);

            foreach (Match match in matches)
            {
                string value = match.Value;

                //string title = StringUtils.ExtractString(value, "[!include[", "]");
                string file = StringUtils.ExtractString(value, "](", ")]");
                if (string.IsNullOrEmpty(file))
                {
                    continue;
                }

                bool   isCode = value.StartsWith("[!code-");
                string syntax = isCode ? StringUtils.ExtractString(value, "[!code-", "[]") : null;

                string filePath;
                bool   hasPreviewWebPath = !string.IsNullOrEmpty(args.MarkdownDocument.PreviewWebRootPath);
                // Fix up paths
                if (hasPreviewWebPath && file.StartsWith("~/"))
                {
                    file = Path.Combine(args.MarkdownDocument.PreviewWebRootPath, file.Substring(2));
                }
                else if (hasPreviewWebPath && file.StartsWith("~") || file.StartsWith("/"))
                {
                    file = Path.Combine(args.MarkdownDocument.PreviewWebRootPath, file.Substring(1));
                }
                if (file.Contains(@":\") || file.Contains(":/"))
                {
                    filePath = file;
                }
                else
                {
                    var lastDoc = nestedDocs.LastOrDefault();
                    if (string.IsNullOrEmpty(lastDoc))
                    {
                        filePath = mmApp.Model.ActiveDocument?.Filename;
                        filePath = Path.GetDirectoryName(filePath);
                    }
                    else
                    {
                        filePath = Path.GetDirectoryName(lastDoc);
                    }

                    if (string.IsNullOrEmpty(filePath))
                    {
                        continue;
                    }
                }


                string includeFile = Path.Combine(filePath, file);
                includeFile = FileUtils.NormalizePath(includeFile);
                if (!File.Exists(includeFile))
                {
                    // escape the embedded link ([] so it doesn't expand
                    args.Markdown = args.Markdown.Replace("[]", "&#91;&#93;");
                    continue;
                }

                var includedContent = File.ReadAllText(includeFile);
                if (isCode)
                {
                    includedContent = $"```{syntax}\n{includedContent.Trim()}\n```";
                }

                nestedDocs.Push(includeFile);

                // We need to process nested content
                var markdownDocument = new MarkdownDocument();
                markdownDocument.PreviewWebRootPath = args.MarkdownDocument.PreviewWebRootPath;
                markdownDocument.CurrentText        = includedContent;
                markdownDocument.OnBeforeDocumentRendered(ref includedContent);

                args.Markdown = args.Markdown.Replace(value, includedContent);

                nestedDocs.Pop();
            }
        }