diff --git a/modules/docs/src/Volo.Docs.Web/Markdown/Extensions/HighlightedCodeBlockExtension.cs b/modules/docs/src/Volo.Docs.Web/Markdown/Extensions/HighlightedCodeBlockExtension.cs new file mode 100644 index 0000000000..d2b7858d7a --- /dev/null +++ b/modules/docs/src/Volo.Docs.Web/Markdown/Extensions/HighlightedCodeBlockExtension.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 HighlightedCodeBlockExtension : 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 HighlightedCodeBlockRenderer()); + } + } + } +} \ 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..7d9a70c80c --- /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 UseHighlightedCodeBlocks(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..a45dc0862f 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() + .UseHighlightedCodeBlocks() .Build(); } diff --git a/modules/docs/src/Volo.Docs.Web/Markdown/Renderers/HighlightedCodeBlockRenderer.cs b/modules/docs/src/Volo.Docs.Web/Markdown/Renderers/HighlightedCodeBlockRenderer.cs new file mode 100644 index 0000000000..9a0f6afcf2 --- /dev/null +++ b/modules/docs/src/Volo.Docs.Web/Markdown/Renderers/HighlightedCodeBlockRenderer.cs @@ -0,0 +1,150 @@ +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 HighlightedCodeBlockRenderer : HtmlObjectRenderer + { + private const string Pattern = @"\{([^}]+)\}"; + public bool OutputAttributesOnPre { get; set; } + + public HashSet BlocksAsDiv { get; } + + public HighlightedCodeBlockRenderer() + { + 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("
');
+                }
+
+                renderer.WriteLeafRawLines(obj, true, true);
+
+                if (renderer.EnableHtmlForBlock)
+                {
+                    renderer.WriteLine("
"); + } + } + + renderer.EnsureLine(); + } + + private void WriteHighlightedCodeLines(HtmlRenderer renderer, FencedCodeBlock fencedCodeBlock) + { + var highlightedLines = GetHighlightedLines(fencedCodeBlock); + if (highlightedLines.Any()) + { + renderer.WriteAttributes(new HtmlAttributes + { + Classes = new List {"line-numbers"} //prevents adding line-numbers for highlighted lines + }); + + var lines = string.Join(",", highlightedLines); + renderer.Write($"data-line={lines}> GetHighlightedLines(FencedCodeBlock fencedCodeBlock) + { + var highlightedLines = new HashSet(); + if (string.IsNullOrWhiteSpace(fencedCodeBlock?.Arguments) || !Regex.IsMatch(pattern: Pattern, input: fencedCodeBlock.Arguments)) + { + return highlightedLines; + } + + 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("-"); + if (numbers.Length > 2) + { + continue; + } + + if (!int.TryParse(numbers[0], out var minLineNumber) || + !int.TryParse(numbers[1], out var maxLineNumber)) + { + continue; + } + + for (var lineNumber = minLineNumber; lineNumber < maxLineNumber + 1; lineNumber++) + { + highlightedLines.Add(lineNumber); + } + } + else + { + if (int.TryParse(line, out var lineNumber)) + { + highlightedLines.Add(lineNumber); + } + } + } + + return highlightedLines; + } + } +} \ No newline at end of file diff --git a/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml index b6a33ec2ad..4d59707f5e 100644 --- a/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml +++ b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/Index.cshtml @@ -27,6 +27,7 @@ + } @section scripts { diff --git a/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/index.css b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/index.css new file mode 100644 index 0000000000..cd3e581996 --- /dev/null +++ b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/index.css @@ -0,0 +1,4 @@ +.code-toolbar .line-highlight { + margin-top: 1.5em !important; + background: rgba(233, 237, 241, 0.34) !important; + padding: 1px !important; } diff --git a/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/index.min.css b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/index.min.css new file mode 100644 index 0000000000..20ff809dd8 --- /dev/null +++ b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/index.min.css @@ -0,0 +1 @@ +.code-toolbar .line-highlight{margin-top:1.5em !important;background:rgba(233,237,241,.34) !important;padding:1px !important;} \ No newline at end of file diff --git a/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/index.scss b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/index.scss new file mode 100644 index 0000000000..2b2a8bb520 --- /dev/null +++ b/modules/docs/src/Volo.Docs.Web/Pages/Documents/Project/index.scss @@ -0,0 +1,7 @@ +.code-toolbar { + .line-highlight { + margin-top: 1.5em !important; + background: rgba(233, 237, 241, 0.34) !important; + padding: 1px !important; + } +} diff --git a/modules/docs/src/Volo.Docs.Web/compilerconfig.json b/modules/docs/src/Volo.Docs.Web/compilerconfig.json index 3645b5a8f5..9b210b9e49 100644 --- a/modules/docs/src/Volo.Docs.Web/compilerconfig.json +++ b/modules/docs/src/Volo.Docs.Web/compilerconfig.json @@ -6,5 +6,9 @@ { "outputFile": "Pages/Documents/Project/index.css", "inputFile": "Pages/Documents/Project/index.less" + }, + { + "outputFile": "Pages/Documents/Project/index.css", + "inputFile": "Pages/Documents/Project/index.scss" } ] \ No newline at end of file