diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowMarkdownCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowMarkdownCommand.cs index 024e41d0620..35e157b74b0 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowMarkdownCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowMarkdownCommand.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; +using System.Globalization; using System.IO; using System.Management.Automation; using System.Management.Automation.Internal; @@ -20,6 +21,7 @@ namespace Microsoft.PowerShell.Commands /// [Cmdlet( VerbsCommon.Show, "Markdown", + DefaultParameterSetName = "Path", HelpUri = "https://go.microsoft.com/fwlink/?linkid=2006266")] [OutputType(typeof(string))] public class ShowMarkdownCommand : PSCmdlet @@ -28,28 +30,43 @@ public class ShowMarkdownCommand : PSCmdlet /// Gets or sets InputObject of type Microsoft.PowerShell.MarkdownRender.MarkdownInfo to display. /// [ValidateNotNullOrEmpty] - [Parameter(Mandatory = true, ValueFromPipeline = true)] + [Parameter(Mandatory = true, ValueFromPipeline = true, ParameterSetName = "InputObject")] public PSObject InputObject { get; set; } + /// + /// Gets or sets path to markdown file(s) to display. + /// + [ValidateNotNullOrEmpty] + [Parameter(Position = 0, Mandatory = true, + ValueFromPipelineByPropertyName = true, ParameterSetName = "Path")] + public string[] Path { get; set; } + + /// + /// Gets or sets the literal path parameter to markdown files(s) to display. + /// + [Parameter(ParameterSetName = "LiteralPath", + Mandatory = true, ValueFromPipeline = false, ValueFromPipelineByPropertyName = true)] + [Alias("PSPath", "LP")] + public string[] LiteralPath + { + get { return Path; } + set { Path = value; } + } + /// /// Gets or sets the switch to view Html in default browser. /// [Parameter] public SwitchParameter UseBrowser { get; set; } - private SteppablePipeline stepPipe; + private System.Management.Automation.PowerShell _powerShell; /// /// Override BeginProcessing. /// protected override void BeginProcessing() { - if (!UseBrowser.IsPresent) - { - // Since UseBrowser is not bound, we use proxy to Out-Default - stepPipe = ScriptBlock.Create(@"Microsoft.PowerShell.Core\Out-Default @PSBoundParameters").GetSteppablePipeline(this.MyInvocation.CommandOrigin); - stepPipe.Begin(this); - } + _powerShell = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace); } /// @@ -57,114 +74,148 @@ protected override void BeginProcessing() /// protected override void ProcessRecord() { - object inpObj = InputObject.BaseObject; + switch (ParameterSetName) + { + case "InputObject": + if (InputObject.BaseObject is MarkdownInfo markdownInfo) + { + ProcessMarkdownInfo(markdownInfo); + } + else + { + ConvertFromMarkdown("InputObject", InputObject.BaseObject); + } + + break; - if (inpObj is MarkdownInfo markdownInfo) + case "Path": + case "LiteralPath": + ConvertFromMarkdown(ParameterSetName, Path); + break; + + default: + throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, ConvertMarkdownStrings.InvalidParameterSet, ParameterSetName)); + } + } + + /// + /// Process markdown as path. + /// + /// Name of parameter to pass to `ConvertFrom-Markdown`. + /// Value of parameter. + private void ConvertFromMarkdown(string parameter, object input) + { + _powerShell.AddCommand("Microsoft.PowerShell.Utility\\ConvertFrom-Markdown").AddParameter(parameter, input); + if (!UseBrowser) { - if (UseBrowser) + _powerShell.AddParameter("AsVT100EncodedString"); + } + + Collection output = _powerShell.Invoke(); + + if (_powerShell.HadErrors) + { + foreach (ErrorRecord errorRecord in _powerShell.Streams.Error) { - var html = markdownInfo.Html; + WriteError(errorRecord); + } + } - if (!string.IsNullOrEmpty(html)) - { - string tmpFilePath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString() + ".html"); + foreach (MarkdownInfo result in output) + { + ProcessMarkdownInfo(result); + } + } - try - { - using (var writer = new StreamWriter(new FileStream(tmpFilePath, FileMode.Create, FileAccess.Write, FileShare.Write))) - { - writer.Write(html); - } - } - catch (Exception e) - { - var errorRecord = new ErrorRecord( - e, - "ErrorWritingTempFile", - ErrorCategory.WriteError, - tmpFilePath); - - WriteError(errorRecord); - return; - } + /// + /// Process markdown as input objects. + /// + /// Markdown object to process. + private void ProcessMarkdownInfo(MarkdownInfo markdownInfo) + { + if (UseBrowser) + { + var html = markdownInfo.Html; - if (InternalTestHooks.ShowMarkdownOutputBypass) - { - WriteObject(html); - return; - } + if (!string.IsNullOrEmpty(html)) + { + string tmpFilePath = System.IO.Path.Combine(System.IO.Path.GetTempPath(), Guid.NewGuid().ToString() + ".html"); - try - { - ProcessStartInfo startInfo = new ProcessStartInfo(); - startInfo.FileName = tmpFilePath; - startInfo.UseShellExecute = true; - Process.Start(startInfo); - } - catch (Exception e) + try + { + using (var writer = new StreamWriter(new FileStream(tmpFilePath, FileMode.Create, FileAccess.Write, FileShare.Write))) { - var errorRecord = new ErrorRecord( - e, - "ErrorLaunchingDefaultApplication", - ErrorCategory.InvalidOperation, - targetObject : null); - - WriteError(errorRecord); - return; + writer.Write(html); } } - else + catch (Exception e) { - string errorMessage = StringUtil.Format(ConvertMarkdownStrings.MarkdownInfoInvalid, "Html"); var errorRecord = new ErrorRecord( - new InvalidDataException(errorMessage), - "HtmlIsNullOrEmpty", - ErrorCategory.InvalidData, - html); + e, + "ErrorWritingTempFile", + ErrorCategory.WriteError, + tmpFilePath); WriteError(errorRecord); + return; } - } - else - { - var vt100String = markdownInfo.VT100EncodedString; - if (!string.IsNullOrEmpty(vt100String)) + if (InternalTestHooks.ShowMarkdownOutputBypass) { - if (InternalTestHooks.ShowMarkdownOutputBypass) - { - WriteObject(vt100String); - return; - } + WriteObject(html); + return; + } - if (stepPipe != null) - { - stepPipe.Process(vt100String); - } + try + { + ProcessStartInfo startInfo = new ProcessStartInfo(); + startInfo.FileName = tmpFilePath; + startInfo.UseShellExecute = true; + Process.Start(startInfo); } - else + catch (Exception e) { - string errorMessage = StringUtil.Format(ConvertMarkdownStrings.MarkdownInfoInvalid, "VT100EncodedString"); var errorRecord = new ErrorRecord( - new InvalidDataException(errorMessage), - "VT100EncodedStringIsNullOrEmpty", - ErrorCategory.InvalidData, - vt100String); + e, + "ErrorLaunchingDefaultApplication", + ErrorCategory.InvalidOperation, + targetObject: null); WriteError(errorRecord); + return; } } + else + { + string errorMessage = StringUtil.Format(ConvertMarkdownStrings.MarkdownInfoInvalid, "Html"); + var errorRecord = new ErrorRecord( + new InvalidDataException(errorMessage), + "HtmlIsNullOrEmpty", + ErrorCategory.InvalidData, + html); + + WriteError(errorRecord); + } } else { - string errorMessage = StringUtil.Format(ConvertMarkdownStrings.InvalidInputObjectType, inpObj.GetType()); - var errorRecord = new ErrorRecord( - new ArgumentException(errorMessage), - "InvalidInputObject", - ErrorCategory.InvalidArgument, - InputObject); - - WriteError(errorRecord); + var vt100String = markdownInfo.VT100EncodedString; + + if (!string.IsNullOrEmpty(vt100String)) + { + WriteObject(vt100String); + } + else + { + string errorMessage = StringUtil.Format(ConvertMarkdownStrings.MarkdownInfoInvalid, "VT100EncodedString"); + var errorRecord = new ErrorRecord( + new InvalidDataException(errorMessage), + "VT100EncodedStringIsNullOrEmpty", + ErrorCategory.InvalidData, + vt100String); + + WriteError(errorRecord); + } } } @@ -173,9 +224,9 @@ protected override void ProcessRecord() /// protected override void EndProcessing() { - if (stepPipe != null) + if (_powerShell != null) { - stepPipe.End(); + _powerShell.Dispose(); } } } diff --git a/src/Microsoft.PowerShell.Commands.Utility/resources/ConvertMarkdownStrings.resx b/src/Microsoft.PowerShell.Commands.Utility/resources/ConvertMarkdownStrings.resx index 14d745ee5f9..d37509d0f50 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/resources/ConvertMarkdownStrings.resx +++ b/src/Microsoft.PowerShell.Commands.Utility/resources/ConvertMarkdownStrings.resx @@ -129,4 +129,7 @@ The property {0} of the given object is null or empty. + + Invalid parameter set name: {0}. + diff --git a/test/powershell/Modules/Microsoft.PowerShell.Utility/MarkdownCmdlets.Tests.ps1 b/test/powershell/Modules/Microsoft.PowerShell.Utility/MarkdownCmdlets.Tests.ps1 index 246909b0deb..1d6d5fd4935 100644 --- a/test/powershell/Modules/Microsoft.PowerShell.Utility/MarkdownCmdlets.Tests.ps1 +++ b/test/powershell/Modules/Microsoft.PowerShell.Utility/MarkdownCmdlets.Tests.ps1 @@ -448,7 +448,7 @@ bool function()`n{`n} [System.Management.Automation.Internal.InternalTestHooks]::SetTestHook("ShowMarkdownOutputBypass", $false) } - It "can show VT100 converted from markdown" { + It "Can show VT100 converted from markdown" { $text = "Bold" $mdText = "**$text**" $expectedString = GetExpectedString -ElementType 'Bold' -Text $text -VT100Support $true @@ -457,7 +457,7 @@ bool function()`n{`n} $result | Should -BeExactly $expectedString } - It "can show HTML converted from markdown" { + It "Can show HTML converted from markdown" { $text = "Bold" $mdText = "**$text**" $expectedString = GetExpectedHTML -ElementType 'Bold' -Text $text @@ -466,7 +466,45 @@ bool function()`n{`n} $result | Should -BeExactly $expectedString } - It "Gets an error if the input object is missing the property." -TestCases @(@{propertyname = 'Html'}, @{propertyname = 'VT100EncodedString'}) { + It "Markdown files work with cmdlet: " -TestCases @( + @{ pathParam = "Path" } + @{ pathParam = "LiteralPath" } + ) { + param($pathParam) + + $text = "Header" + $mdText = "# $text" + $expectedString = GetExpectedString -ElementType Header1 -Text $text -VT100Support $true + $mdFile = Join-Path $TestDrive "test.md" + Set-Content -Path $mdFile -Value $mdText + + $params = @{ $pathParam = $mdFile } + $result = Show-Markdown @params + $result | Should -BeExactly $expectedString + } + + It "Can show markdown piped directly to cmdlet" { + $text = "Header" + $mdText = "# $text" + $expectedString = GetExpectedString -ElementType Header1 -Text $text -VT100Support $true + + $result = $mdText | Show-Markdown + $result | Should -BeExactly $expectedString + } + + It "Can show markdown piped directly to cmdlet as HTML" { + $text = "Header" + $mdText = "# $text" + $expectedString = GetExpectedHTML -ElementType Header1 -Text $text + + $result = $mdText | Show-Markdown -UseBrowser + $result | Should -BeExactly $expectedString + } + + It "Gets an error if the input object is missing the property." -TestCases @( + @{ propertyname = 'Html' } + @{ propertyname = 'VT100EncodedString' } + ) { param($propertyname) $markdownInfo = [Microsoft.PowerShell.MarkdownRender.MarkdownInfo]::new()