Skip to content
54 changes: 53 additions & 1 deletion src/MarkdownReader/CommandHelpMarkdownReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -963,7 +963,8 @@ internal static List<InputOutput> GetInputOutput(ParsedMarkdownContent md)
{
if (md.GetCurrent() is HeadingBlock inputOutputHeader)
{
string inputType = string.Join("", inputOutputHeader.Inline ?? []);
// Extract full text content from all inline elements to handle arrays and other complex types
string inputType = ExtractFullTextFromInline(inputOutputHeader?.Inline);
md.Take();
if (md.GetCurrent() is ParagraphBlock pBlock)
{
Expand All @@ -989,6 +990,57 @@ internal static List<InputOutput> GetInputOutput(ParsedMarkdownContent md)
return ioList;
}

/// <summary>
/// Extract the full text content from markdown inline elements,
/// handling square brackets and other special characters correctly.
/// </summary>
/// <param name="inline">The inline container to extract text from</param>
/// <returns>The full text content</returns>
private static string ExtractFullTextFromInline(ContainerInline? inline)
{
if (inline == null) return string.Empty;

StringBuilder sb = Constants.StringBuilderPool.Get();
ExtractTextRecursive(inline, sb);
try
{
return sb.ToString().Trim();
}
finally
{
Constants.StringBuilderPool.Return(sb);
}
}

/// <summary>
/// Recursively extract text from inline elements
/// </summary>
/// <param name="inline">The inline element to process</param>
/// <param name="sb">The StringBuilder to append text to</param>
private static void ExtractTextRecursive(Inline inline, StringBuilder sb)
{
switch (inline)
{
case LiteralInline literal:
sb.Append(literal.Content.ToString());
break;
case ContainerInline container:
foreach (var child in container)
{
ExtractTextRecursive(child, sb);
}
break;
default:
// For other inline types, try to get their text representation
var text = inline.ToString();
if (!string.IsNullOrEmpty(text))
{
sb.Append(text);
}
break;
}
}

internal static string GetParameterSetName(HeadingBlock parameterSetBlock)
{
if (parameterSetBlock is null)
Expand Down
15 changes: 8 additions & 7 deletions src/Transform/TransformBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ private List<InputOutput> GetOutputInfo(CommandInfo commandInfo, dynamic? helpIt
string outputName = outputType.Name;
if (outputName.EndsWith("[]"))
{
outputName = FixUpTypeName(outputName);
outputName = FixUpTypeName(outputName, preserveArrays: true);
}

if (!outputTypeNames.Contains(outputName))
Expand Down Expand Up @@ -811,7 +811,7 @@ protected List<InputOutput> GetInputOutputItemsFromHelp(dynamic typesInfo)
{
foreach (dynamic ioType in typesInfo)
{
string typeName = FixUpTypeName(ioType.type.name?.Split()?[0] ?? string.Empty);
string typeName = FixUpTypeName(ioType.type.name?.Split()?[0] ?? string.Empty, preserveArrays: true);
if (! string.IsNullOrEmpty(typeName) && string.Compare(typeName, "None", true) != 0)
{
string description = GetStringFromDescriptionArray(ioType.description)?.Trim() ?? string.Empty;
Expand All @@ -828,22 +828,22 @@ protected List<InputOutput> GetInputOutputItemsFromHelp(dynamic typesInfo)
// these are really multiple entries, so split them here.
if (name.IndexOf("\n") == -1 && string.Compare(name, "None", true) != 0)
{
itemList.Add(new InputOutput(FixUpTypeName(name), Constants.FillInDescription));
itemList.Add(new InputOutput(FixUpTypeName(name, preserveArrays: true), Constants.FillInDescription));
}
else
{
foreach(var tName in name.Replace("\\r","").Split('\n'))
{
if (string.Compare(tName, "None", true) != 0)
{
itemList.Add(new InputOutput(FixUpTypeName(tName), Constants.FillInDescription));
itemList.Add(new InputOutput(FixUpTypeName(tName, preserveArrays: true), Constants.FillInDescription));
}
}
}
}
else
{
string typeName = FixUpTypeName(ioTypes.type.name.ToString());
string typeName = FixUpTypeName(ioTypes.type.name.ToString(), preserveArrays: true);
if (! string.IsNullOrEmpty(typeName) && string.Compare(typeName, "None", true) != 0)
{
string description = GetStringFromDescriptionArray(ioTypes.description).Trim();
Expand All @@ -857,7 +857,8 @@ protected List<InputOutput> GetInputOutputItemsFromHelp(dynamic typesInfo)

// We have to remove carriage returns that might be present from help
// We also will remove trailing [] because we should generally return singletons
private string FixUpTypeName(string typename)
// For input/output types, preserve array notation as it's semantically important
private string FixUpTypeName(string typename, bool preserveArrays = false)
{
// If the type is a generic type, we need to remove the backtick and the number.
string fixedString = typename.Replace("System.Nullable`1[[", string.Empty).Trim();
Expand All @@ -867,7 +868,7 @@ private string FixUpTypeName(string typename)
fixedString = fixedString.Substring(0, commaIndex).Trim();
}

if (fixedString.EndsWith("[]"))
if (!preserveArrays && fixedString.EndsWith("[]"))
{
fixedString = fixedString.Remove(fixedString.Length - 2);
}
Expand Down
39 changes: 39 additions & 0 deletions test/Pester/NewMarkdownHelp.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,45 @@ Write-Host 'Hello World!'
}
}

Context 'Array type handling in pipeline parameters' {

BeforeAll {
function global:Test-ArrayPipelineFunction
{
[CmdletBinding()]
param(
[Parameter(Mandatory, ValueFromPipeline)]
[string[]]$ComputerName,

[Parameter()]
[int[]]$Numbers
)
}

$file = New-MarkdownCommandHelp -Command (Get-Command Test-ArrayPipelineFunction) -OutputFolder "$TestDrive/arrayTest" -Force
$content = Import-MarkdownCommandHelp $file
}

It 'should generate correct input type for string array parameter' {
$content.Inputs.Count | Should -Be 1
$content.Inputs[0].Typename | Should -Be 'System.String[]'
}

It 'should not duplicate array and element types in inputs' {
$inputTypes = $content.Inputs | ForEach-Object { $_.Typename }
$inputTypes | Should -Not -Contain 'System.String'
$inputTypes | Should -Contain 'System.String[]'
}

It 'should preserve array notation in markdown and parsing roundtrip' {
Update-MarkdownCommandHelp -Path $file -NoBackup
$updatedContent = Import-MarkdownCommandHelp $file

$updatedContent.Inputs.Count | Should -Be 1
$updatedContent.Inputs[0].Typename | Should -Be 'System.String[]'
}
}

Context 'Generated markdown features: no comment-based help' {
function global:Test-PlatyPSFunction
{
Expand Down