diff --git a/Directory.Packages.props b/Directory.Packages.props index fb787063baa..f33da8e8e92 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -29,6 +29,7 @@ + @@ -46,4 +47,4 @@ - + \ No newline at end of file diff --git a/src/Docfx.MarkdigEngine.Extensions/Docfx.MarkdigEngine.Extensions.csproj b/src/Docfx.MarkdigEngine.Extensions/Docfx.MarkdigEngine.Extensions.csproj index 933e2646ecd..5818d808afb 100644 --- a/src/Docfx.MarkdigEngine.Extensions/Docfx.MarkdigEngine.Extensions.csproj +++ b/src/Docfx.MarkdigEngine.Extensions/Docfx.MarkdigEngine.Extensions.csproj @@ -2,6 +2,7 @@ + diff --git a/src/Docfx.MarkdigEngine.Extensions/MarkdownContext.cs b/src/Docfx.MarkdigEngine.Extensions/MarkdownContext.cs index 4b90214331c..4f5bac9fa8f 100644 --- a/src/Docfx.MarkdigEngine.Extensions/MarkdownContext.cs +++ b/src/Docfx.MarkdigEngine.Extensions/MarkdownContext.cs @@ -36,6 +36,13 @@ public class MarkdownContext /// Image url bound to the path public delegate string GetImageLinkDelegate(string path, MarkdownObject origin, string altText); + /// + /// Allows configuration of extensions + /// + /// Name of the extension being configured + /// Object representing the configuration for the extension + public delegate object GetExtensionConfigurationDelegate(string extension); + /// /// Reads a file as text. /// @@ -51,6 +58,11 @@ public class MarkdownContext /// public GetImageLinkDelegate GetImageLink { get; } + /// + /// Get the configuration for a given extension + /// + public GetExtensionConfigurationDelegate GetExtensionConfiguration { get; } + /// /// Log info /// @@ -86,12 +98,14 @@ public MarkdownContext( LogActionDelegate logError = null, ReadFileDelegate readFile = null, GetLinkDelegate getLink = null, - GetImageLinkDelegate getImageLink = null) + GetImageLinkDelegate getImageLink = null, + GetExtensionConfigurationDelegate getConfig = null) { _getToken = getToken ?? (_ => null); ReadFile = readFile ?? ((a, b) => (a, a)); GetLink = getLink ?? ((a, b) => a); GetImageLink = getImageLink ?? ((a, b, c) => a); + GetExtensionConfiguration = getConfig ?? (_ => null); LogInfo = logInfo ?? ((a, b, c, d) => { }); LogSuggestion = logSuggestion ?? ((a, b, c, d) => { }); LogWarning = logWarning ?? ((a, b, c, d) => { }); diff --git a/src/Docfx.MarkdigEngine.Extensions/MarkdownExtensions.cs b/src/Docfx.MarkdigEngine.Extensions/MarkdownExtensions.cs index a278517b003..8568f4655a9 100644 --- a/src/Docfx.MarkdigEngine.Extensions/MarkdownExtensions.cs +++ b/src/Docfx.MarkdigEngine.Extensions/MarkdownExtensions.cs @@ -11,7 +11,7 @@ namespace Docfx.MarkdigEngine.Extensions; public static class MarkdownExtensions { - public static MarkdownPipelineBuilder UseDocfxExtensions(this MarkdownPipelineBuilder pipeline, MarkdownContext context, Dictionary notes = null) + public static MarkdownPipelineBuilder UseDocfxExtensions(this MarkdownPipelineBuilder pipeline, MarkdownContext context) { return pipeline .UseMathematics() @@ -24,7 +24,7 @@ public static MarkdownPipelineBuilder UseDocfxExtensions(this MarkdownPipelineBu .UseIncludeFile(context) .UseCodeSnippet(context) .UseDFMCodeInfoPrefix() - .UseQuoteSectionNote(context, notes) + .UseQuoteSectionNote(context) .UseXref() .UseEmojiAndSmiley(false) .UseTabGroup(context) @@ -35,6 +35,7 @@ public static MarkdownPipelineBuilder UseDocfxExtensions(this MarkdownPipelineBu .UseTripleColon(context) .UseNoloc() .UseResolveLink(context) + .UsePlantUml(context) .RemoveUnusedExtensions(); } @@ -99,9 +100,9 @@ public static MarkdownPipelineBuilder UseDFMCodeInfoPrefix(this MarkdownPipeline return pipeline; } - public static MarkdownPipelineBuilder UseQuoteSectionNote(this MarkdownPipelineBuilder pipeline, MarkdownContext context, Dictionary notes = null) + public static MarkdownPipelineBuilder UseQuoteSectionNote(this MarkdownPipelineBuilder pipeline, MarkdownContext context) { - pipeline.Extensions.AddIfNotAlready(new QuoteSectionNoteExtension(context, notes)); + pipeline.Extensions.AddIfNotAlready(new QuoteSectionNoteExtension(context)); return pipeline; } @@ -111,6 +112,12 @@ public static MarkdownPipelineBuilder UseLineNumber(this MarkdownPipelineBuilder return pipeline; } + public static MarkdownPipelineBuilder UsePlantUml(this MarkdownPipelineBuilder pipeline, MarkdownContext context) + { + pipeline.Extensions.AddIfNotAlready(new PlantUmlExtension(context)); + return pipeline; + } + public static MarkdownPipelineBuilder UseResolveLink(this MarkdownPipelineBuilder pipeline, MarkdownContext context) { pipeline.Extensions.AddIfNotAlready(new ResolveLinkExtension(context)); diff --git a/src/Docfx.MarkdigEngine.Extensions/PlantUml/PlantUmlCodeBlockRenderer.cs b/src/Docfx.MarkdigEngine.Extensions/PlantUml/PlantUmlCodeBlockRenderer.cs new file mode 100644 index 00000000000..9adc78f3e95 --- /dev/null +++ b/src/Docfx.MarkdigEngine.Extensions/PlantUml/PlantUmlCodeBlockRenderer.cs @@ -0,0 +1,99 @@ +using static System.Text.Encoding; + +using Markdig.Renderers; +using Markdig.Syntax; +using Markdig.Renderers.Html; +using PlantUml.Net; + +namespace Docfx.MarkdigEngine.Extensions; + +/// +/// An HTML renderer for a and . +/// +/// +public class CustomCodeBlockRenderer : CodeBlockRenderer +{ + private readonly MarkdownContext _context; + private readonly DocfxPlantUmlSettings _settings; + private readonly RendererFactory rendererFactory; + + /// + /// Initializes a new instance of the class. + /// + /// + /// + public CustomCodeBlockRenderer(MarkdownContext context, DocfxPlantUmlSettings settings) + { + _context = context; + _settings = settings; + + rendererFactory = new RendererFactory(); + } + + protected override void Write(HtmlRenderer renderer, CodeBlock obj) + { + if (obj is FencedCodeBlock fencedCodeBlock + && fencedCodeBlock.Info is string info + && info.Equals("plantuml", StringComparison.OrdinalIgnoreCase)) + { + IPlantUmlRenderer plantUmlRenderer = rendererFactory.CreateRenderer(_settings); + + // Get PlantUML code. + var plantUmlCode = fencedCodeBlock.Lines.ToString(); + + try + { + byte[] output = plantUmlRenderer.Render(plantUmlCode, _settings.OutputFormat); + + renderer.EnsureLine(); + renderer.Write(FormatOutput(_settings.OutputFormat, output)); + renderer.EnsureLine(); + } + catch (RenderingException ex) + { + _context.LogWarning(nameof(PlantUmlExtension), ex.Message, null); + } + catch (Exception ex) + { + _context.LogError(nameof(PlantUmlExtension), ex.Message, null); + + // If the error is not related to rendering a specific diagram, re-throw to abort + throw; + } + + return; + } + + // Fallback to default CodeBlockRenderer + base.Write(renderer, obj); + } + + private static string FormatOutput(OutputFormat format, byte[] output) + { + switch (format) + { + case OutputFormat.Svg: + string svg = UTF8.GetString(output); + return $"{svg}"; + + case OutputFormat.Ascii: + string ascii = ASCII.GetString(output); + return $"{ascii}"; + + case OutputFormat.Ascii_Unicode: + string asciiUnicode = UTF8.GetString(output); + return $"{asciiUnicode}"; + + case OutputFormat.Png: + case OutputFormat.Eps: + case OutputFormat.Pdf: + case OutputFormat.Vdx: + case OutputFormat.Xmi: + case OutputFormat.Scxml: + case OutputFormat.Html: + case OutputFormat.LaTeX: + default: + throw new NotSupportedException($"Output format {format} is currently not supported"); + } + } +} diff --git a/src/Docfx.MarkdigEngine.Extensions/PlantUml/PlantUmlExtension.cs b/src/Docfx.MarkdigEngine.Extensions/PlantUml/PlantUmlExtension.cs new file mode 100644 index 00000000000..06820c87db0 --- /dev/null +++ b/src/Docfx.MarkdigEngine.Extensions/PlantUml/PlantUmlExtension.cs @@ -0,0 +1,68 @@ +using Markdig; +using Markdig.Renderers; +using Markdig.Renderers.Html; +using PlantUml.Net; + +namespace Docfx.MarkdigEngine.Extensions; + +public class DocfxPlantUmlSettings : PlantUmlSettings +{ + public DocfxPlantUmlSettings() : base() + { + } + + public DocfxPlantUmlSettings(IReadOnlyDictionary config) : this() + { + if (config.TryGetValue("remoteUrl", out var url)) + RemoteUrl = url; + if (config.TryGetValue("outputFormat", out var format)) + OutputFormat = Enum.Parse(format, true); + if (config.TryGetValue("javaPath", out var path)) + JavaPath = path; + if (config.TryGetValue("localPlantUmlPath", out path)) + LocalPlantUmlPath = path; + if (config.TryGetValue("localGraphvizDotPath", out path)) + LocalGraphvizDotPath = path; + if (config.TryGetValue("renderingMode", out var renderMode)) + RenderingMode = Enum.Parse(renderMode, true); + } + + public OutputFormat OutputFormat { get; set; } = OutputFormat.Svg; +} + +internal class PlantUmlExtension : IMarkdownExtension +{ + private readonly MarkdownContext _context; + private readonly DocfxPlantUmlSettings _settings; + + public PlantUmlExtension(MarkdownContext context) + { + _context = context; + _settings = new(); + + if (_context.GetExtensionConfiguration("PlantUml") is Dictionary config) + _settings = new DocfxPlantUmlSettings(config); + } + + public void Setup(MarkdownPipelineBuilder pipeline) + { + } + + public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer) + { + if (renderer is HtmlRenderer { ObjectRenderers: not null } htmlRenderer) + { + var customRenderer = new CustomCodeBlockRenderer(_context, _settings); + var renderers = htmlRenderer.ObjectRenderers; + + if (renderers.Contains()) + { + renderers.InsertBefore(customRenderer); + } + else + { + renderers.AddIfNotAlready(customRenderer); + } + } + } +} diff --git a/src/Docfx.MarkdigEngine.Extensions/QuoteSectionNote/QuoteSectionNoteExtension.cs b/src/Docfx.MarkdigEngine.Extensions/QuoteSectionNote/QuoteSectionNoteExtension.cs index da741da376a..f478b1aa24c 100644 --- a/src/Docfx.MarkdigEngine.Extensions/QuoteSectionNote/QuoteSectionNoteExtension.cs +++ b/src/Docfx.MarkdigEngine.Extensions/QuoteSectionNote/QuoteSectionNoteExtension.cs @@ -1,10 +1,12 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Text.Json.Serialization; using Markdig; using Markdig.Parsers; using Markdig.Renderers; using Markdig.Renderers.Html; +using Newtonsoft.Json; namespace Docfx.MarkdigEngine.Extensions; @@ -20,13 +22,13 @@ public class QuoteSectionNoteExtension : IMarkdownExtension ["CAUTION"] = "CAUTION", }; - public QuoteSectionNoteExtension(MarkdownContext context, Dictionary notes = null) + public QuoteSectionNoteExtension(MarkdownContext context) { _context = context; - if (notes != null) + if (_context.GetExtensionConfiguration("Alerts") is Dictionary config) { - foreach (var (key, value) in notes) + foreach (var (key, value) in config) _notes[key] = value; } } diff --git a/src/Docfx.MarkdigEngine/MarkdigMarkdownService.cs b/src/Docfx.MarkdigEngine/MarkdigMarkdownService.cs index 0ccb04eccdd..be1150af0ed 100644 --- a/src/Docfx.MarkdigEngine/MarkdigMarkdownService.cs +++ b/src/Docfx.MarkdigEngine/MarkdigMarkdownService.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Immutable; +using System.Text; using Docfx.Common; using Docfx.MarkdigEngine.Extensions; using Docfx.Plugins; @@ -33,7 +34,8 @@ public MarkdigMarkdownService( (code, message, origin, line) => Logger.LogError(message, null, InclusionContext.File.ToString(), line?.ToString(), code), ReadFile, GetLink, - GetImageLink); + GetImageLink, + GetExtensionConfiguration); } public MarkupResult Markup(string content, string filePath) @@ -127,7 +129,7 @@ private MarkdownPipeline CreateMarkdownPipeline(bool isInline, bool multipleYaml var builder = new MarkdownPipelineBuilder(); - builder.UseDocfxExtensions(_context, _parameters.Extensions?.Alerts); + builder.UseDocfxExtensions(_context); builder.Extensions.Insert(0, new YamlHeaderExtension(_context) { AllowInMiddleOfDocument = multipleYamlHeader }); if (enableSourceInfo) @@ -185,6 +187,8 @@ private static string GetLink(string path, MarkdownObject origin) return path; } + private object GetExtensionConfiguration(string extension) => _parameters.GetExtensionConfiguration(extension); + private static string GetImageLink(string href, MarkdownObject origin, string altText) => GetLink(href, origin); private static void ReportDependency(RelativePath filePathToDocset, string parentFileDirectoryToDocset) diff --git a/src/Docfx.Plugins/MarkdownServiceParameters.cs b/src/Docfx.Plugins/MarkdownServiceParameters.cs index 1d000c9c499..421f4f7f9b7 100644 --- a/src/Docfx.Plugins/MarkdownServiceParameters.cs +++ b/src/Docfx.Plugins/MarkdownServiceParameters.cs @@ -35,6 +35,13 @@ public class MarkdownServiceProperties [JsonProperty("alerts")] [JsonPropertyName("alerts")] public Dictionary Alerts { get; set; } + + /// + /// PlantUml extension configuration parameters + /// + [JsonProperty("plantUml")] + [JsonPropertyName("plantUml")] + public Dictionary PlantUml { get; set; } } public class MarkdownServiceParameters @@ -43,4 +50,16 @@ public class MarkdownServiceParameters public string TemplateDir { get; set; } public MarkdownServiceProperties Extensions { get; set; } = new(); public ImmutableDictionary Tokens { get; set; } = ImmutableDictionary.Empty; + + public object GetExtensionConfiguration(string extension) + { + if (!string.IsNullOrEmpty(extension) && Extensions != null) + { + var property = typeof(MarkdownServiceProperties).GetProperty(extension); + if (property != null) + return property.GetValue(Extensions); + } + + return null; + } } diff --git a/test/Docfx.MarkdigEngine.Extensions.Tests/PlantUmlTest.cs b/test/Docfx.MarkdigEngine.Extensions.Tests/PlantUmlTest.cs new file mode 100644 index 00000000000..5fb838631fd --- /dev/null +++ b/test/Docfx.MarkdigEngine.Extensions.Tests/PlantUmlTest.cs @@ -0,0 +1,32 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Xunit; + +namespace Docfx.MarkdigEngine.Tests; + +public class PlantUmlTest +{ + [Fact] + public void TestRenderSvg_SequenceDiagram() + { + var source = """ + ```plantuml + @startuml + Bob -> Alice : hello + @enduml + ``` + """; + + var expected = """ + BobBobAliceAlicehello + """; + + TestUtility.VerifyMarkup(source, expected, extensionConfiguration: + new Dictionary + { + { "outputFormat", "svg"}, + { "remoteUrl", "https://www.plantuml.com/plantuml" } + }); + } +} diff --git a/test/Docfx.MarkdigEngine.Extensions.Tests/QuoteSectionNoteTest.cs b/test/Docfx.MarkdigEngine.Extensions.Tests/QuoteSectionNoteTest.cs index 99bd3f4743d..abfd1d8f6e5 100644 --- a/test/Docfx.MarkdigEngine.Extensions.Tests/QuoteSectionNoteTest.cs +++ b/test/Docfx.MarkdigEngine.Extensions.Tests/QuoteSectionNoteTest.cs @@ -459,4 +459,32 @@ public void TestVideoBlock_YouTube() "; TestUtility.VerifyMarkup(source, expected); } + + [Fact] + [Trait("Related", "DfmMarkdown")] + public void QuoteSectionNoteTest_ExtensionConfiguration() + { + var source = @"# Article 2 +> [!TODO] +> This is a custom TODO section +> [!REVIEW] +> This is a custom REVIEW section +"; + var expected = @"Article 2 + +TODO +This is a custom TODO section + + +REVIEW +This is a custom REVIEW section + +"; + TestUtility.VerifyMarkup(source, expected, extensionConfiguration: + new Dictionary + { + { "TODO", "alert alert-secondary" }, + { "REVIEW", "alert alert-primary" } + }); + } } diff --git a/test/Docfx.MarkdigEngine.Extensions.Tests/TestUtility.cs b/test/Docfx.MarkdigEngine.Extensions.Tests/TestUtility.cs index f0f0964bbc1..b6bc6dca515 100644 --- a/test/Docfx.MarkdigEngine.Extensions.Tests/TestUtility.cs +++ b/test/Docfx.MarkdigEngine.Extensions.Tests/TestUtility.cs @@ -4,6 +4,7 @@ using Docfx.MarkdigEngine.Extensions; using Markdig; using Markdig.Syntax; +using Newtonsoft.Json.Linq; using Xunit; namespace Docfx.MarkdigEngine.Tests; @@ -20,7 +21,8 @@ public static void VerifyMarkup( Dictionary tokens = null, Dictionary files = null, Action verifyAST = null, - IEnumerable optionalExtensions = null) + IEnumerable optionalExtensions = null, + object extensionConfiguration = null) { errors ??= Array.Empty(); tokens ??= new Dictionary(); @@ -36,7 +38,8 @@ public static void VerifyMarkup( logSuggestion: Log("suggestion"), logWarning: Log("warning"), logError: Log("error"), - readFile: ReadFile); + readFile: ReadFile, + getConfig: _ => extensionConfiguration); var pipelineBuilder = new MarkdownPipelineBuilder() .UseDocfxExtensions(markdownContext) diff --git a/test/Docfx.MarkdigEngine.Tests/MarkdigServiceTest.cs b/test/Docfx.MarkdigEngine.Tests/MarkdigServiceTest.cs index e085ef103aa..660f04792e2 100644 --- a/test/Docfx.MarkdigEngine.Tests/MarkdigServiceTest.cs +++ b/test/Docfx.MarkdigEngine.Tests/MarkdigServiceTest.cs @@ -2,8 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Collections.Immutable; +using Docfx.Common; using Docfx.MarkdigEngine.Extensions; +using Docfx.Plugins; using Markdig.Syntax; +using Newtonsoft.Json; using Xunit; namespace Docfx.MarkdigEngine.Tests; diff --git a/test/Docfx.MarkdigEngine.Tests/TestUtility/TestUtility.cs b/test/Docfx.MarkdigEngine.Tests/TestUtility/TestUtility.cs index 15b02bb8715..3d217a6b369 100644 --- a/test/Docfx.MarkdigEngine.Tests/TestUtility/TestUtility.cs +++ b/test/Docfx.MarkdigEngine.Tests/TestUtility/TestUtility.cs @@ -54,12 +54,12 @@ public static void WriteToFile(string file, string content) File.WriteAllText(file, content); } - public static MarkdigMarkdownService CreateMarkdownService() + public static MarkdigMarkdownService CreateMarkdownService(MarkdownServiceProperties extensions = null) { var parameter = new MarkdownServiceParameters { BasePath = ".", - Extensions = new() { EnableSourceInfo = false }, + Extensions = extensions ?? new() { EnableSourceInfo = false } }; return new MarkdigMarkdownService(parameter); diff --git a/test/docfx.Tests/Api.verified.cs b/test/docfx.Tests/Api.verified.cs index 98795a9f179..6a28ce300d9 100644 --- a/test/docfx.Tests/Api.verified.cs +++ b/test/docfx.Tests/Api.verified.cs @@ -3227,233 +3227,6 @@ public GlobUtility() { } public static Docfx.FileMapping ExpandFileMapping(string baseDirectory, Docfx.FileMapping fileMapping) { } } } -namespace Docfx.HtmlToPdf -{ - public class ConvertWrapper - { - public ConvertWrapper(Docfx.HtmlToPdf.PdfOptions pdfOptions) { } - public void Convert() { } - public static void PrerequisiteCheck(string filePath) { } - } - public class FileOutput - { - public FileOutput() { } - [Newtonsoft.Json.JsonProperty("is_raw_page")] - [System.Text.Json.Serialization.JsonPropertyName("is_raw_page")] - public bool IsRawPage { get; set; } - [Newtonsoft.Json.JsonProperty("link_to_path")] - [System.Text.Json.Serialization.JsonPropertyName("link_to_path")] - public string LinkToPath { get; set; } - [Newtonsoft.Json.JsonExtensionData] - [System.Text.Json.Serialization.JsonExtensionData] - public System.Collections.Generic.Dictionary Metadata { get; set; } - [Newtonsoft.Json.JsonProperty("relative_path")] - [System.Text.Json.Serialization.JsonPropertyName("relative_path")] - public string RelativePath { get; set; } - [Newtonsoft.Json.JsonProperty("skip_publish", DefaultValueHandling=Newtonsoft.Json.DefaultValueHandling.Ignore)] - [System.Text.Json.Serialization.JsonPropertyName("skip_publish")] - public bool SkipPublish { get; set; } - public override string ToString() { } - } - public class FileOutputs - { - public FileOutputs() { } - [Newtonsoft.Json.JsonProperty(".html")] - [System.Text.Json.Serialization.JsonPropertyName(".html")] - public Docfx.HtmlToPdf.FileOutput Html { get; set; } - [Newtonsoft.Json.JsonProperty(".mta.json")] - [System.Text.Json.Serialization.JsonPropertyName(".mta.json")] - public Docfx.HtmlToPdf.FileOutput MtaJson { get; set; } - [Newtonsoft.Json.JsonExtensionData] - [System.Text.Json.Serialization.JsonExtensionData] - public System.Collections.Generic.Dictionary OtherOutputs { get; set; } - [Newtonsoft.Json.JsonProperty(".raw.page.json")] - [System.Text.Json.Serialization.JsonPropertyName(".raw.page.json")] - public Docfx.HtmlToPdf.FileOutput RawPageJson { get; set; } - [Newtonsoft.Json.JsonProperty("resource")] - [System.Text.Json.Serialization.JsonPropertyName("resource")] - public Docfx.HtmlToPdf.FileOutput Resource { get; set; } - [Newtonsoft.Json.JsonProperty(".json")] - [System.Text.Json.Serialization.JsonPropertyName(".json")] - public Docfx.HtmlToPdf.FileOutput TocJson { get; set; } - } - public static class FolderUtility - { - public static void CopyDirectoryWithAllSubDirectories(string sourceDirectory, string targetDirectory, int maxDegreeOfParallelism = -1) { } - public static void ForceDeleteAllSubDirectories(string directory, int maxDegreeOfParallelism = -1) { } - public static void ForceDeleteDirectoryWithAllSubDirectories(string directory, int maxDegreeOfParallelism = -1) { } - public static void ForceDeleteFile(string filePath) { } - } - public class HtmlModel - { - public HtmlModel() { } - public System.Collections.Generic.IList Children { get; set; } - public string ExternalLink { get; set; } - public string HtmlFilePath { get; set; } - public string Title { get; set; } - } - public class HtmlToPdfConverter - { - public HtmlToPdfConverter(System.Collections.Generic.IList htmlModels, Docfx.HtmlToPdf.HtmlToPdfOptions htmlToPdfOptions) { } - public System.Collections.Generic.IDictionary GetPartialPdfModels(System.Collections.Generic.IList htmlFilePaths) { } - public void Save(string outputFileName) { } - } - public class HtmlToPdfOptions - { - public HtmlToPdfOptions() { } - public string AdditionalArguments { get; set; } - public string BasePath { get; set; } - public string Encoding { get; set; } - public string FilePath { get; set; } - public string FooterHtmlPath { get; set; } - public string HeaderHtmlPath { get; set; } - public bool IsOutputToStdout { get; set; } - public bool IsQuiet { get; set; } - public bool IsReadArgsFromStdin { get; set; } - public string LoadErrorHandling { get; set; } - public int MaxDegreeOfParallelism { get; set; } - public Docfx.HtmlToPdf.OutlineOption OutlineOption { get; set; } - public System.TimeSpan[] RetryIntervals { get; set; } - public string UserStyleSheet { get; set; } - public override string ToString() { } - } - public enum ManifestItemType - { - Toc = 0, - Content = 1, - Resource = 2, - } - public static class ManifestUtility - { - public static string GetAssetId(Docfx.Plugins.ManifestItem manifestItem) { } - public static Docfx.HtmlToPdf.ManifestItemType GetDocumentType(Docfx.Plugins.ManifestItem item) { } - public static string GetRelativePath(Docfx.Plugins.ManifestItem item, Docfx.HtmlToPdf.OutputType type) { } - } - public enum OutlineOption - { - NoOutline = 0, - WkDefaultOutline = 1, - DefaultOutline = 2, - } - public enum OutputType - { - Html = 0, - TocJson = 1, - RawPageJson = 2, - Resource = 3, - } - public class PartialPdfModel - { - public PartialPdfModel() { } - public string FilePath { get; set; } - public int NumberOfPages { get; set; } - public int? PageNumber { get; set; } - } - public static class PdfHelper - { - public static string NormalizeFileLocalPath(string basePath, string relativePath, bool toLower = true) { } - public static string RemoveUrlBookmark(this string url) { } - public static string RemoveUrlQueryString(this string url) { } - public static string TrimStartPath(this string path) { } - } - public class PdfInformation - { - public PdfInformation() { } - [Newtonsoft.Json.JsonProperty("asset_id")] - [System.Text.Json.Serialization.JsonPropertyName("asset_id")] - public string AssetId { get; set; } - [Newtonsoft.Json.JsonProperty("docset_name")] - [System.Text.Json.Serialization.JsonPropertyName("docset_name")] - public string DocsetName { get; set; } - [Newtonsoft.Json.JsonProperty("toc_files")] - [System.Text.Json.Serialization.JsonPropertyName("toc_files")] - public System.Collections.Generic.ICollection TocFiles { get; set; } - [Newtonsoft.Json.JsonProperty("version")] - [System.Text.Json.Serialization.JsonPropertyName("version")] - public string Version { get; set; } - } - public class PdfOptions - { - public PdfOptions() { } - public string AdditionalPdfCommandArgs { get; set; } - public string BasePath { get; set; } - public string CoverPageTitle { get; set; } - public string CssFilePath { get; set; } - public string DestDirectory { get; set; } - public bool ExcludeDefaultToc { get; set; } - public string[] ExcludeTocs { get; set; } - public string ExternalLinkFormat { get; } - public string FilePath { get; set; } - public bool GenerateAppendices { get; set; } - public string Host { get; set; } - public bool KeepRawFiles { get; set; } - public string LoadErrorHandling { get; set; } - public string Locale { get; set; } - public bool NeedGeneratePdfExternalLink { get; set; } - public bool NoInputStreamArgs { get; set; } - public Docfx.HtmlToPdf.OutlineOption OutlineOption { get; set; } - public int PdfConvertParallelism { get; set; } - public string PdfDocsetName { get; set; } - public string SourceDirectory { get; set; } - public string TocTitle { get; set; } - } - public class SelfCleaningFolder : System.IDisposable - { - public SelfCleaningFolder(string path) { } - public string FullPath { get; } - public void Dispose() { } - } - public class TocModel - { - public TocModel() { } - [Newtonsoft.Json.JsonProperty("children")] - [System.Text.Json.Serialization.JsonPropertyName("children")] - public System.Collections.Generic.IList Children { get; set; } - [Newtonsoft.Json.JsonProperty("external_link")] - [System.Text.Json.Serialization.JsonPropertyName("external_link")] - public string ExternalLink { get; set; } - [Newtonsoft.Json.JsonProperty("relative_path_in_depot")] - [System.Text.Json.Serialization.JsonPropertyName("relative_path_in_depot")] - public string HtmlFilePath { get; set; } - [Newtonsoft.Json.JsonProperty("toc_title")] - [System.Text.Json.Serialization.JsonPropertyName("toc_title")] - public string Title { get; set; } - } - public class UrlCache - { - public UrlCache(string basePath, Docfx.Plugins.ManifestItem[] manifestItemWithAssetIds) { } - public UrlCache(string basePath, System.Collections.Generic.IEnumerable items) { } - public bool Contains(string url) { } - public Docfx.Plugins.ManifestItem Query(string url) { } - } -} -namespace Docfx.HtmlToPdf.Transformer -{ - public class AbsolutePathInTocPageFileTransformer : Docfx.HtmlToPdf.Transformer.ITransformer - { - public AbsolutePathInTocPageFileTransformer(Docfx.HtmlToPdf.PdfOptions pdfOptions) { } - public void Transform(System.Collections.Generic.IEnumerable htmlFilePaths) { } - } - public class FrameTransformer : Docfx.HtmlToPdf.Transformer.ITransformer - { - public FrameTransformer(params string[] replaceHosts) { } - public void Transform(System.Collections.Generic.IEnumerable htmlFilePaths) { } - } - public class HtmlNotInTocTransformer : Docfx.HtmlToPdf.Transformer.ITransformer - { - public HtmlNotInTocTransformer(string basePath, Docfx.HtmlToPdf.UrlCache manifestUrlCache, Docfx.HtmlToPdf.PdfOptions pdfOptions) { } - public void Transform(System.Collections.Generic.IEnumerable htmlFilePaths) { } - } - public interface ITransformer - { - void Transform(System.Collections.Generic.IEnumerable htmlFilePaths); - } - public class RemoveQueryStringTransformer : Docfx.HtmlToPdf.Transformer.ITransformer - { - public RemoveQueryStringTransformer() { } - public void Transform(System.Collections.Generic.IEnumerable htmlFilePaths) { } - } -} namespace Docfx.MarkdigEngine.Extensions { public class YamlHeaderExtension : Markdig.IMarkdownExtension @@ -3578,6 +3351,17 @@ public class CodeSnippetParser : Markdig.Parsers.BlockParser public CodeSnippetParser() { } public override Markdig.Parsers.BlockState TryOpen(Markdig.Parsers.BlockProcessor processor) { } } + public class CustomCodeBlockRenderer : Markdig.Renderers.Html.CodeBlockRenderer + { + public CustomCodeBlockRenderer(Docfx.MarkdigEngine.Extensions.MarkdownContext context, Docfx.MarkdigEngine.Extensions.DocfxPlantUmlSettings settings) { } + protected override void Write(Markdig.Renderers.HtmlRenderer renderer, Markdig.Syntax.CodeBlock obj) { } + } + public class DocfxPlantUmlSettings : PlantUml.Net.PlantUmlSettings + { + public DocfxPlantUmlSettings() { } + public DocfxPlantUmlSettings(System.Collections.Generic.IReadOnlyDictionary config) { } + public PlantUml.Net.OutputFormat OutputFormat { get; set; } + } public static class ExtensionsHelper { public static readonly System.Text.RegularExpressions.Regex HtmlEscapeWithEncode; @@ -3759,7 +3543,8 @@ public void Setup(Markdig.MarkdownPipeline pipeline, Markdig.Renderers.IMarkdown } public class MarkdownContext { - public MarkdownContext(System.Func getToken = null, Docfx.MarkdigEngine.Extensions.MarkdownContext.LogActionDelegate logInfo = null, Docfx.MarkdigEngine.Extensions.MarkdownContext.LogActionDelegate logSuggestion = null, Docfx.MarkdigEngine.Extensions.MarkdownContext.LogActionDelegate logWarning = null, Docfx.MarkdigEngine.Extensions.MarkdownContext.LogActionDelegate logError = null, Docfx.MarkdigEngine.Extensions.MarkdownContext.ReadFileDelegate readFile = null, Docfx.MarkdigEngine.Extensions.MarkdownContext.GetLinkDelegate getLink = null, Docfx.MarkdigEngine.Extensions.MarkdownContext.GetImageLinkDelegate getImageLink = null) { } + public MarkdownContext(System.Func getToken = null, Docfx.MarkdigEngine.Extensions.MarkdownContext.LogActionDelegate logInfo = null, Docfx.MarkdigEngine.Extensions.MarkdownContext.LogActionDelegate logSuggestion = null, Docfx.MarkdigEngine.Extensions.MarkdownContext.LogActionDelegate logWarning = null, Docfx.MarkdigEngine.Extensions.MarkdownContext.LogActionDelegate logError = null, Docfx.MarkdigEngine.Extensions.MarkdownContext.ReadFileDelegate readFile = null, Docfx.MarkdigEngine.Extensions.MarkdownContext.GetLinkDelegate getLink = null, Docfx.MarkdigEngine.Extensions.MarkdownContext.GetImageLinkDelegate getImageLink = null, Docfx.MarkdigEngine.Extensions.MarkdownContext.GetExtensionConfigurationDelegate getConfig = null) { } + public Docfx.MarkdigEngine.Extensions.MarkdownContext.GetExtensionConfigurationDelegate GetExtensionConfiguration { get; } public Docfx.MarkdigEngine.Extensions.MarkdownContext.GetImageLinkDelegate GetImageLink { get; } public Docfx.MarkdigEngine.Extensions.MarkdownContext.GetLinkDelegate GetLink { get; } public Docfx.MarkdigEngine.Extensions.MarkdownContext.LogActionDelegate LogError { get; } @@ -3768,6 +3553,7 @@ public MarkdownContext(System.Func getToken = null, Docfx.Markdi public Docfx.MarkdigEngine.Extensions.MarkdownContext.LogActionDelegate LogWarning { get; } public Docfx.MarkdigEngine.Extensions.MarkdownContext.ReadFileDelegate ReadFile { get; } public string GetToken(string key) { } + public delegate object GetExtensionConfigurationDelegate(string extension); public delegate string GetImageLinkDelegate(string path, Markdig.Syntax.MarkdownObject origin, string altText); public delegate string GetLinkDelegate(string path, Markdig.Syntax.MarkdownObject origin); public delegate void LogActionDelegate(string code, string message, Markdig.Syntax.MarkdownObject origin, int? line = default); @@ -3790,7 +3576,7 @@ public static class MarkdownExtensions { public static Markdig.MarkdownPipelineBuilder UseCodeSnippet(this Markdig.MarkdownPipelineBuilder pipeline, Docfx.MarkdigEngine.Extensions.MarkdownContext context) { } public static Markdig.MarkdownPipelineBuilder UseDFMCodeInfoPrefix(this Markdig.MarkdownPipelineBuilder pipeline) { } - public static Markdig.MarkdownPipelineBuilder UseDocfxExtensions(this Markdig.MarkdownPipelineBuilder pipeline, Docfx.MarkdigEngine.Extensions.MarkdownContext context, System.Collections.Generic.Dictionary notes = null) { } + public static Markdig.MarkdownPipelineBuilder UseDocfxExtensions(this Markdig.MarkdownPipelineBuilder pipeline, Docfx.MarkdigEngine.Extensions.MarkdownContext context) { } public static Markdig.MarkdownPipelineBuilder UseHeadingIdRewriter(this Markdig.MarkdownPipelineBuilder pipeline) { } public static Markdig.MarkdownPipelineBuilder UseIncludeFile(this Markdig.MarkdownPipelineBuilder pipeline, Docfx.MarkdigEngine.Extensions.MarkdownContext context) { } public static Markdig.MarkdownPipelineBuilder UseInlineOnly(this Markdig.MarkdownPipelineBuilder pipeline) { } @@ -3800,7 +3586,8 @@ public static Markdig.MarkdownPipelineBuilder UseMonikerRange(this Markdig.Markd public static Markdig.MarkdownPipelineBuilder UseNestedColumn(this Markdig.MarkdownPipelineBuilder pipeline, Docfx.MarkdigEngine.Extensions.MarkdownContext context) { } public static Markdig.MarkdownPipelineBuilder UseNoloc(this Markdig.MarkdownPipelineBuilder pipeline) { } public static Markdig.MarkdownPipelineBuilder UseOptionalExtensions(this Markdig.MarkdownPipelineBuilder pipeline, System.Collections.Generic.IEnumerable optionalExtensions) { } - public static Markdig.MarkdownPipelineBuilder UseQuoteSectionNote(this Markdig.MarkdownPipelineBuilder pipeline, Docfx.MarkdigEngine.Extensions.MarkdownContext context, System.Collections.Generic.Dictionary notes = null) { } + public static Markdig.MarkdownPipelineBuilder UsePlantUml(this Markdig.MarkdownPipelineBuilder pipeline, Docfx.MarkdigEngine.Extensions.MarkdownContext context) { } + public static Markdig.MarkdownPipelineBuilder UseQuoteSectionNote(this Markdig.MarkdownPipelineBuilder pipeline, Docfx.MarkdigEngine.Extensions.MarkdownContext context) { } public static Markdig.MarkdownPipelineBuilder UseResolveLink(this Markdig.MarkdownPipelineBuilder pipeline, Docfx.MarkdigEngine.Extensions.MarkdownContext context) { } public static Markdig.MarkdownPipelineBuilder UseRow(this Markdig.MarkdownPipelineBuilder pipeline, Docfx.MarkdigEngine.Extensions.MarkdownContext context) { } public static Markdig.MarkdownPipelineBuilder UseTabGroup(this Markdig.MarkdownPipelineBuilder pipeline, Docfx.MarkdigEngine.Extensions.MarkdownContext context) { } @@ -3887,7 +3674,7 @@ public QuoteSectionNoteBlock(Markdig.Parsers.BlockParser parser) { } } public class QuoteSectionNoteExtension : Markdig.IMarkdownExtension { - public QuoteSectionNoteExtension(Docfx.MarkdigEngine.Extensions.MarkdownContext context, System.Collections.Generic.Dictionary notes = null) { } + public QuoteSectionNoteExtension(Docfx.MarkdigEngine.Extensions.MarkdownContext context) { } } public class QuoteSectionNoteParser : Markdig.Parsers.BlockParser { @@ -4371,6 +4158,7 @@ public MarkdownServiceParameters() { } public Docfx.Plugins.MarkdownServiceProperties Extensions { get; set; } public string TemplateDir { get; set; } public System.Collections.Immutable.ImmutableDictionary Tokens { get; set; } + public object GetExtensionConfiguration(string extension) { } } public class MarkdownServiceProperties { @@ -4387,6 +4175,9 @@ public MarkdownServiceProperties() { } [Newtonsoft.Json.JsonProperty("markdigExtensions")] [System.Text.Json.Serialization.JsonPropertyName("markdigExtensions")] public string[] MarkdigExtensions { get; set; } + [Newtonsoft.Json.JsonProperty("plantUml")] + [System.Text.Json.Serialization.JsonPropertyName("plantUml")] + public System.Collections.Generic.Dictionary PlantUml { get; set; } } public class MarkupResult { diff --git a/test/docfx.Tests/JsonConverterTest.cs b/test/docfx.Tests/JsonConverterTest.cs index 889b5741b31..dee39bef92f 100644 --- a/test/docfx.Tests/JsonConverterTest.cs +++ b/test/docfx.Tests/JsonConverterTest.cs @@ -4,6 +4,7 @@ using System.Collections; using System.Reflection; using Docfx.Common; +using Docfx.Plugins; using Newtonsoft.Json; using Newtonsoft.Json.Serialization; using Xunit; @@ -46,7 +47,52 @@ public void TestJObjectDictionaryToObjectDictionaryConverterSerializeAndDeserial ContractResolver = new SkipEmptyOrNullContractResolver() }; - Assert.Equal(jsonString, JsonConvert.SerializeObject(buildOptions, settings)); + Assert.Equal(jsonString, JsonConvert.SerializeObject(buildOptions, settings), ignoreLineEndingDifferences: true); + } + + [Fact] + [Trait("Related", "docfx")] + public void TestMarkdownEnginePropertiesSerializeAndDeserialize() + { + string jsonString = """ + { + "markdownEngineProperties": { + "enableSourceInfo": false, + "alerts": { + "TODO": "alert alert-secondary", + "REVIEW": "alert alert-primary" + }, + "plantUml": { + "outputFormat": "svg", + "remoteUrl": "https://www.plantuml.com/plantuml" + } + }, + "disableGitFeatures": true + } + """; + + BuildJsonConfig buildOptions = JsonConvert.DeserializeObject(jsonString); + + Assert.NotNull(buildOptions.MarkdownEngineProperties); + Assert.False(buildOptions.MarkdownEngineProperties.EnableSourceInfo); + Assert.NotNull(buildOptions.MarkdownEngineProperties.Alerts); + Assert.Equal(2, buildOptions.MarkdownEngineProperties.Alerts.Count); + Assert.NotNull(buildOptions.MarkdownEngineProperties.Alerts["TODO"]); + Assert.NotNull(buildOptions.MarkdownEngineProperties.Alerts["REVIEW"]); + Assert.NotNull(buildOptions.MarkdownEngineProperties.PlantUml); + Assert.Equal(2, buildOptions.MarkdownEngineProperties.PlantUml.Count); + Assert.NotNull(buildOptions.MarkdownEngineProperties.PlantUml["outputFormat"]); + Assert.NotNull(buildOptions.MarkdownEngineProperties.PlantUml["remoteUrl"]); + + JsonSerializerSettings settings = new() + { + NullValueHandling = NullValueHandling.Ignore, + Formatting = Formatting.Indented, + ContractResolver = new SkipEmptyOrNullContractResolver() + }; + + string json = JsonConvert.SerializeObject(buildOptions, settings); + Assert.Equal(jsonString, json, ignoreLineEndingDifferences: true); } [Fact]
{ascii}
{asciiUnicode}
This is a custom TODO section
This is a custom REVIEW section