Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -20,6 +21,7 @@ namespace Microsoft.PowerShell.Commands
/// </summary>
[Cmdlet(
VerbsCommon.Show, "Markdown",
DefaultParameterSetName = "Path",
HelpUri = "https://go.microsoft.com/fwlink/?linkid=2006266")]
[OutputType(typeof(string))]
public class ShowMarkdownCommand : PSCmdlet
Expand All @@ -28,143 +30,192 @@ public class ShowMarkdownCommand : PSCmdlet
/// Gets or sets InputObject of type Microsoft.PowerShell.MarkdownRender.MarkdownInfo to display.
/// </summary>
[ValidateNotNullOrEmpty]
[Parameter(Mandatory = true, ValueFromPipeline = true)]
[Parameter(Mandatory = true, ValueFromPipeline = true, ParameterSetName = "InputObject")]
public PSObject InputObject { get; set; }

/// <summary>
/// Gets or sets path to markdown file(s) to display.
/// </summary>
[ValidateNotNullOrEmpty]
[Parameter(Position = 0, Mandatory = true,
ValueFromPipelineByPropertyName = true, ParameterSetName = "Path")]
public string[] Path { get; set; }

/// <summary>
/// Gets or sets the literal path parameter to markdown files(s) to display.
/// </summary>
[Parameter(ParameterSetName = "LiteralPath",
Mandatory = true, ValueFromPipeline = false, ValueFromPipelineByPropertyName = true)]
[Alias("PSPath", "LP")]
public string[] LiteralPath
{
get { return Path; }
set { Path = value; }
}

/// <summary>
/// Gets or sets the switch to view Html in default browser.
/// </summary>
[Parameter]
public SwitchParameter UseBrowser { get; set; }

private SteppablePipeline stepPipe;
private System.Management.Automation.PowerShell _powerShell;

/// <summary>
/// Override BeginProcessing.
/// </summary>
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);
}

/// <summary>
/// Override ProcessRecord.
/// </summary>
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));
}
}

/// <summary>
/// Process markdown as path.
/// </summary>
/// <param name="parameter">Name of parameter to pass to `ConvertFrom-Markdown`.</param>
/// <param name="input">Value of parameter.</param>
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<MarkdownInfo> output = _powerShell.Invoke<MarkdownInfo>();

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;
}
/// <summary>
/// Process markdown as input objects.
/// </summary>
/// <param name="markdownInfo">Markdown object to process.</param>
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());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ConvertMarkdownStrings.InvalidInputObjectType

I guess this resource string can be removed now.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That string is still used by ConvertFrom-Markdown and Set-MarkdownOption cmdlets

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);
}
}
}

Expand All @@ -173,9 +224,9 @@ protected override void ProcessRecord()
/// </summary>
protected override void EndProcessing()
{
if (stepPipe != null)
if (_powerShell != null)
{
stepPipe.End();
_powerShell.Dispose();
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -129,4 +129,7 @@
<data name="MarkdownInfoInvalid" xml:space="preserve">
<value>The property {0} of the given object is null or empty.</value>
</data>
<data name="InvalidParameterSet" xml:space="preserve">
<value>Invalid parameter set name: {0}.</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -466,7 +466,45 @@ bool function()`n{`n}
$result | Should -BeExactly $expectedString
}

It "Gets an error if the input object is missing the <propertyname> property." -TestCases @(@{propertyname = 'Html'}, @{propertyname = 'VT100EncodedString'}) {
It "Markdown files work with cmdlet: <pathParam>" -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 <propertyname> property." -TestCases @(
@{ propertyname = 'Html' }
@{ propertyname = 'VT100EncodedString' }
) {
param($propertyname)

$markdownInfo = [Microsoft.PowerShell.MarkdownRender.MarkdownInfo]::new()
Expand Down