From a1b83c592264a908f067ed2db0ddc2530033c697 Mon Sep 17 00:00:00 2001 From: Yufei Huang Date: Fri, 24 Nov 2023 21:00:00 +0800 Subject: [PATCH] fix: xml intention issue on external --- .../Parsers}/XHtmlWriter.cs | 4 +- src/Docfx.Dotnet/Parsers/XmlComment.cs | 20 ++++++-- .../Parsers/XmlCommentTransformer.cs | 11 ++--- test/Docfx.Dotnet.Tests/XmlCommentUnitTest.cs | 48 +++++++++++++++++++ 4 files changed, 71 insertions(+), 12 deletions(-) rename src/{Docfx.Common => Docfx.Dotnet/Parsers}/XHtmlWriter.cs (97%) diff --git a/src/Docfx.Common/XHtmlWriter.cs b/src/Docfx.Dotnet/Parsers/XHtmlWriter.cs similarity index 97% rename from src/Docfx.Common/XHtmlWriter.cs rename to src/Docfx.Dotnet/Parsers/XHtmlWriter.cs index cfd5798eff9..7ed0ba882dc 100644 --- a/src/Docfx.Common/XHtmlWriter.cs +++ b/src/Docfx.Dotnet/Parsers/XHtmlWriter.cs @@ -5,7 +5,7 @@ namespace Docfx.Common; -public class XHtmlWriter : XmlWriter +class XHtmlWriter : XmlWriter { private static HashSet _voidElements; private string _currentElement; @@ -15,7 +15,7 @@ public class XHtmlWriter : XmlWriter public XHtmlWriter(TextWriter writer) { - _writer = Create(writer); + _writer = Create(writer, new() { OmitXmlDeclaration = true }); // void element (ref: http://www.w3.org/TR/html-markup/syntax.html) _voidElements = new HashSet { "area", "base", "br", "col", "command", "embed", "hr", "img", "input", "keygen", "link", "meta", "param", "source", "track", "wbr" }; } diff --git a/src/Docfx.Dotnet/Parsers/XmlComment.cs b/src/Docfx.Dotnet/Parsers/XmlComment.cs index 0912c5beae6..823e7a92c12 100644 --- a/src/Docfx.Dotnet/Parsers/XmlComment.cs +++ b/src/Docfx.Dotnet/Parsers/XmlComment.cs @@ -68,7 +68,8 @@ private XmlComment(string xml, XmlCommentParserContext context) } // Transform triple slash comment - var doc = XmlCommentTransformer.Transform(xml); + xml = XmlCommentTransformer.Transform(xml); + var doc = XDocument.Parse(xml, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo); _context = context; @@ -139,9 +140,9 @@ private void ResolveCode(XDocument doc, XmlCommentParserContext context) continue; } - var indent = ((IXmlLineInfo)node).LinePosition - 2; + var indent = new string(' ', ((IXmlLineInfo)node).LinePosition - 2); var (lang, value) = ResolveCodeSource(node, context); - value = TrimEachLine(value ?? node.Value, new(' ', indent)); + value = TrimEachLine(value ?? node.Value, indent); var code = new XElement("code", value); if (node.Attribute("language") is { } languageAttribute) @@ -155,7 +156,18 @@ private void ResolveCode(XDocument doc, XmlCommentParserContext context) } code.SetAttributeValue("class", $"lang-{lang}"); - node.ReplaceWith(new XElement("pre", code)); + + if (node.PreviousNode is null) + { + // Xml writer formats
 with unintended identation
+                // when there is no preceeding text node.
+                // Prepend a text node with the same indentation to force 
.
+                node.ReplaceWith($"\n{indent}", new XElement("pre", code));
+            }
+            else
+            {
+                node.ReplaceWith(new XElement("pre", code));
+            }
         }
     }
 
diff --git a/src/Docfx.Dotnet/Parsers/XmlCommentTransformer.cs b/src/Docfx.Dotnet/Parsers/XmlCommentTransformer.cs
index 1ba4d4ae170..40a21f10b6d 100644
--- a/src/Docfx.Dotnet/Parsers/XmlCommentTransformer.cs
+++ b/src/Docfx.Dotnet/Parsers/XmlCommentTransformer.cs
@@ -10,7 +10,7 @@
 
 namespace Docfx.Dotnet;
 
-internal static class XmlCommentTransformer
+static class XmlCommentTransformer
 {
     private static readonly XslCompiledTransform _transform;
 
@@ -25,13 +25,12 @@ static XmlCommentTransformer()
         _transform.Load(reader, xsltSettings, new XmlUrlResolver());
     }
 
-    public static XDocument Transform(string xml)
+    public static string Transform(string xml)
     {
-        using var ms = new MemoryStream();
-        using var writer = new XHtmlWriter(new StreamWriter(ms));
+        using var ms = new StringWriter();
+        using var writer = new XHtmlWriter(ms);
         XDocument doc = XDocument.Parse(xml, LoadOptions.PreserveWhitespace);
         _transform.Transform(doc.CreateNavigator(), writer);
-        ms.Seek(0, SeekOrigin.Begin);
-        return XDocument.Load(ms, LoadOptions.PreserveWhitespace | LoadOptions.SetLineInfo);
+        return ms.ToString();
     }
 }
diff --git a/test/Docfx.Dotnet.Tests/XmlCommentUnitTest.cs b/test/Docfx.Dotnet.Tests/XmlCommentUnitTest.cs
index 3ddc6820594..08a40e9ce7e 100644
--- a/test/Docfx.Dotnet.Tests/XmlCommentUnitTest.cs
+++ b/test/Docfx.Dotnet.Tests/XmlCommentUnitTest.cs
@@ -22,6 +22,25 @@ public static void SeeLangword()
         Verify("", "undefined-langword");
     }
 
+    [Fact]
+    public static void ParaNewLine()
+    {
+        Assert.Equal(
+            """
+            a
+            

b

+

c

+ """, + XmlComment.Parse(""" + + a + b + c + + """).Summary, + ignoreLineEndingDifferences: true); + } + [Fact] public static void Issue8122() { @@ -152,6 +171,35 @@ public void ExternalCodeBlockXaml() ignoreLineEndingDifferences: true); } + [Theory] + [InlineData("")] + [InlineData(""" + + + + """)] + public void Issue9462(string input) + { + var commentModel = XmlComment.Parse(input, new() + { + ResolveCode = _ => + """ + #region SDK_CustomProcessor + + using System; + using System.Collections.Generic; + #endregion + """ + }); + Assert.Equal( + """ +
using System;
+            using System.Collections.Generic;
+ """, + commentModel.Examples.Single(), + ignoreLineEndingDifferences: true); + } + [Fact] public static void MarkdownCodeBlock() {