From aec954e3edf59ec5b30e33049d381196783b1555 Mon Sep 17 00:00:00 2001 From: Aditya Patwardhan Date: Sun, 26 Aug 2018 23:58:19 -0700 Subject: [PATCH] Fix null reference when markdown content is empty (#7463) --- .../FencedCodeBlockRenderer.cs | 30 +++++----- .../HeaderBlockRenderer.cs | 57 ++++++++++--------- .../LinkInlineRenderer.cs | 6 +- .../MarkdownCmdlets.Tests.ps1 | 29 +++++++++- 4 files changed, 80 insertions(+), 42 deletions(-) diff --git a/src/Microsoft.PowerShell.MarkdownRender/FencedCodeBlockRenderer.cs b/src/Microsoft.PowerShell.MarkdownRender/FencedCodeBlockRenderer.cs index 8926f226a3..c90b9b1b48 100644 --- a/src/Microsoft.PowerShell.MarkdownRender/FencedCodeBlockRenderer.cs +++ b/src/Microsoft.PowerShell.MarkdownRender/FencedCodeBlockRenderer.cs @@ -4,6 +4,7 @@ using System; using System.IO; using Markdig; +using Markdig.Helpers; using Markdig.Renderers; using Markdig.Syntax; @@ -16,25 +17,28 @@ namespace Microsoft.PowerShell.MarkdownRender { protected override void Write(VT100Renderer renderer, FencedCodeBlock obj) { - foreach (var codeLine in obj.Lines.Lines) + if (obj?.Lines.Lines != null) { - if (!string.IsNullOrWhiteSpace(codeLine.ToString())) + foreach (StringLine codeLine in obj.Lines.Lines) { - // If the code block is of type YAML, then tab to right to improve readability. - // This specifically helps for parameters help content. - if (string.Equals(obj.Info, "yaml", StringComparison.OrdinalIgnoreCase)) + if (!string.IsNullOrWhiteSpace(codeLine.ToString())) { - renderer.Write("\t").WriteLine(codeLine.ToString()); - } - else - { - renderer.WriteLine(renderer.EscapeSequences.FormatCode(codeLine.ToString(), isInline: false)); + // If the code block is of type YAML, then tab to right to improve readability. + // This specifically helps for parameters help content. + if (string.Equals(obj.Info, "yaml", StringComparison.OrdinalIgnoreCase)) + { + renderer.Write("\t").WriteLine(codeLine.ToString()); + } + else + { + renderer.WriteLine(renderer.EscapeSequences.FormatCode(codeLine.ToString(), isInline: false)); + } } } - } - // Add a blank line after the code block for better readability. - renderer.WriteLine(); + // Add a blank line after the code block for better readability. + renderer.WriteLine(); + } } } } diff --git a/src/Microsoft.PowerShell.MarkdownRender/HeaderBlockRenderer.cs b/src/Microsoft.PowerShell.MarkdownRender/HeaderBlockRenderer.cs index 4aa78c6aea..3c61b8c5e0 100644 --- a/src/Microsoft.PowerShell.MarkdownRender/HeaderBlockRenderer.cs +++ b/src/Microsoft.PowerShell.MarkdownRender/HeaderBlockRenderer.cs @@ -16,38 +16,43 @@ namespace Microsoft.PowerShell.MarkdownRender { protected override void Write(VT100Renderer renderer, HeadingBlock obj) { - // Format header and then add blank line to improve readability. - switch (obj.Level) + string headerText = obj?.Inline?.FirstChild?.ToString(); + + if (!string.IsNullOrEmpty(headerText)) { - case 1: - renderer.WriteLine(renderer.EscapeSequences.FormatHeader1(obj.Inline.FirstChild.ToString())); - renderer.WriteLine(); - break; + // Format header and then add blank line to improve readability. + switch (obj.Level) + { + case 1: + renderer.WriteLine(renderer.EscapeSequences.FormatHeader1(headerText)); + renderer.WriteLine(); + break; - case 2: - renderer.WriteLine(renderer.EscapeSequences.FormatHeader2(obj.Inline.FirstChild.ToString())); - renderer.WriteLine(); - break; + case 2: + renderer.WriteLine(renderer.EscapeSequences.FormatHeader2(headerText)); + renderer.WriteLine(); + break; - case 3: - renderer.WriteLine(renderer.EscapeSequences.FormatHeader3(obj.Inline.FirstChild.ToString())); - renderer.WriteLine(); - break; + case 3: + renderer.WriteLine(renderer.EscapeSequences.FormatHeader3(headerText)); + renderer.WriteLine(); + break; - case 4: - renderer.WriteLine(renderer.EscapeSequences.FormatHeader4(obj.Inline.FirstChild.ToString())); - renderer.WriteLine(); - break; + case 4: + renderer.WriteLine(renderer.EscapeSequences.FormatHeader4(headerText)); + renderer.WriteLine(); + break; - case 5: - renderer.WriteLine(renderer.EscapeSequences.FormatHeader5(obj.Inline.FirstChild.ToString())); - renderer.WriteLine(); - break; + case 5: + renderer.WriteLine(renderer.EscapeSequences.FormatHeader5(headerText)); + renderer.WriteLine(); + break; - case 6: - renderer.WriteLine(renderer.EscapeSequences.FormatHeader6(obj.Inline.FirstChild.ToString())); - renderer.WriteLine(); - break; + case 6: + renderer.WriteLine(renderer.EscapeSequences.FormatHeader6(headerText)); + renderer.WriteLine(); + break; + } } } } diff --git a/src/Microsoft.PowerShell.MarkdownRender/LinkInlineRenderer.cs b/src/Microsoft.PowerShell.MarkdownRender/LinkInlineRenderer.cs index 975efd519f..8bc8a2e952 100644 --- a/src/Microsoft.PowerShell.MarkdownRender/LinkInlineRenderer.cs +++ b/src/Microsoft.PowerShell.MarkdownRender/LinkInlineRenderer.cs @@ -16,14 +16,16 @@ namespace Microsoft.PowerShell.MarkdownRender { protected override void Write(VT100Renderer renderer, LinkInline obj) { + string text = obj.FirstChild?.ToString(); + // Format link as image or link. if (obj.IsImage) { - renderer.Write(renderer.EscapeSequences.FormatImage(obj.FirstChild.ToString())); + renderer.Write(renderer.EscapeSequences.FormatImage(text)); } else { - renderer.Write(renderer.EscapeSequences.FormatLink(obj.FirstChild.ToString(), obj.Url)); + renderer.Write(renderer.EscapeSequences.FormatLink(text, obj.Url)); } } } diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/MarkdownCmdlets.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/MarkdownCmdlets.Tests.ps1 index 1d6d5fd493..bbaf412d3f 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/MarkdownCmdlets.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/MarkdownCmdlets.Tests.ps1 @@ -123,7 +123,6 @@ Describe 'ConvertFrom-Markdown tests' -Tags 'CI' { Context 'Basic tests' { BeforeAll { - $esc = [char]0x1b $mdFile = New-Item -Path $TestDrive/input.md -Value "Some **test string** to write in a file" -Force $mdLiteralPath = New-Item -Path $TestDrive/LiteralPath.md -Value "Some **test string** to write in a file" -Force $expectedStringFromFile = "Some $esc[1mtest string$esc[0m to write in a file`n`n" @@ -280,6 +279,34 @@ bool function()`n{`n} } } + Context "ConvertFrom-Markdown empty or null content tests" { + BeforeAll { + $codeBlock = @' +```CSharp +``` +'@ + + $testCases = @( + @{Type = "CodeBlock"; Markdown = "$codeBlock"; ExpectedOutput = ''} + @{Type = "Header1"; Markdown = "# "; ExpectedOutput = ''} + @{Type = "Header2"; Markdown = "## "; ExpectedOutput = ''} + @{Type = "Header3"; Markdown = "### "; ExpectedOutput = ''} + @{Type = "Header4"; Markdown = "#### "; ExpectedOutput = ''} + @{Type = "Header5"; Markdown = "##### "; ExpectedOutput = ''} + @{Type = "Header6"; Markdown = "###### "; ExpectedOutput = ''} + @{Type = "Image"; Markdown = "'![]()'"; ExpectedOutput = "'$esc[33m[Image]$esc[0m'"} + @{Type = "Link"; Markdown = "'[]()'"; ExpectedOutput = "'$esc[4;38;5;117m`"`"$esc[0m'"} + ) + } + + It "No error if thrown when empty content is provided for mardown element : " -TestCases $testCases { + param($Type, $Markdown, $ExpectedOutput) + + $resultObj = ConvertFrom-Markdown -InputObject $Markdown -AsVT100EncodedString + $resultObj.VT100EncodedString.Trim() | Should -BeExactly $ExpectedOutput + } + } + Context "Get/Set-MarkdownOption tests" { BeforeAll {