diff --git a/.gitignore b/.gitignore index 017f4da7c09..ec764ecafc3 100644 --- a/.gitignore +++ b/.gitignore @@ -57,4 +57,5 @@ src/docfx.website.themes/default/fonts/ # deploy # ############### tools/Deployment/.vscode/ -tools/Deployment/out/ \ No newline at end of file +tools/Deployment/out/ +/package_version_temp.txt diff --git a/src/Microsoft.DocAsCode.Dfm/DfmEngineBuilder.cs b/src/Microsoft.DocAsCode.Dfm/DfmEngineBuilder.cs index d94ca3ca81b..b080d7c44a7 100644 --- a/src/Microsoft.DocAsCode.Dfm/DfmEngineBuilder.cs +++ b/src/Microsoft.DocAsCode.Dfm/DfmEngineBuilder.cs @@ -42,13 +42,14 @@ public DfmEngineBuilder(Options options, string baseDir, string templateDir, IRe { throw new ArgumentException("MarkdownLinkInlineRule should exist!"); } + inlineRules.Insert(index + 1, new DfmXrefShortcutInlineRule()); inlineRules.Insert(index + 1, new DfmEmailInlineRule()); inlineRules.Insert(index + 1, new DfmFencesInlineRule()); + inlineRules.Insert(index + 1, new DfmIncludeInlineShortcutRule()); // xref link inline rule must be before MarkdownLinkInlineRule inlineRules.Insert(index, new DfmIncludeInlineRule()); - inlineRules.Insert(index, new DfmIncludeInlineShortcutRule()); Replace(inlineRules); diff --git a/src/Microsoft.DocAsCode.Dfm/Rules/DfmIncludeInlineShortcutRule.cs b/src/Microsoft.DocAsCode.Dfm/Rules/DfmIncludeInlineShortcutRule.cs index cadb5163228..11a6da18b92 100644 --- a/src/Microsoft.DocAsCode.Dfm/Rules/DfmIncludeInlineShortcutRule.cs +++ b/src/Microsoft.DocAsCode.Dfm/Rules/DfmIncludeInlineShortcutRule.cs @@ -14,7 +14,7 @@ namespace Microsoft.DocAsCode.Dfm public class DfmIncludeInlineShortcutRule : IMarkdownRule { public virtual string Name => "DfmIncludeInlineShortcut"; - private static readonly Regex _inlineIncludeRegex = new Regex(@"@@(?\w+)", RegexOptions.Compiled | RegexOptions.IgnoreCase, TimeSpan.FromSeconds(10)); + internal static readonly Regex _inlineIncludeRegex = new Regex(@"@@(?\w+)", RegexOptions.Compiled | RegexOptions.IgnoreCase, TimeSpan.FromSeconds(10)); public virtual Regex Include => _inlineIncludeRegex; public IMarkdownToken TryMatch(IMarkdownParser parser, IMarkdownParsingContext context) diff --git a/src/Microsoft.DocAsCode.MarkdigEngine.Extensions/Inclusion/InclusionExtension.cs b/src/Microsoft.DocAsCode.MarkdigEngine.Extensions/Inclusion/InclusionExtension.cs index 926fe7b4be8..c62a148b799 100644 --- a/src/Microsoft.DocAsCode.MarkdigEngine.Extensions/Inclusion/InclusionExtension.cs +++ b/src/Microsoft.DocAsCode.MarkdigEngine.Extensions/Inclusion/InclusionExtension.cs @@ -25,6 +25,10 @@ public void Setup(MarkdownPipelineBuilder pipeline) { pipeline.BlockParsers.AddIfNotAlready(); pipeline.InlineParsers.InsertBefore(new InclusionInlineParser()); + + var inlineShortcut = new InclusionInlineShortcutParser(); + pipeline.InlineParsers.InsertBefore(inlineShortcut); + pipeline.InlineParsers.AddIfNotAlready(inlineShortcut); } public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer) @@ -38,6 +42,13 @@ public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer) htmlRenderer.ObjectRenderers.Insert(0, new HtmlInclusionInlineRenderer(_context, inlinePipeline)); } + if (!htmlRenderer.ObjectRenderers.Contains()) + { + var inlinePipeline = LazyInitializer.EnsureInitialized(ref _inlinePipeline, () => CreateInlineOnlyPipeline(pipeline)); + + htmlRenderer.ObjectRenderers.Insert(0, new HtmlInclusionInlineShortcutRenderer(_context, inlinePipeline)); + } + if (!htmlRenderer.ObjectRenderers.Contains()) { htmlRenderer.ObjectRenderers.Insert(0, new HtmlInclusionBlockRenderer(_context, pipeline)); diff --git a/src/Microsoft.DocAsCode.MarkdigEngine.Extensions/Inclusion/InclusionInlineShortcut/HtmlInclusionInlineRenderer.cs b/src/Microsoft.DocAsCode.MarkdigEngine.Extensions/Inclusion/InclusionInlineShortcut/HtmlInclusionInlineRenderer.cs new file mode 100644 index 00000000000..c4501cf96fb --- /dev/null +++ b/src/Microsoft.DocAsCode.MarkdigEngine.Extensions/Inclusion/InclusionInlineShortcut/HtmlInclusionInlineRenderer.cs @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.DocAsCode.MarkdigEngine.Extensions +{ + using Markdig; + using Markdig.Renderers; + using Markdig.Renderers.Html; + using System.Linq; + + public class HtmlInclusionInlineShortcutRenderer : HtmlObjectRenderer + { + private readonly MarkdownContext _context; + private readonly MarkdownPipeline _inlinePipeline; + + public HtmlInclusionInlineShortcutRenderer(MarkdownContext context, MarkdownPipeline inlinePipeline) + { + _context = context; + _inlinePipeline = inlinePipeline; + } + + protected override void Write(HtmlRenderer renderer, InclusionInlineShortcut inclusion) + { + var (content, includeFilePath) = _context.ReadFile(inclusion.IncludedFilePath, inclusion); + + if (content == null) + { + _context.LogWarning("include-not-found", $"Cannot resolve '{inclusion.IncludedFilePath}' relative to '{InclusionContext.File}'.", inclusion); + renderer.Write(inclusion.GetRawToken()); + return; + } + + if (InclusionContext.IsCircularReference(includeFilePath, out var dependencyChain)) + { + _context.LogWarning("circular-reference", $"Build has identified file(s) referencing each other: {string.Join(" --> ", dependencyChain.Select(file => $"'{file}'"))} --> '{includeFilePath}'", inclusion); + renderer.Write(inclusion.GetRawToken()); + return; + } + + using (InclusionContext.PushInclusion(includeFilePath)) + { + renderer.Write(Markdown.ToHtml(content, _inlinePipeline)); + } + } + } +} \ No newline at end of file diff --git a/src/Microsoft.DocAsCode.MarkdigEngine.Extensions/Inclusion/InclusionInlineShortcut/InclusionInlineShortcut.cs b/src/Microsoft.DocAsCode.MarkdigEngine.Extensions/Inclusion/InclusionInlineShortcut/InclusionInlineShortcut.cs new file mode 100644 index 00000000000..57a885f55dd --- /dev/null +++ b/src/Microsoft.DocAsCode.MarkdigEngine.Extensions/Inclusion/InclusionInlineShortcut/InclusionInlineShortcut.cs @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.DocAsCode.MarkdigEngine.Extensions +{ + using Markdig.Syntax.Inlines; + + public class InclusionInlineShortcut : ContainerInline + { + public string RawFilename { get; set; } + + public string IncludedFilePath { get; set; } + + public object ResolvedFilePath { get; set; } + + public string GetRawToken() => $"@@{RawFilename}"; + } +} diff --git a/src/Microsoft.DocAsCode.MarkdigEngine.Extensions/Inclusion/InclusionInlineShortcut/InclusionInlineShortcutParser.cs b/src/Microsoft.DocAsCode.MarkdigEngine.Extensions/Inclusion/InclusionInlineShortcut/InclusionInlineShortcutParser.cs new file mode 100644 index 00000000000..4286cd893a4 --- /dev/null +++ b/src/Microsoft.DocAsCode.MarkdigEngine.Extensions/Inclusion/InclusionInlineShortcut/InclusionInlineShortcutParser.cs @@ -0,0 +1,63 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +namespace Microsoft.DocAsCode.MarkdigEngine.Extensions +{ + using Markdig.Helpers; + using Markdig.Parsers; + using Markdig.Syntax; + + public class InclusionInlineShortcutParser : InlineParser + { + private const string StartString = "@@"; + + public InclusionInlineShortcutParser() + { + OpeningCharacters = new[] { '@' }; + } + + public override bool Match(InlineProcessor processor, ref StringSlice slice) + { + var startPosition = processor.GetSourcePosition(slice.Start, out var line, out var column); + + if (!ExtensionsHelper.MatchStart(ref slice, StartString, false)) + { + return false; + } + + var str = StringBuilderCache.Local(); + + str.Append(slice.CurrentChar); + + var c = slice.NextChar(); + while (c.IsAlphaNumeric()) + { + str.Append(c); + c = slice.NextChar(); + } + + if (str.Length == 0) + { + return false; + } + + string symbol = str.ToString(); + if (string.IsNullOrWhiteSpace(symbol)) + { + return false; + } + + processor.Inline = new InclusionInlineShortcut + { + RawFilename = symbol, + IncludedFilePath = $"~/includes/{symbol}.md", + Line = line, + Column = column, + Span = new SourceSpan(startPosition, processor.GetSourcePosition(slice.Start - 1)), + IsClosed = true, + }; + + return true; + } + } +}