diff --git a/modules/docs/src/Volo.Docs.Web/Markdown/Extensions/CustomCodeBlockExtension.cs b/modules/docs/src/Volo.Docs.Web/Markdown/Extensions/CustomCodeBlockExtension.cs new file mode 100644 index 0000000000..95995c839b --- /dev/null +++ b/modules/docs/src/Volo.Docs.Web/Markdown/Extensions/CustomCodeBlockExtension.cs @@ -0,0 +1,34 @@ +using System; +using Markdig; +using Markdig.Renderers; +using Markdig.Renderers.Html; +using Volo.Docs.Markdown.Renderers; + +namespace Volo.Docs.Markdown.Extensions +{ + public class CustomCodeBlockExtension : IMarkdownExtension + { + public void Setup(MarkdownPipelineBuilder pipeline) + { + } + + public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer) + { + if (renderer == null) + { + throw new ArgumentNullException(nameof(renderer)); + } + + if (renderer is TextRendererBase htmlRenderer) + { + var codeBlockRenderer = htmlRenderer.ObjectRenderers.FindExact(); + if (codeBlockRenderer != null) + { + htmlRenderer.ObjectRenderers.Remove(codeBlockRenderer); + } + + htmlRenderer.ObjectRenderers.AddIfNotAlready(new CustomCodeBlockRenderer()); + } + } + } +} \ No newline at end of file diff --git a/modules/docs/src/Volo.Docs.Web/Markdown/Extensions/MarkdownPipelineBuilderExtensions.cs b/modules/docs/src/Volo.Docs.Web/Markdown/Extensions/MarkdownPipelineBuilderExtensions.cs new file mode 100644 index 0000000000..249bf31b49 --- /dev/null +++ b/modules/docs/src/Volo.Docs.Web/Markdown/Extensions/MarkdownPipelineBuilderExtensions.cs @@ -0,0 +1,13 @@ +using Markdig; + +namespace Volo.Docs.Markdown.Extensions +{ + public static class MarkdownPipelineBuilderExtensions + { + public static MarkdownPipelineBuilder UseCustomCodeBlock(this MarkdownPipelineBuilder pipeline) + { + pipeline.Extensions.AddIfNotAlready(); + return pipeline; + } + } +} \ No newline at end of file diff --git a/modules/docs/src/Volo.Docs.Web/Markdown/MarkDigMarkdownConverter.cs b/modules/docs/src/Volo.Docs.Web/Markdown/MarkDigMarkdownConverter.cs index 1bd858e2d0..183eeabee2 100644 --- a/modules/docs/src/Volo.Docs.Web/Markdown/MarkDigMarkdownConverter.cs +++ b/modules/docs/src/Volo.Docs.Web/Markdown/MarkDigMarkdownConverter.cs @@ -1,6 +1,7 @@ using System.Text; using Markdig; using Volo.Abp.DependencyInjection; +using Volo.Docs.Markdown.Extensions; namespace Volo.Docs.Markdown { @@ -15,6 +16,7 @@ namespace Volo.Docs.Markdown .UseBootstrap() .UseGridTables() .UsePipeTables() + .UseCustomCodeBlock() .Build(); } diff --git a/modules/docs/src/Volo.Docs.Web/Markdown/Renderers/CustomCodeBlockRenderer.cs b/modules/docs/src/Volo.Docs.Web/Markdown/Renderers/CustomCodeBlockRenderer.cs new file mode 100644 index 0000000000..bddc94c439 --- /dev/null +++ b/modules/docs/src/Volo.Docs.Web/Markdown/Renderers/CustomCodeBlockRenderer.cs @@ -0,0 +1,131 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using Markdig.Parsers; +using Markdig.Renderers; +using Markdig.Renderers.Html; +using Markdig.Syntax; + +namespace Volo.Docs.Markdown.Renderers +{ + public class CustomCodeBlockRenderer : HtmlObjectRenderer + { + private const string Pattern = @"\{([^}]+)\}"; + public bool OutputAttributesOnPre { get; set; } + + public HashSet BlocksAsDiv { get; } + + public CustomCodeBlockRenderer() + { + BlocksAsDiv = new HashSet(StringComparer.OrdinalIgnoreCase); + } + + protected override void Write(HtmlRenderer renderer, CodeBlock obj) + { + renderer.EnsureLine(); + + var fencedCodeBlock = obj as FencedCodeBlock; + if (fencedCodeBlock?.Info != null && BlocksAsDiv.Contains(fencedCodeBlock.Info)) + { + var infoPrefix = (obj.Parser as FencedCodeBlockParser)?.InfoPrefix ?? + FencedCodeBlockParser.DefaultInfoPrefix; + + if (renderer.EnableHtmlForBlock) + { + renderer.Write(" cls.StartsWith(infoPrefix, StringComparison.Ordinal) ? cls.Substring(infoPrefix.Length) : cls) + .Write('>'); + } + + renderer.WriteLeafRawLines(obj, true, true, true); + + if (renderer.EnableHtmlForBlock) + { + renderer.WriteLine(""); + } + + } + else + { + if (renderer.EnableHtmlForBlock) + { + renderer.Write("
 {"line-numbers"}});
+
+                        var lines = string.Join(",", highlightedLines);
+                        renderer.Write($"data-line={lines}>');
+                }
+
+                renderer.WriteLeafRawLines(obj, true, true);
+
+                if (renderer.EnableHtmlForBlock)
+                {
+                    renderer.WriteLine("
"); + } + } + + renderer.EnsureLine(); + } + + private List GetHighlightedLines(FencedCodeBlock fencedCodeBlock) + { + var highlightedLines = new List(); + + if (string.IsNullOrWhiteSpace(fencedCodeBlock?.Arguments)) + { + return highlightedLines; + } + + if (Regex.IsMatch(pattern: Pattern, input: fencedCodeBlock.Arguments)) + { + var match = Regex.Match(fencedCodeBlock.Arguments, Pattern); + var groups = match.Groups; + + if (groups.Count < 2 || string.IsNullOrWhiteSpace(groups[1].Value)) + { + return highlightedLines; + } + + var lines = groups[1].Value.Split(","); + foreach (var line in lines) + { + if (line.Contains("-")) + { + var numbers = line.Split("-"); + highlightedLines.AddRange(numbers.Select(number => Convert.ToInt32(number))); + } + else + { + highlightedLines.Add(Convert.ToInt32(line)); + } + } + } + + return highlightedLines; + } + } +} \ No newline at end of file