From ff3ce6d905b83d4d64efced3dfb31c2e7ebd01cc Mon Sep 17 00:00:00 2001 From: Divyansh Srivastava Date: Tue, 21 Jan 2020 17:18:14 -0800 Subject: [PATCH] HTML Parsing Extension for preview pane markdown renderer (#1108) * Added Extension for html post processing * Added unit test poroject for preview pane * Added pipline test and base test function * Added Tests for extension * Added tests for url slashes * Added tests for url and figure caption --- PowerToys.sln | 16 ++- .../HTMLParsingExtension.cs | 93 +++++++++++++++ .../MarkDownPreviewHandler.csproj | 75 ++++++++++++ .../Properties/AssemblyInfo.cs | 43 +++++++ ...nPreviewHandlerHTMLParsingExtensionTest.cs | 109 ++++++++++++++++++ .../PreviewPaneUnitTests.csproj | 69 +++++++++++ .../Properties/AssemblyInfo.cs | 20 ++++ .../common/Properties/AssemblyInfo.cs | 4 +- src/modules/previewpane/common/common.csproj | 4 +- 9 files changed, 428 insertions(+), 5 deletions(-) create mode 100644 src/modules/previewpane/MarkDownPreviewHandler/HTMLParsingExtension.cs create mode 100644 src/modules/previewpane/MarkDownPreviewHandler/MarkDownPreviewHandler.csproj create mode 100644 src/modules/previewpane/MarkDownPreviewHandler/Properties/AssemblyInfo.cs create mode 100644 src/modules/previewpane/PreviewPaneUnitTests/MarkDownPreviewHandlerHTMLParsingExtensionTest.cs create mode 100644 src/modules/previewpane/PreviewPaneUnitTests/PreviewPaneUnitTests.csproj create mode 100644 src/modules/previewpane/PreviewPaneUnitTests/Properties/AssemblyInfo.cs diff --git a/PowerToys.sln b/PowerToys.sln index 29de84d54a3b..ef3227921525 100644 --- a/PowerToys.sln +++ b/PowerToys.sln @@ -132,7 +132,11 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "msi_to_msix_upgrade_lib", " EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "previewpane", "previewpane", "{2F305555-C296-497E-AC20-5FA1B237996A}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Common", "src\modules\previewpane\common\Common.csproj", "{AF2349B8-E5B6-4004-9502-687C1C7730B1}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Common", "src\modules\previewpane\Common\Common.csproj", "{AF2349B8-E5B6-4004-9502-687C1C7730B1}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MarkDownPreviewHandler", "src\modules\previewpane\MarkDownPreviewHandler\MarkDownPreviewHandler.csproj", "{6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PreviewPaneUnitTests", "src\modules\previewpane\PreviewPaneUnitTests\PreviewPaneUnitTests.csproj", "{A2B51B8B-8F90-424E-BC97-F9AB7D76CA1A}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -236,6 +240,14 @@ Global {AF2349B8-E5B6-4004-9502-687C1C7730B1}.Debug|x64.Build.0 = Debug|x64 {AF2349B8-E5B6-4004-9502-687C1C7730B1}.Release|x64.ActiveCfg = Release|x64 {AF2349B8-E5B6-4004-9502-687C1C7730B1}.Release|x64.Build.0 = Release|x64 + {6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB}.Debug|x64.ActiveCfg = Debug|x64 + {6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB}.Debug|x64.Build.0 = Debug|x64 + {6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB}.Release|x64.ActiveCfg = Release|x64 + {6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB}.Release|x64.Build.0 = Release|x64 + {A2B51B8B-8F90-424E-BC97-F9AB7D76CA1A}.Debug|x64.ActiveCfg = Debug|x64 + {A2B51B8B-8F90-424E-BC97-F9AB7D76CA1A}.Debug|x64.Build.0 = Debug|x64 + {A2B51B8B-8F90-424E-BC97-F9AB7D76CA1A}.Release|x64.ActiveCfg = Release|x64 + {A2B51B8B-8F90-424E-BC97-F9AB7D76CA1A}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -267,6 +279,8 @@ Global {17DA04DF-E393-4397-9CF0-84DABE11032E} = {1AFB6476-670D-4E80-A464-657E01DFF482} {2F305555-C296-497E-AC20-5FA1B237996A} = {4574FDD0-F61D-4376-98BF-E5A1262C11EC} {AF2349B8-E5B6-4004-9502-687C1C7730B1} = {2F305555-C296-497E-AC20-5FA1B237996A} + {6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB} = {2F305555-C296-497E-AC20-5FA1B237996A} + {A2B51B8B-8F90-424E-BC97-F9AB7D76CA1A} = {2F305555-C296-497E-AC20-5FA1B237996A} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0} diff --git a/src/modules/previewpane/MarkDownPreviewHandler/HTMLParsingExtension.cs b/src/modules/previewpane/MarkDownPreviewHandler/HTMLParsingExtension.cs new file mode 100644 index 000000000000..bfa792525dd5 --- /dev/null +++ b/src/modules/previewpane/MarkDownPreviewHandler/HTMLParsingExtension.cs @@ -0,0 +1,93 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.IO; +using Markdig; +using Markdig.Extensions.Figures; +using Markdig.Extensions.Tables; +using Markdig.Renderers; +using Markdig.Renderers.Html; +using Markdig.Syntax; +using Markdig.Syntax.Inlines; + +namespace MarkDownPreviewHandler +{ + /// + /// Markdig Extension to process html nodes in markdown AST. + /// + internal class HTMLParsingExtension : IMarkdownExtension + { + /// + /// Initializes a new instance of the class. + /// + public HTMLParsingExtension(string baseUrl = "") + { + this.BaseUrl = baseUrl; + } + + /// + /// Gets or sets path to directory containing markdown file. + /// + public string BaseUrl { get; set; } + + /// + public void Setup(MarkdownPipelineBuilder pipeline) + { + // Make sure we don't have a delegate twice + pipeline.DocumentProcessed -= this.PipelineOnDocumentProcessed; + pipeline.DocumentProcessed += this.PipelineOnDocumentProcessed; + } + + /// + public void Setup(MarkdownPipeline pipeline, IMarkdownRenderer renderer) + { + } + + /// + /// Process nodes in markdown AST. + /// + /// Markdown Document. + public void PipelineOnDocumentProcessed(MarkdownDocument document) + { + foreach (var node in document.Descendants()) + { + if (node is Block) + { + if (node is Table) + { + node.GetAttributes().AddClass("table table-striped table-bordered"); + } + else if (node is QuoteBlock) + { + node.GetAttributes().AddClass("blockquote"); + } + else if (node is Figure) + { + node.GetAttributes().AddClass("figure"); + } + else if (node is FigureCaption) + { + node.GetAttributes().AddClass("figure-caption"); + } + } + else if (node is Inline) + { + if (node is LinkInline link && link.IsImage) + { + if (!Uri.TryCreate(link.Url, UriKind.Absolute, out Uri uriLink)) + { + link.Url = link.Url.TrimStart('/', '\\'); + this.BaseUrl = this.BaseUrl.TrimEnd('/', '\\'); + uriLink = new Uri(Path.Combine(this.BaseUrl, link.Url)); + link.Url = uriLink.ToString(); + } + + link.GetAttributes().AddClass("img-fluid"); + } + } + } + } + } +} diff --git a/src/modules/previewpane/MarkDownPreviewHandler/MarkDownPreviewHandler.csproj b/src/modules/previewpane/MarkDownPreviewHandler/MarkDownPreviewHandler.csproj new file mode 100644 index 000000000000..9bf61acbf140 --- /dev/null +++ b/src/modules/previewpane/MarkDownPreviewHandler/MarkDownPreviewHandler.csproj @@ -0,0 +1,75 @@ + + + + + Debug + AnyCPU + {6A71162E-FC4C-4A2C-B90F-3CF94F59A9BB} + Library + Properties + MarkDownPreviewHandler + MarkDownPreviewHandler + v4.8 + 512 + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + true + bin/Debug/MarkdownPreviewPaneDocumentation.xml + x64 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + true + bin/Release/commonDocumentation.xml + x64 + + + + + + + + + + + + + + + + + + 0.18.0 + + + 1.1.118 + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + StyleCop.json + + + + + {af2349b8-e5b6-4004-9502-687c1c7730b1} + Common + + + + \ No newline at end of file diff --git a/src/modules/previewpane/MarkDownPreviewHandler/Properties/AssemblyInfo.cs b/src/modules/previewpane/MarkDownPreviewHandler/Properties/AssemblyInfo.cs new file mode 100644 index 000000000000..ce0cc72f073a --- /dev/null +++ b/src/modules/previewpane/MarkDownPreviewHandler/Properties/AssemblyInfo.cs @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation +// The Microsoft Corporation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("MarkDownPreviewHandler")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("MarkDownPreviewHandler")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Make assembly visible to UnitTest Project +[assembly: InternalsVisibleTo("PreviewPaneUnitTests")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("6a71162e-fc4c-4a2c-b90f-3cf94f59a9bb")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/modules/previewpane/PreviewPaneUnitTests/MarkDownPreviewHandlerHTMLParsingExtensionTest.cs b/src/modules/previewpane/PreviewPaneUnitTests/MarkDownPreviewHandlerHTMLParsingExtensionTest.cs new file mode 100644 index 000000000000..6cd979373ae6 --- /dev/null +++ b/src/modules/previewpane/PreviewPaneUnitTests/MarkDownPreviewHandlerHTMLParsingExtensionTest.cs @@ -0,0 +1,109 @@ +using System; +using Markdig; +using MarkDownPreviewHandler; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace PreviewPaneUnitTests +{ + [TestClass] + public class MarkDownPreviewHandlerHTMLParsingExtensionTest + { + public MarkdownPipeline BuidPipeline(IMarkdownExtension extension) + { + MarkdownPipelineBuilder pipelineBuilder = new MarkdownPipelineBuilder().UseAdvancedExtensions(); + pipelineBuilder.Extensions.Add(extension); + return pipelineBuilder.Build(); + } + + [TestMethod] + public void Extension_UpdatesTablesClass_WhenUsed() + { + // Arrange + String mdString = "| A | B |\n| -- | -- | "; + HTMLParsingExtension htmlParsingExtension = new HTMLParsingExtension(); + MarkdownPipeline markdownPipeline = BuidPipeline(htmlParsingExtension); + + // Act + String html = Markdown.ToHtml(mdString, markdownPipeline); + + // Assert + Assert.AreEqual(html, "\n\n\n\n\n\n\n
AB
\n"); + } + + + [TestMethod] + public void Extension_UpdatesBlockQuotesClass_WhenUsed() + { + // Arrange + String mdString = "> Blockquotes."; + HTMLParsingExtension htmlParsingExtension = new HTMLParsingExtension(); + MarkdownPipeline markdownPipeline = BuidPipeline(htmlParsingExtension); + + // Act + String html = Markdown.ToHtml(mdString, markdownPipeline); + + // Assert + Assert.AreEqual(html, "
\n

Blockquotes.

\n
\n"); + } + + [TestMethod] + public void Extension_UpdatesFigureClassAndRelativeUrltoAbsolute_WhenUsed() + { + // arrange + String mdString = "![text](a.jpg \"Figure\")"; + HTMLParsingExtension htmlParsingExtension = new HTMLParsingExtension("C:\\Users\\"); + MarkdownPipeline markdownPipeline = BuidPipeline(htmlParsingExtension); + + // Act + String html = Markdown.ToHtml(mdString, markdownPipeline); + + // Assert + Assert.AreEqual(html, "

\"text\"

\n"); + } + + [TestMethod] + public void Extension_CreatesCorrectAbsoluteLinkByTrimmingForwardSlash_WhenUsed() + { + // arrange + String mdString = "![text](\\document\\a.jpg \"Figure\")"; + HTMLParsingExtension htmlParsingExtension = new HTMLParsingExtension("C:\\Users\\"); + MarkdownPipeline markdownPipeline = BuidPipeline(htmlParsingExtension); + + // Act + String html = Markdown.ToHtml(mdString, markdownPipeline); + + // Assert + Assert.AreEqual(html, "

\"text\"

\n"); + } + + [TestMethod] + public void Extension_CreatesCorrectAbsoluteLinkByTrimmingBackwardSlash_WhenUsed() + { + // arrange + String mdString = "![text](/document/a.jpg \"Figure\")"; + HTMLParsingExtension htmlParsingExtension = new HTMLParsingExtension("C:/Users/"); + MarkdownPipeline markdownPipeline = BuidPipeline(htmlParsingExtension); + + // Act + String html = Markdown.ToHtml(mdString, markdownPipeline); + + // Assert + Assert.AreEqual(html, "

\"text\"

\n"); + } + + [TestMethod] + public void Extension_AddsClassToFigureCaption_WhenUsed() + { + // arrange + String mdString = "^^^ This is a caption"; + HTMLParsingExtension htmlParsingExtension = new HTMLParsingExtension("C:/Users/"); + MarkdownPipeline markdownPipeline = BuidPipeline(htmlParsingExtension); + + // Act + String html = Markdown.ToHtml(mdString, markdownPipeline); + + // Assert + Assert.AreEqual(html, "
\n
This is a caption
\n
\n"); + } + } +} diff --git a/src/modules/previewpane/PreviewPaneUnitTests/PreviewPaneUnitTests.csproj b/src/modules/previewpane/PreviewPaneUnitTests/PreviewPaneUnitTests.csproj new file mode 100644 index 000000000000..97adf7a55a8d --- /dev/null +++ b/src/modules/previewpane/PreviewPaneUnitTests/PreviewPaneUnitTests.csproj @@ -0,0 +1,69 @@ + + + + + Debug + AnyCPU + {A2B51B8B-8F90-424E-BC97-F9AB7D76CA1A} + Library + Properties + PreviewPaneUnitTests + PreviewPaneUnitTests + v4.8 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 15.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages + False + UnitTest + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + x64 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + x64 + + + + + + + + + + + + 0.18.0 + + + 1.3.2 + + + 1.3.2 + + + + + {6a71162e-fc4c-4a2c-b90f-3cf94f59a9bb} + MarkDownPreviewHandler + + + + + \ No newline at end of file diff --git a/src/modules/previewpane/PreviewPaneUnitTests/Properties/AssemblyInfo.cs b/src/modules/previewpane/PreviewPaneUnitTests/Properties/AssemblyInfo.cs new file mode 100644 index 000000000000..aef1d7b1d8e4 --- /dev/null +++ b/src/modules/previewpane/PreviewPaneUnitTests/Properties/AssemblyInfo.cs @@ -0,0 +1,20 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("PreviewPaneUnitTests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("PreviewPaneUnitTests")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +[assembly: ComVisible(false)] + +[assembly: Guid("a2b51b8b-8f90-424e-bc97-f9ab7d76ca1a")] + +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/modules/previewpane/common/Properties/AssemblyInfo.cs b/src/modules/previewpane/common/Properties/AssemblyInfo.cs index 23f260cae0ae..0afe1c322bbb 100644 --- a/src/modules/previewpane/common/Properties/AssemblyInfo.cs +++ b/src/modules/previewpane/common/Properties/AssemblyInfo.cs @@ -9,11 +9,11 @@ // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. -[assembly: AssemblyTitle("common")] +[assembly: AssemblyTitle("Common")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("common")] +[assembly: AssemblyProduct("Common")] [assembly: AssemblyCopyright("Copyright © 2020")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] diff --git a/src/modules/previewpane/common/common.csproj b/src/modules/previewpane/common/common.csproj index ccba0d677e41..f3763f5953d4 100644 --- a/src/modules/previewpane/common/common.csproj +++ b/src/modules/previewpane/common/common.csproj @@ -7,8 +7,8 @@ {AF2349B8-E5B6-4004-9502-687C1C7730B1} Library Properties - common - common + Common + Common v4.8 512 true